all: sync with master; upd chlog

This commit is contained in:
Ainar Garipov
2023-04-12 14:48:42 +03:00
parent 0dad53b5f7
commit d9c57cdd9a
181 changed files with 6992 additions and 3430 deletions

View File

@@ -1,47 +1,14 @@
package aghnet
import (
"net"
"strconv"
"fmt"
"net/netip"
"strings"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/stringutil"
)
// The maximum lengths of generated hostnames for different IP versions.
const (
ipv4HostnameMaxLen = len("192-168-100-100")
ipv6HostnameMaxLen = len("ff80-f076-0000-0000-0000-0000-0000-0010")
)
// generateIPv4Hostname generates the hostname by IP address version 4.
func generateIPv4Hostname(ipv4 net.IP) (hostname string) {
hnData := make([]byte, 0, ipv4HostnameMaxLen)
for i, part := range ipv4 {
if i > 0 {
hnData = append(hnData, '-')
}
hnData = strconv.AppendUint(hnData, uint64(part), 10)
}
return string(hnData)
}
// generateIPv6Hostname generates the hostname by IP address version 6.
func generateIPv6Hostname(ipv6 net.IP) (hostname string) {
hnData := make([]byte, 0, ipv6HostnameMaxLen)
for i, partsNum := 0, net.IPv6len/2; i < partsNum; i++ {
if i > 0 {
hnData = append(hnData, '-')
}
for _, val := range ipv6[i*2 : i*2+2] {
if val < 10 {
hnData = append(hnData, '0')
}
hnData = strconv.AppendUint(hnData, uint64(val), 16)
}
}
return string(hnData)
}
// GenerateHostname generates the hostname from ip. In case of using IPv4 the
// result should be like:
//
@@ -52,10 +19,42 @@ func generateIPv6Hostname(ipv6 net.IP) (hostname string) {
// ff80-f076-0000-0000-0000-0000-0000-0010
//
// ip must be either an IPv4 or an IPv6.
func GenerateHostname(ip net.IP) (hostname string) {
if ipv4 := ip.To4(); ipv4 != nil {
return generateIPv4Hostname(ipv4)
func GenerateHostname(ip netip.Addr) (hostname string) {
if !ip.IsValid() {
// TODO(s.chzhen): Get rid of it.
panic("aghnet generate hostname: invalid ip")
}
return generateIPv6Hostname(ip)
ip = ip.Unmap()
hostname = ip.StringExpanded()
if ip.Is4() {
return strings.Replace(hostname, ".", "-", -1)
}
return strings.Replace(hostname, ":", "-", -1)
}
// NewDomainNameSet returns nil and error, if list has duplicate or empty
// domain name. Otherwise returns a set, which contains non-FQDN domain names,
// and nil error.
func NewDomainNameSet(list []string) (set *stringutil.Set, err error) {
set = stringutil.NewSet()
for i, v := range list {
host := strings.ToLower(strings.TrimSuffix(v, "."))
// TODO(a.garipov): Think about ignoring empty (".") names in the
// future.
if host == "" {
return nil, errors.Error("host name is empty")
}
if set.Has(host) {
return nil, fmt.Errorf("duplicate host name %q at index %d", host, i)
}
set.Add(host)
}
return set, nil
}

View File

@@ -1,7 +1,7 @@
package aghnet
import (
"net"
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
@@ -12,19 +12,19 @@ func TestGenerateHostName(t *testing.T) {
testCases := []struct {
name string
want string
ip net.IP
ip netip.Addr
}{{
name: "good_ipv4",
want: "127-0-0-1",
ip: net.IP{127, 0, 0, 1},
ip: netip.MustParseAddr("127.0.0.1"),
}, {
name: "good_ipv6",
want: "fe00-0000-0000-0000-0000-0000-0000-0001",
ip: net.ParseIP("fe00::1"),
ip: netip.MustParseAddr("fe00::1"),
}, {
name: "4to6",
want: "1-2-3-4",
ip: net.ParseIP("::ffff:1.2.3.4"),
ip: netip.MustParseAddr("::ffff:1.2.3.4"),
}}
for _, tc := range testCases {
@@ -36,29 +36,6 @@ func TestGenerateHostName(t *testing.T) {
})
t.Run("invalid", func(t *testing.T) {
testCases := []struct {
name string
ip net.IP
}{{
name: "bad_ipv4",
ip: net.IP{127, 0, 0, 1, 0},
}, {
name: "bad_ipv6",
ip: net.IP{
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
},
}, {
name: "nil",
ip: nil,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
assert.Panics(t, func() { GenerateHostname(tc.ip) })
})
}
assert.Panics(t, func() { GenerateHostname(netip.Addr{}) })
})
}

View File

@@ -56,7 +56,7 @@ func (rm *requestMatcher) MatchRequest(
) (res *urlfilter.DNSResult, ok bool) {
switch req.DNSType {
case dns.TypeA, dns.TypeAAAA, dns.TypePTR:
log.Debug("%s: handling the request for %s", hostsContainerPref, req.Hostname)
log.Debug("%s: handling the request for %s", hostsContainerPrefix, req.Hostname)
default:
return nil, false
}
@@ -90,9 +90,9 @@ func (rm *requestMatcher) resetEng(rulesStrg *filterlist.RuleStorage, tr map[str
rm.translator = tr
}
// hostsContainerPref is a prefix for logging and wrapping errors in
// hostsContainerPrefix is a prefix for logging and wrapping errors in
// HostsContainer's methods.
const hostsContainerPref = "hosts container"
const hostsContainerPrefix = "hosts container"
// HostsContainer stores the relevant hosts database provided by the OS and
// processes both A/AAAA and PTR DNS requests for those.
@@ -115,8 +115,8 @@ type HostsContainer struct {
// fsys is the working file system to read hosts files from.
fsys fs.FS
// w tracks the changes in specified files and directories.
w aghos.FSWatcher
// watcher tracks the changes in specified files and directories.
watcher aghos.FSWatcher
// patterns stores specified paths in the fs.Glob-compatible form.
patterns []string
@@ -160,7 +160,7 @@ func NewHostsContainer(
w aghos.FSWatcher,
paths ...string,
) (hc *HostsContainer, err error) {
defer func() { err = errors.Annotate(err, "%s: %w", hostsContainerPref) }()
defer func() { err = errors.Annotate(err, "%s: %w", hostsContainerPrefix) }()
if len(paths) == 0 {
return nil, ErrNoHostsPaths
@@ -182,11 +182,11 @@ func NewHostsContainer(
done: make(chan struct{}, 1),
updates: make(chan HostsRecords, 1),
fsys: fsys,
w: w,
watcher: w,
patterns: patterns,
}
log.Debug("%s: starting", hostsContainerPref)
log.Debug("%s: starting", hostsContainerPrefix)
// Load initially.
if err = hc.refresh(); err != nil {
@@ -199,7 +199,7 @@ func NewHostsContainer(
return nil, fmt.Errorf("adding path: %w", err)
}
log.Debug("%s: %s is expected to exist but doesn't", hostsContainerPref, p)
log.Debug("%s: %s is expected to exist but doesn't", hostsContainerPrefix, p)
}
}
@@ -208,14 +208,21 @@ func NewHostsContainer(
return hc, nil
}
// Close implements the io.Closer interface for *HostsContainer. Close must
// only be called once. The returned err is always nil.
// Close implements the [io.Closer] interface for *HostsContainer. It closes
// both itself and its [aghos.FSWatcher]. Close must only be called once.
func (hc *HostsContainer) Close() (err error) {
log.Debug("%s: closing", hostsContainerPref)
log.Debug("%s: closing", hostsContainerPrefix)
err = hc.watcher.Close()
if err != nil {
err = fmt.Errorf("closing fs watcher: %w", err)
// Go on and close the container either way.
}
close(hc.done)
return nil
return err
}
// Upd returns the channel into which the updates are sent.
@@ -251,22 +258,22 @@ func pathsToPatterns(fsys fs.FS, paths []string) (patterns []string, err error)
// update channel of HostsContainer when finishes. It's used to be called
// within a separate goroutine.
func (hc *HostsContainer) handleEvents() {
defer log.OnPanic(fmt.Sprintf("%s: handling events", hostsContainerPref))
defer log.OnPanic(fmt.Sprintf("%s: handling events", hostsContainerPrefix))
defer close(hc.updates)
ok, eventsCh := true, hc.w.Events()
ok, eventsCh := true, hc.watcher.Events()
for ok {
select {
case _, ok = <-eventsCh:
if !ok {
log.Debug("%s: watcher closed the events channel", hostsContainerPref)
log.Debug("%s: watcher closed the events channel", hostsContainerPrefix)
continue
}
if err := hc.refresh(); err != nil {
log.Error("%s: %s", hostsContainerPref, err)
log.Error("%s: %s", hostsContainerPrefix, err)
}
case _, ok = <-hc.done:
// Go on.
@@ -345,7 +352,7 @@ func (hp *hostsParser) parseLine(line string) (ip netip.Addr, hosts []string) {
// TODO(e.burkov): Investigate if hosts may contain DNS-SD domains.
err = netutil.ValidateHostname(f)
if err != nil {
log.Error("%s: host %q is invalid, ignoring", hostsContainerPref, f)
log.Error("%s: host %q is invalid, ignoring", hostsContainerPrefix, f)
continue
}
@@ -389,7 +396,7 @@ func (hp *hostsParser) addRules(ip netip.Addr, host, line string) {
rule, rulePtr := hp.writeRules(host, ip)
hp.translations[rule], hp.translations[rulePtr] = line, line
log.Debug("%s: added ip-host pair %q-%q", hostsContainerPref, ip, host)
log.Debug("%s: added ip-host pair %q-%q", hostsContainerPrefix, ip, host)
}
// writeRules writes the actual rule for the qtype and the PTR for the host-ip
@@ -443,7 +450,7 @@ func (hp *hostsParser) writeRules(host string, ip netip.Addr) (rule, rulePtr str
// sendUpd tries to send the parsed data to the ch.
func (hp *hostsParser) sendUpd(ch chan HostsRecords) {
log.Debug("%s: sending upd", hostsContainerPref)
log.Debug("%s: sending upd", hostsContainerPrefix)
upd := hp.table
select {
@@ -451,11 +458,11 @@ func (hp *hostsParser) sendUpd(ch chan HostsRecords) {
// Updates are delivered. Go on.
case <-ch:
ch <- upd
log.Debug("%s: replaced the last update", hostsContainerPref)
log.Debug("%s: replaced the last update", hostsContainerPrefix)
case ch <- upd:
// The previous update was just read and the next one pushed. Go on.
default:
log.Error("%s: the updates channel is broken", hostsContainerPref)
log.Error("%s: the updates channel is broken", hostsContainerPrefix)
}
}
@@ -473,7 +480,7 @@ func (hp *hostsParser) newStrg(id int) (s *filterlist.RuleStorage, err error) {
//
// TODO(e.burkov): Accept a parameter to specify the files to refresh.
func (hc *HostsContainer) refresh() (err error) {
log.Debug("%s: refreshing", hostsContainerPref)
log.Debug("%s: refreshing", hostsContainerPrefix)
hp := hc.newHostsParser()
if _, err = aghos.FileWalker(hp.parseFile).Walk(hc.fsys, hc.patterns...); err != nil {
@@ -482,7 +489,7 @@ func (hc *HostsContainer) refresh() (err error) {
// hc.last is nil on the first refresh, so let that one through.
if hc.last != nil && maps.EqualFunc(hp.table, hc.last, (*HostsRecord).equal) {
log.Debug("%s: no changes detected", hostsContainerPref)
log.Debug("%s: no changes detected", hostsContainerPrefix)
return nil
}

View File

@@ -35,4 +35,4 @@
1.3.5.7 domain4 domain4.alias
7.5.3.1 domain4.alias domain4
::13 domain6 domain6.alias
::31 domain6.alias domain6
::31 domain6.alias domain6

View File

@@ -1 +1 @@
iface sample_name inet static
iface sample_name inet static

View File

@@ -2,4 +2,4 @@
# parent directory. Real interface files usually contain only absolute paths.
source ./testdata/ifaces
source ./testdata/*
source ./testdata/*

View File

@@ -3,4 +3,4 @@ IP address HW type Flags HW address Mask Device
::ffff:ffff 0x1 0x0 ef:cd:ab:ef:cd:ab * br-lan
0.0.0.0 0x0 0x0 00:00:00:00:00:00 * unspec
1.2.3.4.5 0x1 0x2 aa:bb:cc:dd:ee:ff * wan
1.2.3.4 0x1 0x2 12:34:56:78:910 * wan
1.2.3.4 0x1 0x2 12:34:56:78:910 * wan