all: sync with master; upd chlog
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
)
|
||||
|
||||
// unit is a convenient alias for struct{}
|
||||
@@ -127,8 +128,12 @@ func (a *accessManager) isBlockedClientID(id string) (ok bool) {
|
||||
}
|
||||
|
||||
// isBlockedHost returns true if host should be blocked.
|
||||
func (a *accessManager) isBlockedHost(host string) (ok bool) {
|
||||
_, ok = a.blockedHostsEng.Match(strings.ToLower(host))
|
||||
func (a *accessManager) isBlockedHost(host string, qt rules.RRType) (ok bool) {
|
||||
_, ok = a.blockedHostsEng.MatchRequest(&urlfilter.DNSRequest{
|
||||
Hostname: host,
|
||||
ClientIP: "0.0.0.0",
|
||||
DNSType: qt,
|
||||
})
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -28,54 +30,75 @@ func TestIsBlockedHost(t *testing.T) {
|
||||
"host1",
|
||||
"*.host.com",
|
||||
"||host3.com^",
|
||||
"||*^$dnstype=HTTPS",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
want assert.BoolAssertionFunc
|
||||
name string
|
||||
host string
|
||||
want bool
|
||||
qt rules.RRType
|
||||
}{{
|
||||
want: assert.True,
|
||||
name: "plain_match",
|
||||
host: "host1",
|
||||
want: true,
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "plain_mismatch",
|
||||
host: "host2",
|
||||
want: false,
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "subdomain_match_short",
|
||||
host: "asdf.host.com",
|
||||
want: true,
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "subdomain_match_long",
|
||||
host: "qwer.asdf.host.com",
|
||||
want: true,
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "subdomain_mismatch_no_lead",
|
||||
host: "host.com",
|
||||
want: false,
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "subdomain_mismatch_bad_asterisk",
|
||||
host: "asdf.zhost.com",
|
||||
want: false,
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "rule_match_simple",
|
||||
host: "host3.com",
|
||||
want: true,
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "rule_match_complex",
|
||||
host: "asdf.host3.com",
|
||||
want: true,
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "rule_mismatch",
|
||||
host: ".host3.com",
|
||||
want: false,
|
||||
qt: dns.TypeA,
|
||||
}, {
|
||||
want: assert.True,
|
||||
name: "by_qtype",
|
||||
host: "site-with-https-record.example",
|
||||
qt: dns.TypeHTTPS,
|
||||
}, {
|
||||
want: assert.False,
|
||||
name: "by_qtype_other",
|
||||
host: "site-with-https-record.example",
|
||||
qt: dns.TypeA,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.Equal(t, tc.want, a.isBlockedHost(tc.host))
|
||||
tc.want(t, a.isBlockedHost(tc.host, tc.qt))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -93,29 +116,29 @@ func TestIsBlockedIP(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
ip netip.Addr
|
||||
name string
|
||||
wantRule string
|
||||
ip netip.Addr
|
||||
wantBlocked bool
|
||||
}{{
|
||||
ip: netip.MustParseAddr("1.2.3.4"),
|
||||
name: "match_ip",
|
||||
wantRule: "1.2.3.4",
|
||||
ip: netip.MustParseAddr("1.2.3.4"),
|
||||
wantBlocked: true,
|
||||
}, {
|
||||
ip: netip.MustParseAddr("5.6.7.100"),
|
||||
name: "match_cidr",
|
||||
wantRule: "5.6.7.8/24",
|
||||
ip: netip.MustParseAddr("5.6.7.100"),
|
||||
wantBlocked: true,
|
||||
}, {
|
||||
ip: netip.MustParseAddr("9.2.3.4"),
|
||||
name: "no_match_ip",
|
||||
wantRule: "",
|
||||
ip: netip.MustParseAddr("9.2.3.4"),
|
||||
wantBlocked: false,
|
||||
}, {
|
||||
ip: netip.MustParseAddr("9.6.7.100"),
|
||||
name: "no_match_cidr",
|
||||
wantRule: "",
|
||||
ip: netip.MustParseAddr("9.6.7.100"),
|
||||
wantBlocked: false,
|
||||
}}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
// ValidateClientID returns an error if id is not a valid ClientID.
|
||||
func ValidateClientID(id string) (err error) {
|
||||
err = netutil.ValidateDomainNameLabel(id)
|
||||
err = netutil.ValidateHostnameLabel(id)
|
||||
if err != nil {
|
||||
// Replace the domain name label wrapper with our own.
|
||||
return fmt.Errorf("invalid clientid %q: %w", id, errors.Unwrap(err))
|
||||
|
||||
@@ -119,7 +119,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
cliSrvName: "!!!.example.com",
|
||||
wantClientID: "",
|
||||
wantErrMsg: `clientid check: invalid clientid "!!!": ` +
|
||||
`bad domain name label rune '!'`,
|
||||
`bad hostname label rune '!'`,
|
||||
inclHTTPTLS: false,
|
||||
strictSNI: true,
|
||||
}, {
|
||||
@@ -131,7 +131,7 @@ func TestServer_clientIDFromDNSContext(t *testing.T) {
|
||||
wantClientID: "",
|
||||
wantErrMsg: `clientid check: invalid clientid "abcdefghijklmno` +
|
||||
`pqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789": ` +
|
||||
`domain name label is too long: got 72, max 63`,
|
||||
`hostname label is too long: got 72, max 63`,
|
||||
inclHTTPTLS: false,
|
||||
strictSNI: true,
|
||||
}, {
|
||||
@@ -330,7 +330,7 @@ func TestClientIDFromDNSContextHTTPS(t *testing.T) {
|
||||
path: "/dns-query/!!!",
|
||||
cliSrvName: "example.com",
|
||||
wantClientID: "",
|
||||
wantErrMsg: `clientid check: invalid clientid "!!!": bad domain name label rune '!'`,
|
||||
wantErrMsg: `clientid check: invalid clientid "!!!": bad hostname label rune '!'`,
|
||||
}, {
|
||||
name: "both_ids",
|
||||
path: "/dns-query/right",
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -23,6 +22,7 @@ import (
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/ameshkov/dnscrypt/v2"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// BlockingMode is an enum of all allowed blocking modes.
|
||||
@@ -53,7 +53,6 @@ const (
|
||||
// The zero FilteringConfig is empty and ready for use.
|
||||
type FilteringConfig struct {
|
||||
// Callbacks for other modules
|
||||
// --
|
||||
|
||||
// FilterHandler is an optional additional filtering callback.
|
||||
FilterHandler func(clientAddr net.IP, clientID string, settings *filtering.Settings) `yaml:"-"`
|
||||
@@ -64,50 +63,82 @@ type FilteringConfig struct {
|
||||
GetCustomUpstreamByClient func(id string) (conf *proxy.UpstreamConfig, err error) `yaml:"-"`
|
||||
|
||||
// Protection configuration
|
||||
// --
|
||||
|
||||
ProtectionEnabled bool `yaml:"protection_enabled"` // whether or not use any of filtering features
|
||||
BlockingMode BlockingMode `yaml:"blocking_mode"` // mode how to answer filtered requests
|
||||
BlockingIPv4 net.IP `yaml:"blocking_ipv4"` // IP address to be returned for a blocked A request
|
||||
BlockingIPv6 net.IP `yaml:"blocking_ipv6"` // IP address to be returned for a blocked AAAA request
|
||||
BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"` // if 0, then default is used (3600)
|
||||
// ProtectionEnabled defines whether or not use any of filtering features.
|
||||
ProtectionEnabled bool `yaml:"protection_enabled"`
|
||||
|
||||
// IP (or domain name) which is used to respond to DNS requests blocked by parental control or safe-browsing
|
||||
ParentalBlockHost string `yaml:"parental_block_host"`
|
||||
// BlockingMode defines the way how blocked responses are constructed.
|
||||
BlockingMode BlockingMode `yaml:"blocking_mode"`
|
||||
|
||||
// BlockingIPv4 is the IP address to be returned for a blocked A request.
|
||||
BlockingIPv4 net.IP `yaml:"blocking_ipv4"`
|
||||
|
||||
// BlockingIPv6 is the IP address to be returned for a blocked AAAA
|
||||
// request.
|
||||
BlockingIPv6 net.IP `yaml:"blocking_ipv6"`
|
||||
|
||||
// BlockedResponseTTL is the time-to-live value for blocked responses. If
|
||||
// 0, then default value is used (3600).
|
||||
BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"`
|
||||
|
||||
// ParentalBlockHost is the IP (or domain name) which is used to respond to
|
||||
// DNS requests blocked by parental control.
|
||||
ParentalBlockHost string `yaml:"parental_block_host"`
|
||||
|
||||
// SafeBrowsingBlockHost is the IP (or domain name) which is used to
|
||||
// respond to DNS requests blocked by safe-browsing.
|
||||
SafeBrowsingBlockHost string `yaml:"safebrowsing_block_host"`
|
||||
|
||||
// Anti-DNS amplification
|
||||
// --
|
||||
|
||||
Ratelimit uint32 `yaml:"ratelimit"` // max number of requests per second from a given IP (0 to disable)
|
||||
RatelimitWhitelist []string `yaml:"ratelimit_whitelist"` // a list of whitelisted client IP addresses
|
||||
RefuseAny bool `yaml:"refuse_any"` // if true, refuse ANY requests
|
||||
// Ratelimit is the maximum number of requests per second from a given IP
|
||||
// (0 to disable).
|
||||
Ratelimit uint32 `yaml:"ratelimit"`
|
||||
|
||||
// RatelimitWhitelist is the list of whitelisted client IP addresses.
|
||||
RatelimitWhitelist []string `yaml:"ratelimit_whitelist"`
|
||||
|
||||
// RefuseAny, if true, refuse ANY requests.
|
||||
RefuseAny bool `yaml:"refuse_any"`
|
||||
|
||||
// Upstream DNS servers configuration
|
||||
// --
|
||||
|
||||
UpstreamDNS []string `yaml:"upstream_dns"`
|
||||
UpstreamDNSFileName string `yaml:"upstream_dns_file"`
|
||||
BootstrapDNS []string `yaml:"bootstrap_dns"` // a list of bootstrap DNS for DoH and DoT (plain DNS only)
|
||||
AllServers bool `yaml:"all_servers"` // if true, parallel queries to all configured upstream servers are enabled
|
||||
FastestAddr bool `yaml:"fastest_addr"` // use Fastest Address algorithm
|
||||
// UpstreamDNS is the list of upstream DNS servers.
|
||||
UpstreamDNS []string `yaml:"upstream_dns"`
|
||||
|
||||
// UpstreamDNSFileName, if set, points to the file which contains upstream
|
||||
// DNS servers.
|
||||
UpstreamDNSFileName string `yaml:"upstream_dns_file"`
|
||||
|
||||
// BootstrapDNS is the list of bootstrap DNS servers for DoH and DoT
|
||||
// resolvers (plain DNS only).
|
||||
BootstrapDNS []string `yaml:"bootstrap_dns"`
|
||||
|
||||
// AllServers, if true, parallel queries to all configured upstream servers
|
||||
// are enabled.
|
||||
AllServers bool `yaml:"all_servers"`
|
||||
|
||||
// FastestAddr, if true, use Fastest Address algorithm.
|
||||
FastestAddr bool `yaml:"fastest_addr"`
|
||||
|
||||
// FastestTimeout replaces the default timeout for dialing IP addresses
|
||||
// when FastestAddr is true.
|
||||
FastestTimeout timeutil.Duration `yaml:"fastest_timeout"`
|
||||
|
||||
// Access settings
|
||||
// --
|
||||
|
||||
// AllowedClients is the slice of IP addresses, CIDR networks, and ClientIDs
|
||||
// of allowed clients. If not empty, only these clients are allowed, and
|
||||
// [FilteringConfig.DisallowedClients] are ignored.
|
||||
// AllowedClients is the slice of IP addresses, CIDR networks, and
|
||||
// ClientIDs of allowed clients. If not empty, only these clients are
|
||||
// allowed, and [FilteringConfig.DisallowedClients] are ignored.
|
||||
AllowedClients []string `yaml:"allowed_clients"`
|
||||
|
||||
// DisallowedClients is the slice of IP addresses, CIDR networks, and
|
||||
// ClientIDs of disallowed clients.
|
||||
DisallowedClients []string `yaml:"disallowed_clients"`
|
||||
|
||||
BlockedHosts []string `yaml:"blocked_hosts"` // hosts that should be blocked
|
||||
// BlockedHosts is the list of hosts that should be blocked.
|
||||
BlockedHosts []string `yaml:"blocked_hosts"`
|
||||
|
||||
// TrustedProxies is the list of IP addresses and CIDR networks to detect
|
||||
// proxy servers addresses the DoH requests from which should be handled.
|
||||
// The value of nil or an empty slice for this field makes Proxy not trust
|
||||
@@ -115,26 +146,46 @@ type FilteringConfig struct {
|
||||
TrustedProxies []string `yaml:"trusted_proxies"`
|
||||
|
||||
// DNS cache settings
|
||||
// --
|
||||
|
||||
CacheSize uint32 `yaml:"cache_size"` // DNS cache size (in bytes)
|
||||
CacheMinTTL uint32 `yaml:"cache_ttl_min"` // override TTL value (minimum) received from upstream server
|
||||
CacheMaxTTL uint32 `yaml:"cache_ttl_max"` // override TTL value (maximum) received from upstream server
|
||||
// CacheSize is the DNS cache size (in bytes).
|
||||
CacheSize uint32 `yaml:"cache_size"`
|
||||
|
||||
// CacheMinTTL is the override TTL value (minimum) received from upstream
|
||||
// server.
|
||||
CacheMinTTL uint32 `yaml:"cache_ttl_min"`
|
||||
|
||||
// CacheMaxTTL is the override TTL value (maximum) received from upstream
|
||||
// server.
|
||||
CacheMaxTTL uint32 `yaml:"cache_ttl_max"`
|
||||
|
||||
// CacheOptimistic defines if optimistic cache mechanism should be used.
|
||||
CacheOptimistic bool `yaml:"cache_optimistic"`
|
||||
|
||||
// Other settings
|
||||
// --
|
||||
|
||||
BogusNXDomain []string `yaml:"bogus_nxdomain"` // transform responses with these IP addresses to NXDOMAIN
|
||||
AAAADisabled bool `yaml:"aaaa_disabled"` // Respond with an empty answer to all AAAA requests
|
||||
EnableDNSSEC bool `yaml:"enable_dnssec"` // Set AD flag in outcoming DNS request
|
||||
EnableEDNSClientSubnet bool `yaml:"edns_client_subnet"` // Enable EDNS Client Subnet option
|
||||
MaxGoroutines uint32 `yaml:"max_goroutines"` // Max. number of parallel goroutines for processing incoming requests
|
||||
HandleDDR bool `yaml:"handle_ddr"` // Handle DDR requests
|
||||
// BogusNXDomain is the list of IP addresses, responses with them will be
|
||||
// transformed to NXDOMAIN.
|
||||
BogusNXDomain []string `yaml:"bogus_nxdomain"`
|
||||
|
||||
// IpsetList is the ipset configuration that allows AdGuard Home to add
|
||||
// IP addresses of the specified domain names to an ipset list. Syntax:
|
||||
// AAAADisabled, if true, respond with an empty answer to all AAAA
|
||||
// requests.
|
||||
AAAADisabled bool `yaml:"aaaa_disabled"`
|
||||
|
||||
// EnableDNSSEC, if true, set AD flag in outcoming DNS request.
|
||||
EnableDNSSEC bool `yaml:"enable_dnssec"`
|
||||
|
||||
// EDNSClientSubnet is the settings list for EDNS Client Subnet.
|
||||
EDNSClientSubnet *EDNSClientSubnet `yaml:"edns_client_subnet"`
|
||||
|
||||
// MaxGoroutines is the max number of parallel goroutines for processing
|
||||
// incoming requests.
|
||||
MaxGoroutines uint32 `yaml:"max_goroutines"`
|
||||
|
||||
// HandleDDR, if true, handle DDR requests
|
||||
HandleDDR bool `yaml:"handle_ddr"`
|
||||
|
||||
// IpsetList is the ipset configuration that allows AdGuard Home to add IP
|
||||
// addresses of the specified domain names to an ipset list. Syntax:
|
||||
//
|
||||
// DOMAIN[,DOMAIN].../IPSET_NAME
|
||||
//
|
||||
@@ -146,6 +197,18 @@ type FilteringConfig struct {
|
||||
IpsetListFileName string `yaml:"ipset_file"`
|
||||
}
|
||||
|
||||
// EDNSClientSubnet is the settings list for EDNS Client Subnet.
|
||||
type EDNSClientSubnet struct {
|
||||
// CustomIP for EDNS Client Subnet.
|
||||
CustomIP string `yaml:"custom_ip"`
|
||||
|
||||
// Enabled defines if EDNS Client Subnet is enabled.
|
||||
Enabled bool `yaml:"enabled"`
|
||||
|
||||
// UseCustom defines if CustomIP should be used.
|
||||
UseCustom bool `yaml:"use_custom"`
|
||||
}
|
||||
|
||||
// TLSConfig is the TLS configuration for HTTPS, DNS-over-HTTPS, and DNS-over-TLS
|
||||
type TLSConfig struct {
|
||||
cert tls.Certificate
|
||||
@@ -270,12 +333,24 @@ func (s *Server) createProxyConfig() (conf proxy.Config, err error) {
|
||||
UpstreamConfig: srvConf.UpstreamConfig,
|
||||
BeforeRequestHandler: s.beforeRequestHandler,
|
||||
RequestHandler: s.handleDNSRequest,
|
||||
EnableEDNSClientSubnet: srvConf.EnableEDNSClientSubnet,
|
||||
EnableEDNSClientSubnet: srvConf.EDNSClientSubnet.Enabled,
|
||||
MaxGoroutines: int(srvConf.MaxGoroutines),
|
||||
UseDNS64: srvConf.UseDNS64,
|
||||
DNS64Prefs: srvConf.DNS64Prefixes,
|
||||
}
|
||||
|
||||
if srvConf.EDNSClientSubnet.UseCustom {
|
||||
// TODO(s.chzhen): Add wrapper around netip.Addr.
|
||||
var ip net.IP
|
||||
ip, err = netutil.ParseIP(srvConf.EDNSClientSubnet.CustomIP)
|
||||
if err != nil {
|
||||
return conf, fmt.Errorf("edns: %w", err)
|
||||
}
|
||||
|
||||
// TODO(s.chzhen): Use netip.Addr instead of net.IP inside dnsproxy.
|
||||
conf.EDNSAddr = ip
|
||||
}
|
||||
|
||||
if srvConf.CacheSize != 0 {
|
||||
conf.CacheEnabled = true
|
||||
conf.CacheSizeBytes = int(srvConf.CacheSize)
|
||||
@@ -510,7 +585,7 @@ func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) {
|
||||
if len(cert.DNSNames) != 0 {
|
||||
s.conf.dnsNames = cert.DNSNames
|
||||
log.Debug("dnsforward: using certificate's SAN as DNS names: %v", cert.DNSNames)
|
||||
sort.Strings(s.conf.dnsNames)
|
||||
slices.Sort(s.conf.dnsNames)
|
||||
} else {
|
||||
s.conf.dnsNames = append(s.conf.dnsNames, cert.Subject.CommonName)
|
||||
log.Debug("dnsforward: using certificate's CN as DNS name: %s", cert.Subject.CommonName)
|
||||
@@ -526,16 +601,6 @@ func (s *Server) prepareTLS(proxyConfig *proxy.Config) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// isInSorted returns true if s is in the sorted slice strs.
|
||||
func isInSorted(strs []string, s string) (ok bool) {
|
||||
i := sort.SearchStrings(strs, s)
|
||||
if i == len(strs) || strs[i] != s {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// isWildcard returns true if host is a wildcard hostname.
|
||||
func isWildcard(host string) (ok bool) {
|
||||
return len(host) >= 2 && host[0] == '*' && host[1] == '.'
|
||||
@@ -550,11 +615,12 @@ func matchesDomainWildcard(host, pat string) (ok bool) {
|
||||
// anyNameMatches returns true if sni, the client's SNI value, matches any of
|
||||
// the DNS names and patterns from certificate. dnsNames must be sorted.
|
||||
func anyNameMatches(dnsNames []string, sni string) (ok bool) {
|
||||
if netutil.ValidateDomainName(sni) != nil {
|
||||
// Check sni is either a valid hostname or a valid IP address.
|
||||
if netutil.ValidateHostname(sni) != nil && net.ParseIP(sni) == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if isInSorted(dnsNames, sni) {
|
||||
if _, ok = slices.BinarySearch(dnsNames, sni); ok {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package dnsforward
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func TestAnyNameMatches(t *testing.T) {
|
||||
dnsNames := []string{"host1", "*.host2", "1.2.3.4"}
|
||||
sort.Strings(dnsNames)
|
||||
slices.Sort(dnsNames)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
@@ -31,6 +31,10 @@ func TestAnyNameMatches(t *testing.T) {
|
||||
name: "match",
|
||||
dnsName: "1.2.3.4",
|
||||
want: true,
|
||||
}, {
|
||||
name: "mismatch_bad_ip",
|
||||
dnsName: "1.2.3.256",
|
||||
want: false,
|
||||
}, {
|
||||
name: "mismatch",
|
||||
dnsName: "host2",
|
||||
|
||||
@@ -230,7 +230,7 @@ func (s *Server) onDHCPLeaseChanged(flags int) {
|
||||
for _, l := range ll {
|
||||
// TODO(a.garipov): Remove this after we're finished with the client
|
||||
// hostname validations in the DHCP server code.
|
||||
err := netutil.ValidateDomainName(l.Hostname)
|
||||
err := netutil.ValidateHostname(l.Hostname)
|
||||
if err != nil {
|
||||
log.Debug("dnsforward: skipping invalid hostname %q from dhcp: %s", l.Hostname, err)
|
||||
|
||||
@@ -468,7 +468,7 @@ func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
|
||||
return resultCodeError
|
||||
}
|
||||
|
||||
log.Debug("dnsforward: request is for a service domain")
|
||||
log.Debug("dnsforward: request is not for arpa domain")
|
||||
|
||||
return resultCodeSuccess
|
||||
}
|
||||
|
||||
@@ -273,18 +273,25 @@ func TestServer_HandleDNSRequest_dns64(t *testing.T) {
|
||||
return resp, nil
|
||||
})
|
||||
|
||||
s := createTestServer(t, &filtering.Config{}, ServerConfig{
|
||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
UseDNS64: true,
|
||||
}, localUps)
|
||||
|
||||
client := &dns.Client{
|
||||
Net: "tcp",
|
||||
Timeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
// TODO(e.burkov): It seems [proxy.Proxy] isn't intended to be reused
|
||||
// right after stop, due to a data race in [proxy.Proxy.Init] method
|
||||
// when setting an OOB size. As a temporary workaround, recreate the
|
||||
// whole server for each test case.
|
||||
s := createTestServer(t, &filtering.Config{}, ServerConfig{
|
||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
UseDNS64: true,
|
||||
FilteringConfig: FilteringConfig{
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
},
|
||||
}, localUps)
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newUps(tc.upsAns)}
|
||||
startDeferStop(t, s)
|
||||
|
||||
@@ -467,6 +467,11 @@ func TestServer_ProcessRestrictLocal(t *testing.T) {
|
||||
s := createTestServer(t, &filtering.Config{}, ServerConfig{
|
||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
// TODO(s.chzhen): Add tests where EDNSClientSubnet.Enabled is true.
|
||||
// Improve FilteringConfig declaration for tests.
|
||||
FilteringConfig: FilteringConfig{
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
},
|
||||
}, ups)
|
||||
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{ups}
|
||||
startDeferStop(t, s)
|
||||
@@ -539,6 +544,9 @@ func TestServer_ProcessLocalPTR_usingResolvers(t *testing.T) {
|
||||
ServerConfig{
|
||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
FilteringConfig: FilteringConfig{
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
},
|
||||
},
|
||||
aghtest.NewUpstreamMock(func(req *dns.Msg) (resp *dns.Msg, err error) {
|
||||
return aghalg.Coalesce(
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
@@ -155,6 +156,9 @@ func createTestTLS(t *testing.T, tlsConf TLSConfig) (s *Server, certPem []byte)
|
||||
s = createTestServer(t, &filtering.Config{}, ServerConfig{
|
||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
FilteringConfig: FilteringConfig{
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
tlsConf.CertificateChainData, tlsConf.PrivateKeyData = certPem, keyPem
|
||||
@@ -266,6 +270,9 @@ func TestServer(t *testing.T) {
|
||||
s := createTestServer(t, &filtering.Config{}, ServerConfig{
|
||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
FilteringConfig: FilteringConfig{
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
},
|
||||
}, nil)
|
||||
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
|
||||
startDeferStop(t, s)
|
||||
@@ -304,7 +311,8 @@ func TestServer_timeout(t *testing.T) {
|
||||
srvConf := &ServerConfig{
|
||||
UpstreamTimeout: timeout,
|
||||
FilteringConfig: FilteringConfig{
|
||||
BlockingMode: BlockingModeDefault,
|
||||
BlockingMode: BlockingModeDefault,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -322,6 +330,9 @@ func TestServer_timeout(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
s.conf.FilteringConfig.BlockingMode = BlockingModeDefault
|
||||
s.conf.FilteringConfig.EDNSClientSubnet = &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
}
|
||||
err = s.Prepare(&s.conf)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -333,6 +344,9 @@ func TestServerWithProtectionDisabled(t *testing.T) {
|
||||
s := createTestServer(t, &filtering.Config{}, ServerConfig{
|
||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
FilteringConfig: FilteringConfig{
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
},
|
||||
}, nil)
|
||||
s.conf.UpstreamConfig.Upstreams = []upstream.Upstream{newGoogleUpstream()}
|
||||
startDeferStop(t, s)
|
||||
@@ -437,6 +451,9 @@ func TestSafeSearch(t *testing.T) {
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
FilteringConfig: FilteringConfig{
|
||||
ProtectionEnabled: true,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
s := createTestServer(t, filterConf, forwardConf, nil)
|
||||
@@ -492,6 +509,11 @@ func TestInvalidRequest(t *testing.T) {
|
||||
s := createTestServer(t, &filtering.Config{}, ServerConfig{
|
||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
FilteringConfig: FilteringConfig{
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
startDeferStop(t, s)
|
||||
|
||||
@@ -518,6 +540,9 @@ func TestBlockedRequest(t *testing.T) {
|
||||
FilteringConfig: FilteringConfig{
|
||||
ProtectionEnabled: true,
|
||||
BlockingMode: BlockingModeDefault,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
s := createTestServer(t, &filtering.Config{}, forwardConf, nil)
|
||||
@@ -543,6 +568,9 @@ func TestServerCustomClientUpstream(t *testing.T) {
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
FilteringConfig: FilteringConfig{
|
||||
ProtectionEnabled: true,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
s := createTestServer(t, &filtering.Config{}, forwardConf, nil)
|
||||
@@ -591,6 +619,11 @@ func TestBlockCNAMEProtectionEnabled(t *testing.T) {
|
||||
s := createTestServer(t, &filtering.Config{}, ServerConfig{
|
||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
FilteringConfig: FilteringConfig{
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
testUpstm := &aghtest.Upstream{
|
||||
CName: testCNAMEs,
|
||||
@@ -621,6 +654,9 @@ func TestBlockCNAME(t *testing.T) {
|
||||
FilteringConfig: FilteringConfig{
|
||||
ProtectionEnabled: true,
|
||||
BlockingMode: BlockingModeDefault,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
s := createTestServer(t, &filtering.Config{}, forwardConf, nil)
|
||||
@@ -690,6 +726,9 @@ func TestClientRulesForCNAMEMatching(t *testing.T) {
|
||||
FilterHandler: func(_ net.IP, _ string, settings *filtering.Settings) {
|
||||
settings.FilteringEnabled = false
|
||||
},
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
s := createTestServer(t, &filtering.Config{}, forwardConf, nil)
|
||||
@@ -731,6 +770,9 @@ func TestNullBlockedRequest(t *testing.T) {
|
||||
FilteringConfig: FilteringConfig{
|
||||
ProtectionEnabled: true,
|
||||
BlockingMode: BlockingModeNullIP,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
s := createTestServer(t, &filtering.Config{}, forwardConf, nil)
|
||||
@@ -783,6 +825,9 @@ func TestBlockedCustomIP(t *testing.T) {
|
||||
BlockingMode: BlockingModeCustomIP,
|
||||
BlockingIPv4: nil,
|
||||
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -831,6 +876,9 @@ func TestBlockedByHosts(t *testing.T) {
|
||||
FilteringConfig: FilteringConfig{
|
||||
ProtectionEnabled: true,
|
||||
BlockingMode: BlockingModeDefault,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -864,6 +912,9 @@ func TestBlockedBySafeBrowsing(t *testing.T) {
|
||||
FilteringConfig: FilteringConfig{
|
||||
SafeBrowsingBlockHost: ans4.String(),
|
||||
ProtectionEnabled: true,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
s := createTestServer(t, filterConf, forwardConf, nil)
|
||||
@@ -918,6 +969,9 @@ func TestRewrite(t *testing.T) {
|
||||
ProtectionEnabled: true,
|
||||
BlockingMode: BlockingModeDefault,
|
||||
UpstreamDNS: []string{"8.8.8.8:53"},
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -1009,7 +1063,7 @@ var testDHCP = &dhcpd.MockInterface{
|
||||
}}
|
||||
},
|
||||
OnSetOnLeaseChanged: func(olct dhcpd.OnLeaseChangedT) {},
|
||||
OnFindMACbyIP: func(ip net.IP) (mac net.HardwareAddr) { panic("not implemented") },
|
||||
OnFindMACbyIP: func(ip netip.Addr) (mac net.HardwareAddr) { panic("not implemented") },
|
||||
OnWriteDiskConfig: func(c *dhcpd.ServerConfig) { panic("not implemented") },
|
||||
}
|
||||
|
||||
@@ -1032,6 +1086,7 @@ func TestPTRResponseFromDHCPLeases(t *testing.T) {
|
||||
s.conf.UpstreamDNS = []string{"127.0.0.1:53"}
|
||||
s.conf.FilteringConfig.ProtectionEnabled = true
|
||||
s.conf.FilteringConfig.BlockingMode = BlockingModeDefault
|
||||
s.conf.FilteringConfig.EDNSClientSubnet = &EDNSClientSubnet{Enabled: false}
|
||||
|
||||
err = s.Prepare(&s.conf)
|
||||
require.NoError(t, err)
|
||||
@@ -1107,6 +1162,7 @@ func TestPTRResponseFromHosts(t *testing.T) {
|
||||
s.conf.TCPListenAddrs = []*net.TCPAddr{{}}
|
||||
s.conf.UpstreamDNS = []string{"127.0.0.1:53"}
|
||||
s.conf.FilteringConfig.BlockingMode = BlockingModeDefault
|
||||
s.conf.FilteringConfig.EDNSClientSubnet = &EDNSClientSubnet{Enabled: false}
|
||||
|
||||
err = s.Prepare(&s.conf)
|
||||
require.NoError(t, err)
|
||||
@@ -1171,7 +1227,8 @@ func TestNewServer(t *testing.T) {
|
||||
LocalDomain: "!!!",
|
||||
},
|
||||
wantErrMsg: `local domain: bad domain name "!!!": ` +
|
||||
`bad domain name label "!!!": bad domain name label rune '!'`,
|
||||
`bad top-level domain name label "!!!": ` +
|
||||
`bad top-level domain name label rune '!'`,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
||||
@@ -31,9 +31,11 @@ func (s *Server) beforeRequestHandler(
|
||||
}
|
||||
|
||||
if len(pctx.Req.Question) == 1 {
|
||||
host := strings.TrimSuffix(pctx.Req.Question[0].Name, ".")
|
||||
if s.access.isBlockedHost(host) {
|
||||
log.Debug("host %s is in access blocklist", host)
|
||||
q := pctx.Req.Question[0]
|
||||
qt := q.Qtype
|
||||
host := strings.TrimSuffix(q.Name, ".")
|
||||
if s.access.isBlockedHost(host, qt) {
|
||||
log.Debug("request %s %s is in access blocklist", dns.Type(qt), host)
|
||||
|
||||
return s.preBlockedResponse(pctx)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||
FilteringConfig: FilteringConfig{
|
||||
ProtectionEnabled: true,
|
||||
BlockingMode: BlockingModeDefault,
|
||||
EDNSClientSubnet: &EDNSClientSubnet{
|
||||
Enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
filters := []filtering.Filter{{
|
||||
|
||||
@@ -57,7 +57,7 @@ func (s *Server) getDNSConfig() (c *jsonDNSConfig) {
|
||||
blockingIPv4 := s.conf.BlockingIPv4
|
||||
blockingIPv6 := s.conf.BlockingIPv6
|
||||
ratelimit := s.conf.Ratelimit
|
||||
enableEDNSClientSubnet := s.conf.EnableEDNSClientSubnet
|
||||
enableEDNSClientSubnet := s.conf.EDNSClientSubnet.Enabled
|
||||
enableDNSSEC := s.conf.EnableDNSSEC
|
||||
aaaaDisabled := s.conf.AAAADisabled
|
||||
cacheSize := s.conf.CacheSize
|
||||
@@ -280,7 +280,7 @@ func (s *Server) setConfigRestartable(dc *jsonDNSConfig) (shouldRestart bool) {
|
||||
setIfNotNil(&s.conf.LocalPTRResolvers, dc.LocalPTRUpstreams),
|
||||
setIfNotNil(&s.conf.UpstreamDNSFileName, dc.UpstreamsFile),
|
||||
setIfNotNil(&s.conf.BootstrapDNS, dc.Bootstraps),
|
||||
setIfNotNil(&s.conf.EnableEDNSClientSubnet, dc.EDNSCSEnabled),
|
||||
setIfNotNil(&s.conf.EDNSClientSubnet.Enabled, dc.EDNSCSEnabled),
|
||||
setIfNotNil(&s.conf.CacheSize, dc.CacheSize),
|
||||
setIfNotNil(&s.conf.CacheMinTTL, dc.CacheMinTTL),
|
||||
setIfNotNil(&s.conf.CacheMaxTTL, dc.CacheMaxTTL),
|
||||
|
||||
@@ -69,6 +69,7 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
|
||||
ProtectionEnabled: true,
|
||||
BlockingMode: BlockingModeDefault,
|
||||
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
},
|
||||
ConfigModified: func() {},
|
||||
}
|
||||
@@ -144,6 +145,7 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
|
||||
ProtectionEnabled: true,
|
||||
BlockingMode: BlockingModeDefault,
|
||||
UpstreamDNS: []string{"8.8.8.8:53", "8.8.4.4:53"},
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
},
|
||||
ConfigModified: func() {},
|
||||
}
|
||||
@@ -227,7 +229,10 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
|
||||
require.True(t, ok)
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Cleanup(func() { s.conf = defaultConf })
|
||||
t.Cleanup(func() {
|
||||
s.conf = defaultConf
|
||||
s.conf.FilteringConfig.EDNSClientSubnet.Enabled = false
|
||||
})
|
||||
|
||||
rBody := io.NopCloser(bytes.NewReader(caseData.Req))
|
||||
var r *http.Request
|
||||
@@ -337,7 +342,8 @@ func TestValidateUpstreams(t *testing.T) {
|
||||
}, {
|
||||
name: "bad_domain",
|
||||
wantErr: `bad upstream for domain "[/!/]8.8.8.8": domain at index 0: ` +
|
||||
`bad domain name "!": bad domain name label "!": bad domain name label rune '!'`,
|
||||
`bad domain name "!": bad top-level domain name label "!": ` +
|
||||
`bad top-level domain name label rune '!'`,
|
||||
set: []string{"[/!/]8.8.8.8"},
|
||||
}}
|
||||
|
||||
@@ -442,6 +448,9 @@ func TestServer_handleTestUpstreaDNS(t *testing.T) {
|
||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||
UpstreamTimeout: upsTimeout,
|
||||
FilteringConfig: FilteringConfig{
|
||||
EDNSClientSubnet: &EDNSClientSubnet{Enabled: false},
|
||||
},
|
||||
}, nil)
|
||||
startDeferStop(t, srv)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user