|
|
@@ -4,6 +4,7 @@ import (
|
|
|
"context"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
+ "net"
|
|
|
"net/http"
|
|
|
"net/mail"
|
|
|
"strings"
|
|
|
@@ -74,20 +75,15 @@ func ValidLength(vMin, vMax int, err error) ValidatorFunc {
|
|
|
}
|
|
|
|
|
|
/* ValidFieldIn verifies if item is within the list of items */
|
|
|
-func ValidFieldIn(list []string) ValidatorFunc {
|
|
|
- var EInvalidValue = errors.New(
|
|
|
- fmt.Sprintf(
|
|
|
- "field value must be one of: %s",
|
|
|
- strings.Join(list, ","),
|
|
|
- ),
|
|
|
- )
|
|
|
+func ValidFieldIn(list []string, err error) ValidatorFunc {
|
|
|
+ EInvalidValue := fmt.Errorf("field value must be one of: %s", strings.Join(list, ","))
|
|
|
return func(field *FormField, ctx context.Context) error {
|
|
|
for _, item := range list {
|
|
|
if item == field.GetString() {
|
|
|
return nil
|
|
|
}
|
|
|
}
|
|
|
- return EInvalidValue
|
|
|
+ return fallbackError(err, EInvalidValue)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -101,16 +97,11 @@ func ValidInt(field *FormField, ctx context.Context) error {
|
|
|
}
|
|
|
|
|
|
/* ValidBetween makes sure that field is integer value within the specified range. */
|
|
|
-func ValidBetween(min, max int) ValidatorFunc {
|
|
|
- var EInvalidInterval = errors.New(
|
|
|
- fmt.Sprintf("must be integer between %d and %d", min, max))
|
|
|
+func ValidBetween(minV, maxV int, err error) ValidatorFunc {
|
|
|
+ var EInvalidInterval = fmt.Errorf("must be integer between %d and %d", minV, maxV)
|
|
|
return func(field *FormField, ctx context.Context) error {
|
|
|
- value, err := field.GetInt()
|
|
|
- if err != nil {
|
|
|
- return EInvalidInteger
|
|
|
- }
|
|
|
- if value < min || value > max {
|
|
|
- return EInvalidInterval
|
|
|
+ if value, err := field.GetInt(); err != nil || (value < minV || value > maxV) {
|
|
|
+ return fallbackError(err, EInvalidInterval)
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
@@ -163,50 +154,65 @@ func ValidExact(text string, err error) ValidatorFunc {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/* validDomainNameStr performs checks if the domain name stored in *FormField is valid */
|
|
|
-func validDomainNameStr(err error) func(string) error {
|
|
|
- EInvalidDomain := errors.New("invalid domain name")
|
|
|
- return func(domain string) error {
|
|
|
- domain = strings.ToLower(strings.TrimSpace(domain))
|
|
|
- domainLen := len(domain)
|
|
|
- /* common domain name checks */
|
|
|
- if domainLen == 0 || domainLen > 253 || strings.ContainsAny(domain, " \t\r\n") {
|
|
|
- return fallbackError(err, EInvalidDomain)
|
|
|
+/* validHostnameStr performs checks if the hostname name stored in *FormField is valid */
|
|
|
+func validHostnameStr(minLevel int, err error) func(string) error {
|
|
|
+ EInvalidHostname := errors.New("invalid hostname")
|
|
|
+ return func(hostname string) error {
|
|
|
+ hostname = strings.ToLower(strings.TrimSpace(hostname))
|
|
|
+ hostnameLen := len(hostname)
|
|
|
+ /* common hostname name checks */
|
|
|
+ if hostnameLen == 0 || hostnameLen > 253 || strings.ContainsAny(hostname, " \t\r\n") {
|
|
|
+ return fallbackError(err, EInvalidHostname)
|
|
|
}
|
|
|
- if domain[0] == '.' || domain[domainLen-1] == '.' {
|
|
|
- return fallbackError(err, EInvalidDomain)
|
|
|
+ if hostname[0] == '.' {
|
|
|
+ return fallbackError(err, EInvalidHostname)
|
|
|
}
|
|
|
- /* make sure domain name only contains allowed letters */
|
|
|
- for _, r := range domain {
|
|
|
+ /* make sure hostname name only contains allowed letters */
|
|
|
+ for _, r := range hostname {
|
|
|
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '.' || r == '-' {
|
|
|
continue
|
|
|
}
|
|
|
- return fallbackError(err, EInvalidDomain)
|
|
|
+ return fallbackError(err, EInvalidHostname)
|
|
|
}
|
|
|
- /* split to levels (tld, domain[, subdomain...] */
|
|
|
- levels := strings.Split(domain, ".")
|
|
|
- levelsLen := len(levels)
|
|
|
- if levelsLen < 2 {
|
|
|
- return fallbackError(err, EInvalidDomain)
|
|
|
+ /* split to levels (tld, hostname[, subhostname...] */
|
|
|
+ levels := strings.Split(hostname, ".")
|
|
|
+ if len(levels) < minLevel {
|
|
|
+ return fallbackError(err, EInvalidHostname)
|
|
|
}
|
|
|
/* perform common checks on levels */
|
|
|
for _, level := range levels {
|
|
|
levelLen := len(level)
|
|
|
- if levelLen == 0 || levelLen > 63 {
|
|
|
- return fallbackError(err, EInvalidDomain)
|
|
|
+ if levelLen == 0 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if levelLen > 63 {
|
|
|
+ return fallbackError(err, EInvalidHostname)
|
|
|
}
|
|
|
if level[0] == '-' || level[levelLen-1] == '-' {
|
|
|
- return fallbackError(err, EInvalidDomain)
|
|
|
+ return fallbackError(err, EInvalidHostname)
|
|
|
}
|
|
|
|
|
|
}
|
|
|
+ /* make sure hostname is not an IP address */
|
|
|
+ if ip := net.ParseIP(hostname); ip != nil {
|
|
|
+ return fallbackError(err, EInvalidHostname)
|
|
|
+ }
|
|
|
return nil
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* ValidHostname performs basic hostname checks */
|
|
|
+func ValidHostname(err error) ValidatorFunc {
|
|
|
+ callbackCheck := validHostnameStr(0, err)
|
|
|
+ /* hostname name checks */
|
|
|
+ return func(field *FormField, ctx context.Context) error {
|
|
|
+ return callbackCheck(field.GetString())
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/* ValidDomainName performs basic domain name checks */
|
|
|
func ValidDomainName(err error) ValidatorFunc {
|
|
|
- callbackCheck := validDomainNameStr(err)
|
|
|
+ callbackCheck := validHostnameStr(2, err)
|
|
|
/* domain name checks */
|
|
|
return func(field *FormField, ctx context.Context) error {
|
|
|
return callbackCheck(field.GetString())
|
|
|
@@ -234,7 +240,7 @@ func ValidDomainNameTLD(err error) ValidatorFunc {
|
|
|
/* ValidEmail checks if field contains a valid email address */
|
|
|
func ValidEmail(err error) ValidatorFunc {
|
|
|
EInvalidEmail := errors.New("invalid email address")
|
|
|
- domainValidator := validDomainNameStr(nil)
|
|
|
+ domainValidator := validHostnameStr(2, nil)
|
|
|
return func(field *FormField, ctx context.Context) error {
|
|
|
/* sanitize input */
|
|
|
addrStr := strings.TrimSpace(field.GetString())
|
|
|
@@ -255,6 +261,39 @@ func ValidEmail(err error) ValidatorFunc {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* ValidIP checks if field contains a valid network address */
|
|
|
+func ValidIP(err error) ValidatorFunc {
|
|
|
+ EInvalidAddress := errors.New("invalid network address")
|
|
|
+ return func(field *FormField, ctx context.Context) error {
|
|
|
+ if ip := net.ParseIP(field.GetString()); ip == nil {
|
|
|
+ return fallbackError(err, EInvalidAddress)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* ValidIPv4 checks if field contains a valid IPv4 network address */
|
|
|
+func ValidIPv4(err error) ValidatorFunc {
|
|
|
+ EInvalidAddress := errors.New("invalid ipv4 address")
|
|
|
+ return func(field *FormField, ctx context.Context) error {
|
|
|
+ if ip := net.ParseIP(field.GetString()); ip == nil || ip.To4() == nil {
|
|
|
+ return fallbackError(err, EInvalidAddress)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* ValidIPv6 checks if field contains a valid IPv6 network address */
|
|
|
+func ValidIPv6(err error) ValidatorFunc {
|
|
|
+ EInvalidAddress := errors.New("invalid ipv6 address")
|
|
|
+ return func(field *FormField, ctx context.Context) error {
|
|
|
+ if ip := net.ParseIP(field.GetString()); ip == nil || ip.To4() != nil {
|
|
|
+ return fallbackError(err, EInvalidAddress)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/* ValidCSRF checks if CSRF token is valid */
|
|
|
func ValidCSRF(r *http.Request) ValidatorFunc {
|
|
|
return func(field *FormField, ctx context.Context) error {
|