diff --git a/baked_in.go b/baked_in.go index 1241f35..cbefb30 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[len(val)-2] != '.' { + val = strings.TrimRight(val, ".") + } + + return hostnameRegex.MatchString(val) && + (strings.IndexAny(val, ".") > -1) +} diff --git a/doc.go b/doc.go index 7da5e59..77e35fe 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 stringa 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..d8d8a22 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-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-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..444fb07 100644 --- a/validator_test.go +++ b/validator_test.go @@ -7119,3 +7119,96 @@ 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}, + {"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) + } + } + } + } +}