mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2024-12-16 03:45:45 +03:00
d4c3a43bcb
Merge in DNS/adguard-home from add-dnssvc to master
Squashed commit of the following:
commit 55f4f114bab65a03c0d65383e89020a7356cff32
Merge: 95dc28d9 6e63757f
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date: Mon Aug 15 20:53:07 2022 +0300
Merge branch 'master' into add-dnssvc
commit 95dc28d9d77d06e8ac98c1e6772557bffbf1705b
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date: Mon Aug 15 20:52:50 2022 +0300
all: imp tests, docs
commit 0d9d02950d84afd160b4b1c118da856cee6f12e5
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date: Thu Aug 11 19:27:59 2022 +0300
all: imp docs
commit 8990e038a81da4430468da12fcebedf79fe14df6
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date: Thu Aug 11 19:05:29 2022 +0300
all: imp tests more
commit 92730d93a2a1ac77888c2655508e43efaf0e9fde
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date: Thu Aug 11 18:37:48 2022 +0300
all: imp tests more
commit 8cd45ba30da7ac310e9dc666fb2af438e577b02d
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date: Thu Aug 11 18:11:15 2022 +0300
all: add v1 dnssvc stub; refactor tests
194 lines
4.9 KiB
Go
194 lines
4.9 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"
|
|
"time"
|
|
|
|
"github.com/AdguardTeam/AdGuardHome/internal/v1/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"
|
|
)
|
|
|
|
// Config is the AdGuard Home DNS service configuration structure.
|
|
//
|
|
// TODO(a.garipov): Add timeout for incoming requests.
|
|
type Config struct {
|
|
// Addresses are the addresses on which to serve plain DNS queries.
|
|
Addresses []netip.AddrPort
|
|
|
|
// Upstreams are the DNS upstreams to use. If not set, upstreams are
|
|
// created using data from BootstrapServers, UpstreamServers, and
|
|
// UpstreamTimeout.
|
|
//
|
|
// TODO(a.garipov): Think of a better scheme. Those other three parameters
|
|
// are here only to make Config work properly.
|
|
Upstreams []upstream.Upstream
|
|
|
|
// BootstrapServers are the addresses for bootstrapping the upstream DNS
|
|
// server addresses.
|
|
BootstrapServers []string
|
|
|
|
// UpstreamServers are the upstream DNS server addresses to use.
|
|
UpstreamServers []string
|
|
|
|
// UpstreamTimeout is the timeout for upstream requests.
|
|
UpstreamTimeout time.Duration
|
|
}
|
|
|
|
// Service is the AdGuard Home DNS service. A nil *Service is a valid
|
|
// [agh.Service] that does nothing.
|
|
type Service struct {
|
|
proxy *proxy.Proxy
|
|
bootstraps []string
|
|
upstreams []string
|
|
upsTimeout time.Duration
|
|
}
|
|
|
|
// 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,
|
|
upsTimeout: c.UpstreamTimeout,
|
|
}
|
|
|
|
var upstreams []upstream.Upstream
|
|
if len(c.Upstreams) > 0 {
|
|
upstreams = c.Upstreams
|
|
} else {
|
|
upstreams, err = addressesToUpstreams(
|
|
c.UpstreamServers,
|
|
c.BootstrapServers,
|
|
c.UpstreamTimeout,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("converting upstreams: %w", err)
|
|
}
|
|
}
|
|
|
|
svc.proxy = &proxy.Proxy{
|
|
Config: proxy.Config{
|
|
UDPListenAddr: udpAddrs(c.Addresses),
|
|
TCPListenAddr: tcpAddrs(c.Addresses),
|
|
UpstreamConfig: &proxy.UpstreamConfig{
|
|
Upstreams: upstreams,
|
|
},
|
|
},
|
|
}
|
|
|
|
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,
|
|
) (upstreams []upstream.Upstream, err error) {
|
|
upstreams = make([]upstream.Upstream, len(upsStrs))
|
|
for i, upsStr := range upsStrs {
|
|
upstreams[i], err = upstream.AddressToUpstream(upsStr, &upstream.Options{
|
|
Bootstrap: bootstraps,
|
|
Timeout: timeout,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("upstream at index %d: %w", i, err)
|
|
}
|
|
}
|
|
|
|
return upstreams, 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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
return svc.proxy.Stop()
|
|
}
|
|
|
|
// Config returns the current configuration of the web service.
|
|
func (svc *Service) Config() (c *Config) {
|
|
// TODO(a.garipov): Do we need to get the TCP addresses separately?
|
|
udpAddrs := svc.proxy.Addrs(proxy.ProtoUDP)
|
|
addrs := make([]netip.AddrPort, len(udpAddrs))
|
|
for i, a := range udpAddrs {
|
|
addrs[i] = a.(*net.UDPAddr).AddrPort()
|
|
}
|
|
|
|
c = &Config{
|
|
Addresses: addrs,
|
|
BootstrapServers: svc.bootstraps,
|
|
UpstreamServers: svc.upstreams,
|
|
UpstreamTimeout: svc.upsTimeout,
|
|
}
|
|
|
|
return c
|
|
}
|