package ident import ( "fmt" "net" "regexp" "strconv" ) // Request is an ident protocol request, as seen by the client or server. type Request struct { // ClientPort is the port number on the client side of the indent protocol, // ie. the port local to the ident client. ClientPort uint16 // ServerPort is the port number on the server side of the ident protocol, // ie. the port local to the ident server. ServerPort uint16 // ClientAddress is the address of the ident protocol client. This is set // by the ident Server before invoking handlers, and is ignored by the // ident protocol Client. // In handlers this can be used to ensure that responses are only returned // to clients who are running on the remote side of the connection that // they are querying about. ClientAddress net.Addr } // encode encodes ths Request as per RFC1413, including the terminating \r\n. func (r *Request) encode() []byte { return []byte(fmt.Sprintf("%d,%d\r\n", r.ServerPort, r.ClientPort)) } var ( // reRequest matches request from RFC1413, but allows extra whitespace // between significant tokens. reRequest = regexp.MustCompile(`^\s*(\d{1,5})\s*,\s*(\d{1,5})\s*$`) ) // decodeRequest parses the given bytes as an ident request. The data must be // stripped of the trailing \r\n. func decodeRequest(data []byte) (*Request, error) { match := reRequest.FindStringSubmatch(string(data)) if match == nil { return nil, fmt.Errorf("unparseable request") } serverPort, err := strconv.ParseUint(match[1], 10, 16) if err != nil { return nil, fmt.Errorf("invalid server port: %w", err) } clientPort, err := strconv.ParseUint(match[2], 10, 16) if err != nil { return nil, fmt.Errorf("invalid client port: %w", err) } return &Request{ ClientPort: uint16(clientPort), ServerPort: uint16(serverPort), }, nil }