cluster/identd/ident: add basic ident protocol server
This adds an ident protocol server and tests for it.
Change-Id: I830f85faa7dce4220bd7001635b20e88b4a8b417
diff --git a/cluster/identd/ident/response.go b/cluster/identd/ident/response.go
index 5eab431..f54b728 100644
--- a/cluster/identd/ident/response.go
+++ b/cluster/identd/ident/response.go
@@ -7,17 +7,6 @@
"strings"
)
-var (
- // reErrorReply matches error-reply from RFC1413, but also allows extra
- // whitespace between significant tokens. It does not ensure that the
- // error-type is one of the standardized values.
- reErrorReply = regexp.MustCompile(`^\s*(\d{1,5})\s*,\s*(\d{1,5})\s*:\s*ERROR\s*:\s*(.+)$`)
- // reIdentReply matches ident-reply from RFC1413, but also allows extra
- // whitespace between significant tokens. It does not ensure that that
- // opsys-field and user-id parts are RFC compliant.
- reIdentReply = regexp.MustCompile(`^\s*(\d{1,5})\s*,\s*(\d{1,5})\s*:\s*USERID\s*:\s*([^:,]+)(,([^:]+))?\s*:(.+)$`)
-)
-
// Response is an ident protocol response, as seen by the client or server.
type Response struct {
// ClientPort is the port number on the client side of the indent protocol,
@@ -100,6 +89,47 @@
UserID string
}
+// encode encodes the given Response. If the Response is unencodable/malformed,
+// nil is returned.
+func (r *Response) encode() []byte {
+ // Both Error and Ident cannot be set at once.
+ if r.Error != "" && r.Ident != nil {
+ return nil
+ }
+
+ if r.Error != "" {
+ if !r.Error.IsError() {
+ return nil
+ }
+ return []byte(fmt.Sprintf("%d,%d:ERROR:%s\r\n", r.ServerPort, r.ClientPort, r.Error))
+ }
+ if r.Ident != nil {
+ id := r.Ident
+ os := id.OperatingSystem
+ if os == "" {
+ return nil
+ }
+ // For compatibility, do not set US-ASCII explicitly.
+ if id.CharacterSet != "" && id.CharacterSet != "US-ASCII" {
+ os += "," + id.CharacterSet
+ }
+ return []byte(fmt.Sprintf("%d,%d:USERID:%s:%s\r\n", r.ServerPort, r.ClientPort, os, id.UserID))
+ }
+ // Malformed response, return nil.
+ return nil
+}
+
+var (
+ // reErrorReply matches error-reply from RFC1413, but also allows extra
+ // whitespace between significant tokens. It does not ensure that the
+ // error-type is one of the standardized values.
+ reErrorReply = regexp.MustCompile(`^\s*(\d{1,5})\s*,\s*(\d{1,5})\s*:\s*ERROR\s*:\s*(.+)$`)
+ // reIdentReply matches ident-reply from RFC1413, but also allows extra
+ // whitespace between significant tokens. It does not ensure that that
+ // opsys-field and user-id parts are RFC compliant.
+ reIdentReply = regexp.MustCompile(`^\s*(\d{1,5})\s*,\s*(\d{1,5})\s*:\s*USERID\s*:\s*([^:,]+)(,([^:]+))?\s*:(.+)$`)
+)
+
// decodeResponse parses the given bytes as an ident response. The data must be
// stripped of the trailing \r\n.
func decodeResponse(data []byte) (*Response, error) {