mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2025-01-13 09:27:23 +03:00
6dbeb5b971
Updates #6321. Squashed commit of the following: commit 033f4d427bc965c8c5b5fc452ddc938b71c8d4f4 Merge: febf5b568f7995aa02
Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Jan 16 12:42:50 2024 +0300 Merge branch 'master' into 6321-upd-proxy commit febf5b568b012ccdce86c1d0dde710b99f5729b2 Merge: 7e4b247a458b47adaf
Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Tue Jan 16 12:34:28 2024 +0300 Merge branch 'master' into 6321-upd-proxy commit 7e4b247a40263225dda3aaa77b823c056ae49541 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Fri Jan 12 14:37:28 2024 +0300 all: upd proxy revision commit 0e925c1fcfb5ea4b712360721b62ecef1c8355d8 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Thu Jan 11 14:24:47 2024 +0300 dnsforward: imp code commit 6f5c271895f973f6b3ad842cb987cdc2c89856f8 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Jan 10 16:34:02 2024 +0300 all: use caching resolver more, imp code commit ce3f3137d2a2ed5da7829f9ecf49bab388d44852 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Jan 10 16:00:08 2024 +0300 all: log changes commit ce90dbcda8586fda0d12d86ef609de505ae87164 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Jan 10 13:01:25 2024 +0300 dnsforward: use caching resolvers commit 208fbaf0aa50df33f8482a441d361d106fc7b146 Author: Eugene Burkov <E.Burkov@AdGuard.COM> Date: Wed Jan 10 12:52:15 2024 +0300 all: upd proxy
232 lines
6.1 KiB
Go
232 lines
6.1 KiB
Go
// Package dnssvc contains the AdGuard Home DNS service.
|
|
//
|
|
// TODO(a.garipov): Define, if all methods of a *Service should work with a nil
|
|
// receiver.
|
|
package dnssvc
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"net/netip"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
|
"github.com/AdguardTeam/AdGuardHome/internal/next/agh"
|
|
|
|
// TODO(a.garipov): Add a “dnsproxy proxy” package to shield us from changes
|
|
// and replacement of module dnsproxy.
|
|
"github.com/AdguardTeam/dnsproxy/proxy"
|
|
"github.com/AdguardTeam/dnsproxy/upstream"
|
|
"github.com/AdguardTeam/golibs/errors"
|
|
)
|
|
|
|
// Service is the AdGuard Home DNS service. A nil *Service is a valid
|
|
// [agh.Service] that does nothing.
|
|
//
|
|
// TODO(a.garipov): Consider saving a [*proxy.Config] instance for those
|
|
// fields that are only used in [New] and [Service.Config].
|
|
type Service struct {
|
|
proxy *proxy.Proxy
|
|
bootstraps []string
|
|
bootstrapResolvers []*upstream.UpstreamResolver
|
|
upstreams []string
|
|
dns64Prefixes []netip.Prefix
|
|
upsTimeout time.Duration
|
|
running atomic.Bool
|
|
bootstrapPreferIPv6 bool
|
|
useDNS64 bool
|
|
}
|
|
|
|
// New returns a new properly initialized *Service. If c is nil, svc is a nil
|
|
// *Service that does nothing. The fields of c must not be modified after
|
|
// calling New.
|
|
func New(c *Config) (svc *Service, err error) {
|
|
if c == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
svc = &Service{
|
|
bootstraps: c.BootstrapServers,
|
|
upstreams: c.UpstreamServers,
|
|
dns64Prefixes: c.DNS64Prefixes,
|
|
upsTimeout: c.UpstreamTimeout,
|
|
bootstrapPreferIPv6: c.BootstrapPreferIPv6,
|
|
useDNS64: c.UseDNS64,
|
|
}
|
|
|
|
upstreams, resolvers, err := addressesToUpstreams(
|
|
c.UpstreamServers,
|
|
c.BootstrapServers,
|
|
c.UpstreamTimeout,
|
|
c.BootstrapPreferIPv6,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("converting upstreams: %w", err)
|
|
}
|
|
|
|
svc.bootstrapResolvers = resolvers
|
|
svc.proxy = &proxy.Proxy{
|
|
Config: proxy.Config{
|
|
UDPListenAddr: udpAddrs(c.Addresses),
|
|
TCPListenAddr: tcpAddrs(c.Addresses),
|
|
UpstreamConfig: &proxy.UpstreamConfig{
|
|
Upstreams: upstreams,
|
|
},
|
|
UseDNS64: c.UseDNS64,
|
|
DNS64Prefs: c.DNS64Prefixes,
|
|
},
|
|
}
|
|
|
|
err = svc.proxy.Init()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("proxy: %w", err)
|
|
}
|
|
|
|
return svc, nil
|
|
}
|
|
|
|
// addressesToUpstreams is a wrapper around [upstream.AddressToUpstream]. It
|
|
// accepts a slice of addresses and other upstream parameters, and returns a
|
|
// slice of upstreams.
|
|
func addressesToUpstreams(
|
|
upsStrs []string,
|
|
bootstraps []string,
|
|
timeout time.Duration,
|
|
preferIPv6 bool,
|
|
) (upstreams []upstream.Upstream, boots []*upstream.UpstreamResolver, err error) {
|
|
opts := &upstream.Options{
|
|
Timeout: timeout,
|
|
PreferIPv6: preferIPv6,
|
|
}
|
|
|
|
boots, err = aghnet.ParseBootstraps(bootstraps, opts)
|
|
if err != nil {
|
|
// Don't wrap the error, since it's informative enough as is.
|
|
return nil, nil, err
|
|
}
|
|
|
|
// TODO(e.burkov): Add system hosts resolver here.
|
|
var bootstrap upstream.ParallelResolver
|
|
for _, r := range boots {
|
|
bootstrap = append(bootstrap, upstream.NewCachingResolver(r))
|
|
}
|
|
|
|
upstreams = make([]upstream.Upstream, len(upsStrs))
|
|
for i, upsStr := range upsStrs {
|
|
upstreams[i], err = upstream.AddressToUpstream(upsStr, &upstream.Options{
|
|
Bootstrap: bootstrap,
|
|
Timeout: timeout,
|
|
PreferIPv6: preferIPv6,
|
|
})
|
|
if err != nil {
|
|
return nil, boots, fmt.Errorf("upstream at index %d: %w", i, err)
|
|
}
|
|
}
|
|
|
|
return upstreams, boots, nil
|
|
}
|
|
|
|
// tcpAddrs converts []netip.AddrPort into []*net.TCPAddr.
|
|
func tcpAddrs(addrPorts []netip.AddrPort) (tcpAddrs []*net.TCPAddr) {
|
|
if addrPorts == nil {
|
|
return nil
|
|
}
|
|
|
|
tcpAddrs = make([]*net.TCPAddr, len(addrPorts))
|
|
for i, a := range addrPorts {
|
|
tcpAddrs[i] = net.TCPAddrFromAddrPort(a)
|
|
}
|
|
|
|
return tcpAddrs
|
|
}
|
|
|
|
// udpAddrs converts []netip.AddrPort into []*net.UDPAddr.
|
|
func udpAddrs(addrPorts []netip.AddrPort) (udpAddrs []*net.UDPAddr) {
|
|
if addrPorts == nil {
|
|
return nil
|
|
}
|
|
|
|
udpAddrs = make([]*net.UDPAddr, len(addrPorts))
|
|
for i, a := range addrPorts {
|
|
udpAddrs[i] = net.UDPAddrFromAddrPort(a)
|
|
}
|
|
|
|
return udpAddrs
|
|
}
|
|
|
|
// type check
|
|
var _ agh.Service = (*Service)(nil)
|
|
|
|
// Start implements the [agh.Service] interface for *Service. svc may be nil.
|
|
// After Start exits, all DNS servers have tried to start, but there is no
|
|
// guarantee that they did. Errors from the servers are written to the log.
|
|
func (svc *Service) Start() (err error) {
|
|
if svc == nil {
|
|
return nil
|
|
}
|
|
|
|
defer func() {
|
|
// TODO(a.garipov): [proxy.Proxy.Start] doesn't actually have any way to
|
|
// tell when all servers are actually up, so at best this is merely an
|
|
// assumption.
|
|
svc.running.Store(err == nil)
|
|
}()
|
|
|
|
return svc.proxy.Start()
|
|
}
|
|
|
|
// Shutdown implements the [agh.Service] interface for *Service. svc may be
|
|
// nil.
|
|
func (svc *Service) Shutdown(ctx context.Context) (err error) {
|
|
if svc == nil {
|
|
return nil
|
|
}
|
|
|
|
errs := []error{
|
|
svc.proxy.Stop(),
|
|
}
|
|
|
|
for _, b := range svc.bootstrapResolvers {
|
|
errs = append(errs, errors.Annotate(b.Close(), "closing bootstrap %s: %w", b.Address()))
|
|
}
|
|
|
|
return errors.Join(errs...)
|
|
}
|
|
|
|
// Config returns the current configuration of the web service. Config must not
|
|
// be called simultaneously with Start. If svc was initialized with ":0"
|
|
// addresses, addrs will not return the actual bound ports until Start is
|
|
// finished.
|
|
func (svc *Service) Config() (c *Config) {
|
|
// TODO(a.garipov): Do we need to get the TCP addresses separately?
|
|
|
|
var addrs []netip.AddrPort
|
|
if svc.running.Load() {
|
|
udpAddrs := svc.proxy.Addrs(proxy.ProtoUDP)
|
|
addrs = make([]netip.AddrPort, len(udpAddrs))
|
|
for i, a := range udpAddrs {
|
|
addrs[i] = a.(*net.UDPAddr).AddrPort()
|
|
}
|
|
} else {
|
|
conf := svc.proxy.Config
|
|
udpAddrs := conf.UDPListenAddr
|
|
addrs = make([]netip.AddrPort, len(udpAddrs))
|
|
for i, a := range udpAddrs {
|
|
addrs[i] = a.AddrPort()
|
|
}
|
|
}
|
|
|
|
c = &Config{
|
|
Addresses: addrs,
|
|
BootstrapServers: svc.bootstraps,
|
|
UpstreamServers: svc.upstreams,
|
|
DNS64Prefixes: svc.dns64Prefixes,
|
|
UpstreamTimeout: svc.upsTimeout,
|
|
BootstrapPreferIPv6: svc.bootstrapPreferIPv6,
|
|
UseDNS64: svc.useDNS64,
|
|
}
|
|
|
|
return c
|
|
}
|