Skip to content

Commit

Permalink
FQDN checker is added. Fixes #61 (#62)
Browse files Browse the repository at this point in the history
* FQDN checker is added. Fixes #61

* Cover coverage, added test.
  • Loading branch information
cinar committed Jun 18, 2023
1 parent c894900 commit 4b34bf5
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ This package currently provides the following checkers:
- [ascii](doc/checkers/ascii.md) checks if the given string consists of only ASCII characters.
- [cidr](doc/checkers/cidr.md) checker checks if the value is a valid CIDR notation IP address and prefix length.
- [digits](doc/checkers/digits.md) checks if the given string consists of only digit characters.
- [fqdn](doc/checkers/fqdn.md) checks if the given string is a fully qualified domain name.
- [ip](doc/checkers/ip.md) checks if the given value is an IP address.
- [ipv4](doc/checkers/ipv4.md) checks if the given value is an IPv4 address.
- [ipv6](doc/checkers/ipv6.md) checks if the given value is an IPv6 address.
Expand Down
1 change: 1 addition & 0 deletions checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var makers = map[string]MakeFunc{
CheckerAscii: makeAscii,
CheckerCidr: makeCidr,
CheckerDigits: makeDigits,
CheckerFqdn: makeFqdn,
CheckerIp: makeIp,
CheckerIpV4: makeIpV4,
CheckerIpV6: makeIpV6,
Expand Down
28 changes: 28 additions & 0 deletions doc/checkers/fqdn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# FQDN Checker

The Full Qualified Domain Name (FQDN) is the complete domain name for a computer or host on the internet. The ```fqdn``` checker checks if the given string consists of a FQDN. If the string is not a valid FQDN, the checker will return the ```NOT_FQDN``` result. Here is an example:

```golang
type Request struct {
Domain string `checkers:"fqdn"`
}

request := &Request{
Domain: "zdo.com",
}

_, valid := Check(request)
if !valid {
// Send the mistakes back to the user
}
```

In your custom checkers, you can call the ```fqdn``` checker function ```IsFqdn``` to validate the user input. Here is an example:

```golang
result := IsFqdn("zdo.com")

if result != ResultValid {
// Send the mistakes back to the user
}
```
71 changes: 71 additions & 0 deletions fqdn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package checker

import (
"reflect"
"regexp"
"strings"
)

// CheckerFqdn is the name of the checker.
const CheckerFqdn = "fqdn"

// ResultNotFqdn indicates that the given string contains non-ASCII characters.
const ResultNotFqdn = "NOT_FQDN"

// Valid characters excluding full-width characters.
var fqdnValidChars = regexp.MustCompile("^[a-z0-9\u00a1-\uff00\uff06-\uffff-]+$")

// IsFqdn checks if the given string is a fully qualified domain name.
func IsFqdn(domain string) Result {
parts := strings.Split(domain, ".")

// Require TLD
if len(parts) < 2 {
return ResultNotFqdn
}

tld := parts[len(parts)-1]

// Should be all numeric TLD
if IsDigits(tld) == ResultValid {
return ResultNotFqdn
}

// Short TLD
if len(tld) < 2 {
return ResultNotFqdn
}

for _, part := range parts {
// Cannot be more than 63 characters
if len(part) > 63 {
return ResultNotFqdn
}

// Check for valid characters
if !fqdnValidChars.MatchString(part) {
return ResultNotFqdn
}

// Should not start or end with a hyphen (-) character.
if part[0] == '-' || part[len(part)-1] == '-' {
return ResultNotFqdn
}
}

return ResultValid
}

// makeFqdn makes a checker function for the ascii checker.
func makeFqdn(_ string) CheckFunc {
return checkFqdn
}

// checkFqdn checks if the given string is a fully qualified domain name.
func checkFqdn(value, _ reflect.Value) Result {
if value.Kind() != reflect.String {
panic("string expected")
}

return IsFqdn(value.String())
}
84 changes: 84 additions & 0 deletions fqdn_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package checker

import "testing"

func TestCheckFdqnWithoutTld(t *testing.T) {
if IsFqdn("abcd") != ResultNotFqdn {
t.Fail()
}
}

func TestCheckFdqnShortTld(t *testing.T) {
if IsFqdn("abcd.c") != ResultNotFqdn {
t.Fail()
}
}

func TestCheckFdqnNumericTld(t *testing.T) {
if IsFqdn("abcd.1234") != ResultNotFqdn {
t.Fail()
}
}

func TestCheckFdqnLong(t *testing.T) {
if IsFqdn("abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd.com") != ResultNotFqdn {
t.Fail()
}
}

func TestCheckFdqnInvalidCharacters(t *testing.T) {
if IsFqdn("ab_cd.com") != ResultNotFqdn {
t.Fail()
}
}

func TestCheckFdqnStaringWithHyphen(t *testing.T) {
if IsFqdn("-abcd.com") != ResultNotFqdn {
t.Fail()
}
}

func TestCheckFdqnStaringEndingWithHyphen(t *testing.T) {
if IsFqdn("abcd-.com") != ResultNotFqdn {
t.Fail()
}
}

func TestCheckFdqnStartingWithDot(t *testing.T) {
if IsFqdn(".abcd.com") != ResultNotFqdn {
t.Fail()
}
}

func TestCheckFdqnEndingWithDot(t *testing.T) {
if IsFqdn("abcd.com.") != ResultNotFqdn {
t.Fail()
}
}

func TestCheckFqdnNonString(t *testing.T) {
defer FailIfNoPanic(t)

type Request struct {
Domain int `checkers:"fqdn"`
}

request := &Request{}

Check(request)
}

func TestCheckFqdnValid(t *testing.T) {
type Request struct {
Domain string `checkers:"fqdn"`
}

request := &Request{
Domain: "zdo.com",
}

_, valid := Check(request)
if !valid {
t.Fail()
}
}

0 comments on commit 4b34bf5

Please sign in to comment.