-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add GetDomain() and CheckDomain()
- Loading branch information
Showing
2 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package utils | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"strings" | ||
"unicode/utf8" | ||
) | ||
|
||
// GetDomain 检查并返回清除前后空白的域名 | ||
func GetDomain(name string) string { | ||
name = strings.TrimSpace(name) | ||
if CheckDomain(name) != nil { | ||
return "" | ||
} | ||
return name | ||
} | ||
|
||
// CheckDomain returns an error if the host name is not valid. | ||
// See https://tools.ietf.org/html/rfc1034#section-3.5 and | ||
// https://tools.ietf.org/html/rfc1123#section-2. | ||
// Ref: chmike/domain | ||
func CheckDomain(name string) error { | ||
if len(name) == 0 { | ||
return errors.New("domain name is empty") | ||
} | ||
if name[len(name)-1] == '.' { | ||
if len(name) > 254 { | ||
return fmt.Errorf("domain name length is %d, can't exceed 254 with a trailing dot", len(name)) | ||
} | ||
name = name[:len(name)-1] // drop valid trailing dot | ||
if len(name) == 0 { | ||
return errors.New("domain name is a single dot") | ||
} | ||
} else if len(name) > 253 { | ||
return fmt.Errorf("domain name length is %d, can't exceed 253 without a trailing dot", len(name)) | ||
} | ||
var l int | ||
for i := 0; i < len(name); i++ { | ||
b := name[i] | ||
if b == '.' { | ||
// check domain labels validity | ||
switch { | ||
case i == l: | ||
return fmt.Errorf("domain has an empty label at offset %d", l) | ||
case i-l > 63: | ||
return fmt.Errorf("domain byte length of label '%s' is %d, can't exceed 63", name[l:i], i-l) | ||
case name[l] == '-': | ||
return fmt.Errorf("domain label '%s' at offset %d begins with a hyphen", name[l:i], l) | ||
case name[i-1] == '-': | ||
return fmt.Errorf("domain label '%s' at offset %d ends with a hyphen", name[l:i], l) | ||
} | ||
l = i + 1 | ||
continue | ||
} | ||
// test label character validity, note: tests are ordered by decreasing validity frequency | ||
if !(b >= 'a' && b <= 'z' || b >= '0' && b <= '9' || b == '-' || b >= 'A' && b <= 'Z') { | ||
// show the printable unicode character starting at byte offset i | ||
c, _ := utf8.DecodeRuneInString(name[i:]) | ||
if c == utf8.RuneError { | ||
return fmt.Errorf("domain has invalid rune at offset %d", i) | ||
} | ||
return fmt.Errorf("domain has invalid character '%c' at offset %d", c, i) | ||
} | ||
} | ||
// check top level domain validity | ||
switch { | ||
case len(name)-l > 63: | ||
return fmt.Errorf("domain's top level domain '%s' has byte length %d, can't exceed 63", name[l:], len(name)-l) | ||
case name[l] == '-': | ||
return fmt.Errorf("domain's top level domain '%s' at offset %d begin with a hyphen", name[l:], l) | ||
case name[len(name)-1] == '-': | ||
return fmt.Errorf("domain's top level domain '%s' at offset %d ends with a hyphen", name[l:], l) | ||
case name[l] >= '0' && name[l] <= '9': | ||
return fmt.Errorf("domain's top level domain '%s' at offset %d begins with a digit", name[l:], l) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package utils | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"github.com/fufuok/utils/assert" | ||
) | ||
|
||
var longName = strings.Repeat(strings.Repeat("a", 63)+".", 4) | ||
|
||
func TestDomainRegexp(t *testing.T) { | ||
f63 := strings.Repeat("f", 63) | ||
cases := []struct { | ||
in, want string | ||
}{ | ||
{"f", "f"}, | ||
{"f.cn", "f.cn"}, | ||
{"7.cn", "7.cn"}, | ||
{"f-7.com.cn", "f-7.com.cn"}, | ||
{"f--f.cn", "f--f.cn"}, | ||
{"-f.cn", ""}, | ||
{"f-.cn", ""}, | ||
{"f_.cn", ""}, | ||
{"f_f.cn", ""}, | ||
{"f.f", "f.f"}, | ||
{"f.77", ""}, | ||
{"f." + f63, "f." + f63}, | ||
{"f." + f63 + ".cn", "f." + f63 + ".cn"}, | ||
{"f.f" + f63, ""}, | ||
{"f.f" + f63 + ".cn", ""}, | ||
{"_.f.cn", ""}, | ||
{"*.f.cn", ""}, | ||
{"xn--fiq06l2rdsvs.xn--vuq861b.xn--fiqs8s", "xn--fiq06l2rdsvs.xn--vuq861b.xn--fiqs8s"}, | ||
} | ||
for _, c := range cases { | ||
assert.Equal(t, c.want, GetDomain(c.in), c.in) | ||
} | ||
} | ||
|
||
// Ref: chmike/domain | ||
func TestCheckDomain(t *testing.T) { | ||
tests := []struct { | ||
n string | ||
e string | ||
}{ | ||
// 0 | ||
{n: "", e: "domain name is empty"}, | ||
{n: "example.com"}, | ||
{n: "EXAMPLE.com"}, | ||
{n: "foo-bar.com"}, | ||
{n: "www1.foo-bar.com"}, | ||
// 5 | ||
{n: "192.168.1.1.example.com"}, | ||
{n: strings.Repeat("a", 70) + ".com", e: "domain byte length of label 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' is 70, can't exceed 63"}, | ||
{n: "example.com" + strings.Repeat("a", 70), e: "domain's top level domain 'comaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' has byte length 73, can't exceed 63"}, | ||
{n: "?", e: "domain has invalid character '?' at offset 0"}, | ||
{n: "\t", e: "domain has invalid character '\t' at offset 0"}, | ||
// 10 | ||
{n: "exàmple.com", e: "domain has invalid character 'à' at offset 2"}, | ||
{n: "www.\xbd\xb2.com", e: "domain has invalid rune at offset 4"}, | ||
{n: "-example.com", e: "domain label '-example' at offset 0 begins with a hyphen"}, | ||
{n: "example-.com", e: "domain label 'example-' at offset 0 ends with a hyphen"}, | ||
{n: "example.-com", e: "domain's top level domain '-com' at offset 8 begin with a hyphen"}, | ||
// 15 | ||
{n: "example.com-", e: "domain's top level domain 'com-' at offset 8 ends with a hyphen"}, | ||
{n: "example.1com", e: "domain's top level domain '1com' at offset 8 begins with a digit"}, | ||
{n: ".example.com", e: "domain has an empty label at offset 0"}, | ||
{n: "example..com", e: "domain has an empty label at offset 8"}, | ||
{n: "example.com."}, | ||
// 20 | ||
{n: longName, e: "domain name length is 256, can't exceed 254 with a trailing dot"}, | ||
{n: longName[:253]}, | ||
{n: longName[:253] + "."}, | ||
{n: longName[:254], e: "domain name length is 254, can't exceed 253 without a trailing dot"}, | ||
{n: longName[:255] + ".", e: "domain name length is 256, can't exceed 254 with a trailing dot"}, | ||
// 25 | ||
{n: ".", e: "domain name is a single dot"}, | ||
} | ||
for i, test := range tests { | ||
err := CheckDomain(test.n) | ||
if (err == nil) != (test.e == "") { | ||
if err != nil { | ||
t.Errorf("%2d unexpected error: %q", i, err) | ||
} else { | ||
t.Errorf("%2d unexpected nil error", i) | ||
} | ||
continue | ||
} | ||
if err != nil && err.Error() != test.e { | ||
t.Errorf("%2d expect error %q, got %q", i, test.e, err) | ||
} | ||
} | ||
} |