Skip to content

Commit

Permalink
Pull request: all: add idna handling, imp domain validation
Browse files Browse the repository at this point in the history
Updates AdguardTeam#2915.

Squashed commit of the following:

commit b907324
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 7 16:26:41 2021 +0300

    all: imp docs, upd

commit c022f75
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 7 15:51:30 2021 +0300

    all: add idna handling, imp domain validation
  • Loading branch information
ainar-g authored and heyxkhoa committed Mar 17, 2023
1 parent db10ffc commit 7d34fed
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 215 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ require (
github.com/u-root/u-root v7.0.0+incompatible
go.etcd.io/bbolt v1.3.5
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/text v0.3.5 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -516,8 +516,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
Expand Down Expand Up @@ -582,8 +582,8 @@ golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
Expand Down
78 changes: 78 additions & 0 deletions internal/aghnet/addr.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package aghnet
import (
"fmt"
"net"
"strings"

"github.com/AdguardTeam/AdGuardHome/internal/agherr"
"golang.org/x/net/idna"
)

// ValidateHardwareAddress returns an error if hwa is not a valid EUI-48,
Expand All @@ -21,3 +23,79 @@ func ValidateHardwareAddress(hwa net.HardwareAddr) (err error) {
return fmt.Errorf("bad len: %d", l)
}
}

// maxDomainLabelLen is the maximum allowed length of a domain name label
// according to RFC 1035.
const maxDomainLabelLen = 63

// maxDomainNameLen is the maximum allowed length of a full domain name
// according to RFC 1035.
//
// See https://stackoverflow.com/a/32294443/1892060.
const maxDomainNameLen = 253

const invalidCharMsg = "invalid char %q at index %d in %q"

// isValidHostFirstRune returns true if r is a valid first rune for a hostname
// label.
func isValidHostFirstRune(r rune) (ok bool) {
return (r >= 'a' && r <= 'z') ||
(r >= 'A' && r <= 'Z') ||
(r >= '0' && r <= '9')
}

// isValidHostRune returns true if r is a valid rune for a hostname label.
func isValidHostRune(r rune) (ok bool) {
return r == '-' || isValidHostFirstRune(r)
}

// ValidateDomainNameLabel returns an error if label is not a valid label of
// a domain name.
func ValidateDomainNameLabel(label string) (err error) {
if len(label) > maxDomainLabelLen {
return fmt.Errorf("%q is too long, max: %d", label, maxDomainLabelLen)
} else if len(label) == 0 {
return agherr.Error("label is empty")
}

if r := label[0]; !isValidHostFirstRune(rune(r)) {
return fmt.Errorf(invalidCharMsg, r, 0, label)
}

for i, r := range label[1:] {
if !isValidHostRune(r) {
return fmt.Errorf(invalidCharMsg, r, i+1, label)
}
}

return nil
}

// ValidateDomainName validates the domain name in accordance to RFC 952, RFC
// 1035, and with RFC-1123's inclusion of digits at the start of the host. It
// doesn't validate against two or more hyphens to allow punycode and
// internationalized domains.
//
// TODO(a.garipov): After making sure that this works correctly, port this into
// module golibs.
func ValidateDomainName(name string) (err error) {
name, err = idna.ToASCII(name)
if err != nil {
return err
}

l := len(name)
if l == 0 || l > maxDomainNameLen {
return fmt.Errorf("%q is too long, max: %d", name, maxDomainNameLen)
}

labels := strings.Split(name, ".")
for i, l := range labels {
err = ValidateDomainNameLabel(l)
if err != nil {
return fmt.Errorf("invalid domain name label at index %d: %w", i, err)
}
}

return nil
}
76 changes: 76 additions & 0 deletions internal/aghnet/addr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package aghnet

import (
"net"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -50,6 +51,81 @@ func TestValidateHardwareAddress(t *testing.T) {
assert.NoError(t, err)
} else {
require.Error(t, err)

assert.Equal(t, tc.wantErrMsg, err.Error())
}
})
}
}

func repeatStr(b *strings.Builder, s string, n int) {
for i := 0; i < n; i++ {
_, _ = b.WriteString(s)
}
}

func TestValidateDomainName(t *testing.T) {
b := &strings.Builder{}
repeatStr(b, "a", 255)
longDomainName := b.String()

b.Reset()
repeatStr(b, "a", 64)
longLabel := b.String()

_, _ = b.WriteString(".com")
longLabelDomainName := b.String()

testCases := []struct {
name string
in string
wantErrMsg string
}{{
name: "success",
in: "example.com",
wantErrMsg: "",
}, {
name: "success_idna",
in: "пример.рф",
wantErrMsg: "",
}, {
name: "bad_symbol",
in: "!!!",
wantErrMsg: `invalid domain name label at index 0: ` +
`invalid char '!' at index 0 in "!!!"`,
}, {
name: "bad_length",
in: longDomainName,
wantErrMsg: `"` + longDomainName + `" is too long, max: 253`,
}, {
name: "bad_label_length",
in: longLabelDomainName,
wantErrMsg: `invalid domain name label at index 0: "` + longLabel +
`" is too long, max: 63`,
}, {
name: "bad_label_empty",
in: "example..com",
wantErrMsg: `invalid domain name label at index 1: label is empty`,
}, {
name: "bad_label_first_symbol",
in: "example.-aa.com",
wantErrMsg: `invalid domain name label at index 1:` +
` invalid char '-' at index 0 in "-aa"`,
}, {
name: "bad_label_symbol",
in: "example.a!!!.com",
wantErrMsg: `invalid domain name label at index 1:` +
` invalid char '!' at index 1 in "a!!!"`,
}}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := ValidateDomainName(tc.in)
if tc.wantErrMsg == "" {
assert.NoError(t, err)
} else {
require.Error(t, err)

assert.Equal(t, tc.wantErrMsg, err.Error())
}
})
Expand Down
23 changes: 2 additions & 21 deletions internal/dnsforward/clientid.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,14 @@ import (
"path"
"strings"

"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/lucas-clemente/quic-go"
)

// maxDomainLabelLen is the maximum allowed length of a domain name label
// according to RFC 1035.
const maxDomainLabelLen = 63

// validateDomainNameLabel returns an error if label is not a valid label of
// a domain name.
func validateDomainNameLabel(label string) (err error) {
if len(label) > maxDomainLabelLen {
return fmt.Errorf("%q is too long, max: %d", label, maxDomainLabelLen)
}

for i, r := range label {
if (r < 'a' || r > 'z') && (r < '0' || r > '9') && r != '-' {
return fmt.Errorf("invalid char %q at index %d in %q", r, i, label)
}
}

return nil
}

// ValidateClientID returns an error if clientID is not a valid client ID.
func ValidateClientID(clientID string) (err error) {
err = validateDomainNameLabel(clientID)
err = aghnet.ValidateDomainNameLabel(clientID)
if err != nil {
return fmt.Errorf("invalid client id: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/dnsforward/clientid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,8 @@ func TestProcessClientID_https(t *testing.T) {
name: "invalid_client_id",
path: "/dns-query/!!!",
wantClientID: "",
wantErrMsg: `client id check: invalid client id: invalid char '!'` +
` at index 0 in "!!!"`,
wantErrMsg: `client id check: invalid client id: invalid char '!' ` +
`at index 0 in "!!!"`,
wantRes: resultCodeError,
}}

Expand Down
2 changes: 1 addition & 1 deletion internal/dnsforward/dnsforward.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func NewServer(p DNSCreateParams) (s *Server, err error) {
if p.AutohostTLD == "" {
autohostSuffix = defaultAutohostSuffix
} else {
err = validateDomainNameLabel(p.AutohostTLD)
err = aghnet.ValidateDomainNameLabel(p.AutohostTLD)
if err != nil {
return nil, fmt.Errorf("autohost tld: %w", err)
}
Expand Down
Loading

0 comments on commit 7d34fed

Please sign in to comment.