Pull request: all: allow local non-top-level domains

Updates #2961.

Squashed commit of the following:

commit 207eeb85caf6caee81a669302daf4e10a5b61585
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Thu Apr 15 18:48:50 2021 +0300

    all: allow local non-top-level domains
This commit is contained in:
Ainar Garipov 2021-04-15 19:00:31 +03:00
parent a1450c5595
commit d83091fc1f
11 changed files with 128 additions and 54 deletions

View File

@ -24,7 +24,8 @@ and this project adheres to
- Logging of the client's IP address after failed login attempts ([#2824]). - Logging of the client's IP address after failed login attempts ([#2824]).
- Search by clients' names in the query log ([#1273]). - Search by clients' names in the query log ([#1273]).
- Verbose version output with `-v --version` ([#2416]). - 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]). - The ability to serve DNS queries on multiple hosts and interfaces ([#1401]).
- `ips` and `text` DHCP server options ([#2385]). - `ips` and `text` DHCP server options ([#2385]).
- `SRV` records support in `$dnsrewrite` filters ([#2533]). - `SRV` records support in `$dnsrewrite` filters ([#2533]).
@ -80,6 +81,7 @@ and this project adheres to
[#2934]: https://github.com/AdguardTeam/AdGuardHome/issues/2934 [#2934]: https://github.com/AdguardTeam/AdGuardHome/issues/2934
[#2945]: https://github.com/AdguardTeam/AdGuardHome/issues/2945 [#2945]: https://github.com/AdguardTeam/AdGuardHome/issues/2945
[#2947]: https://github.com/AdguardTeam/AdGuardHome/issues/2947 [#2947]: https://github.com/AdguardTeam/AdGuardHome/issues/2947
[#2961]: https://github.com/AdguardTeam/AdGuardHome/issues/2961

View File

@ -422,9 +422,9 @@ func (d *DNSFilter) CheckHost(
return Result{}, nil 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. // always nil, it is only there to make this a valid hostChecker function.
func (d *DNSFilter) checkAutoHosts( func (d *DNSFilter) checkEtcHosts(
host string, host string,
qtype uint16, qtype uint16,
_ *FilteringSettings, _ *FilteringSettings,
@ -829,8 +829,8 @@ func New(c *Config, blockFilters []Filter) *DNSFilter {
} }
d.hostCheckers = []hostChecker{{ d.hostCheckers = []hostChecker{{
check: d.checkAutoHosts, check: d.checkEtcHosts,
name: "autohosts", name: "etchosts",
}, { }, {
check: d.matchHost, check: d.matchHost,
name: "filtering", name: "filtering",

View File

@ -264,7 +264,7 @@ func (s *Server) processInternalHosts(dctx *dnsContext) (rc resultCode) {
} }
reqHost := strings.ToLower(q.Name) reqHost := strings.ToLower(q.Name)
host := strings.TrimSuffix(reqHost, s.autohostSuffix) host := strings.TrimSuffix(reqHost, s.localDomainSuffix)
if host == reqHost { if host == reqHost {
return resultCodeSuccess return resultCodeSuccess
} }

View File

@ -90,7 +90,7 @@ func TestServer_ProcessInternalHosts_localRestriction(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
s := &Server{ s := &Server{
autohostSuffix: defaultAutohostSuffix, localDomainSuffix: defaultLocalDomainSuffix,
tableHostToIP: hostToIPTable{ tableHostToIP: hostToIPTable{
"example": knownIP, "example": knownIP,
}, },
@ -157,35 +157,35 @@ func TestServer_ProcessInternalHosts(t *testing.T) {
}{{ }{{
name: "success_external", name: "success_external",
host: examplecom, host: examplecom,
suffix: defaultAutohostSuffix, suffix: defaultLocalDomainSuffix,
wantIP: nil, wantIP: nil,
wantRes: resultCodeSuccess, wantRes: resultCodeSuccess,
qtyp: dns.TypeA, qtyp: dns.TypeA,
}, { }, {
name: "success_external_non_a", name: "success_external_non_a",
host: examplecom, host: examplecom,
suffix: defaultAutohostSuffix, suffix: defaultLocalDomainSuffix,
wantIP: nil, wantIP: nil,
wantRes: resultCodeSuccess, wantRes: resultCodeSuccess,
qtyp: dns.TypeCNAME, qtyp: dns.TypeCNAME,
}, { }, {
name: "success_internal", name: "success_internal",
host: examplelan, host: examplelan,
suffix: defaultAutohostSuffix, suffix: defaultLocalDomainSuffix,
wantIP: knownIP, wantIP: knownIP,
wantRes: resultCodeSuccess, wantRes: resultCodeSuccess,
qtyp: dns.TypeA, qtyp: dns.TypeA,
}, { }, {
name: "success_internal_unknown", name: "success_internal_unknown",
host: "example-new.lan", host: "example-new.lan",
suffix: defaultAutohostSuffix, suffix: defaultLocalDomainSuffix,
wantIP: nil, wantIP: nil,
wantRes: resultCodeFinish, wantRes: resultCodeFinish,
qtyp: dns.TypeA, qtyp: dns.TypeA,
}, { }, {
name: "success_internal_aaaa", name: "success_internal_aaaa",
host: examplelan, host: examplelan,
suffix: defaultAutohostSuffix, suffix: defaultLocalDomainSuffix,
wantIP: nil, wantIP: nil,
wantRes: resultCodeSuccess, wantRes: resultCodeSuccess,
qtyp: dns.TypeAAAA, qtyp: dns.TypeAAAA,
@ -201,7 +201,7 @@ func TestServer_ProcessInternalHosts(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
s := &Server{ s := &Server{
autohostSuffix: tc.suffix, localDomainSuffix: tc.suffix,
tableHostToIP: hostToIPTable{ tableHostToIP: hostToIPTable{
"example": knownIP, "example": knownIP,
}, },

View File

@ -70,9 +70,9 @@ type Server struct {
stats stats.Stats stats stats.Stats
access *accessCtx access *accessCtx
// autohostSuffix is the suffix used to detect internal hosts. It must // localDomainSuffix is the suffix used to detect internal hosts. It
// be a valid top-level domain plus dots on each side. // must be a valid domain name plus dots on each side.
autohostSuffix string localDomainSuffix string
ipset ipsetCtx ipset ipsetCtx
subnetDetector *aghnet.SubnetDetector subnetDetector *aghnet.SubnetDetector
@ -94,9 +94,11 @@ type Server struct {
conf ServerConfig conf ServerConfig
} }
// defaultAutohostSuffix is the default suffix used to detect internal hosts // defaultLocalDomainSuffix is the default suffix used to detect internal hosts
// when no suffix is provided. See the documentation for Server.autohostSuffix. // when no suffix is provided.
const defaultAutohostSuffix = ".lan." //
// See the documentation for Server.localDomainSuffix.
const defaultLocalDomainSuffix = ".lan."
// DNSCreateParams are parameters to create a new server. // DNSCreateParams are parameters to create a new server.
type DNSCreateParams struct { type DNSCreateParams struct {
@ -105,11 +107,11 @@ type DNSCreateParams struct {
QueryLog querylog.QueryLog QueryLog querylog.QueryLog
DHCPServer dhcpd.ServerInterface DHCPServer dhcpd.ServerInterface
SubnetDetector *aghnet.SubnetDetector SubnetDetector *aghnet.SubnetDetector
AutohostTLD string LocalDomain string
} }
// tldToSuffix converts a top-level domain into an autohost suffix. // domainNameToSuffix converts a domain name into a local domain suffix.
func tldToSuffix(tld string) (suffix string) { func domainNameToSuffix(tld string) (suffix string) {
l := len(tld) + 2 l := len(tld) + 2
b := make([]byte, l) b := make([]byte, l)
b[0] = '.' b[0] = '.'
@ -122,16 +124,16 @@ func tldToSuffix(tld string) (suffix string) {
// NewServer creates a new instance of the dnsforward.Server // NewServer creates a new instance of the dnsforward.Server
// Note: this function must be called only once // Note: this function must be called only once
func NewServer(p DNSCreateParams) (s *Server, err error) { func NewServer(p DNSCreateParams) (s *Server, err error) {
var autohostSuffix string var localDomainSuffix string
if p.AutohostTLD == "" { if p.LocalDomain == "" {
autohostSuffix = defaultAutohostSuffix localDomainSuffix = defaultLocalDomainSuffix
} else { } else {
err = aghnet.ValidateDomainNameLabel(p.AutohostTLD) err = aghnet.ValidateDomainName(p.LocalDomain)
if err != nil { 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{ s = &Server{
@ -139,7 +141,7 @@ func NewServer(p DNSCreateParams) (s *Server, err error) {
stats: p.Stats, stats: p.Stats,
queryLog: p.QueryLog, queryLog: p.QueryLog,
subnetDetector: p.SubnetDetector, subnetDetector: p.SubnetDetector,
autohostSuffix: autohostSuffix, localDomainSuffix: localDomainSuffix,
} }
if p.DHCPServer != nil { if p.DHCPServer != nil {

View File

@ -232,10 +232,10 @@ func sendTestMessages(t *testing.T, conn *dns.Conn) {
for i := 0; i < testMessagesCount; i++ { for i := 0; i < testMessagesCount; i++ {
req := createGoogleATestMessage() req := createGoogleATestMessage()
err := conn.WriteMsg(req) 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() 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) assertGoogleAResponse(t, res)
} }
} }
@ -1088,7 +1088,6 @@ func TestPTRResponseFromHosts(t *testing.T) {
_, _ = hf.WriteString(" 127.0.0.1 host # comment \n") _, _ = hf.WriteString(" 127.0.0.1 host # comment \n")
_, _ = hf.WriteString(" ::1 localhost#comment \n") _, _ = hf.WriteString(" ::1 localhost#comment \n")
// Init auto hosts.
c.EtcHosts.Init(hf.Name()) c.EtcHosts.Init(hf.Name())
t.Cleanup(c.EtcHosts.Close) t.Cleanup(c.EtcHosts.Close)
@ -1145,17 +1144,24 @@ func TestNewServer(t *testing.T) {
in: DNSCreateParams{}, in: DNSCreateParams{},
wantErrMsg: "", wantErrMsg: "",
}, { }, {
name: "success_autohost_tld", name: "success_local_tld",
in: DNSCreateParams{ in: DNSCreateParams{
AutohostTLD: "mynet", LocalDomain: "mynet",
}, },
wantErrMsg: "", wantErrMsg: "",
}, { }, {
name: "bad_autohost_tld", name: "success_local_domain",
in: DNSCreateParams{ 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 { for _, tc := range testCases {

View File

@ -237,7 +237,7 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http
var cj clientJSON var cj clientJSON
if !ok { if !ok {
var found bool var found bool
cj, found = clients.findTemporary(ip, idStr) cj, found = clients.findRuntime(ip, idStr)
if !found { if !found {
continue 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 // findRuntime looks up the IP in runtime and temporary storages, like
// blocklists. // /etc/hosts tables, DHCP leases, or blocklists.
func (clients *clientsContainer) findTemporary(ip net.IP, idStr string) (cj clientJSON, found bool) { func (clients *clientsContainer) findRuntime(ip net.IP, idStr string) (cj clientJSON, found bool) {
if ip == nil { if ip == nil {
return cj, false return cj, false
} }

View File

@ -94,10 +94,10 @@ type dnsConfig struct {
FiltersUpdateIntervalHours uint32 `yaml:"filters_update_interval"` // time period to update filters (in hours) FiltersUpdateIntervalHours uint32 `yaml:"filters_update_interval"` // time period to update filters (in hours)
DnsfilterConf dnsfilter.Config `yaml:",inline"` 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 // For example, a machine called "myhost" can be addressed as
// "myhost.lan" when AutohostTLD is "lan". // "myhost.lan" when LocalDomainName is "lan".
AutohostTLD string `yaml:"autohost_tld"` LocalDomainName string `yaml:"local_domain_name"`
// ResolveClients enables and disables resolving clients with RDNS. // ResolveClients enables and disables resolving clients with RDNS.
ResolveClients bool `yaml:"resolve_clients"` ResolveClients bool `yaml:"resolve_clients"`
@ -156,7 +156,7 @@ var config = configuration{
}, },
FilteringEnabled: true, // whether or not use filter lists FilteringEnabled: true, // whether or not use filter lists
FiltersUpdateIntervalHours: 24, FiltersUpdateIntervalHours: 24,
AutohostTLD: "lan", LocalDomainName: "lan",
ResolveClients: true, ResolveClients: true,
}, },
TLS: tlsConfigSettings{ TLS: tlsConfigSettings{

View File

@ -67,7 +67,7 @@ func initDNSServer() error {
Stats: Context.stats, Stats: Context.stats,
QueryLog: Context.queryLog, QueryLog: Context.queryLog,
SubnetDetector: Context.subnetDetector, SubnetDetector: Context.subnetDetector,
AutohostTLD: config.DNS.AutohostTLD, LocalDomain: config.DNS.LocalDomainName,
} }
if Context.dhcpServer != nil { if Context.dhcpServer != nil {
p.DHCPServer = Context.dhcpServer p.DHCPServer = Context.dhcpServer

View File

@ -14,7 +14,7 @@ import (
) )
// currentSchemaVersion is the current schema version. // currentSchemaVersion is the current schema version.
const currentSchemaVersion = 8 const currentSchemaVersion = 9
// These aliases are provided for convenience. // These aliases are provided for convenience.
type ( type (
@ -74,6 +74,7 @@ func upgradeConfigSchema(oldVersion int, diskConf yobj) (err error) {
upgradeSchema5to6, upgradeSchema5to6,
upgradeSchema6to7, upgradeSchema6to7,
upgradeSchema7to8, upgradeSchema7to8,
upgradeSchema8to9,
} }
n := 0 n := 0
@ -464,6 +465,43 @@ func upgradeSchema7to8(diskConf yobj) (err error) {
return nil 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 // TODO(a.garipov): Replace with log.Output when we port it to our logging
// package. // package.
func funcName() string { func funcName() string {

View File

@ -13,7 +13,7 @@ func TestUpgradeSchema1to2(t *testing.T) {
diskConf := testDiskConf(1) diskConf := testDiskConf(1)
err := upgradeSchema1to2(diskConf) err := upgradeSchema1to2(diskConf)
require.Nil(t, err) require.NoError(t, err)
require.Equal(t, diskConf["schema_version"], 2) require.Equal(t, diskConf["schema_version"], 2)
@ -36,7 +36,7 @@ func TestUpgradeSchema2to3(t *testing.T) {
diskConf := testDiskConf(2) diskConf := testDiskConf(2)
err := upgradeSchema2to3(diskConf) err := upgradeSchema2to3(diskConf)
require.Nil(t, err) require.NoError(t, err)
require.Equal(t, diskConf["schema_version"], 3) require.Equal(t, diskConf["schema_version"], 3)
@ -74,7 +74,7 @@ func TestUpgradeSchema7to8(t *testing.T) {
} }
err := upgradeSchema7to8(oldConf) err := upgradeSchema7to8(oldConf)
require.Nil(t, err) require.NoError(t, err)
require.Equal(t, oldConf["schema_version"], 8) require.Equal(t, oldConf["schema_version"], 8)
@ -90,6 +90,32 @@ func TestUpgradeSchema7to8(t *testing.T) {
assert.Equal(t, host, newBindHosts[0]) 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. // assertEqualExcept removes entries from configs and compares them.
func assertEqualExcept(t *testing.T, oldConf, newConf yobj, oldKeys, newKeys []string) { func assertEqualExcept(t *testing.T, oldConf, newConf yobj, oldKeys, newKeys []string) {
t.Helper() t.Helper()