Pull request 2100: v0.107.42-rc
Squashed commit of the following: commit 284190f748345c7556e60b67f051ec5f6f080948 Author: Ainar Garipov <A.Garipov@AdGuard.COM> Date: Wed Dec 6 19:36:00 2023 +0300 all: sync with master; upd chlog
This commit is contained in:
@@ -14,12 +14,13 @@ import (
|
||||
|
||||
// Client contains information about persistent clients.
|
||||
type Client struct {
|
||||
// upstreamConfig is the custom upstream config for this client. If
|
||||
// it's nil, it has not been initialized yet. If it's non-nil and
|
||||
// empty, there are no valid upstreams. If it's non-nil and non-empty,
|
||||
// these upstream must be used.
|
||||
upstreamConfig *proxy.UpstreamConfig
|
||||
// upstreamConfig is the custom upstream configuration for this client. If
|
||||
// it's nil, it has not been initialized yet. If it's non-nil and empty,
|
||||
// there are no valid upstreams. If it's non-nil and non-empty, these
|
||||
// upstream must be used.
|
||||
upstreamConfig *proxy.CustomUpstreamConfig
|
||||
|
||||
// TODO(d.kolyshev): Make safeSearchConf a pointer.
|
||||
safeSearchConf filtering.SafeSearchConfig
|
||||
SafeSearch filtering.SafeSearch
|
||||
|
||||
@@ -32,6 +33,9 @@ type Client struct {
|
||||
Tags []string
|
||||
Upstreams []string
|
||||
|
||||
UpstreamsCacheSize uint32
|
||||
UpstreamsCacheEnabled bool
|
||||
|
||||
UseOwnSettings bool
|
||||
FilteringEnabled bool
|
||||
SafeBrowsingEnabled bool
|
||||
@@ -57,8 +61,7 @@ func (c *Client) ShallowClone() (sh *Client) {
|
||||
// closeUpstreams closes the client-specific upstream config of c if any.
|
||||
func (c *Client) closeUpstreams() (err error) {
|
||||
if c.upstreamConfig != nil {
|
||||
err = c.upstreamConfig.Close()
|
||||
if err != nil {
|
||||
if err = c.upstreamConfig.Close(); err != nil {
|
||||
return fmt.Errorf("closing upstreams of client %q: %w", c.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,13 @@ func (clients *clientsContainer) Init(
|
||||
return nil
|
||||
}
|
||||
|
||||
if clients.etcHosts != nil {
|
||||
// The clients.etcHosts may be nil even if config.Clients.Sources.HostsFile
|
||||
// is true, because of the deprecated option --no-etc-hosts.
|
||||
//
|
||||
// TODO(e.burkov): The option should probably be returned, since hosts file
|
||||
// currently used not only for clients' information enrichment, but also in
|
||||
// the filtering module and upstream addresses resolution.
|
||||
if config.Clients.Sources.HostsFile && clients.etcHosts != nil {
|
||||
go clients.handleHostsUpdates()
|
||||
}
|
||||
|
||||
@@ -179,6 +185,14 @@ type clientObject struct {
|
||||
Tags []string `yaml:"tags"`
|
||||
Upstreams []string `yaml:"upstreams"`
|
||||
|
||||
// UpstreamsCacheSize is the DNS cache size (in bytes).
|
||||
//
|
||||
// TODO(d.kolyshev): Use [datasize.Bytesize].
|
||||
UpstreamsCacheSize uint32 `yaml:"upstreams_cache_size"`
|
||||
|
||||
// UpstreamsCacheEnabled indicates if the DNS cache is enabled.
|
||||
UpstreamsCacheEnabled bool `yaml:"upstreams_cache_enabled"`
|
||||
|
||||
UseGlobalSettings bool `yaml:"use_global_settings"`
|
||||
FilteringEnabled bool `yaml:"filtering_enabled"`
|
||||
ParentalEnabled bool `yaml:"parental_enabled"`
|
||||
@@ -210,6 +224,8 @@ func (clients *clientsContainer) addFromConfig(
|
||||
UseOwnBlockedServices: !o.UseGlobalBlockedServices,
|
||||
IgnoreQueryLog: o.IgnoreQueryLog,
|
||||
IgnoreStatistics: o.IgnoreStatistics,
|
||||
UpstreamsCacheEnabled: o.UpstreamsCacheEnabled,
|
||||
UpstreamsCacheSize: o.UpstreamsCacheSize,
|
||||
}
|
||||
|
||||
if o.SafeSearchConf.Enabled {
|
||||
@@ -278,6 +294,8 @@ func (clients *clientsContainer) forConfig() (objs []*clientObject) {
|
||||
UseGlobalBlockedServices: !cli.UseOwnBlockedServices,
|
||||
IgnoreQueryLog: cli.IgnoreQueryLog,
|
||||
IgnoreStatistics: cli.IgnoreStatistics,
|
||||
UpstreamsCacheEnabled: cli.UpstreamsCacheEnabled,
|
||||
UpstreamsCacheSize: cli.UpstreamsCacheSize,
|
||||
}
|
||||
|
||||
objs = append(objs, o)
|
||||
@@ -419,18 +437,24 @@ func (clients *clientsContainer) shouldCountClient(ids []string) (y bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
// findUpstreams returns upstreams configured for the client, identified either
|
||||
// by its IP address or its ClientID. upsConf is nil if the client isn't found
|
||||
// or if the client has no custom upstreams.
|
||||
func (clients *clientsContainer) findUpstreams(
|
||||
// type check
|
||||
var _ dnsforward.ClientsContainer = (*clientsContainer)(nil)
|
||||
|
||||
// UpstreamConfigByID implements the [dnsforward.ClientsContainer] interface for
|
||||
// *clientsContainer. upsConf is nil if the client isn't found or if the client
|
||||
// has no custom upstreams.
|
||||
func (clients *clientsContainer) UpstreamConfigByID(
|
||||
id string,
|
||||
) (upsConf *proxy.UpstreamConfig, err error) {
|
||||
bootstrap upstream.Resolver,
|
||||
) (conf *proxy.CustomUpstreamConfig, err error) {
|
||||
clients.lock.Lock()
|
||||
defer clients.lock.Unlock()
|
||||
|
||||
c, ok := clients.findLocked(id)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
} else if c.upstreamConfig != nil {
|
||||
return c.upstreamConfig, nil
|
||||
}
|
||||
|
||||
upstreams := stringutil.FilterOut(c.Upstreams, dnsforward.IsCommentOrEmpty)
|
||||
@@ -438,24 +462,27 @@ func (clients *clientsContainer) findUpstreams(
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if c.upstreamConfig != nil {
|
||||
return c.upstreamConfig, nil
|
||||
}
|
||||
|
||||
var conf *proxy.UpstreamConfig
|
||||
conf, err = proxy.ParseUpstreamsConfig(
|
||||
var upsConf *proxy.UpstreamConfig
|
||||
upsConf, err = proxy.ParseUpstreamsConfig(
|
||||
upstreams,
|
||||
&upstream.Options{
|
||||
Bootstrap: config.DNS.BootstrapDNS,
|
||||
Bootstrap: bootstrap,
|
||||
Timeout: config.DNS.UpstreamTimeout.Duration,
|
||||
HTTPVersions: dnsforward.UpstreamHTTPVersions(config.DNS.UseHTTP3Upstreams),
|
||||
PreferIPv6: config.DNS.BootstrapPreferIPv6,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
// Don't wrap the error since it's informative enough as is.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf = proxy.NewCustomUpstreamConfig(
|
||||
upsConf,
|
||||
c.UpstreamsCacheEnabled,
|
||||
int(c.UpstreamsCacheSize),
|
||||
config.DNS.EDNSClientSubnet.Enabled,
|
||||
)
|
||||
c.upstreamConfig = conf
|
||||
|
||||
return conf, nil
|
||||
@@ -672,10 +699,6 @@ func (clients *clientsContainer) Del(name string) (ok bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
if err := c.closeUpstreams(); err != nil {
|
||||
log.Error("client container: removing client %s: %s", name, err)
|
||||
}
|
||||
|
||||
clients.del(c)
|
||||
|
||||
return true
|
||||
@@ -683,10 +706,14 @@ func (clients *clientsContainer) Del(name string) (ok bool) {
|
||||
|
||||
// del removes c from the indexes. clients.lock is expected to be locked.
|
||||
func (clients *clientsContainer) del(c *Client) {
|
||||
// update Name index
|
||||
if err := c.closeUpstreams(); err != nil {
|
||||
log.Error("client container: removing client %s: %s", c.Name, err)
|
||||
}
|
||||
|
||||
// Update the name index.
|
||||
delete(clients.list, c.Name)
|
||||
|
||||
// update ID index
|
||||
// Update the ID index.
|
||||
for _, id := range c.IDs {
|
||||
delete(clients.idIndex, id)
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ func TestClientsAddExisting(t *testing.T) {
|
||||
|
||||
clients.dhcp = dhcpServer
|
||||
|
||||
err = dhcpServer.AddStaticLease(&dhcpd.Lease{
|
||||
err = dhcpServer.AddStaticLease(&dhcpsvc.Lease{
|
||||
HWAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
|
||||
IP: ip,
|
||||
Hostname: "testhost",
|
||||
@@ -355,13 +355,11 @@ func TestClientsCustomUpstream(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.True(t, ok)
|
||||
|
||||
config, err := clients.findUpstreams("1.2.3.4")
|
||||
assert.Nil(t, config)
|
||||
upsConf, err := clients.UpstreamConfigByID("1.2.3.4", net.DefaultResolver)
|
||||
assert.Nil(t, upsConf)
|
||||
assert.NoError(t, err)
|
||||
|
||||
config, err = clients.findUpstreams("1.1.1.1")
|
||||
require.NotNil(t, config)
|
||||
upsConf, err = clients.UpstreamConfigByID("1.1.1.1", net.DefaultResolver)
|
||||
require.NotNil(t, upsConf)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, config.Upstreams, 1)
|
||||
assert.Len(t, config.DomainReservedUpstreams, 1)
|
||||
}
|
||||
|
||||
@@ -56,34 +56,9 @@ type clientJSON struct {
|
||||
|
||||
IgnoreQueryLog aghalg.NullBool `json:"ignore_querylog"`
|
||||
IgnoreStatistics aghalg.NullBool `json:"ignore_statistics"`
|
||||
}
|
||||
|
||||
// copySettings returns a copy of specific settings from JSON or a previous
|
||||
// client.
|
||||
func (j *clientJSON) copySettings(
|
||||
prev *Client,
|
||||
) (weekly *schedule.Weekly, ignoreQueryLog, ignoreStatistics bool) {
|
||||
if j.Schedule != nil {
|
||||
weekly = j.Schedule.Clone()
|
||||
} else if prev != nil && prev.BlockedServices != nil {
|
||||
weekly = prev.BlockedServices.Schedule.Clone()
|
||||
} else {
|
||||
weekly = schedule.EmptyWeekly()
|
||||
}
|
||||
|
||||
if j.IgnoreQueryLog != aghalg.NBNull {
|
||||
ignoreQueryLog = j.IgnoreQueryLog == aghalg.NBTrue
|
||||
} else if prev != nil {
|
||||
ignoreQueryLog = prev.IgnoreQueryLog
|
||||
}
|
||||
|
||||
if j.IgnoreStatistics != aghalg.NBNull {
|
||||
ignoreStatistics = j.IgnoreStatistics == aghalg.NBTrue
|
||||
} else if prev != nil {
|
||||
ignoreStatistics = prev.IgnoreStatistics
|
||||
}
|
||||
|
||||
return weekly, ignoreQueryLog, ignoreStatistics
|
||||
UpstreamsCacheSize uint32 `json:"upstreams_cache_size"`
|
||||
UpstreamsCacheEnabled aghalg.NullBool `json:"upstreams_cache_enabled"`
|
||||
}
|
||||
|
||||
type runtimeClientJSON struct {
|
||||
@@ -142,36 +117,35 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http
|
||||
|
||||
// jsonToClient converts JSON object to Client object.
|
||||
func (clients *clientsContainer) jsonToClient(cj clientJSON, prev *Client) (c *Client, err error) {
|
||||
var safeSearchConf filtering.SafeSearchConfig
|
||||
if cj.SafeSearchConf != nil {
|
||||
safeSearchConf = *cj.SafeSearchConf
|
||||
} else {
|
||||
// TODO(d.kolyshev): Remove after cleaning the deprecated
|
||||
// [clientJSON.SafeSearchEnabled] field.
|
||||
safeSearchConf = filtering.SafeSearchConfig{
|
||||
Enabled: cj.SafeSearchEnabled,
|
||||
}
|
||||
safeSearchConf := copySafeSearch(cj.SafeSearchConf, cj.SafeSearchEnabled)
|
||||
|
||||
// Set default service flags for enabled safesearch.
|
||||
if safeSearchConf.Enabled {
|
||||
safeSearchConf.Bing = true
|
||||
safeSearchConf.DuckDuckGo = true
|
||||
safeSearchConf.Google = true
|
||||
safeSearchConf.Pixabay = true
|
||||
safeSearchConf.Yandex = true
|
||||
safeSearchConf.YouTube = true
|
||||
}
|
||||
var ignoreQueryLog bool
|
||||
if cj.IgnoreQueryLog != aghalg.NBNull {
|
||||
ignoreQueryLog = cj.IgnoreQueryLog == aghalg.NBTrue
|
||||
} else if prev != nil {
|
||||
ignoreQueryLog = prev.IgnoreQueryLog
|
||||
}
|
||||
|
||||
weekly, ignoreQueryLog, ignoreStatistics := cj.copySettings(prev)
|
||||
|
||||
bs := &filtering.BlockedServices{
|
||||
Schedule: weekly,
|
||||
IDs: cj.BlockedServices,
|
||||
var ignoreStatistics bool
|
||||
if cj.IgnoreStatistics != aghalg.NBNull {
|
||||
ignoreStatistics = cj.IgnoreStatistics == aghalg.NBTrue
|
||||
} else if prev != nil {
|
||||
ignoreStatistics = prev.IgnoreStatistics
|
||||
}
|
||||
err = bs.Validate()
|
||||
|
||||
var upsCacheEnabled bool
|
||||
var upsCacheSize uint32
|
||||
if cj.UpstreamsCacheEnabled != aghalg.NBNull {
|
||||
upsCacheEnabled = cj.UpstreamsCacheEnabled == aghalg.NBTrue
|
||||
upsCacheSize = cj.UpstreamsCacheSize
|
||||
} else if prev != nil {
|
||||
upsCacheEnabled = prev.UpstreamsCacheEnabled
|
||||
upsCacheSize = prev.UpstreamsCacheSize
|
||||
}
|
||||
|
||||
svcs, err := copyBlockedServices(cj.Schedule, cj.BlockedServices, prev)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validating blocked services: %w", err)
|
||||
return nil, fmt.Errorf("invalid blocked services: %w", err)
|
||||
}
|
||||
|
||||
c = &Client{
|
||||
@@ -179,7 +153,7 @@ func (clients *clientsContainer) jsonToClient(cj clientJSON, prev *Client) (c *C
|
||||
|
||||
Name: cj.Name,
|
||||
|
||||
BlockedServices: bs,
|
||||
BlockedServices: svcs,
|
||||
|
||||
IDs: cj.IDs,
|
||||
Tags: cj.Tags,
|
||||
@@ -192,6 +166,8 @@ func (clients *clientsContainer) jsonToClient(cj clientJSON, prev *Client) (c *C
|
||||
UseOwnBlockedServices: !cj.UseGlobalBlockedServices,
|
||||
IgnoreQueryLog: ignoreQueryLog,
|
||||
IgnoreStatistics: ignoreStatistics,
|
||||
UpstreamsCacheEnabled: upsCacheEnabled,
|
||||
UpstreamsCacheSize: upsCacheSize,
|
||||
}
|
||||
|
||||
if safeSearchConf.Enabled {
|
||||
@@ -208,6 +184,63 @@ func (clients *clientsContainer) jsonToClient(cj clientJSON, prev *Client) (c *C
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// copySafeSearch returns safe search config created from provided parameters.
|
||||
func copySafeSearch(
|
||||
jsonConf *filtering.SafeSearchConfig,
|
||||
enabled bool,
|
||||
) (conf filtering.SafeSearchConfig) {
|
||||
if jsonConf != nil {
|
||||
return *jsonConf
|
||||
}
|
||||
|
||||
// TODO(d.kolyshev): Remove after cleaning the deprecated
|
||||
// [clientJSON.SafeSearchEnabled] field.
|
||||
conf = filtering.SafeSearchConfig{
|
||||
Enabled: enabled,
|
||||
}
|
||||
|
||||
// Set default service flags for enabled safesearch.
|
||||
if conf.Enabled {
|
||||
conf.Bing = true
|
||||
conf.DuckDuckGo = true
|
||||
conf.Google = true
|
||||
conf.Pixabay = true
|
||||
conf.Yandex = true
|
||||
conf.YouTube = true
|
||||
}
|
||||
|
||||
return conf
|
||||
}
|
||||
|
||||
// copyBlockedServices converts a json blocked services to an internal blocked
|
||||
// services.
|
||||
func copyBlockedServices(
|
||||
sch *schedule.Weekly,
|
||||
svcStrs []string,
|
||||
prev *Client,
|
||||
) (svcs *filtering.BlockedServices, err error) {
|
||||
var weekly *schedule.Weekly
|
||||
if sch != nil {
|
||||
weekly = sch.Clone()
|
||||
} else if prev != nil && prev.BlockedServices != nil {
|
||||
weekly = prev.BlockedServices.Schedule.Clone()
|
||||
} else {
|
||||
weekly = schedule.EmptyWeekly()
|
||||
}
|
||||
|
||||
svcs = &filtering.BlockedServices{
|
||||
Schedule: weekly,
|
||||
IDs: svcStrs,
|
||||
}
|
||||
|
||||
err = svcs.Validate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validating blocked services: %w", err)
|
||||
}
|
||||
|
||||
return svcs, nil
|
||||
}
|
||||
|
||||
// clientToJSON converts Client object to JSON.
|
||||
func clientToJSON(c *Client) (cj *clientJSON) {
|
||||
// TODO(d.kolyshev): Remove after cleaning the deprecated
|
||||
@@ -235,6 +268,9 @@ func clientToJSON(c *Client) (cj *clientJSON) {
|
||||
|
||||
IgnoreQueryLog: aghalg.BoolToNullBool(c.IgnoreQueryLog),
|
||||
IgnoreStatistics: aghalg.BoolToNullBool(c.IgnoreStatistics),
|
||||
|
||||
UpstreamsCacheSize: c.UpstreamsCacheSize,
|
||||
UpstreamsCacheEnabled: aghalg.BoolToNullBool(c.UpstreamsCacheEnabled),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,8 @@ type configuration struct {
|
||||
// Theme is a UI theme for current user.
|
||||
Theme Theme `yaml:"theme"`
|
||||
|
||||
// TODO(a.garipov): Make DNS and the fields below pointers and validate
|
||||
// and/or reset on explicit nulling.
|
||||
DNS dnsConfig `yaml:"dns"`
|
||||
TLS tlsConfigSettings `yaml:"tls"`
|
||||
QueryLog queryLogConfig `yaml:"querylog"`
|
||||
@@ -214,18 +216,21 @@ type dnsConfig struct {
|
||||
// DNS64Prefixes is the list of NAT64 prefixes to be used for DNS64.
|
||||
DNS64Prefixes []netip.Prefix `yaml:"dns64_prefixes"`
|
||||
|
||||
// ServeHTTP3 defines if HTTP/3 is be allowed for incoming requests.
|
||||
// ServeHTTP3 defines if HTTP/3 is allowed for incoming requests.
|
||||
//
|
||||
// TODO(a.garipov): Add to the UI when HTTP/3 support is no longer
|
||||
// experimental.
|
||||
ServeHTTP3 bool `yaml:"serve_http3"`
|
||||
|
||||
// UseHTTP3Upstreams defines if HTTP/3 is be allowed for DNS-over-HTTPS
|
||||
// UseHTTP3Upstreams defines if HTTP/3 is allowed for DNS-over-HTTPS
|
||||
// upstreams.
|
||||
//
|
||||
// TODO(a.garipov): Add to the UI when HTTP/3 support is no longer
|
||||
// experimental.
|
||||
UseHTTP3Upstreams bool `yaml:"use_http3_upstreams"`
|
||||
|
||||
// ServePlainDNS defines if plain DNS is allowed for incoming requests.
|
||||
ServePlainDNS bool `yaml:"serve_plain_dns"`
|
||||
}
|
||||
|
||||
type tlsConfigSettings struct {
|
||||
@@ -333,6 +338,7 @@ var config = &configuration{
|
||||
},
|
||||
UpstreamTimeout: timeutil.Duration{Duration: dnsforward.DefaultTimeout},
|
||||
UsePrivateRDNS: true,
|
||||
ServePlainDNS: true,
|
||||
},
|
||||
TLS: tlsConfigSettings{
|
||||
PortHTTPS: defaultPortHTTPS,
|
||||
|
||||
@@ -138,28 +138,28 @@ func initDNSServer(
|
||||
QueryLog: qlog,
|
||||
PrivateNets: privateNets,
|
||||
Anonymizer: anonymizer,
|
||||
LocalDomain: config.DHCP.LocalDomainName,
|
||||
DHCPServer: dhcpSrv,
|
||||
EtcHosts: Context.etcHosts,
|
||||
LocalDomain: config.DHCP.LocalDomainName,
|
||||
})
|
||||
defer func() {
|
||||
if err != nil {
|
||||
closeDNSServer()
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
closeDNSServer()
|
||||
|
||||
return fmt.Errorf("dnsforward.NewServer: %w", err)
|
||||
}
|
||||
|
||||
Context.clients.dnsServer = Context.dnsServer
|
||||
|
||||
dnsConf, err := newServerConfig(tlsConf, httpReg)
|
||||
dnsConf, err := newServerConfig(&config.DNS, config.Clients.Sources, tlsConf, httpReg)
|
||||
if err != nil {
|
||||
closeDNSServer()
|
||||
|
||||
return fmt.Errorf("newServerConfig: %w", err)
|
||||
}
|
||||
|
||||
err = Context.dnsServer.Prepare(dnsConf)
|
||||
if err != nil {
|
||||
closeDNSServer()
|
||||
|
||||
return fmt.Errorf("dnsServer.Prepare: %w", err)
|
||||
}
|
||||
|
||||
@@ -222,21 +222,37 @@ func ipsToUDPAddrs(ips []netip.Addr, port uint16) (udpAddrs []*net.UDPAddr) {
|
||||
return udpAddrs
|
||||
}
|
||||
|
||||
// newServerConfig converts values from the configuration file into the internal
|
||||
// DNS server configuration. All arguments must not be nil.
|
||||
func newServerConfig(
|
||||
dnsConf *dnsConfig,
|
||||
clientSrcConf *clientSourcesConfig,
|
||||
tlsConf *tlsConfigSettings,
|
||||
httpReg aghhttp.RegisterFunc,
|
||||
) (newConf *dnsforward.ServerConfig, err error) {
|
||||
dnsConf := config.DNS
|
||||
hosts := aghalg.CoalesceSlice(dnsConf.BindHosts, []netip.Addr{netutil.IPv4Localhost()})
|
||||
|
||||
fwdConf := dnsConf.Config
|
||||
fwdConf.FilterHandler = applyAdditionalFiltering
|
||||
fwdConf.ClientsContainer = &Context.clients
|
||||
|
||||
newConf = &dnsforward.ServerConfig{
|
||||
UDPListenAddrs: ipsToUDPAddrs(hosts, dnsConf.Port),
|
||||
TCPListenAddrs: ipsToTCPAddrs(hosts, dnsConf.Port),
|
||||
Config: dnsConf.Config,
|
||||
ConfigModified: onConfigModified,
|
||||
HTTPRegister: httpReg,
|
||||
UseDNS64: config.DNS.UseDNS64,
|
||||
DNS64Prefixes: config.DNS.DNS64Prefixes,
|
||||
UDPListenAddrs: ipsToUDPAddrs(hosts, dnsConf.Port),
|
||||
TCPListenAddrs: ipsToTCPAddrs(hosts, dnsConf.Port),
|
||||
Config: fwdConf,
|
||||
TLSConfig: newDNSTLSConfig(tlsConf, hosts),
|
||||
TLSAllowUnencryptedDoH: tlsConf.AllowUnencryptedDoH,
|
||||
UpstreamTimeout: dnsConf.UpstreamTimeout.Duration,
|
||||
TLSv12Roots: Context.tlsRoots,
|
||||
ConfigModified: onConfigModified,
|
||||
HTTPRegister: httpReg,
|
||||
LocalPTRResolvers: dnsConf.LocalPTRResolvers,
|
||||
UseDNS64: dnsConf.UseDNS64,
|
||||
DNS64Prefixes: dnsConf.DNS64Prefixes,
|
||||
UsePrivateRDNS: dnsConf.UsePrivateRDNS,
|
||||
ServeHTTP3: dnsConf.ServeHTTP3,
|
||||
UseHTTP3Upstreams: dnsConf.UseHTTP3Upstreams,
|
||||
ServePlainDNS: dnsConf.ServePlainDNS,
|
||||
}
|
||||
|
||||
var initialAddresses []netip.Addr
|
||||
@@ -254,79 +270,81 @@ func newServerConfig(
|
||||
AddressUpdater: &Context.clients,
|
||||
InitialAddresses: initialAddresses,
|
||||
CatchPanics: true,
|
||||
UseRDNS: config.Clients.Sources.RDNS,
|
||||
UseWHOIS: config.Clients.Sources.WHOIS,
|
||||
UseRDNS: clientSrcConf.RDNS,
|
||||
UseWHOIS: clientSrcConf.WHOIS,
|
||||
}
|
||||
|
||||
if tlsConf.Enabled {
|
||||
newConf.TLSConfig = tlsConf.TLSConfig
|
||||
newConf.TLSConfig.ServerName = tlsConf.ServerName
|
||||
|
||||
if tlsConf.PortHTTPS != 0 {
|
||||
newConf.HTTPSListenAddrs = ipsToTCPAddrs(hosts, tlsConf.PortHTTPS)
|
||||
}
|
||||
|
||||
if tlsConf.PortDNSOverTLS != 0 {
|
||||
newConf.TLSListenAddrs = ipsToTCPAddrs(hosts, tlsConf.PortDNSOverTLS)
|
||||
}
|
||||
|
||||
if tlsConf.PortDNSOverQUIC != 0 {
|
||||
newConf.QUICListenAddrs = ipsToUDPAddrs(hosts, tlsConf.PortDNSOverQUIC)
|
||||
}
|
||||
|
||||
if tlsConf.PortDNSCrypt != 0 {
|
||||
newConf.DNSCryptConfig, err = newDNSCrypt(hosts, *tlsConf)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's already wrapped by
|
||||
// newDNSCrypt.
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
newConf.DNSCryptConfig, err = newDNSCryptConfig(tlsConf, hosts)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's already wrapped by
|
||||
// newDNSCryptConfig.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newConf.TLSv12Roots = Context.tlsRoots
|
||||
newConf.TLSAllowUnencryptedDoH = tlsConf.AllowUnencryptedDoH
|
||||
|
||||
newConf.FilterHandler = applyAdditionalFiltering
|
||||
newConf.GetCustomUpstreamByClient = Context.clients.findUpstreams
|
||||
|
||||
newConf.LocalPTRResolvers = dnsConf.LocalPTRResolvers
|
||||
newConf.UpstreamTimeout = dnsConf.UpstreamTimeout.Duration
|
||||
|
||||
newConf.UsePrivateRDNS = dnsConf.UsePrivateRDNS
|
||||
newConf.ServeHTTP3 = dnsConf.ServeHTTP3
|
||||
newConf.UseHTTP3Upstreams = dnsConf.UseHTTP3Upstreams
|
||||
|
||||
return newConf, nil
|
||||
}
|
||||
|
||||
func newDNSCrypt(hosts []netip.Addr, tlsConf tlsConfigSettings) (dnscc dnsforward.DNSCryptConfig, err error) {
|
||||
if tlsConf.DNSCryptConfigFile == "" {
|
||||
return dnscc, errors.Error("no dnscrypt_config_file")
|
||||
// newDNSTLSConfig converts values from the configuration file into the internal
|
||||
// TLS settings for the DNS server. tlsConf must not be nil.
|
||||
func newDNSTLSConfig(conf *tlsConfigSettings, addrs []netip.Addr) (dnsConf dnsforward.TLSConfig) {
|
||||
if !conf.Enabled {
|
||||
return dnsforward.TLSConfig{}
|
||||
}
|
||||
|
||||
f, err := os.Open(tlsConf.DNSCryptConfigFile)
|
||||
dnsConf = conf.TLSConfig
|
||||
dnsConf.ServerName = conf.ServerName
|
||||
|
||||
if conf.PortHTTPS != 0 {
|
||||
dnsConf.HTTPSListenAddrs = ipsToTCPAddrs(addrs, conf.PortHTTPS)
|
||||
}
|
||||
|
||||
if conf.PortDNSOverTLS != 0 {
|
||||
dnsConf.TLSListenAddrs = ipsToTCPAddrs(addrs, conf.PortDNSOverTLS)
|
||||
}
|
||||
|
||||
if conf.PortDNSOverQUIC != 0 {
|
||||
dnsConf.QUICListenAddrs = ipsToUDPAddrs(addrs, conf.PortDNSOverQUIC)
|
||||
}
|
||||
|
||||
return dnsConf
|
||||
}
|
||||
|
||||
// newDNSCryptConfig converts values from the configuration file into the
|
||||
// internal DNSCrypt settings for the DNS server. conf must not be nil.
|
||||
func newDNSCryptConfig(
|
||||
conf *tlsConfigSettings,
|
||||
addrs []netip.Addr,
|
||||
) (dnsCryptConf dnsforward.DNSCryptConfig, err error) {
|
||||
if !conf.Enabled || conf.PortDNSCrypt == 0 {
|
||||
return dnsforward.DNSCryptConfig{}, nil
|
||||
}
|
||||
|
||||
if conf.DNSCryptConfigFile == "" {
|
||||
return dnsforward.DNSCryptConfig{}, errors.Error("no dnscrypt_config_file")
|
||||
}
|
||||
|
||||
f, err := os.Open(conf.DNSCryptConfigFile)
|
||||
if err != nil {
|
||||
return dnscc, fmt.Errorf("opening dnscrypt config: %w", err)
|
||||
return dnsforward.DNSCryptConfig{}, fmt.Errorf("opening dnscrypt config: %w", err)
|
||||
}
|
||||
defer func() { err = errors.WithDeferred(err, f.Close()) }()
|
||||
|
||||
rc := &dnscrypt.ResolverConfig{}
|
||||
err = yaml.NewDecoder(f).Decode(rc)
|
||||
if err != nil {
|
||||
return dnscc, fmt.Errorf("decoding dnscrypt config: %w", err)
|
||||
return dnsforward.DNSCryptConfig{}, fmt.Errorf("decoding dnscrypt config: %w", err)
|
||||
}
|
||||
|
||||
cert, err := rc.CreateCert()
|
||||
if err != nil {
|
||||
return dnscc, fmt.Errorf("creating dnscrypt cert: %w", err)
|
||||
return dnsforward.DNSCryptConfig{}, fmt.Errorf("creating dnscrypt cert: %w", err)
|
||||
}
|
||||
|
||||
return dnsforward.DNSCryptConfig{
|
||||
ResolverCert: cert,
|
||||
ProviderName: rc.ProviderName,
|
||||
UDPListenAddrs: ipsToUDPAddrs(hosts, tlsConf.PortDNSCrypt),
|
||||
TCPListenAddrs: ipsToTCPAddrs(hosts, tlsConf.PortDNSCrypt),
|
||||
UDPListenAddrs: ipsToUDPAddrs(addrs, conf.PortDNSCrypt),
|
||||
TCPListenAddrs: ipsToTCPAddrs(addrs, conf.PortDNSCrypt),
|
||||
Enabled: true,
|
||||
}, nil
|
||||
}
|
||||
@@ -342,34 +360,36 @@ func getDNSEncryption() (de dnsEncryption) {
|
||||
|
||||
Context.tls.WriteDiskConfig(&tlsConf)
|
||||
|
||||
if tlsConf.Enabled && len(tlsConf.ServerName) != 0 {
|
||||
hostname := tlsConf.ServerName
|
||||
if tlsConf.PortHTTPS != 0 {
|
||||
addr := hostname
|
||||
if p := tlsConf.PortHTTPS; p != defaultPortHTTPS {
|
||||
addr = netutil.JoinHostPort(addr, p)
|
||||
}
|
||||
if !tlsConf.Enabled || len(tlsConf.ServerName) == 0 {
|
||||
return dnsEncryption{}
|
||||
}
|
||||
|
||||
de.https = (&url.URL{
|
||||
Scheme: "https",
|
||||
Host: addr,
|
||||
Path: "/dns-query",
|
||||
}).String()
|
||||
hostname := tlsConf.ServerName
|
||||
if tlsConf.PortHTTPS != 0 {
|
||||
addr := hostname
|
||||
if p := tlsConf.PortHTTPS; p != defaultPortHTTPS {
|
||||
addr = netutil.JoinHostPort(addr, p)
|
||||
}
|
||||
|
||||
if p := tlsConf.PortDNSOverTLS; p != 0 {
|
||||
de.tls = (&url.URL{
|
||||
Scheme: "tls",
|
||||
Host: netutil.JoinHostPort(hostname, p),
|
||||
}).String()
|
||||
}
|
||||
de.https = (&url.URL{
|
||||
Scheme: "https",
|
||||
Host: addr,
|
||||
Path: "/dns-query",
|
||||
}).String()
|
||||
}
|
||||
|
||||
if p := tlsConf.PortDNSOverQUIC; p != 0 {
|
||||
de.quic = (&url.URL{
|
||||
Scheme: "quic",
|
||||
Host: netutil.JoinHostPort(hostname, p),
|
||||
}).String()
|
||||
}
|
||||
if p := tlsConf.PortDNSOverTLS; p != 0 {
|
||||
de.tls = (&url.URL{
|
||||
Scheme: "tls",
|
||||
Host: netutil.JoinHostPort(hostname, p),
|
||||
}).String()
|
||||
}
|
||||
|
||||
if p := tlsConf.PortDNSOverQUIC; p != 0 {
|
||||
de.quic = (&url.URL{
|
||||
Scheme: "quic",
|
||||
Host: netutil.JoinHostPort(hostname, p),
|
||||
}).String()
|
||||
}
|
||||
|
||||
return de
|
||||
@@ -454,7 +474,7 @@ func reconfigureDNSServer() (err error) {
|
||||
tlsConf := &tlsConfigSettings{}
|
||||
Context.tls.WriteDiskConfig(tlsConf)
|
||||
|
||||
newConf, err := newServerConfig(tlsConf, httpRegister)
|
||||
newConf, err := newServerConfig(&config.DNS, config.Clients.Sources, tlsConf, httpRegister)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generating forwarding dns server config: %w", err)
|
||||
}
|
||||
@@ -520,11 +540,13 @@ type safeSearchResolver struct{}
|
||||
var _ filtering.Resolver = safeSearchResolver{}
|
||||
|
||||
// LookupIP implements [filtering.Resolver] interface for safeSearchResolver.
|
||||
// It returns the slice of net.IP with IPv4 and IPv6 instances.
|
||||
//
|
||||
// TODO(a.garipov): Support network.
|
||||
func (r safeSearchResolver) LookupIP(_ context.Context, _, host string) (ips []net.IP, err error) {
|
||||
addrs, err := Context.dnsServer.Resolve(host)
|
||||
// It returns the slice of net.Addr with IPv4 and IPv6 instances.
|
||||
func (r safeSearchResolver) LookupIP(
|
||||
ctx context.Context,
|
||||
network string,
|
||||
host string,
|
||||
) (ips []net.IP, err error) {
|
||||
addrs, err := Context.dnsServer.Resolve(ctx, network, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -534,7 +556,7 @@ func (r safeSearchResolver) LookupIP(_ context.Context, _, host string) (ips []n
|
||||
}
|
||||
|
||||
for _, a := range addrs {
|
||||
ips = append(ips, a.IP)
|
||||
ips = append(ips, a.AsSlice())
|
||||
}
|
||||
|
||||
return ips, nil
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
@@ -160,7 +159,7 @@ func setupContext(opts options) (err error) {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if !opts.noEtcHosts && config.Clients.Sources.HostsFile {
|
||||
if !opts.noEtcHosts {
|
||||
err = setupHostsContainer()
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
@@ -239,13 +238,13 @@ func setupHostsContainer() (err error) {
|
||||
)
|
||||
if err != nil {
|
||||
closeErr := hostsWatcher.Close()
|
||||
if errors.Is(err, aghnet.ErrNoHostsPaths) && closeErr == nil {
|
||||
if errors.Is(err, aghnet.ErrNoHostsPaths) {
|
||||
log.Info("warning: initing hosts container: %s", err)
|
||||
|
||||
return nil
|
||||
return closeErr
|
||||
}
|
||||
|
||||
return errors.WithDeferred(fmt.Errorf("initing hosts container: %w", err), closeErr)
|
||||
return errors.Join(fmt.Errorf("initializing hosts container: %w", err), closeErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -294,19 +293,13 @@ func initContextClients() (err error) {
|
||||
arpDB = arpdb.New()
|
||||
}
|
||||
|
||||
err = Context.clients.Init(
|
||||
return Context.clients.Init(
|
||||
config.Clients.Persistent,
|
||||
Context.dhcpServer,
|
||||
Context.etcHosts,
|
||||
arpDB,
|
||||
config.Filtering,
|
||||
)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupBindOpts overrides bind host/port from the opts.
|
||||
@@ -376,11 +369,15 @@ func setupDNSFilteringConf(conf *filtering.Config) (err error) {
|
||||
|
||||
upsOpts := &upstream.Options{
|
||||
Timeout: dnsTimeout,
|
||||
ServerIPAddrs: []net.IP{
|
||||
{94, 140, 14, 15},
|
||||
{94, 140, 15, 16},
|
||||
net.ParseIP("2a10:50c0::bad1:ff"),
|
||||
net.ParseIP("2a10:50c0::bad2:ff"),
|
||||
Bootstrap: upstream.StaticResolver{
|
||||
// 94.140.14.15.
|
||||
netip.AddrFrom4([4]byte{94, 140, 14, 15}),
|
||||
// 94.140.14.16.
|
||||
netip.AddrFrom4([4]byte{94, 140, 14, 16}),
|
||||
// 2a10:50c0::bad1:ff.
|
||||
netip.AddrFrom16([16]byte{42, 16, 80, 192, 12: 186, 209, 0, 255}),
|
||||
// 2a10:50c0::bad2:ff.
|
||||
netip.AddrFrom16([16]byte{42, 16, 80, 192, 12: 186, 210, 0, 255}),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user