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:
parent
7c6557b05e
commit
e671f43a2f
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
|
@ -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]))
|
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
Loading…
Reference in New Issue