332 lines
9.1 KiB
Go
Raw Permalink Normal View History

2022-08-26 14:18:35 +03:00
package dnsserver
import (
2024-06-07 14:27:46 +03:00
"cmp"
2022-08-26 14:18:35 +03:00
"context"
2024-10-14 17:44:24 +03:00
"fmt"
"math"
2022-08-26 14:18:35 +03:00
"net"
"sync"
"time"
2023-02-03 15:27:58 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/netext"
2022-11-07 10:21:24 +03:00
"github.com/AdguardTeam/golibs/errors"
2022-08-26 14:18:35 +03:00
"github.com/AdguardTeam/golibs/log"
2024-01-04 19:22:32 +03:00
"github.com/AdguardTeam/golibs/syncutil"
2022-08-26 14:18:35 +03:00
"github.com/miekg/dns"
2022-12-29 15:36:26 +03:00
"github.com/panjf2000/ants/v2"
2022-08-26 14:18:35 +03:00
)
const (
// DefaultReadTimeout is the default ServerDNS.ReadTimeout.
DefaultReadTimeout = 2 * time.Second
// DefaultWriteTimeout is the default ServerDNS.WriteTimeout.
DefaultWriteTimeout = 2 * time.Second
// DefaultTCPIdleTimeout is the default ServerDNS.TCPIdleTimeout.
//
// RFC5966:
// "It is therefore RECOMMENDED that the default application-level idle
// period should be of the order of seconds, but no particular value is
// specified"
DefaultTCPIdleTimeout = 30 * time.Second
2024-10-14 17:44:24 +03:00
// MaxTCPIdleTimeout is the maximum TCP idle timeout per RFC 7828.
MaxTCPIdleTimeout = math.MaxUint16 * 100 * time.Millisecond
2022-08-26 14:18:35 +03:00
)
// ConfigDNS is a struct that needs to be passed to NewServerDNS to
// initialize a new ServerDNS instance.
type ConfigDNS struct {
ConfigBase
// ReadTimeout is the net.Conn.SetReadTimeout value for new connections.
// If not set it defaults to DefaultReadTimeout.
ReadTimeout time.Duration
// WriteTimeout is the net.Conn.SetWriteTimeout value for connections. If
// not set it defaults to DefaultWriteTimeout.
WriteTimeout time.Duration
2024-07-10 19:49:07 +03:00
// TCPIdleTimeout is the timeout for waiting between multiple queries. If
2024-10-14 17:44:24 +03:00
// not set it defaults to [DefaultTCPIdleTimeout]. It must not be greater
// than [MaxTCPIdleTimeout].
2024-07-10 19:49:07 +03:00
TCPIdleTimeout time.Duration
// MaxPipelineCount is the maximum number of simultaneously processing TCP
// messages per one connection. If MaxPipelineEnabled is true, it must be
// greater than zero.
MaxPipelineCount uint
2024-01-04 19:22:32 +03:00
// UDPSize is the size of the buffers used to read incoming UDP messages.
// If not set it defaults to [dns.MinMsgSize], 512 B.
2022-08-26 14:18:35 +03:00
UDPSize int
2024-01-04 19:22:32 +03:00
// TCPSize is the initial size of the buffers used to read incoming TCP
// messages. If not set it defaults to [dns.MinMsgSize], 512 B.
2022-08-26 14:18:35 +03:00
TCPSize int
2024-01-04 19:22:32 +03:00
// MaxUDPRespSize is the maximum size of DNS response over UDP protocol.
MaxUDPRespSize uint16
// MaxPipelineEnabled, if true, enables TCP pipeline limiting.
MaxPipelineEnabled bool
2022-08-26 14:18:35 +03:00
}
// ServerDNS is a plain DNS server (e.g. it supports UDP and TCP protocols).
type ServerDNS struct {
*ServerBase
2022-12-29 15:36:26 +03:00
// workerPool is a goroutine workerPool we use to process DNS queries.
// Complicated logic may require growing the goroutine's stack, and we
// experienced it in AdGuard DNS. The easiest way to avoid spending extra
// time on this is to reuse already existing goroutines.
workerPool *ants.Pool
2022-08-26 14:18:35 +03:00
2024-01-04 19:22:32 +03:00
// udpPool is a pool for UDP request buffers.
udpPool *syncutil.Pool[[]byte]
// tcpPool is a pool for TCP request buffers.
tcpPool *syncutil.Pool[[]byte]
2022-08-26 14:18:35 +03:00
2024-01-04 19:22:32 +03:00
// respPool is a pool for response buffers.
respPool *syncutil.Pool[[]byte]
2022-08-26 14:18:35 +03:00
2022-12-29 15:36:26 +03:00
// tcpConns is a set that is used to track active connections.
tcpConns map[net.Conn]struct{}
2024-01-04 19:22:32 +03:00
tcpConnsMu *sync.Mutex
// TODO(ameshkov, a.garipov): Only save the parameters a server actually
// needs.
conf ConfigDNS
2022-08-26 14:18:35 +03:00
}
// type check
var _ Server = (*ServerDNS)(nil)
2022-11-07 10:21:24 +03:00
// NewServerDNS creates a new ServerDNS instance.
2022-08-26 14:18:35 +03:00
func NewServerDNS(conf ConfigDNS) (s *ServerDNS) {
2022-11-07 10:21:24 +03:00
return newServerDNS(ProtoDNS, conf)
2022-08-26 14:18:35 +03:00
}
2022-11-07 10:21:24 +03:00
// newServerDNS initializes a new ServerDNS instance with the specified proto.
// This function is reused in ServerTLS as it is basically a plain DNS-over-TCP
// server with a TLS layer on top of it.
func newServerDNS(proto Protocol, conf ConfigDNS) (s *ServerDNS) {
// Init default settings first.
2024-06-07 14:27:46 +03:00
conf.ReadTimeout = cmp.Or(conf.ReadTimeout, DefaultReadTimeout)
conf.WriteTimeout = cmp.Or(conf.WriteTimeout, DefaultWriteTimeout)
conf.TCPIdleTimeout = cmp.Or(conf.TCPIdleTimeout, DefaultTCPIdleTimeout)
2024-10-14 17:44:24 +03:00
// TODO(a.garipov): Return an error instead.
if t := conf.TCPIdleTimeout; t < 0 || t > MaxTCPIdleTimeout {
panic(fmt.Errorf(
"newServerDNS: tcp idle timeout: %w: must be >= 0 and <= %s, got %s",
errors.ErrOutOfRange,
MaxTCPIdleTimeout,
t,
))
}
2024-06-07 14:27:46 +03:00
// Use dns.MinMsgSize since 99% of DNS queries fit this size, so this is a
// sensible default.
conf.UDPSize = cmp.Or(conf.UDPSize, dns.MinMsgSize)
conf.TCPSize = cmp.Or(conf.TCPSize, dns.MinMsgSize)
2022-08-26 14:18:35 +03:00
2023-02-03 15:27:58 +03:00
if conf.ListenConfig == nil {
2023-06-11 12:58:40 +03:00
conf.ListenConfig = netext.DefaultListenConfigWithOOB(nil)
2023-02-03 15:27:58 +03:00
}
2022-08-26 14:18:35 +03:00
s = &ServerDNS{
2022-11-07 10:21:24 +03:00
ServerBase: newServerBase(proto, conf.ConfigBase),
2022-12-29 15:36:26 +03:00
workerPool: newPoolNonblocking(),
2022-08-26 14:18:35 +03:00
2024-01-04 19:22:32 +03:00
udpPool: syncutil.NewSlicePool[byte](conf.UDPSize),
tcpPool: syncutil.NewSlicePool[byte](conf.TCPSize),
respPool: syncutil.NewSlicePool[byte](dns.MinMsgSize),
tcpConns: map[net.Conn]struct{}{},
tcpConnsMu: &sync.Mutex{},
conf: conf,
}
2022-08-26 14:18:35 +03:00
return s
}
2022-11-07 10:21:24 +03:00
// Start implements the dnsserver.Server interface for *ServerDNS.
2022-08-26 14:18:35 +03:00
func (s *ServerDNS) Start(ctx context.Context) (err error) {
2022-12-29 15:36:26 +03:00
defer func() { err = errors.Annotate(err, "starting dns server: %w") }()
2022-11-07 10:21:24 +03:00
2024-07-10 19:49:07 +03:00
s.mu.Lock()
defer s.mu.Unlock()
2022-08-26 14:18:35 +03:00
if s.started {
return ErrServerAlreadyStarted
}
log.Info("[%s]: Starting the server", s.Name())
2024-01-04 19:22:32 +03:00
ctx = ContextWithServerInfo(ctx, &ServerInfo{
2022-08-26 14:18:35 +03:00
Name: s.name,
Addr: s.addr,
Proto: s.proto,
})
2022-11-07 10:21:24 +03:00
if s.proto != ProtoDNS {
return ErrInvalidArgument
}
// Start listening to UDP on the specified address.
if s.network.CanUDP() {
2022-08-26 14:18:35 +03:00
err = s.listenUDP(ctx)
if err != nil {
return err
}
s.wg.Add(1)
go s.startServeUDP(ctx)
2022-11-07 10:21:24 +03:00
}
// Start listening to TCP on the specified address.
if s.network.CanTCP() {
2022-08-26 14:18:35 +03:00
err = s.listenTCP(ctx)
if err != nil {
return err
}
s.wg.Add(1)
go s.startServeTCP(ctx)
}
2024-01-04 19:22:32 +03:00
s.started = true
2022-08-26 14:18:35 +03:00
log.Info("[%s]: Server has been started", s.Name())
return nil
}
2022-11-07 10:21:24 +03:00
// Shutdown implements the dnsserver.Server interface for *ServerDNS.
2022-08-26 14:18:35 +03:00
func (s *ServerDNS) Shutdown(ctx context.Context) (err error) {
2022-12-29 15:36:26 +03:00
defer func() { err = errors.Annotate(err, "shutting down dns server: %w") }()
2022-11-07 10:21:24 +03:00
2022-08-26 14:18:35 +03:00
err = s.shutdown()
if err != nil {
log.Info("[%s]: Failed to shutdown: %v", s.Name(), err)
2022-12-29 15:36:26 +03:00
2022-08-26 14:18:35 +03:00
return err
}
s.unblockTCPConns()
err = s.waitShutdown(ctx)
2022-12-29 15:36:26 +03:00
// Close the workerPool and releases all workers.
s.workerPool.Release()
2022-08-26 14:18:35 +03:00
log.Info("[%s]: Finished stopping the server", s.Name())
2022-12-29 15:36:26 +03:00
2022-08-26 14:18:35 +03:00
return err
}
// startServeUDP starts the UDP listener loop.
func (s *ServerDNS) startServeUDP(ctx context.Context) {
// Do not recover from panics here since if this goroutine panics, the
// application won't be able to continue listening to UDP.
defer s.handlePanicAndExit(ctx)
defer s.wg.Done()
log.Info("[%s]: Start listening to udp://%s", s.Name(), s.Addr())
err := s.serveUDP(ctx, s.udpListener)
if err != nil {
log.Info("[%s]: Finished listening to udp://%s due to %v", s.Name(), s.Addr(), err)
}
}
// startServeTCP starts the TCP listener loop.
func (s *ServerDNS) startServeTCP(ctx context.Context) {
// Do not recover from panics here since if this goroutine panics, the
// application won't be able to continue listening to TCP.
defer s.handlePanicAndExit(ctx)
defer s.wg.Done()
log.Info("[%s]: Start listening to tcp://%s", s.Name(), s.Addr())
err := s.serveTCP(ctx, s.tcpListener)
if err != nil {
log.Info("[%s]: Finished listening to tcp://%s due to %v", s.Name(), s.Addr(), err)
}
}
// shutdown marks the server as stopped and closes active listeners.
func (s *ServerDNS) shutdown() (err error) {
2024-07-10 19:49:07 +03:00
s.mu.Lock()
defer s.mu.Unlock()
2022-08-26 14:18:35 +03:00
if !s.started {
return ErrServerNotStarted
}
// First, mark it as stopped.
s.started = false
// Now close all listeners.
s.closeListeners()
2022-12-29 15:36:26 +03:00
2022-08-26 14:18:35 +03:00
return nil
}
// unblockTCPConns unblocks reads for all active TCP connections.
func (s *ServerDNS) unblockTCPConns() {
s.tcpConnsMu.Lock()
defer s.tcpConnsMu.Unlock()
for conn := range s.tcpConns {
err := conn.SetReadDeadline(time.Unix(1, 0))
if err != nil {
log.Debug("[%s]: Failed to set read deadline: %v", s.Name(), err)
}
}
}
2023-02-03 15:27:58 +03:00
// writeDeadlineSetter is an interface for connections that can set write
// deadlines.
type writeDeadlineSetter interface {
SetWriteDeadline(t time.Time) (err error)
}
2024-01-04 19:22:32 +03:00
// withWriteDeadline is a helper that takes the deadline of the context and
// timeout into account. It sets the write deadline on conn before calling f
// and resets it once f is done.
2023-02-03 15:27:58 +03:00
func withWriteDeadline(
ctx context.Context,
2024-01-04 19:22:32 +03:00
timeout time.Duration,
2023-02-03 15:27:58 +03:00
conn writeDeadlineSetter,
f func(),
) {
2024-01-04 19:22:32 +03:00
// Add the given timeout and let context.WithTimeout decide which one is
// sooner.
ctx, cancel := context.WithTimeout(ctx, timeout)
2022-08-26 14:18:35 +03:00
defer func() {
2024-01-04 19:22:32 +03:00
cancel()
2022-08-26 14:18:35 +03:00
err := conn.SetWriteDeadline(time.Time{})
2022-12-29 15:36:26 +03:00
if err != nil && !errors.Is(err, net.ErrClosed) {
2024-01-04 19:22:32 +03:00
// Consider deadline errors non-critical. Ignore [net.ErrClosed] as
2022-12-29 15:36:26 +03:00
// it is expected to happen when the client closes connections.
2024-01-04 19:22:32 +03:00
log.Error("dnsserver: removing deadlines: %s", err)
2022-08-26 14:18:35 +03:00
}
}()
2024-01-04 19:22:32 +03:00
// Since context.WithTimeout has been called, this should return a non-empty
// deadline.
dl, _ := ctx.Deadline()
2022-08-26 14:18:35 +03:00
err := conn.SetWriteDeadline(dl)
2022-12-29 15:36:26 +03:00
if err != nil && !errors.Is(err, net.ErrClosed) {
2024-01-04 19:22:32 +03:00
// Consider deadline errors non-critical. Ignore [net.ErrClosed] as it
// is expected to happen when the client closes connections.
log.Error("dnsserver: setting deadlines: %s", err)
2022-08-26 14:18:35 +03:00
}
f()
}