blob: 9c94e759430772f3086bb039ad5b1b619bb44874 [file] [log] [blame]
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
}