Skip to content

Commit

Permalink
Pull request: aghnet: imp host validation for system resolvers
Browse files Browse the repository at this point in the history
Updates AdguardTeam#3022.

Squashed commit of the following:

commit 2f63b4e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 28 21:29:28 2021 +0300

    aghnet: fix doc

commit efdc1bb
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 28 21:19:54 2021 +0300

    all: doc changes

commit 8154797
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Wed Apr 28 21:15:42 2021 +0300

    aghnet: imp host validation for system resolvers
  • Loading branch information
ainar-g authored and heyxkhoa committed Mar 17, 2023
1 parent 0bfbae7 commit 89a4749
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 14 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ and this project adheres to
## [v0.106.1] - 2021-05-17 (APPROX.)
-->

### Fixed

- Validation of IPv6 addresses with zones in system resolvers ([#3022]).

[#3022]: https://github.com/AdguardTeam/AdGuardHome/issues/3022



## [v0.106.0] - 2021-04-28

### Added
Expand Down
12 changes: 8 additions & 4 deletions internal/aghnet/systemresolvers.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@ type SystemResolvers interface {
}

const (
// fakeDialErr is an error which dialFunc is expected to return.
fakeDialErr agherr.Error = "this error signals the successful dialFunc work"
// errBadAddrPassed is returned when dialFunc can't parse an IP address.
errBadAddrPassed agherr.Error = "the passed string is not a valid IP address"

// badAddrPassedErr is returned when dialFunc can't parse an IP address.
badAddrPassedErr agherr.Error = "the passed string is not a valid IP address"
// errFakeDial is an error which dialFunc is expected to return.
errFakeDial agherr.Error = "this error signals the successful dialFunc work"

// errUnexpectedHostFormat is returned by validateDialedHost when the host has
// more than one percent sign.
errUnexpectedHostFormat agherr.Error = "unexpected host format"
)

// refreshWithTicker refreshes the cache of sr after each tick form tickCh.
Expand Down
37 changes: 31 additions & 6 deletions internal/aghnet/systemresolvers_others.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"net"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -35,7 +36,7 @@ func (sr *systemResolvers) refresh() (err error) {

_, err = sr.resolver.LookupHost(context.Background(), sr.hostGenFunc())
dnserr := &net.DNSError{}
if errors.As(err, &dnserr) && dnserr.Err == fakeDialErr.Error() {
if errors.As(err, &dnserr) && dnserr.Err == errFakeDial.Error() {
return nil
}

Expand All @@ -58,27 +59,51 @@ func newSystemResolvers(refreshIvl time.Duration, hostGenFunc HostGenFunc) (sr S
return s
}

// validateDialedHost validated the host used by resolvers in dialFunc.
func validateDialedHost(host string) (err error) {
defer agherr.Annotate("parsing %q: %w", &err, host)

var ipStr string
parts := strings.Split(host, "%")
switch len(parts) {
case 1:
ipStr = host
case 2:
// Remove the zone and check the IP address part.
ipStr = parts[0]
default:
return errUnexpectedHostFormat
}

if net.ParseIP(ipStr) == nil {
return errBadAddrPassed
}

return nil
}

// dialFunc gets the resolver's address and puts it into internal cache.
func (sr *systemResolvers) dialFunc(_ context.Context, _, address string) (_ net.Conn, err error) {
// Just validate the passed address is a valid IP.
var host string
host, err = SplitHost(address)
if err != nil {
// TODO(e.burkov): Maybe use a structured badAddrPassedErr to
// TODO(e.burkov): Maybe use a structured errBadAddrPassed to
// allow unwrapping of the real error.
return nil, fmt.Errorf("%s: %w", err, badAddrPassedErr)
return nil, fmt.Errorf("%s: %w", err, errBadAddrPassed)
}

if net.ParseIP(host) == nil {
return nil, fmt.Errorf("parsing %q: %w", host, badAddrPassedErr)
err = validateDialedHost(host)
if err != nil {
return nil, fmt.Errorf("validating dialed host: %w", err)
}

sr.addrsLock.Lock()
defer sr.addrsLock.Unlock()

sr.addrs.Add(host)

return nil, fakeDialErr
return nil, errFakeDial
}

func (sr *systemResolvers) Get() (rs []string) {
Expand Down
20 changes: 16 additions & 4 deletions internal/aghnet/systemresolvers_others_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,33 @@ func TestSystemResolvers_DialFunc(t *testing.T) {
imp := createTestSystemResolversImp(t, 0, nil)

testCases := []struct {
want error
name string
address string
want error
}{{
want: errFakeDial,
name: "valid",
address: "127.0.0.1",
want: fakeDialErr,
}, {
want: errFakeDial,
name: "valid_ipv6_port",
address: "[::1]:53",
}, {
want: errFakeDial,
name: "valid_ipv6_zone_port",
address: "[::1%lo0]:53",
}, {
want: errBadAddrPassed,
name: "invalid_split_host",
address: "127.0.0.1::123",
want: badAddrPassedErr,
}, {
want: errUnexpectedHostFormat,
name: "invalid_ipv6_zone_port",
address: "[::1%%lo0]:53",
}, {
want: errBadAddrPassed,
name: "invalid_parse_ip",
address: "not-ip",
want: badAddrPassedErr,
}}

for _, tc := range testCases {
Expand Down

0 comments on commit 89a4749

Please sign in to comment.