diff --git a/baked_in.go b/baked_in.go index 1241f35..7ea3ec3 100644 --- a/baked_in.go +++ b/baked_in.go @@ -125,6 +125,8 @@ var ( "ip_addr": isIPAddrResolvable, "unix_addr": isUnixAddrResolvable, "mac": isMAC, + "hostname": isHostname, + "fqdn": isFQDN, } ) @@ -1477,3 +1479,18 @@ func isIP6Addr(fl FieldLevel) bool { return ip != nil && ip.To4() == nil } + +func isHostname(fl FieldLevel) bool { + return hostnameRegex.MatchString(fl.Field().String()) +} + +func isFQDN(fl FieldLevel) bool { + val := fl.Field().String() + + if val[len(val)-1] == '.' { + val = val[0 : len(val)-1] + } + + return (strings.IndexAny(val, ".") > -1) && + hostnameRegex.MatchString(val) +} diff --git a/doc.go b/doc.go index 7da5e59..d7f3606 100644 --- a/doc.go +++ b/doc.go @@ -794,6 +794,18 @@ Note: See Go's ParseMAC for accepted formats and types: http://golang.org/src/net/mac.go?s=866:918#L29 +Hostname + +This validates that a string value is a valid Hostname + + Usage: hostname + +Full Qualified Domain Name (FQDN) + +This validates that a string value contains a valid FQDN. + + Usage: fqdn + Alias Validators and Tags NOTE: When returning an error, the tag returned in "FieldError" will be diff --git a/regexes.go b/regexes.go index c2d9830..ec78ceb 100644 --- a/regexes.go +++ b/regexes.go @@ -30,6 +30,7 @@ const ( latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" sSNRegexString = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` + hostnameRegexString = `^[a-zA-Z][a-zA-Z0-9\-\.]+[a-z-Az0-9]$` ) var ( @@ -60,4 +61,5 @@ var ( latitudeRegex = regexp.MustCompile(latitudeRegexString) longitudeRegex = regexp.MustCompile(longitudeRegexString) sSNRegex = regexp.MustCompile(sSNRegexString) + hostnameRegex = regexp.MustCompile(hostnameRegexString) ) diff --git a/validator_test.go b/validator_test.go index 565e23d..26e4e17 100644 --- a/validator_test.go +++ b/validator_test.go @@ -7119,3 +7119,97 @@ func TestValidateStructRegisterCtx(t *testing.T) { Equal(t, ctxVal, "testval") Equal(t, ctxSlVal, "slVal") } + +func TestHostnameValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"test.example.com", true}, + {"example.com", true}, + {"example24.com", true}, + {"test.example24.com", true}, + {"test24.example24.com", true}, + {"example", true}, + {"test.example.com.", false}, + {"example.com.", false}, + {"example24.com.", false}, + {"test.example24.com.", false}, + {"test24.example24.com.", false}, + {"example.", false}, + {"192.168.0.1", false}, + {"email@example.com", false}, + {"2001:cdba:0000:0000:0000:0000:3257:9652", false}, + {"2001:cdba:0:0:0:0:3257:9652", false}, + {"2001:cdba::3257:9652", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "hostname") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d hostname failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d hostname failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "hostname" { + t.Fatalf("Index: %d hostname failed Error: %s", i, errs) + } + } + } + } +} + +func TestFQDNValidation(t *testing.T) { + tests := []struct { + param string + expected bool + }{ + {"test.example.com", true}, + {"example.com", true}, + {"example24.com", true}, + {"test.example24.com", true}, + {"test24.example24.com", true}, + {"test.example.com.", true}, + {"example.com.", true}, + {"example24.com.", true}, + {"test.example24.com.", true}, + {"test24.example24.com.", true}, + {"test24.example24.com..", false}, + {"example", false}, + {"192.168.0.1", false}, + {"email@example.com", false}, + {"2001:cdba:0000:0000:0000:0000:3257:9652", false}, + {"2001:cdba:0:0:0:0:3257:9652", false}, + {"2001:cdba::3257:9652", false}, + } + + validate := New() + + for i, test := range tests { + + errs := validate.Var(test.param, "fqdn") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Index: %d fqdn failed Error: %s", i, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Index: %d fqdn failed Error: %s", i, errs) + } else { + val := getError(errs, "", "") + if val.Tag() != "fqdn" { + t.Fatalf("Index: %d fqdn failed Error: %s", i, errs) + } + } + } + } +}