From d83091fc1fc36d84ee88e4c08a3c928b5cd5456c Mon Sep 17 00:00:00 2001 From: Ainar Garipov Date: Thu, 15 Apr 2021 19:00:31 +0300 Subject: [PATCH] Pull request: all: allow local non-top-level domains Updates #2961. Squashed commit of the following: commit 207eeb85caf6caee81a669302daf4e10a5b61585 Author: Ainar Garipov Date: Thu Apr 15 18:48:50 2021 +0300 all: allow local non-top-level domains --- CHANGELOG.md | 4 ++- internal/dnsfilter/dnsfilter.go | 8 ++--- internal/dnsforward/dns.go | 2 +- internal/dnsforward/dns_test.go | 14 ++++----- internal/dnsforward/dnsforward.go | 42 ++++++++++++++------------ internal/dnsforward/dnsforward_test.go | 22 +++++++++----- internal/home/clientshttp.go | 8 ++--- internal/home/config.go | 8 ++--- internal/home/dns.go | 2 +- internal/home/upgrade.go | 40 +++++++++++++++++++++++- internal/home/upgrade_test.go | 32 ++++++++++++++++++-- 11 files changed, 128 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e7a7cc4..3c52cc8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,8 @@ and this project adheres to - Logging of the client's IP address after failed login attempts ([#2824]). - Search by clients' names in the query log ([#1273]). - Verbose version output with `-v --version` ([#2416]). -- The ability to set a custom TLD for known local-network hosts ([#2393]). +- The ability to set a custom TLD or domain name for known hosts in the local + network ([#2393], [#2961]). - The ability to serve DNS queries on multiple hosts and interfaces ([#1401]). - `ips` and `text` DHCP server options ([#2385]). - `SRV` records support in `$dnsrewrite` filters ([#2533]). @@ -80,6 +81,7 @@ and this project adheres to [#2934]: https://github.com/AdguardTeam/AdGuardHome/issues/2934 [#2945]: https://github.com/AdguardTeam/AdGuardHome/issues/2945 [#2947]: https://github.com/AdguardTeam/AdGuardHome/issues/2947 +[#2961]: https://github.com/AdguardTeam/AdGuardHome/issues/2961 diff --git a/internal/dnsfilter/dnsfilter.go b/internal/dnsfilter/dnsfilter.go index a0413a84..3a5abdf3 100644 --- a/internal/dnsfilter/dnsfilter.go +++ b/internal/dnsfilter/dnsfilter.go @@ -422,9 +422,9 @@ func (d *DNSFilter) CheckHost( return Result{}, nil } -// checkAutoHosts compares the host against our autohosts table. The err is +// checkEtcHosts compares the host against our /etc/hosts table. The err is // always nil, it is only there to make this a valid hostChecker function. -func (d *DNSFilter) checkAutoHosts( +func (d *DNSFilter) checkEtcHosts( host string, qtype uint16, _ *FilteringSettings, @@ -829,8 +829,8 @@ func New(c *Config, blockFilters []Filter) *DNSFilter { } d.hostCheckers = []hostChecker{{ - check: d.checkAutoHosts, - name: "autohosts", + check: d.checkEtcHosts, + name: "etchosts", }, { check: d.matchHost, name: "filtering", diff --git a/internal/dnsforward/dns.go b/internal/dnsforward/dns.go index 31a40221..11a01557 100644 --- a/internal/dnsforward/dns.go +++ b/internal/dnsforward/dns.go @@ -264,7 +264,7 @@ func (s *Server) processInternalHosts(dctx *dnsContext) (rc resultCode) { } reqHost := strings.ToLower(q.Name) - host := strings.TrimSuffix(reqHost, s.autohostSuffix) + host := strings.TrimSuffix(reqHost, s.localDomainSuffix) if host == reqHost { return resultCodeSuccess } diff --git a/internal/dnsforward/dns_test.go b/internal/dnsforward/dns_test.go index 0addc008..04f065dd 100644 --- a/internal/dnsforward/dns_test.go +++ b/internal/dnsforward/dns_test.go @@ -90,7 +90,7 @@ func TestServer_ProcessInternalHosts_localRestriction(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { s := &Server{ - autohostSuffix: defaultAutohostSuffix, + localDomainSuffix: defaultLocalDomainSuffix, tableHostToIP: hostToIPTable{ "example": knownIP, }, @@ -157,35 +157,35 @@ func TestServer_ProcessInternalHosts(t *testing.T) { }{{ name: "success_external", host: examplecom, - suffix: defaultAutohostSuffix, + suffix: defaultLocalDomainSuffix, wantIP: nil, wantRes: resultCodeSuccess, qtyp: dns.TypeA, }, { name: "success_external_non_a", host: examplecom, - suffix: defaultAutohostSuffix, + suffix: defaultLocalDomainSuffix, wantIP: nil, wantRes: resultCodeSuccess, qtyp: dns.TypeCNAME, }, { name: "success_internal", host: examplelan, - suffix: defaultAutohostSuffix, + suffix: defaultLocalDomainSuffix, wantIP: knownIP, wantRes: resultCodeSuccess, qtyp: dns.TypeA, }, { name: "success_internal_unknown", host: "example-new.lan", - suffix: defaultAutohostSuffix, + suffix: defaultLocalDomainSuffix, wantIP: nil, wantRes: resultCodeFinish, qtyp: dns.TypeA, }, { name: "success_internal_aaaa", host: examplelan, - suffix: defaultAutohostSuffix, + suffix: defaultLocalDomainSuffix, wantIP: nil, wantRes: resultCodeSuccess, qtyp: dns.TypeAAAA, @@ -201,7 +201,7 @@ func TestServer_ProcessInternalHosts(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { s := &Server{ - autohostSuffix: tc.suffix, + localDomainSuffix: tc.suffix, tableHostToIP: hostToIPTable{ "example": knownIP, }, diff --git a/internal/dnsforward/dnsforward.go b/internal/dnsforward/dnsforward.go index 57ce3000..3757d2f8 100644 --- a/internal/dnsforward/dnsforward.go +++ b/internal/dnsforward/dnsforward.go @@ -70,9 +70,9 @@ type Server struct { stats stats.Stats access *accessCtx - // autohostSuffix is the suffix used to detect internal hosts. It must - // be a valid top-level domain plus dots on each side. - autohostSuffix string + // localDomainSuffix is the suffix used to detect internal hosts. It + // must be a valid domain name plus dots on each side. + localDomainSuffix string ipset ipsetCtx subnetDetector *aghnet.SubnetDetector @@ -94,9 +94,11 @@ type Server struct { conf ServerConfig } -// defaultAutohostSuffix is the default suffix used to detect internal hosts -// when no suffix is provided. See the documentation for Server.autohostSuffix. -const defaultAutohostSuffix = ".lan." +// defaultLocalDomainSuffix is the default suffix used to detect internal hosts +// when no suffix is provided. +// +// See the documentation for Server.localDomainSuffix. +const defaultLocalDomainSuffix = ".lan." // DNSCreateParams are parameters to create a new server. type DNSCreateParams struct { @@ -105,11 +107,11 @@ type DNSCreateParams struct { QueryLog querylog.QueryLog DHCPServer dhcpd.ServerInterface SubnetDetector *aghnet.SubnetDetector - AutohostTLD string + LocalDomain string } -// tldToSuffix converts a top-level domain into an autohost suffix. -func tldToSuffix(tld string) (suffix string) { +// domainNameToSuffix converts a domain name into a local domain suffix. +func domainNameToSuffix(tld string) (suffix string) { l := len(tld) + 2 b := make([]byte, l) b[0] = '.' @@ -122,24 +124,24 @@ func tldToSuffix(tld string) (suffix string) { // NewServer creates a new instance of the dnsforward.Server // Note: this function must be called only once func NewServer(p DNSCreateParams) (s *Server, err error) { - var autohostSuffix string - if p.AutohostTLD == "" { - autohostSuffix = defaultAutohostSuffix + var localDomainSuffix string + if p.LocalDomain == "" { + localDomainSuffix = defaultLocalDomainSuffix } else { - err = aghnet.ValidateDomainNameLabel(p.AutohostTLD) + err = aghnet.ValidateDomainName(p.LocalDomain) if err != nil { - return nil, fmt.Errorf("autohost tld: %w", err) + return nil, fmt.Errorf("local domain: %w", err) } - autohostSuffix = tldToSuffix(p.AutohostTLD) + localDomainSuffix = domainNameToSuffix(p.LocalDomain) } s = &Server{ - dnsFilter: p.DNSFilter, - stats: p.Stats, - queryLog: p.QueryLog, - subnetDetector: p.SubnetDetector, - autohostSuffix: autohostSuffix, + dnsFilter: p.DNSFilter, + stats: p.Stats, + queryLog: p.QueryLog, + subnetDetector: p.SubnetDetector, + localDomainSuffix: localDomainSuffix, } if p.DHCPServer != nil { diff --git a/internal/dnsforward/dnsforward_test.go b/internal/dnsforward/dnsforward_test.go index aa954a7c..c52a327c 100644 --- a/internal/dnsforward/dnsforward_test.go +++ b/internal/dnsforward/dnsforward_test.go @@ -232,10 +232,10 @@ func sendTestMessages(t *testing.T, conn *dns.Conn) { for i := 0; i < testMessagesCount; i++ { req := createGoogleATestMessage() err := conn.WriteMsg(req) - assert.Nilf(t, err, "cannot write message #%d: %s", i, err) + assert.NoErrorf(t, err, "cannot write message #%d: %s", i, err) res, err := conn.ReadMsg() - assert.Nilf(t, err, "cannot read response to message #%d: %s", i, err) + assert.NoErrorf(t, err, "cannot read response to message #%d: %s", i, err) assertGoogleAResponse(t, res) } } @@ -1088,7 +1088,6 @@ func TestPTRResponseFromHosts(t *testing.T) { _, _ = hf.WriteString(" 127.0.0.1 host # comment \n") _, _ = hf.WriteString(" ::1 localhost#comment \n") - // Init auto hosts. c.EtcHosts.Init(hf.Name()) t.Cleanup(c.EtcHosts.Close) @@ -1145,17 +1144,24 @@ func TestNewServer(t *testing.T) { in: DNSCreateParams{}, wantErrMsg: "", }, { - name: "success_autohost_tld", + name: "success_local_tld", in: DNSCreateParams{ - AutohostTLD: "mynet", + LocalDomain: "mynet", }, wantErrMsg: "", }, { - name: "bad_autohost_tld", + name: "success_local_domain", in: DNSCreateParams{ - AutohostTLD: "!!!", + LocalDomain: "my.local.net", }, - wantErrMsg: `autohost tld: invalid char '!' at index 0 in "!!!"`, + wantErrMsg: "", + }, { + name: "bad_local_domain", + in: DNSCreateParams{ + LocalDomain: "!!!", + }, + wantErrMsg: `local domain: invalid domain name label at index 0: ` + + `invalid char '!' at index 0 in "!!!"`, }} for _, tc := range testCases { diff --git a/internal/home/clientshttp.go b/internal/home/clientshttp.go index 681e9c55..21df856b 100644 --- a/internal/home/clientshttp.go +++ b/internal/home/clientshttp.go @@ -237,7 +237,7 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http var cj clientJSON if !ok { var found bool - cj, found = clients.findTemporary(ip, idStr) + cj, found = clients.findRuntime(ip, idStr) if !found { continue } @@ -258,9 +258,9 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http } } -// findTemporary looks up the IP in temporary storages, like autohosts or -// blocklists. -func (clients *clientsContainer) findTemporary(ip net.IP, idStr string) (cj clientJSON, found bool) { +// findRuntime looks up the IP in runtime and temporary storages, like +// /etc/hosts tables, DHCP leases, or blocklists. +func (clients *clientsContainer) findRuntime(ip net.IP, idStr string) (cj clientJSON, found bool) { if ip == nil { return cj, false } diff --git a/internal/home/config.go b/internal/home/config.go index e9a77452..f2b0b93c 100644 --- a/internal/home/config.go +++ b/internal/home/config.go @@ -94,10 +94,10 @@ type dnsConfig struct { FiltersUpdateIntervalHours uint32 `yaml:"filters_update_interval"` // time period to update filters (in hours) DnsfilterConf dnsfilter.Config `yaml:",inline"` - // AutohostTLD is the top-level domain used for known internal hosts. + // LocalDomainName is the domain name used for known internal hosts. // For example, a machine called "myhost" can be addressed as - // "myhost.lan" when AutohostTLD is "lan". - AutohostTLD string `yaml:"autohost_tld"` + // "myhost.lan" when LocalDomainName is "lan". + LocalDomainName string `yaml:"local_domain_name"` // ResolveClients enables and disables resolving clients with RDNS. ResolveClients bool `yaml:"resolve_clients"` @@ -156,7 +156,7 @@ var config = configuration{ }, FilteringEnabled: true, // whether or not use filter lists FiltersUpdateIntervalHours: 24, - AutohostTLD: "lan", + LocalDomainName: "lan", ResolveClients: true, }, TLS: tlsConfigSettings{ diff --git a/internal/home/dns.go b/internal/home/dns.go index 35d58540..0e276091 100644 --- a/internal/home/dns.go +++ b/internal/home/dns.go @@ -67,7 +67,7 @@ func initDNSServer() error { Stats: Context.stats, QueryLog: Context.queryLog, SubnetDetector: Context.subnetDetector, - AutohostTLD: config.DNS.AutohostTLD, + LocalDomain: config.DNS.LocalDomainName, } if Context.dhcpServer != nil { p.DHCPServer = Context.dhcpServer diff --git a/internal/home/upgrade.go b/internal/home/upgrade.go index 8f4aa6c6..e3441ffc 100644 --- a/internal/home/upgrade.go +++ b/internal/home/upgrade.go @@ -14,7 +14,7 @@ import ( ) // currentSchemaVersion is the current schema version. -const currentSchemaVersion = 8 +const currentSchemaVersion = 9 // These aliases are provided for convenience. type ( @@ -74,6 +74,7 @@ func upgradeConfigSchema(oldVersion int, diskConf yobj) (err error) { upgradeSchema5to6, upgradeSchema6to7, upgradeSchema7to8, + upgradeSchema8to9, } n := 0 @@ -464,6 +465,43 @@ func upgradeSchema7to8(diskConf yobj) (err error) { return nil } +// upgradeSchema8to9 performs the following changes: +// +// # BEFORE: +// 'dns': +// 'autohost_tld': 'lan' +// +// # AFTER: +// 'dns': +// 'local_domain_name': 'lan' +// +func upgradeSchema8to9(diskConf yobj) (err error) { + log.Printf("Upgrade yaml: 8 to 9") + + diskConf["schema_version"] = 9 + + dnsVal, ok := diskConf["dns"] + if !ok { + return nil + } + + dns, ok := dnsVal.(yobj) + if !ok { + return fmt.Errorf("unexpected type of dns: %T", dnsVal) + } + + autohostTLDVal := dns["autohost_tld"] + autohostTLD, ok := autohostTLDVal.(string) + if !ok { + return fmt.Errorf("undexpected type of dns.autohost_tld: %T", autohostTLDVal) + } + + delete(dns, "autohost_tld") + dns["local_domain_name"] = autohostTLD + + return nil +} + // TODO(a.garipov): Replace with log.Output when we port it to our logging // package. func funcName() string { diff --git a/internal/home/upgrade_test.go b/internal/home/upgrade_test.go index 34a6dd2b..2d56cddb 100644 --- a/internal/home/upgrade_test.go +++ b/internal/home/upgrade_test.go @@ -13,7 +13,7 @@ func TestUpgradeSchema1to2(t *testing.T) { diskConf := testDiskConf(1) err := upgradeSchema1to2(diskConf) - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, diskConf["schema_version"], 2) @@ -36,7 +36,7 @@ func TestUpgradeSchema2to3(t *testing.T) { diskConf := testDiskConf(2) err := upgradeSchema2to3(diskConf) - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, diskConf["schema_version"], 3) @@ -74,7 +74,7 @@ func TestUpgradeSchema7to8(t *testing.T) { } err := upgradeSchema7to8(oldConf) - require.Nil(t, err) + require.NoError(t, err) require.Equal(t, oldConf["schema_version"], 8) @@ -90,6 +90,32 @@ func TestUpgradeSchema7to8(t *testing.T) { assert.Equal(t, host, newBindHosts[0]) } +func TestUpgradeSchema8to9(t *testing.T) { + const tld = "foo" + oldConf := yobj{ + "dns": yobj{ + "autohost_tld": tld, + }, + "schema_version": 8, + } + + err := upgradeSchema8to9(oldConf) + require.NoError(t, err) + + require.Equal(t, oldConf["schema_version"], 9) + + dnsVal, ok := oldConf["dns"] + require.True(t, ok) + + newDNSConf, ok := dnsVal.(yobj) + require.True(t, ok) + + localDomainName, ok := newDNSConf["local_domain_name"].(string) + require.True(t, ok) + + assert.Equal(t, tld, localDomainName) +} + // assertEqualExcept removes entries from configs and compares them. func assertEqualExcept(t *testing.T, oldConf, newConf yobj, oldKeys, newKeys []string) { t.Helper()