From f34a73f8a6bae122ee14c2cc84068b3d711d0e30 Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Tue, 29 Oct 2013 20:40:47 -0700 Subject: [PATCH] Domain validator + tests (unwired) --- smtpd/utils.go | 51 +++++++++++++++++++++++++++++++++++++++++++++ smtpd/utils_test.go | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/smtpd/utils.go b/smtpd/utils.go index e0d93a8..0e4c2e3 100644 --- a/smtpd/utils.go +++ b/smtpd/utils.go @@ -38,3 +38,54 @@ func JoinStringList(listOfStrings *list.List) string { } return strings.Join(s, ",") } + +// ValidateDomainPart returns true if the domain part complies to RFC3696, RFC1035 +func ValidateDomainPart(domain string) bool { + if len(domain) == 0 { + return false + } + if len(domain) > 255 { + return false + } + if domain[len(domain)-1] != '.' { + domain += "." + } + prev := '.' + labelLen := 0 + hasLetters := false + + for _, c := range domain { + switch { + case ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_': + // Must contain some of these to be a valid label + hasLetters = true + labelLen++ + case '0' <= c && c <= '9': + labelLen++ + case c == '-': + if prev == '.' { + // Cannot lead with hyphen + return false + } + case c == '.': + if prev == '.' || prev == '-' { + // Cannot end with hyphen or double-dot + return false + } + if labelLen > 63 { + return false + } + if !hasLetters { + return false + } + labelLen = 0 + hasLetters = false + default: + // Unknown character + return false + } + prev = c + } + + return true +} diff --git a/smtpd/utils_test.go b/smtpd/utils_test.go index 0c2d198..cc30d8f 100644 --- a/smtpd/utils_test.go +++ b/smtpd/utils_test.go @@ -2,6 +2,7 @@ package smtpd import ( "github.com/stretchr/testify/assert" + "strings" "testing" ) @@ -14,3 +15,40 @@ func TestParseMailboxName(t *testing.T) { func TestHashMailboxName(t *testing.T) { assert.Equal(t, HashMailboxName("mail"), "1d6e1cf70ec6f9ab28d3ea4b27a49a77654d370e") } + +func TestValidateDomain(t *testing.T) { + assert.True(t, ValidateDomainPart("jhillyerd.github.com"), + "Simple domain failed") + assert.False(t, ValidateDomainPart(""), "Empty domain is not valid") + assert.False(t, ValidateDomainPart(strings.Repeat("a", 256)), + "Max domain length is 255") + assert.False(t, ValidateDomainPart(strings.Repeat("a", 64)+".com"), + "Max label length is 63") + assert.True(t, ValidateDomainPart(strings.Repeat("a", 63)+".com"), + "Should allow 63 char label") + + var testTable = []struct { + input string + expect bool + msg string + }{ + {"hostname", true, "Just a hostname is valid"}, + {"github.com", true, "Two labels should be just fine"}, + {"my-domain.com", true, "Hyphen is allowed mid-label"}, + {"_domainkey.foo.com", true, "Underscores are allowed"}, + {"bar.com.", true, "Must be able to end with a dot"}, + {"ABC.6DBS.com", true, "Mixed case is OK"}, + {"google..com", false, "Double dot not valid"}, + {".foo.com", false, "Cannot start with a dot"}, + {"mail.123.com", false, "Number only label not valid"}, + {"google\r.com", false, "Special chars not allowed"}, + {"foo.-bar.com", false, "Label cannot start with hyphen"}, + {"foo-.bar.com", false, "Label cannot end with hyphen"}, + } + + for _, tt := range testTable { + if ValidateDomainPart(tt.input) != tt.expect { + t.Errorf("Expected %v for %q: %s", tt.expect, tt.input, tt.msg) + } + } +}