mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2025-02-18 02:49:51 +03:00
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:
parent
3e1f922252
commit
298f74ba81
5 changed files with 116 additions and 65 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
// Package dnsfilter implements a DNS filter.
|
||||||
package dnsfilter
|
package dnsfilter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -281,7 +282,7 @@ type Result struct {
|
||||||
CanonName string `json:",omitempty"` // CNAME value
|
CanonName string `json:",omitempty"` // CNAME value
|
||||||
|
|
||||||
// for RewriteEtcHosts:
|
// for RewriteEtcHosts:
|
||||||
ReverseHost string `json:",omitempty"`
|
ReverseHosts []string `json:",omitempty"`
|
||||||
|
|
||||||
// for ReasonRewrite & RewriteEtcHosts:
|
// for ReasonRewrite & RewriteEtcHosts:
|
||||||
IPList []net.IP `json:",omitempty"` // list of IP addresses
|
IPList []net.IP `json:",omitempty"` // list of IP addresses
|
||||||
|
@ -325,18 +326,9 @@ func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
|
||||||
// Now check the hosts file -- do we have any rules for it?
|
// Now check the hosts file -- do we have any rules for it?
|
||||||
// just like DNS rewrites, it has higher priority than filtering rules.
|
// just like DNS rewrites, it has higher priority than filtering rules.
|
||||||
if d.Config.AutoHosts != nil {
|
if d.Config.AutoHosts != nil {
|
||||||
ips := d.Config.AutoHosts.Process(host, qtype)
|
matched, err := d.checkAutoHosts(host, qtype, &result)
|
||||||
if ips != nil {
|
if matched {
|
||||||
result.Reason = RewriteEtcHosts
|
return result, err
|
||||||
result.IPList = ips
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
revHost := d.Config.AutoHosts.ProcessReverse(host, qtype)
|
|
||||||
if len(revHost) != 0 {
|
|
||||||
result.Reason = RewriteEtcHosts
|
|
||||||
result.ReverseHost = revHost + "."
|
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,6 +393,31 @@ func (d *Dnsfilter) CheckHost(host string, qtype uint16, setts *RequestFiltering
|
||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Dnsfilter) checkAutoHosts(host string, qtype uint16, result *Result) (matched bool, err error) {
|
||||||
|
ips := d.Config.AutoHosts.Process(host, qtype)
|
||||||
|
if ips != nil {
|
||||||
|
result.Reason = RewriteEtcHosts
|
||||||
|
result.IPList = ips
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
revHosts := d.Config.AutoHosts.ProcessReverse(host, qtype)
|
||||||
|
if len(revHosts) != 0 {
|
||||||
|
result.Reason = RewriteEtcHosts
|
||||||
|
|
||||||
|
// TODO(a.garipov): Optimize this with a buffer.
|
||||||
|
result.ReverseHosts = make([]string, len(revHosts))
|
||||||
|
for i := range revHosts {
|
||||||
|
result.ReverseHosts[i] = revHosts[i] + "."
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Process rewrites table
|
// Process rewrites table
|
||||||
// . Find CNAME for a domain name (exact match or by wildcard)
|
// . Find CNAME for a domain name (exact match or by wildcard)
|
||||||
// . if found and CNAME equals to domain name - this is an exception; exit
|
// . if found and CNAME equals to domain name - this is an exception; exit
|
||||||
|
|
|
@ -54,24 +54,28 @@ func (s *Server) filterDNSRequest(ctx *dnsContext) (*dnsfilter.Result, error) {
|
||||||
} else if res.IsFiltered {
|
} else if res.IsFiltered {
|
||||||
log.Tracef("Host %s is filtered, reason - %q, matched rule: %q", host, res.Reason, res.Rule)
|
log.Tracef("Host %s is filtered, reason - %q, matched rule: %q", host, res.Reason, res.Rule)
|
||||||
d.Res = s.genDNSFilterMessage(d, &res)
|
d.Res = s.genDNSFilterMessage(d, &res)
|
||||||
|
|
||||||
} else if res.Reason == dnsfilter.ReasonRewrite && len(res.CanonName) != 0 && len(res.IPList) == 0 {
|
} else if res.Reason == dnsfilter.ReasonRewrite && len(res.CanonName) != 0 && len(res.IPList) == 0 {
|
||||||
ctx.origQuestion = d.Req.Question[0]
|
ctx.origQuestion = d.Req.Question[0]
|
||||||
// resolve canonical name, not the original host name
|
// resolve canonical name, not the original host name
|
||||||
d.Req.Question[0].Name = dns.Fqdn(res.CanonName)
|
d.Req.Question[0].Name = dns.Fqdn(res.CanonName)
|
||||||
|
} else if res.Reason == dnsfilter.RewriteEtcHosts && len(res.ReverseHosts) != 0 {
|
||||||
} else if res.Reason == dnsfilter.RewriteEtcHosts && len(res.ReverseHost) != 0 {
|
|
||||||
|
|
||||||
resp := s.makeResponse(req)
|
resp := s.makeResponse(req)
|
||||||
ptr := &dns.PTR{}
|
for _, h := range res.ReverseHosts {
|
||||||
ptr.Hdr = dns.RR_Header{
|
hdr := dns.RR_Header{
|
||||||
Name: req.Question[0].Name,
|
Name: req.Question[0].Name,
|
||||||
Rrtype: dns.TypePTR,
|
Rrtype: dns.TypePTR,
|
||||||
Ttl: s.conf.BlockedResponseTTL,
|
Ttl: s.conf.BlockedResponseTTL,
|
||||||
Class: dns.ClassINET,
|
Class: dns.ClassINET,
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr := &dns.PTR{
|
||||||
|
Hdr: hdr,
|
||||||
|
Ptr: h,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Answer = append(resp.Answer, ptr)
|
||||||
}
|
}
|
||||||
ptr.Ptr = res.ReverseHost
|
|
||||||
resp.Answer = append(resp.Answer, ptr)
|
|
||||||
d.Res = resp
|
d.Res = resp
|
||||||
} else if res.Reason == dnsfilter.ReasonRewrite || res.Reason == dnsfilter.RewriteEtcHosts {
|
} else if res.Reason == dnsfilter.ReasonRewrite || res.Reason == dnsfilter.RewriteEtcHosts {
|
||||||
resp := s.makeResponse(req)
|
resp := s.makeResponse(req)
|
||||||
|
|
|
@ -606,22 +606,25 @@ func (clients *clientsContainer) rmHosts(source clientSource) int {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill clients array from system hosts-file
|
// addFromHostsFile fills the clients hosts list from the system's hosts files.
|
||||||
func (clients *clientsContainer) addFromHostsFile() {
|
func (clients *clientsContainer) addFromHostsFile() {
|
||||||
hosts := clients.autoHosts.List()
|
hosts := clients.autoHosts.List()
|
||||||
|
|
||||||
clients.lock.Lock()
|
clients.lock.Lock()
|
||||||
defer clients.lock.Unlock()
|
defer clients.lock.Unlock()
|
||||||
|
|
||||||
_ = clients.rmHosts(ClientSourceHostsFile)
|
_ = clients.rmHosts(ClientSourceHostsFile)
|
||||||
|
|
||||||
n := 0
|
n := 0
|
||||||
for ip, name := range hosts {
|
for ip, names := range hosts {
|
||||||
ok, err := clients.addHost(ip, name, ClientSourceHostsFile)
|
for _, name := range names {
|
||||||
if err != nil {
|
ok, err := clients.addHost(ip, name, ClientSourceHostsFile)
|
||||||
log.Debug("Clients: %s", err)
|
if err != nil {
|
||||||
}
|
log.Debug("Clients: %s", err)
|
||||||
if ok {
|
}
|
||||||
n++
|
if ok {
|
||||||
|
n++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,14 @@ type onChangedT func()
|
||||||
|
|
||||||
// AutoHosts - automatic DNS records
|
// AutoHosts - automatic DNS records
|
||||||
type AutoHosts struct {
|
type AutoHosts struct {
|
||||||
lock sync.Mutex // serialize access to table
|
// lock protects table and tableReverse.
|
||||||
table map[string][]net.IP // 'hostname -> IP' table
|
lock sync.Mutex
|
||||||
tableReverse map[string]string // "IP -> hostname" table for reverse lookup
|
// 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
|
hostsFn string // path to the main hosts-file
|
||||||
hostsDirs []string // paths to OS-specific directories with hosts-files
|
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
|
return ipsCopy
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessReverse - process PTR request
|
// ProcessReverse processes a PTR request. It returns nil if nothing is found.
|
||||||
// Return "" if not found or an error occurred
|
func (a *AutoHosts) ProcessReverse(addr string, qtype uint16) (hosts []string) {
|
||||||
func (a *AutoHosts) ProcessReverse(addr string, qtype uint16) string {
|
|
||||||
if qtype != dns.TypePTR {
|
if qtype != dns.TypePTR {
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ipReal := DNSUnreverseAddr(addr)
|
ipReal := DNSUnreverseAddr(addr)
|
||||||
if ipReal == nil {
|
if ipReal == nil {
|
||||||
return "" // invalid IP in question
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ipStr := ipReal.String()
|
ipStr := ipReal.String()
|
||||||
|
|
||||||
a.lock.Lock()
|
a.lock.Lock()
|
||||||
host := a.tableReverse[ipStr]
|
defer a.lock.Unlock()
|
||||||
a.lock.Unlock()
|
|
||||||
|
|
||||||
if len(host) == 0 {
|
hosts = a.tableReverse[ipStr]
|
||||||
return "" // not found
|
|
||||||
|
if len(hosts) == 0 {
|
||||||
|
return nil // not found
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("AutoHosts: reverse-lookup: %s -> %s", addr, host)
|
log.Debug("AutoHosts: reverse-lookup: %s -> %s", addr, hosts)
|
||||||
return host
|
|
||||||
|
return hosts
|
||||||
}
|
}
|
||||||
|
|
||||||
// List - get "IP -> hostname" table. Thread-safe.
|
// List returns an IP-to-hostnames table. It is safe for concurrent use.
|
||||||
func (a *AutoHosts) List() map[string]string {
|
func (a *AutoHosts) List() (ipToHosts map[string][]string) {
|
||||||
table := make(map[string]string)
|
|
||||||
a.lock.Lock()
|
a.lock.Lock()
|
||||||
|
defer a.lock.Unlock()
|
||||||
|
|
||||||
|
ipToHosts = make(map[string][]string, len(a.tableReverse))
|
||||||
for k, v := range a.tableReverse {
|
for k, v := range a.tableReverse {
|
||||||
table[k] = v
|
ipToHosts[k] = v
|
||||||
}
|
}
|
||||||
a.lock.Unlock()
|
|
||||||
return table
|
return ipToHosts
|
||||||
}
|
}
|
||||||
|
|
||||||
// update table
|
// update table
|
||||||
|
@ -187,19 +196,30 @@ func (a *AutoHosts) updateTable(table map[string][]net.IP, host string, ipAddr n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// update "reverse" table
|
// updateTableRev updates the reverse address table.
|
||||||
func (a *AutoHosts) updateTableRev(tableRev map[string]string, host string, ipAddr net.IP) {
|
func (a *AutoHosts) updateTableRev(tableRev map[string][]string, newHost string, ipAddr net.IP) {
|
||||||
ipStr := ipAddr.String()
|
ipStr := ipAddr.String()
|
||||||
_, ok := tableRev[ipStr]
|
hosts, ok := tableRev[ipStr]
|
||||||
if !ok {
|
if !ok {
|
||||||
tableRev[ipStr] = host
|
tableRev[ipStr] = []string{newHost}
|
||||||
log.Debug("AutoHosts: added reverse-address %s -> %s", ipStr, host)
|
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
|
// Read IP-hostname pairs from file
|
||||||
// Multiple hostnames per line (per one IP) is supported.
|
// 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)
|
f, err := os.Open(fn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("AutoHosts: %s", err)
|
log.Error("AutoHosts: %s", err)
|
||||||
|
@ -306,7 +326,7 @@ func (a *AutoHosts) updateLoop() {
|
||||||
// updateHosts - loads system hosts
|
// updateHosts - loads system hosts
|
||||||
func (a *AutoHosts) updateHosts() {
|
func (a *AutoHosts) updateHosts() {
|
||||||
table := make(map[string][]net.IP)
|
table := make(map[string][]net.IP)
|
||||||
tableRev := make(map[string]string)
|
tableRev := make(map[string][]string)
|
||||||
|
|
||||||
a.load(table, tableRev, a.hostsFn)
|
a.load(table, tableRev, a.hostsFn)
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
func prepareTestDir() string {
|
func prepareTestDir() string {
|
||||||
const dir = "./agh-test"
|
const dir = "./agh-test"
|
||||||
_ = os.RemoveAll(dir)
|
_ = os.RemoveAll(dir)
|
||||||
_ = os.MkdirAll(dir, 0755)
|
_ = os.MkdirAll(dir, 0o755)
|
||||||
return dir
|
return dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,17 +50,24 @@ func TestAutoHostsResolution(t *testing.T) {
|
||||||
|
|
||||||
// Test hosts file
|
// Test hosts file
|
||||||
table := ah.List()
|
table := ah.List()
|
||||||
name, ok := table["127.0.0.1"]
|
names, ok := table["127.0.0.1"]
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.Equal(t, "host", name)
|
assert.Equal(t, []string{"host", "localhost"}, names)
|
||||||
|
|
||||||
// Test PTR
|
// Test PTR
|
||||||
a, _ := dns.ReverseAddr("127.0.0.1")
|
a, _ := dns.ReverseAddr("127.0.0.1")
|
||||||
a = strings.TrimSuffix(a, ".")
|
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, _ = dns.ReverseAddr("::1")
|
||||||
a = strings.TrimSuffix(a, ".")
|
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) {
|
func TestAutoHostsFSNotify(t *testing.T) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue