Pull request: 1947 disable autohosts option

Merge in DNS/adguard-home from 1947-hosts-opt to master

Updates #1947.
Updates #2829.

Squashed commit of the following:

commit d09285c3dbfa7816469eec223b88c320c255c8fe
Merge: cff8c4cd 7c6557b0
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Apr 12 18:23:20 2021 +0300

    Merge branch 'master' into 1947-hosts-opt

commit cff8c4cdbf4bcd1f5f413c541d7f4a9e42b8b05b
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Apr 12 17:46:19 2021 +0300

    home: fix help

commit 1fa01d5b30f5adeda564dcc85a7064e2921d5981
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Apr 12 17:40:48 2021 +0300

    home: fix option order

commit 9d83cb604aaddcc8cbe99bafa544636f8f0b7e54
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Apr 12 17:28:30 2021 +0300

    aghnet: add important todo

commit 7f1386ff5c3081e07e975b640164a7a05e1319c9
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Apr 12 17:17:17 2021 +0300

    all: correct naming

commit cbe2b2e4b21d5bceb3ee88e09cad154ba62b5cef
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Apr 12 15:55:46 2021 +0300

    all: mv functionality from util

commit e82ad53862682d903dd0dd10844db65997a758bc
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Apr 12 15:41:35 2021 +0300

    home: imp code, docs

commit 9058977f3ff99648fabaebbd7c1c354c71671327
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Mon Apr 12 15:02:34 2021 +0300

    home: add an option to disable autohosts
This commit is contained in:
Eugene Burkov 2021-04-12 18:31:45 +03:00
parent 7c6557b05e
commit e671f43a2f
10 changed files with 178 additions and 137 deletions

View File

@ -15,6 +15,8 @@ and this project adheres to
### Added ### Added
- New flag `--no-etc-hosts` to disable client domain name lookups in the
operating system's /etc/hosts files ([#1947]).
- The ability to set up custom upstreams to resolve PTR queries for local - The ability to set up custom upstreams to resolve PTR queries for local
addresses and to disable the automatic resolving of clients' addresses addresses and to disable the automatic resolving of clients' addresses
([#2704]). ([#2704]).
@ -54,6 +56,7 @@ and this project adheres to
[#1273]: https://github.com/AdguardTeam/AdGuardHome/issues/1273 [#1273]: https://github.com/AdguardTeam/AdGuardHome/issues/1273
[#1401]: https://github.com/AdguardTeam/AdGuardHome/issues/1401 [#1401]: https://github.com/AdguardTeam/AdGuardHome/issues/1401
[#1947]: https://github.com/AdguardTeam/AdGuardHome/issues/1947
[#2385]: https://github.com/AdguardTeam/AdGuardHome/issues/2385 [#2385]: https://github.com/AdguardTeam/AdGuardHome/issues/2385
[#2393]: https://github.com/AdguardTeam/AdGuardHome/issues/2393 [#2393]: https://github.com/AdguardTeam/AdGuardHome/issues/2393
[#2412]: https://github.com/AdguardTeam/AdGuardHome/issues/2412 [#2412]: https://github.com/AdguardTeam/AdGuardHome/issues/2412

View File

@ -1,8 +1,4 @@
// Package util contains various utilities. package aghnet
//
// TODO(a.garipov): Such packages are widely considered an antipattern. Remove
// this when we refactor our project structure.
package util
import ( import (
"bufio" "bufio"
@ -16,7 +12,6 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/AdGuardHome/internal/aghos" "github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
@ -25,8 +20,11 @@ import (
type onChangedT func() type onChangedT func()
// AutoHosts - automatic DNS records // EtcHostsContainer - automatic DNS records
type AutoHosts struct { //
// TODO(e.burkov): Move the logic under interface. Refactor. Probably remove
// the resolving logic.
type EtcHostsContainer struct {
// lock protects table and tableReverse. // lock protects table and tableReverse.
lock sync.RWMutex lock sync.RWMutex
// table is the host-to-IPs map. // table is the host-to-IPs map.
@ -47,63 +45,67 @@ type AutoHosts struct {
} }
// SetOnChanged - set callback function that will be called when the data is changed // SetOnChanged - set callback function that will be called when the data is changed
func (a *AutoHosts) SetOnChanged(onChanged onChangedT) { func (ehc *EtcHostsContainer) SetOnChanged(onChanged onChangedT) {
a.onChanged = onChanged ehc.onChanged = onChanged
} }
// Notify other modules // Notify other modules
func (a *AutoHosts) notify() { func (ehc *EtcHostsContainer) notify() {
if a.onChanged == nil { if ehc.onChanged == nil {
return return
} }
a.onChanged() ehc.onChanged()
} }
// Init - initialize // Init - initialize
// hostsFn: Override default name for the hosts-file (optional) // hostsFn: Override default name for the hosts-file (optional)
func (a *AutoHosts) Init(hostsFn string) { func (ehc *EtcHostsContainer) Init(hostsFn string) {
a.table = make(map[string][]net.IP) ehc.table = make(map[string][]net.IP)
a.onlyWritesChan = make(chan fsnotify.Event, 2) ehc.onlyWritesChan = make(chan fsnotify.Event, 2)
a.hostsFn = "/etc/hosts" ehc.hostsFn = "/etc/hosts"
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
a.hostsFn = os.ExpandEnv("$SystemRoot\\system32\\drivers\\etc\\hosts") ehc.hostsFn = os.ExpandEnv("$SystemRoot\\system32\\drivers\\etc\\hosts")
} }
if len(hostsFn) != 0 { if len(hostsFn) != 0 {
a.hostsFn = hostsFn ehc.hostsFn = hostsFn
} }
if aghos.IsOpenWrt() { if aghos.IsOpenWrt() {
// OpenWrt: "/tmp/hosts/dhcp.cfg01411c". // OpenWrt: "/tmp/hosts/dhcp.cfg01411c".
a.hostsDirs = append(a.hostsDirs, "/tmp/hosts") ehc.hostsDirs = append(ehc.hostsDirs, "/tmp/hosts")
} }
// Load hosts initially // Load hosts initially
a.updateHosts() ehc.updateHosts()
var err error var err error
a.watcher, err = fsnotify.NewWatcher() ehc.watcher, err = fsnotify.NewWatcher()
if err != nil { if err != nil {
log.Error("autohosts: %s", err) log.Error("etchostscontainer: %s", err)
} }
} }
// Start - start module // Start - start module
func (a *AutoHosts) Start() { func (ehc *EtcHostsContainer) Start() {
log.Debug("Start AutoHosts module") if ehc == nil {
return
}
a.updateHosts() log.Debug("Start etchostscontainer module")
if a.watcher != nil { ehc.updateHosts()
go a.watcherLoop()
err := a.watcher.Add(a.hostsFn) if ehc.watcher != nil {
go ehc.watcherLoop()
err := ehc.watcher.Add(ehc.hostsFn)
if err != nil { if err != nil {
log.Error("Error while initializing watcher for a file %s: %s", a.hostsFn, err) log.Error("Error while initializing watcher for a file %s: %s", ehc.hostsFn, err)
} }
for _, dir := range a.hostsDirs { for _, dir := range ehc.hostsDirs {
err = a.watcher.Add(dir) err = ehc.watcher.Add(dir)
if err != nil { if err != nil {
log.Error("Error while initializing watcher for a directory %s: %s", dir, err) log.Error("Error while initializing watcher for a directory %s: %s", dir, err)
} }
@ -112,67 +114,71 @@ func (a *AutoHosts) Start() {
} }
// Close - close module // Close - close module
func (a *AutoHosts) Close() { func (ehc *EtcHostsContainer) Close() {
if a.watcher != nil { if ehc == nil {
_ = a.watcher.Close() return
} }
close(a.onlyWritesChan)
if ehc.watcher != nil {
_ = ehc.watcher.Close()
}
close(ehc.onlyWritesChan)
} }
// Process returns the list of IP addresses for the hostname or nil if nothing // Process returns the list of IP addresses for the hostname or nil if nothing
// found. // found.
func (a *AutoHosts) Process(host string, qtype uint16) []net.IP { func (ehc *EtcHostsContainer) Process(host string, qtype uint16) []net.IP {
if qtype == dns.TypePTR { if qtype == dns.TypePTR {
return nil return nil
} }
var ipsCopy []net.IP var ipsCopy []net.IP
a.lock.RLock() ehc.lock.RLock()
defer a.lock.RUnlock() defer ehc.lock.RUnlock()
if ips, ok := a.table[host]; ok { if ips, ok := ehc.table[host]; ok {
ipsCopy = make([]net.IP, len(ips)) ipsCopy = make([]net.IP, len(ips))
copy(ipsCopy, ips) copy(ipsCopy, ips)
} }
log.Debug("autohosts: answer: %s -> %v", host, ipsCopy) log.Debug("etchostscontainer: answer: %s -> %v", host, ipsCopy)
return ipsCopy return ipsCopy
} }
// ProcessReverse processes a PTR request. It returns nil if nothing is found. // ProcessReverse processes a PTR request. It returns nil if nothing is found.
func (a *AutoHosts) ProcessReverse(addr string, qtype uint16) (hosts []string) { func (ehc *EtcHostsContainer) ProcessReverse(addr string, qtype uint16) (hosts []string) {
if qtype != dns.TypePTR { if qtype != dns.TypePTR {
return nil return nil
} }
ipReal := aghnet.UnreverseAddr(addr) ipReal := UnreverseAddr(addr)
if ipReal == nil { if ipReal == nil {
return nil return nil
} }
ipStr := ipReal.String() ipStr := ipReal.String()
a.lock.RLock() ehc.lock.RLock()
defer a.lock.RUnlock() defer ehc.lock.RUnlock()
hosts = a.tableReverse[ipStr] hosts = ehc.tableReverse[ipStr]
if len(hosts) == 0 { if len(hosts) == 0 {
return nil // not found return nil // not found
} }
log.Debug("autohosts: reverse-lookup: %s -> %s", addr, hosts) log.Debug("etchostscontainer: reverse-lookup: %s -> %s", addr, hosts)
return hosts return hosts
} }
// List returns an IP-to-hostnames table. It is safe for concurrent use. // List returns an IP-to-hostnames table. It is safe for concurrent use.
func (a *AutoHosts) List() (ipToHosts map[string][]string) { func (ehc *EtcHostsContainer) List() (ipToHosts map[string][]string) {
a.lock.RLock() ehc.lock.RLock()
defer a.lock.RUnlock() defer ehc.lock.RUnlock()
ipToHosts = make(map[string][]string, len(a.tableReverse)) ipToHosts = make(map[string][]string, len(ehc.tableReverse))
for k, v := range a.tableReverse { for k, v := range ehc.tableReverse {
ipToHosts[k] = v ipToHosts[k] = v
} }
@ -180,7 +186,7 @@ func (a *AutoHosts) List() (ipToHosts map[string][]string) {
} }
// update table // update table
func (a *AutoHosts) updateTable(table map[string][]net.IP, host string, ipAddr net.IP) { func (ehc *EtcHostsContainer) updateTable(table map[string][]net.IP, host string, ipAddr net.IP) {
ips, ok := table[host] ips, ok := table[host]
if ok { if ok {
for _, ip := range ips { for _, ip := range ips {
@ -199,17 +205,17 @@ func (a *AutoHosts) updateTable(table map[string][]net.IP, host string, ipAddr n
ok = true ok = true
} }
if ok { if ok {
log.Debug("autohosts: added %s -> %s", ipAddr, host) log.Debug("etchostscontainer: added %s -> %s", ipAddr, host)
} }
} }
// updateTableRev updates the reverse address table. // updateTableRev updates the reverse address table.
func (a *AutoHosts) updateTableRev(tableRev map[string][]string, newHost string, ipAddr net.IP) { func (ehc *EtcHostsContainer) updateTableRev(tableRev map[string][]string, newHost string, ipAddr net.IP) {
ipStr := ipAddr.String() ipStr := ipAddr.String()
hosts, ok := tableRev[ipStr] hosts, ok := tableRev[ipStr]
if !ok { if !ok {
tableRev[ipStr] = []string{newHost} tableRev[ipStr] = []string{newHost}
log.Debug("autohosts: added reverse-address %s -> %s", ipStr, newHost) log.Debug("etchostscontainer: added reverse-address %s -> %s", ipStr, newHost)
return return
} }
@ -221,20 +227,20 @@ func (a *AutoHosts) updateTableRev(tableRev map[string][]string, newHost string,
} }
tableRev[ipStr] = append(tableRev[ipStr], newHost) tableRev[ipStr] = append(tableRev[ipStr], newHost)
log.Debug("autohosts: added reverse-address %s -> %s", ipStr, newHost) log.Debug("etchostscontainer: 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 (ehc *EtcHostsContainer) 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("etchostscontainer: %s", err)
return return
} }
defer f.Close() defer f.Close()
r := bufio.NewReader(f) r := bufio.NewReader(f)
log.Debug("autohosts: loading hosts from file %s", fn) log.Debug("etchostscontainer: loading hosts from file %s", fn)
for done := false; !done; { for done := false; !done; {
var line string var line string
@ -242,7 +248,7 @@ func (a *AutoHosts) load(table map[string][]net.IP, tableRev map[string][]string
if err == io.EOF { if err == io.EOF {
done = true done = true
} else if err != nil { } else if err != nil {
log.Error("autohosts: %s", err) log.Error("etchostscontainer: %s", err)
return return
} }
@ -276,8 +282,8 @@ func (a *AutoHosts) load(table map[string][]net.IP, tableRev map[string][]string
host = host[:sharp] host = host[:sharp]
} }
a.updateTable(table, host, ip) ehc.updateTable(table, host, ip)
a.updateTableRev(tableRev, host, ip) ehc.updateTableRev(tableRev, host, ip)
if sharp >= 0 { if sharp >= 0 {
// Skip the comments again. // Skip the comments again.
break break
@ -287,20 +293,20 @@ func (a *AutoHosts) load(table map[string][]net.IP, tableRev map[string][]string
} }
// onlyWrites is a filter for (*fsnotify.Watcher).Events. // onlyWrites is a filter for (*fsnotify.Watcher).Events.
func (a *AutoHosts) onlyWrites() { func (ehc *EtcHostsContainer) onlyWrites() {
for event := range a.watcher.Events { for event := range ehc.watcher.Events {
if event.Op&fsnotify.Write == fsnotify.Write { if event.Op&fsnotify.Write == fsnotify.Write {
a.onlyWritesChan <- event ehc.onlyWritesChan <- event
} }
} }
} }
// Receive notifications from fsnotify package // Receive notifications from fsnotify package
func (a *AutoHosts) watcherLoop() { func (ehc *EtcHostsContainer) watcherLoop() {
go a.onlyWrites() go ehc.onlyWrites()
for { for {
select { select {
case event, ok := <-a.onlyWritesChan: case event, ok := <-ehc.onlyWritesChan:
if !ok { if !ok {
return return
} }
@ -310,7 +316,7 @@ func (a *AutoHosts) watcherLoop() {
repeat := true repeat := true
for repeat { for repeat {
select { select {
case _, ok = <-a.onlyWritesChan: case _, ok = <-ehc.onlyWritesChan:
repeat = ok repeat = ok
default: default:
repeat = false repeat = false
@ -318,48 +324,48 @@ func (a *AutoHosts) watcherLoop() {
} }
if event.Op&fsnotify.Write == fsnotify.Write { if event.Op&fsnotify.Write == fsnotify.Write {
log.Debug("autohosts: modified: %s", event.Name) log.Debug("etchostscontainer: modified: %s", event.Name)
a.updateHosts() ehc.updateHosts()
} }
case err, ok := <-a.watcher.Errors: case err, ok := <-ehc.watcher.Errors:
if !ok { if !ok {
return return
} }
log.Error("autohosts: %s", err) log.Error("etchostscontainer: %s", err)
} }
} }
} }
// updateHosts - loads system hosts // updateHosts - loads system hosts
func (a *AutoHosts) updateHosts() { func (ehc *EtcHostsContainer) 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) ehc.load(table, tableRev, ehc.hostsFn)
for _, dir := range a.hostsDirs { for _, dir := range ehc.hostsDirs {
fis, err := ioutil.ReadDir(dir) fis, err := ioutil.ReadDir(dir)
if err != nil { if err != nil {
if !errors.Is(err, os.ErrNotExist) { if !errors.Is(err, os.ErrNotExist) {
log.Error("autohosts: Opening directory: %q: %s", dir, err) log.Error("etchostscontainer: Opening directory: %q: %s", dir, err)
} }
continue continue
} }
for _, fi := range fis { for _, fi := range fis {
a.load(table, tableRev, filepath.Join(dir, fi.Name())) ehc.load(table, tableRev, filepath.Join(dir, fi.Name()))
} }
} }
func() { func() {
a.lock.Lock() ehc.lock.Lock()
defer a.lock.Unlock() defer ehc.lock.Unlock()
a.table = table ehc.table = table
a.tableReverse = tableRev ehc.tableReverse = tableRev
}() }()
a.notify() ehc.notify()
} }

View File

@ -1,4 +1,4 @@
package util package aghnet
import ( import (
"io/ioutil" "io/ioutil"
@ -43,8 +43,8 @@ func assertWriting(t *testing.T, f *os.File, strs ...string) {
} }
} }
func TestAutoHostsResolution(t *testing.T) { func TestEtcHostsContainerResolution(t *testing.T) {
ah := &AutoHosts{} ehc := &EtcHostsContainer{}
f := prepareTestFile(t) f := prepareTestFile(t)
@ -52,25 +52,25 @@ func TestAutoHostsResolution(t *testing.T) {
" 127.0.0.1 host localhost # comment \n", " 127.0.0.1 host localhost # comment \n",
" ::1 localhost#comment \n", " ::1 localhost#comment \n",
) )
ah.Init(f.Name()) ehc.Init(f.Name())
t.Run("existing_host", func(t *testing.T) { t.Run("existing_host", func(t *testing.T) {
ips := ah.Process("localhost", dns.TypeA) ips := ehc.Process("localhost", dns.TypeA)
require.Len(t, ips, 1) require.Len(t, ips, 1)
assert.Equal(t, net.IPv4(127, 0, 0, 1), ips[0]) assert.Equal(t, net.IPv4(127, 0, 0, 1), ips[0])
}) })
t.Run("unknown_host", func(t *testing.T) { t.Run("unknown_host", func(t *testing.T) {
ips := ah.Process("newhost", dns.TypeA) ips := ehc.Process("newhost", dns.TypeA)
assert.Nil(t, ips) assert.Nil(t, ips)
// Comment. // Comment.
ips = ah.Process("comment", dns.TypeA) ips = ehc.Process("comment", dns.TypeA)
assert.Nil(t, ips) assert.Nil(t, ips)
}) })
t.Run("hosts_file", func(t *testing.T) { t.Run("hosts_file", func(t *testing.T) {
names, ok := ah.List()["127.0.0.1"] names, ok := ehc.List()["127.0.0.1"]
require.True(t, ok) require.True(t, ok)
assert.Equal(t, []string{"host", "localhost"}, names) assert.Equal(t, []string{"host", "localhost"}, names)
}) })
@ -90,29 +90,29 @@ func TestAutoHostsResolution(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
a = strings.TrimSuffix(a, ".") a = strings.TrimSuffix(a, ".")
hosts := ah.ProcessReverse(a, dns.TypePTR) hosts := ehc.ProcessReverse(a, dns.TypePTR)
require.Len(t, hosts, tc.wantLen) require.Len(t, hosts, tc.wantLen)
assert.Equal(t, tc.wantHost, hosts[0]) assert.Equal(t, tc.wantHost, hosts[0])
} }
}) })
} }
func TestAutoHostsFSNotify(t *testing.T) { func TestEtcHostsContainerFSNotify(t *testing.T) {
ah := &AutoHosts{} ehc := &EtcHostsContainer{}
f := prepareTestFile(t) f := prepareTestFile(t)
assertWriting(t, f, " 127.0.0.1 host localhost \n") assertWriting(t, f, " 127.0.0.1 host localhost \n")
ah.Init(f.Name()) ehc.Init(f.Name())
t.Run("unknown_host", func(t *testing.T) { t.Run("unknown_host", func(t *testing.T) {
ips := ah.Process("newhost", dns.TypeA) ips := ehc.Process("newhost", dns.TypeA)
assert.Nil(t, ips) assert.Nil(t, ips)
}) })
// Start monitoring for changes. // Start monitoring for changes.
ah.Start() ehc.Start()
t.Cleanup(ah.Close) t.Cleanup(ehc.Close)
assertWriting(t, f, "127.0.0.2 newhost\n") assertWriting(t, f, "127.0.0.2 newhost\n")
require.Nil(t, f.Sync()) require.Nil(t, f.Sync())
@ -122,7 +122,7 @@ func TestAutoHostsFSNotify(t *testing.T) {
time.Sleep(50 * time.Millisecond) time.Sleep(50 * time.Millisecond)
t.Run("notified", func(t *testing.T) { t.Run("notified", func(t *testing.T) {
ips := ah.Process("newhost", dns.TypeA) ips := ehc.Process("newhost", dns.TypeA)
assert.NotNil(t, ips) assert.NotNil(t, ips)
require.Len(t, ips, 1) require.Len(t, ips, 1)
assert.True(t, net.IP{127, 0, 0, 2}.Equal(ips[0])) assert.True(t, net.IP{127, 0, 0, 2}.Equal(ips[0]))

View File

@ -13,7 +13,7 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/AdguardTeam/AdGuardHome/internal/util" "github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/cache" "github.com/AdguardTeam/golibs/cache"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
@ -65,8 +65,9 @@ type Config struct {
// Per-client settings can override this configuration. // Per-client settings can override this configuration.
BlockedServices []string `yaml:"blocked_services"` BlockedServices []string `yaml:"blocked_services"`
// IP-hostname pairs taken from system configuration (e.g. /etc/hosts) files // EtcHosts is a container of IP-hostname pairs taken from the operating
AutoHosts *util.AutoHosts `yaml:"-"` // system configuration files (e.g. /etc/hosts).
EtcHosts *aghnet.EtcHostsContainer `yaml:"-"`
// Called when the configuration is changed by HTTP request // Called when the configuration is changed by HTTP request
ConfigModified func() `yaml:"-"` ConfigModified func() `yaml:"-"`
@ -428,11 +429,11 @@ func (d *DNSFilter) checkAutoHosts(
qtype uint16, qtype uint16,
_ *FilteringSettings, _ *FilteringSettings,
) (res Result, err error) { ) (res Result, err error) {
if d.Config.AutoHosts == nil { if d.Config.EtcHosts == nil {
return Result{}, nil return Result{}, nil
} }
ips := d.Config.AutoHosts.Process(host, qtype) ips := d.Config.EtcHosts.Process(host, qtype)
if ips != nil { if ips != nil {
res = Result{ res = Result{
Reason: RewrittenAutoHosts, Reason: RewrittenAutoHosts,
@ -442,7 +443,7 @@ func (d *DNSFilter) checkAutoHosts(
return res, nil return res, nil
} }
revHosts := d.Config.AutoHosts.ProcessReverse(host, qtype) revHosts := d.Config.EtcHosts.ProcessReverse(host, qtype)
if len(revHosts) != 0 { if len(revHosts) != 0 {
res = Result{ res = Result{
Reason: RewrittenAutoHosts, Reason: RewrittenAutoHosts,

View File

@ -23,7 +23,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghtest" "github.com/AdguardTeam/AdGuardHome/internal/aghtest"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd" "github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter" "github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
"github.com/AdguardTeam/AdGuardHome/internal/util"
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/dnsproxy/upstream"
"github.com/miekg/dns" "github.com/miekg/dns"
@ -1074,7 +1073,7 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
func TestPTRResponseFromHosts(t *testing.T) { func TestPTRResponseFromHosts(t *testing.T) {
c := dnsfilter.Config{ c := dnsfilter.Config{
AutoHosts: &util.AutoHosts{}, EtcHosts: &aghnet.EtcHostsContainer{},
} }
// Prepare test hosts file. // Prepare test hosts file.
@ -1090,8 +1089,8 @@ func TestPTRResponseFromHosts(t *testing.T) {
_, _ = hf.WriteString(" ::1 localhost#comment \n") _, _ = hf.WriteString(" ::1 localhost#comment \n")
// Init auto hosts. // Init auto hosts.
c.AutoHosts.Init(hf.Name()) c.EtcHosts.Init(hf.Name())
t.Cleanup(c.AutoHosts.Close) t.Cleanup(c.EtcHosts.Close)
var snd *aghnet.SubnetDetector var snd *aghnet.SubnetDetector
snd, err = aghnet.NewSubnetDetector() snd, err = aghnet.NewSubnetDetector()

View File

@ -18,7 +18,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/dnsfilter" "github.com/AdguardTeam/AdGuardHome/internal/dnsfilter"
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward" "github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
"github.com/AdguardTeam/AdGuardHome/internal/querylog" "github.com/AdguardTeam/AdGuardHome/internal/querylog"
"github.com/AdguardTeam/AdGuardHome/internal/util"
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
@ -92,7 +91,7 @@ type clientsContainer struct {
// dnsServer is used for checking clients IP status access list status // dnsServer is used for checking clients IP status access list status
dnsServer *dnsforward.Server dnsServer *dnsforward.Server
autoHosts *util.AutoHosts // get entries from system hosts-files etcHosts *aghnet.EtcHostsContainer // get entries from system hosts-files
testing bool // if TRUE, this object is used for internal tests testing bool // if TRUE, this object is used for internal tests
} }
@ -100,7 +99,11 @@ type clientsContainer struct {
// Init initializes clients container // Init initializes clients container
// dhcpServer: optional // dhcpServer: optional
// Note: this function must be called only once // Note: this function must be called only once
func (clients *clientsContainer) Init(objects []clientObject, dhcpServer *dhcpd.Server, autoHosts *util.AutoHosts) { func (clients *clientsContainer) Init(
objects []clientObject,
dhcpServer *dhcpd.Server,
etcHosts *aghnet.EtcHostsContainer,
) {
if clients.list != nil { if clients.list != nil {
log.Fatal("clients.list != nil") log.Fatal("clients.list != nil")
} }
@ -114,7 +117,9 @@ func (clients *clientsContainer) Init(objects []clientObject, dhcpServer *dhcpd.
} }
clients.dhcpServer = dhcpServer clients.dhcpServer = dhcpServer
clients.autoHosts = autoHosts if etcHosts != nil {
clients.etcHosts = etcHosts
}
clients.addFromConfig(objects) clients.addFromConfig(objects)
if !clients.testing { if !clients.testing {
@ -122,7 +127,9 @@ func (clients *clientsContainer) Init(objects []clientObject, dhcpServer *dhcpd.
if clients.dhcpServer != nil { if clients.dhcpServer != nil {
clients.dhcpServer.SetOnLeaseChanged(clients.onDHCPLeaseChanged) clients.dhcpServer.SetOnLeaseChanged(clients.onDHCPLeaseChanged)
} }
clients.autoHosts.SetOnChanged(clients.onHostsChanged) if clients.etcHosts != nil {
clients.etcHosts.SetOnChanged(clients.onHostsChanged)
}
} }
} }
@ -692,7 +699,11 @@ func (clients *clientsContainer) rmHostsBySrc(src clientSource) {
// addFromHostsFile fills the client-hostname pairing index from the system's // addFromHostsFile fills the client-hostname pairing index from the system's
// hosts files. // hosts files.
func (clients *clientsContainer) addFromHostsFile() { func (clients *clientsContainer) addFromHostsFile() {
hosts := clients.autoHosts.List() if clients.etcHosts == nil {
return
}
hosts := clients.etcHosts.List()
clients.lock.Lock() clients.lock.Lock()
defer clients.lock.Unlock() defer clients.lock.Unlock()

View File

@ -57,7 +57,7 @@ func initDNSServer() error {
Context.queryLog = querylog.New(conf) Context.queryLog = querylog.New(conf)
filterConf := config.DNS.DnsfilterConf filterConf := config.DNS.DnsfilterConf
filterConf.AutoHosts = &Context.autoHosts filterConf.EtcHosts = Context.etcHosts
filterConf.ConfigModified = onConfigModified filterConf.ConfigModified = onConfigModified
filterConf.HTTPRegister = httpRegister filterConf.HTTPRegister = httpRegister
Context.dnsFilter = dnsfilter.New(&filterConf, nil) Context.dnsFilter = dnsfilter.New(&filterConf, nil)

View File

@ -46,19 +46,19 @@ type homeContext struct {
// Modules // Modules
// -- // --
clients clientsContainer // per-client-settings module clients clientsContainer // per-client-settings module
stats stats.Stats // statistics module stats stats.Stats // statistics module
queryLog querylog.QueryLog // query log module queryLog querylog.QueryLog // query log module
dnsServer *dnsforward.Server // DNS module dnsServer *dnsforward.Server // DNS module
rdns *RDNS // rDNS module rdns *RDNS // rDNS module
whois *Whois // WHOIS module whois *Whois // WHOIS module
dnsFilter *dnsfilter.DNSFilter // DNS filtering module dnsFilter *dnsfilter.DNSFilter // DNS filtering module
dhcpServer *dhcpd.Server // DHCP module dhcpServer *dhcpd.Server // DHCP module
auth *Auth // HTTP authentication module auth *Auth // HTTP authentication module
filters Filtering // DNS filtering module filters Filtering // DNS filtering module
web *Web // Web (HTTP, HTTPS) module web *Web // Web (HTTP, HTTPS) module
tls *TLSMod // TLS module tls *TLSMod // TLS module
autoHosts util.AutoHosts // IP-hostname pairs taken from system configuration (e.g. /etc/hosts) files etcHosts *aghnet.EtcHostsContainer // IP-hostname pairs taken from system configuration (e.g. /etc/hosts) files
updater *updater.Updater updater *updater.Updater
subnetDetector *aghnet.SubnetDetector subnetDetector *aghnet.SubnetDetector
@ -186,8 +186,6 @@ func setupConfig(args options) {
log.Fatalf("can't initialize dhcp module") log.Fatalf("can't initialize dhcp module")
} }
Context.autoHosts.Init("")
Context.updater = updater.NewUpdater(&updater.Config{ Context.updater = updater.NewUpdater(&updater.Config{
Client: Context.client, Client: Context.client,
Version: version.Version(), Version: version.Version(),
@ -200,7 +198,11 @@ func setupConfig(args options) {
ConfName: config.getConfigFilename(), ConfName: config.getConfigFilename(),
}) })
Context.clients.Init(config.Clients, Context.dhcpServer, &Context.autoHosts) if !args.noEtcHosts {
Context.etcHosts = &aghnet.EtcHostsContainer{}
Context.etcHosts.Init("")
Context.clients.Init(config.Clients, Context.dhcpServer, Context.etcHosts)
}
config.Clients = nil config.Clients = nil
if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") && if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") &&
@ -317,7 +319,7 @@ func run(args options) {
} }
Context.tls.Start() Context.tls.Start()
Context.autoHosts.Start() Context.etcHosts.Start()
go func() { go func() {
serr := startDNSServer() serr := startDNSServer()
@ -530,7 +532,7 @@ func cleanup(ctx context.Context) {
Context.dhcpServer.Stop() Context.dhcpServer.Stop()
} }
Context.autoHosts.Close() Context.etcHosts.Close()
if Context.tls != nil { if Context.tls != nil {
Context.tls.Close() Context.tls.Close()

View File

@ -32,6 +32,10 @@ type options struct {
disableMemoryOptimization bool disableMemoryOptimization bool
glinetMode bool // Activate GL-Inet compatibility mode glinetMode bool // Activate GL-Inet compatibility mode
// noEtcHosts flag should be provided when /etc/hosts file shouldn't be
// used.
noEtcHosts bool
} }
// functions used for their side-effects // functions used for their side-effects
@ -191,7 +195,7 @@ var glinetArg = arg{
} }
var versionArg = arg{ var versionArg = arg{
description: "Show the version and exit", description: "Show the version and exit. Show more detailed version description with -v",
longName: "version", longName: "version",
shortName: "", shortName: "",
updateWithValue: nil, updateWithValue: nil,
@ -212,7 +216,7 @@ var versionArg = arg{
} }
var helpArg = arg{ var helpArg = arg{
"Print this help. Show more detailed version description with -v", "Print this help",
"help", "", "help", "",
nil, nil, func(o options, exec string) (effect, error) { nil, nil, func(o options, exec string) (effect, error) {
return func() error { _ = printHelp(exec); os.Exit(64); return nil }, nil return func() error { _ = printHelp(exec); os.Exit(64); return nil }, nil
@ -220,6 +224,16 @@ var helpArg = arg{
func(o options) []string { return nil }, func(o options) []string { return nil },
} }
var noEtcHostsArg = arg{
description: "Do not use the OS-provided hosts.",
longName: "no-etc-hosts",
shortName: "",
updateWithValue: nil,
updateNoValue: func(o options) (options, error) { o.noEtcHosts = true; return o, nil },
effect: nil,
serialize: func(o options) []string { return boolSliceOrNil(o.noEtcHosts) },
}
func init() { func init() {
args = []arg{ args = []arg{
configArg, configArg,
@ -232,6 +246,7 @@ func init() {
checkConfigArg, checkConfigArg,
noCheckUpdateArg, noCheckUpdateArg,
disableMemoryOptimizationArg, disableMemoryOptimizationArg,
noEtcHostsArg,
verboseArg, verboseArg,
glinetArg, glinetArg,
versionArg, versionArg,

View File

@ -1,3 +1,7 @@
// Package util contains various utilities.
//
// TODO(a.garipov): Such packages are widely considered an antipattern. Remove
// this when we refactor our project structure.
package util package util
import ( import (