forked from AdguardTeam/AdGuardHome
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pull request: 2704 local resolvers vol.1
Merge in DNS/adguard-home from 2704-local-addresses-vol.1 to master Updates AdguardTeam#2704. Updates AdguardTeam#2829. Updates AdguardTeam#2846. Squashed commit of the following: commit 9a49b3d Author: Eugene Burkov <[email protected]> Date: Mon Mar 22 15:39:17 2021 +0300 aghnet: imp docs and logging commit 74f95a2 Author: Eugene Burkov <[email protected]> Date: Fri Mar 19 20:56:51 2021 +0300 all: fix friday evening mistakes commit 0e2066b Author: Eugene Burkov <[email protected]> Date: Fri Mar 19 20:51:15 2021 +0300 all: upd testify, imp code quality commit 8237c50 Author: Eugene Burkov <[email protected]> Date: Fri Mar 19 20:19:29 2021 +0300 aghnet: imp test naming commit 14eb1e1 Author: Eugene Burkov <[email protected]> Date: Fri Mar 19 19:41:43 2021 +0300 aghnet: isolate windows-specific functionality commit d461ac8 Author: Eugene Burkov <[email protected]> Date: Fri Mar 19 14:50:05 2021 +0300 aghnet: imp code quality commit d0ee01c Author: Eugene Burkov <[email protected]> Date: Fri Mar 19 11:59:10 2021 +0300 all: mv funcs to agherr, mk system resolvers getter
- Loading branch information
1 parent
a54133b
commit c79e60f
Showing
19 changed files
with
568 additions
and
48 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
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
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
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
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
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
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 aghnet | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/AdguardTeam/AdGuardHome/internal/agherr" | ||
"github.com/AdguardTeam/golibs/log" | ||
) | ||
|
||
// DefaultRefreshIvl is the default period of time between refreshing cached | ||
// addresses. | ||
// const DefaultRefreshIvl = 5 * time.Minute | ||
|
||
// HostGenFunc is the signature for functions generating fake hostnames. The | ||
// implementation must be safe for concurrent use. | ||
type HostGenFunc func() (host string) | ||
|
||
// unit is an alias for an existing map value. | ||
type unit = struct{} | ||
|
||
// SystemResolvers helps to work with local resolvers' addresses provided by OS. | ||
type SystemResolvers interface { | ||
// Get returns the slice of local resolvers' addresses. | ||
// It should be safe for concurrent use. | ||
Get() (rs []string) | ||
// Refresh refreshes the local resolvers' addresses cache. It should be | ||
// safe for concurrent use. | ||
Refresh() (err error) | ||
} | ||
|
||
const ( | ||
// fakeDialErr is an error which dialFunc is expected to return. | ||
fakeDialErr agherr.Error = "this error signals the successful dialFunc work" | ||
|
||
// badAddrPassedErr is returned when dialFunc can't parse an IP address. | ||
badAddrPassedErr agherr.Error = "the passed string is not a valid IP address" | ||
) | ||
|
||
// refreshWithTicker refreshes the cache of sr after each tick form tickCh. | ||
func refreshWithTicker(sr SystemResolvers, tickCh <-chan time.Time) { | ||
defer agherr.LogPanic("systemResolvers") | ||
|
||
// TODO(e.burkov): Implement a functionality to stop ticker. | ||
for range tickCh { | ||
err := sr.Refresh() | ||
if err != nil { | ||
log.Error("systemResolvers: error in refreshing goroutine: %s", err) | ||
|
||
continue | ||
} | ||
|
||
log.Debug("systemResolvers: local addresses cache is refreshed") | ||
} | ||
} | ||
|
||
// NewSystemResolvers returns a SystemResolvers with the cache refresh rate | ||
// defined by refreshIvl. It disables auto-resfreshing if refreshIvl is 0. If | ||
// nil is passed for hostGenFunc, the default generator will be used. | ||
func NewSystemResolvers( | ||
refreshIvl time.Duration, | ||
hostGenFunc HostGenFunc, | ||
) (sr SystemResolvers, err error) { | ||
sr = newSystemResolvers(refreshIvl, hostGenFunc) | ||
|
||
// Fill cache. | ||
err = sr.Refresh() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if refreshIvl > 0 { | ||
ticker := time.NewTicker(refreshIvl) | ||
|
||
go refreshWithTicker(sr, ticker.C) | ||
} | ||
|
||
return sr, 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,96 @@ | ||
// +build !windows | ||
|
||
package aghnet | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"net" | ||
"sync" | ||
"time" | ||
|
||
"github.com/AdguardTeam/AdGuardHome/internal/agherr" | ||
) | ||
|
||
// defaultHostGen is the default method of generating host for Refresh. | ||
func defaultHostGen() (host string) { | ||
// TODO(e.burkov): Use strings.Builder. | ||
return fmt.Sprintf("test%d.org", time.Now().UnixNano()) | ||
} | ||
|
||
// systemResolvers is a default implementation of SystemResolvers interface. | ||
type systemResolvers struct { | ||
resolver *net.Resolver | ||
hostGenFunc HostGenFunc | ||
|
||
// addrs is the map that contains cached local resolvers' addresses. | ||
addrs map[string]unit | ||
addrsLock sync.RWMutex | ||
} | ||
|
||
func (sr *systemResolvers) Refresh() (err error) { | ||
defer agherr.Annotate("systemResolvers: %w", &err) | ||
|
||
_, err = sr.resolver.LookupHost(context.Background(), sr.hostGenFunc()) | ||
dnserr := &net.DNSError{} | ||
if errors.As(err, &dnserr) && dnserr.Err == fakeDialErr.Error() { | ||
return nil | ||
} | ||
|
||
return err | ||
} | ||
|
||
func newSystemResolvers(refreshIvl time.Duration, hostGenFunc HostGenFunc) (sr SystemResolvers) { | ||
if hostGenFunc == nil { | ||
hostGenFunc = defaultHostGen | ||
} | ||
s := &systemResolvers{ | ||
resolver: &net.Resolver{ | ||
PreferGo: true, | ||
}, | ||
hostGenFunc: hostGenFunc, | ||
addrs: make(map[string]unit), | ||
} | ||
s.resolver.Dial = s.dialFunc | ||
|
||
return s | ||
} | ||
|
||
// 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 | ||
// allow unwrapping of the real error. | ||
return nil, fmt.Errorf("%s: %w", err, badAddrPassedErr) | ||
} | ||
|
||
if net.ParseIP(host) == nil { | ||
return nil, fmt.Errorf("parsing %q: %w", host, badAddrPassedErr) | ||
} | ||
|
||
sr.addrsLock.Lock() | ||
defer sr.addrsLock.Unlock() | ||
|
||
sr.addrs[address] = unit{} | ||
|
||
return nil, fakeDialErr | ||
} | ||
|
||
func (sr *systemResolvers) Get() (rs []string) { | ||
sr.addrsLock.RLock() | ||
defer sr.addrsLock.RUnlock() | ||
|
||
addrs := sr.addrs | ||
rs = make([]string, len(addrs)) | ||
var i int | ||
for addr := range addrs { | ||
rs[i] = addr | ||
i++ | ||
} | ||
|
||
return rs | ||
} |
Oops, something went wrong.