Pull request: * all: allow multiple hosts in reverse lookups

Merge in DNS/adguard-home from 2269-multiple-hosts to master

For #2269.

Squashed commit of the following:

commit f8ae452540b106f2d5b130b8edb08c4e76b003f4
Merge: 8dd06f7cc 3e1f92225
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Fri Nov 6 17:28:12 2020 +0300

    Merge branch 'master' into 2269-multiple-hosts

commit 8dd06f7cca27ec32a4690e2673603b166f82af0a
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Nov 5 20:28:33 2020 +0300

    * all: allow multiple hosts in reverse lookups
This commit is contained in:
Ainar Garipov
2020-11-06 17:34:40 +03:00
parent 3e1f922252
commit 298f74ba81
5 changed files with 116 additions and 65 deletions

View File

@@ -20,9 +20,14 @@ type onChangedT func()
// AutoHosts - automatic DNS records
type AutoHosts struct {
lock sync.Mutex // serialize access to table
table map[string][]net.IP // 'hostname -> IP' table
tableReverse map[string]string // "IP -> hostname" table for reverse lookup
// lock protects table and tableReverse.
lock sync.Mutex
// table is the host-to-IPs map.
table map[string][]net.IP
// tableReverse is the IP-to-hosts map.
//
// TODO(a.garipov): Make better use of newtypes. Perhaps a custom map.
tableReverse map[string][]string
hostsFn string // path to the main hosts-file
hostsDirs []string // paths to OS-specific directories with hosts-files
@@ -127,40 +132,44 @@ func (a *AutoHosts) Process(host string, qtype uint16) []net.IP {
return ipsCopy
}
// ProcessReverse - process PTR request
// Return "" if not found or an error occurred
func (a *AutoHosts) ProcessReverse(addr string, qtype uint16) string {
// ProcessReverse processes a PTR request. It returns nil if nothing is found.
func (a *AutoHosts) ProcessReverse(addr string, qtype uint16) (hosts []string) {
if qtype != dns.TypePTR {
return ""
return nil
}
ipReal := DNSUnreverseAddr(addr)
if ipReal == nil {
return "" // invalid IP in question
return nil
}
ipStr := ipReal.String()
a.lock.Lock()
host := a.tableReverse[ipStr]
a.lock.Unlock()
defer a.lock.Unlock()
if len(host) == 0 {
return "" // not found
hosts = a.tableReverse[ipStr]
if len(hosts) == 0 {
return nil // not found
}
log.Debug("AutoHosts: reverse-lookup: %s -> %s", addr, host)
return host
log.Debug("AutoHosts: reverse-lookup: %s -> %s", addr, hosts)
return hosts
}
// List - get "IP -> hostname" table. Thread-safe.
func (a *AutoHosts) List() map[string]string {
table := make(map[string]string)
// List returns an IP-to-hostnames table. It is safe for concurrent use.
func (a *AutoHosts) List() (ipToHosts map[string][]string) {
a.lock.Lock()
defer a.lock.Unlock()
ipToHosts = make(map[string][]string, len(a.tableReverse))
for k, v := range a.tableReverse {
table[k] = v
ipToHosts[k] = v
}
a.lock.Unlock()
return table
return ipToHosts
}
// update table
@@ -187,19 +196,30 @@ func (a *AutoHosts) updateTable(table map[string][]net.IP, host string, ipAddr n
}
}
// update "reverse" table
func (a *AutoHosts) updateTableRev(tableRev map[string]string, host string, ipAddr net.IP) {
// updateTableRev updates the reverse address table.
func (a *AutoHosts) updateTableRev(tableRev map[string][]string, newHost string, ipAddr net.IP) {
ipStr := ipAddr.String()
_, ok := tableRev[ipStr]
hosts, ok := tableRev[ipStr]
if !ok {
tableRev[ipStr] = host
log.Debug("AutoHosts: added reverse-address %s -> %s", ipStr, host)
tableRev[ipStr] = []string{newHost}
log.Debug("AutoHosts: added reverse-address %s -> %s", ipStr, newHost)
return
}
for _, host := range hosts {
if host == newHost {
return
}
}
tableRev[ipStr] = append(tableRev[ipStr], newHost)
log.Debug("AutoHosts: added reverse-address %s -> %s", ipStr, newHost)
}
// Read IP-hostname pairs from file
// Multiple hostnames per line (per one IP) is supported.
func (a *AutoHosts) load(table map[string][]net.IP, tableRev map[string]string, fn string) {
func (a *AutoHosts) load(table map[string][]net.IP, tableRev map[string][]string, fn string) {
f, err := os.Open(fn)
if err != nil {
log.Error("AutoHosts: %s", err)
@@ -306,7 +326,7 @@ func (a *AutoHosts) updateLoop() {
// updateHosts - loads system hosts
func (a *AutoHosts) updateHosts() {
table := make(map[string][]net.IP)
tableRev := make(map[string]string)
tableRev := make(map[string][]string)
a.load(table, tableRev, a.hostsFn)

View File

@@ -15,7 +15,7 @@ import (
func prepareTestDir() string {
const dir = "./agh-test"
_ = os.RemoveAll(dir)
_ = os.MkdirAll(dir, 0755)
_ = os.MkdirAll(dir, 0o755)
return dir
}
@@ -50,17 +50,24 @@ func TestAutoHostsResolution(t *testing.T) {
// Test hosts file
table := ah.List()
name, ok := table["127.0.0.1"]
names, ok := table["127.0.0.1"]
assert.True(t, ok)
assert.Equal(t, "host", name)
assert.Equal(t, []string{"host", "localhost"}, names)
// Test PTR
a, _ := dns.ReverseAddr("127.0.0.1")
a = strings.TrimSuffix(a, ".")
assert.True(t, ah.ProcessReverse(a, dns.TypePTR) == "host")
hosts := ah.ProcessReverse(a, dns.TypePTR)
if assert.Len(t, hosts, 2) {
assert.Equal(t, hosts[0], "host")
}
a, _ = dns.ReverseAddr("::1")
a = strings.TrimSuffix(a, ".")
assert.True(t, ah.ProcessReverse(a, dns.TypePTR) == "localhost")
hosts = ah.ProcessReverse(a, dns.TypePTR)
if assert.Len(t, hosts, 1) {
assert.Equal(t, hosts[0], "localhost")
}
}
func TestAutoHostsFSNotify(t *testing.T) {