| 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 |
| } |