2015-12-24 09:51:32 +00:00
|
|
|
package libnetwork
|
|
|
|
|
|
|
|
import (
|
2022-12-16 21:26:06 +00:00
|
|
|
"context"
|
2023-09-23 09:44:55 +00:00
|
|
|
"errors"
|
2015-12-24 09:51:32 +00:00
|
|
|
"fmt"
|
2016-02-21 15:39:53 +00:00
|
|
|
"math/rand"
|
2015-12-24 09:51:32 +00:00
|
|
|
"net"
|
2024-03-15 17:04:41 +00:00
|
|
|
"net/netip"
|
2023-08-17 08:43:18 +00:00
|
|
|
"strconv"
|
2015-12-24 09:51:32 +00:00
|
|
|
"strings"
|
2016-01-24 05:47:57 +00:00
|
|
|
"sync"
|
2023-10-09 23:13:25 +00:00
|
|
|
"sync/atomic"
|
2016-01-24 05:47:57 +00:00
|
|
|
"time"
|
2015-12-24 09:51:32 +00:00
|
|
|
|
2023-09-13 15:41:45 +00:00
|
|
|
"github.com/containerd/log"
|
2024-03-15 17:04:41 +00:00
|
|
|
"github.com/docker/docker/libnetwork/internal/netiputil"
|
2021-04-06 00:24:47 +00:00
|
|
|
"github.com/docker/docker/libnetwork/types"
|
2015-12-24 09:51:32 +00:00
|
|
|
"github.com/miekg/dns"
|
2023-09-23 09:44:55 +00:00
|
|
|
"go.opentelemetry.io/otel"
|
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
|
|
"go.opentelemetry.io/otel/codes"
|
|
|
|
"go.opentelemetry.io/otel/trace"
|
2022-12-16 21:26:06 +00:00
|
|
|
"golang.org/x/sync/semaphore"
|
|
|
|
"golang.org/x/time/rate"
|
2015-12-24 09:51:32 +00:00
|
|
|
)
|
|
|
|
|
2016-09-19 22:48:06 +00:00
|
|
|
// DNSBackend represents a backend DNS resolver used for DNS name
|
2017-05-22 02:25:52 +00:00
|
|
|
// resolution. All the queries to the resolver are forwarded to the
|
2016-09-19 22:48:06 +00:00
|
|
|
// backend resolver.
|
|
|
|
type DNSBackend interface {
|
|
|
|
// ResolveName resolves a service name to an IPv4 or IPv6 address by searching
|
|
|
|
// the networks the sandbox is connected to. For IPv6 queries, second return
|
|
|
|
// value will be true if the name exists in docker domain but doesn't have an
|
|
|
|
// IPv6 address. Such queries shouldn't be forwarded to external nameservers.
|
2023-09-23 09:44:55 +00:00
|
|
|
ResolveName(ctx context.Context, name string, iplen int) ([]net.IP, bool)
|
2016-09-19 22:48:06 +00:00
|
|
|
// ResolveIP returns the service name for the passed in IP. IP is in reverse dotted
|
|
|
|
// notation; the format used for DNS PTR records
|
2023-09-23 09:44:55 +00:00
|
|
|
ResolveIP(ctx context.Context, name string) string
|
2016-09-19 22:48:06 +00:00
|
|
|
// ResolveService returns all the backend details about the containers or hosts
|
|
|
|
// backing a service. Its purpose is to satisfy an SRV query
|
2023-09-23 09:44:55 +00:00
|
|
|
ResolveService(ctx context.Context, name string) ([]*net.SRV, []net.IP)
|
2016-09-19 22:48:06 +00:00
|
|
|
// ExecFunc allows a function to be executed in the context of the backend
|
|
|
|
// on behalf of the resolver.
|
|
|
|
ExecFunc(f func()) error
|
2021-06-09 17:37:10 +00:00
|
|
|
// NdotsSet queries the backends ndots dns option settings
|
2016-09-19 22:48:06 +00:00
|
|
|
NdotsSet() bool
|
2016-12-06 22:56:24 +00:00
|
|
|
// HandleQueryResp passes the name & IP from a response to the backend. backend
|
|
|
|
// can use it to maintain any required state about the resolution
|
|
|
|
HandleQueryResp(name string, ip net.IP)
|
2016-09-19 22:48:06 +00:00
|
|
|
}
|
|
|
|
|
2015-12-24 09:51:32 +00:00
|
|
|
const (
|
2022-12-16 00:21:11 +00:00
|
|
|
dnsPort = "53"
|
|
|
|
ptrIPv4domain = ".in-addr.arpa."
|
|
|
|
ptrIPv6domain = ".ip6.arpa."
|
|
|
|
respTTL = 600
|
|
|
|
maxExtDNS = 3 // max number of external servers to try
|
|
|
|
extIOTimeout = 4 * time.Second
|
|
|
|
maxConcurrent = 1024
|
|
|
|
logInterval = 2 * time.Second
|
2015-12-24 09:51:32 +00:00
|
|
|
)
|
|
|
|
|
2016-01-24 05:47:57 +00:00
|
|
|
type extDNSEntry struct {
|
2017-01-19 22:25:26 +00:00
|
|
|
IPStr string
|
2023-01-06 23:25:00 +00:00
|
|
|
port uint16 // for testing
|
2017-01-19 22:25:26 +00:00
|
|
|
HostLoopback bool
|
2016-06-14 05:56:59 +00:00
|
|
|
}
|
|
|
|
|
2024-03-15 17:04:41 +00:00
|
|
|
func (e extDNSEntry) String() string {
|
|
|
|
if e.HostLoopback {
|
|
|
|
return "host(" + e.IPStr + ")"
|
|
|
|
}
|
|
|
|
return e.IPStr
|
|
|
|
}
|
|
|
|
|
2023-01-20 21:58:23 +00:00
|
|
|
// Resolver is the embedded DNS server in Docker. It operates by listening on
|
|
|
|
// the container's loopback interface for DNS queries.
|
|
|
|
type Resolver struct {
|
2016-09-19 22:48:06 +00:00
|
|
|
backend DNSBackend
|
2024-03-15 17:04:41 +00:00
|
|
|
extDNSList [maxExtDNS]extDNSEntry // Ext servers to use when there's no entry in ipToExtDNS.
|
|
|
|
ipToExtDNS addrToExtDNSMap // DNS query source IP -> ext servers.
|
2016-09-19 22:48:06 +00:00
|
|
|
server *dns.Server
|
|
|
|
conn *net.UDPConn
|
|
|
|
tcpServer *dns.Server
|
|
|
|
tcpListen *net.TCPListener
|
|
|
|
err error
|
2024-03-15 17:04:41 +00:00
|
|
|
listenAddress netip.Addr
|
2023-10-09 23:13:25 +00:00
|
|
|
proxyDNS atomic.Bool
|
2016-11-21 19:08:41 +00:00
|
|
|
startCh chan struct{}
|
2023-09-27 09:15:38 +00:00
|
|
|
logger *log.Entry
|
2022-12-16 21:26:06 +00:00
|
|
|
|
|
|
|
fwdSem *semaphore.Weighted // Limit the number of concurrent external DNS requests in-flight
|
|
|
|
logInverval rate.Sometimes // Rate-limit logging about hitting the fwdSem limit
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewResolver creates a new instance of the Resolver
|
2023-01-20 21:58:23 +00:00
|
|
|
func NewResolver(address string, proxyDNS bool, backend DNSBackend) *Resolver {
|
2023-10-09 23:13:25 +00:00
|
|
|
r := &Resolver{
|
2024-03-15 17:04:41 +00:00
|
|
|
backend: backend,
|
|
|
|
err: fmt.Errorf("setup not done yet"),
|
|
|
|
startCh: make(chan struct{}, 1),
|
|
|
|
fwdSem: semaphore.NewWeighted(maxConcurrent),
|
|
|
|
logInverval: rate.Sometimes{Interval: logInterval},
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
2024-03-15 17:04:41 +00:00
|
|
|
r.listenAddress, _ = netip.ParseAddr(address)
|
2023-10-09 23:13:25 +00:00
|
|
|
r.proxyDNS.Store(proxyDNS)
|
|
|
|
|
|
|
|
return r
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
|
|
|
|
2024-03-15 17:04:41 +00:00
|
|
|
type addrToExtDNSMap struct {
|
|
|
|
mu sync.Mutex
|
|
|
|
eMap map[netip.Addr][maxExtDNS]extDNSEntry
|
|
|
|
}
|
|
|
|
|
|
|
|
func (am *addrToExtDNSMap) get(addr netip.Addr) ([maxExtDNS]extDNSEntry, bool) {
|
|
|
|
am.mu.Lock()
|
|
|
|
defer am.mu.Unlock()
|
|
|
|
entries, ok := am.eMap[addr]
|
|
|
|
return entries, ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func (am *addrToExtDNSMap) set(addr netip.Addr, entries []extDNSEntry) {
|
|
|
|
var e [maxExtDNS]extDNSEntry
|
|
|
|
copy(e[:], entries)
|
|
|
|
am.mu.Lock()
|
|
|
|
defer am.mu.Unlock()
|
|
|
|
if len(entries) > 0 {
|
|
|
|
if am.eMap == nil {
|
|
|
|
am.eMap = map[netip.Addr][maxExtDNS]extDNSEntry{}
|
|
|
|
}
|
|
|
|
am.eMap[addr] = e
|
|
|
|
} else {
|
|
|
|
delete(am.eMap, addr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-27 09:15:38 +00:00
|
|
|
func (r *Resolver) log(ctx context.Context) *log.Entry {
|
2023-05-19 15:27:15 +00:00
|
|
|
if r.logger == nil {
|
2023-09-27 09:15:38 +00:00
|
|
|
return log.G(ctx)
|
2023-05-19 15:27:15 +00:00
|
|
|
}
|
|
|
|
return r.logger
|
|
|
|
}
|
|
|
|
|
2023-01-20 21:58:23 +00:00
|
|
|
// SetupFunc returns the setup function that should be run in the container's
|
|
|
|
// network namespace.
|
2024-03-15 17:04:41 +00:00
|
|
|
func (r *Resolver) SetupFunc(port uint16) func() {
|
2018-05-29 12:39:35 +00:00
|
|
|
return func() {
|
2015-12-24 09:51:32 +00:00
|
|
|
var err error
|
|
|
|
|
2016-01-22 05:23:30 +00:00
|
|
|
// DNS operates primarily on UDP
|
2024-03-15 17:04:41 +00:00
|
|
|
r.conn, err = net.ListenUDP("udp", net.UDPAddrFromAddrPort(
|
|
|
|
netip.AddrPortFrom(r.listenAddress, port)),
|
|
|
|
)
|
2015-12-24 09:51:32 +00:00
|
|
|
if err != nil {
|
|
|
|
r.err = fmt.Errorf("error in opening name server socket %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-01-22 05:23:30 +00:00
|
|
|
// Listen on a TCP as well
|
2024-03-15 17:04:41 +00:00
|
|
|
r.tcpListen, err = net.ListenTCP("tcp", net.TCPAddrFromAddrPort(
|
|
|
|
netip.AddrPortFrom(r.listenAddress, port)),
|
|
|
|
)
|
2016-01-22 05:23:30 +00:00
|
|
|
if err != nil {
|
|
|
|
r.err = fmt.Errorf("error in opening name TCP server socket %v", err)
|
|
|
|
return
|
|
|
|
}
|
2015-12-24 09:51:32 +00:00
|
|
|
r.err = nil
|
2018-05-29 12:39:35 +00:00
|
|
|
}
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
|
|
|
|
2023-01-20 21:58:23 +00:00
|
|
|
// Start starts the name server for the container.
|
|
|
|
func (r *Resolver) Start() error {
|
2016-11-21 19:08:41 +00:00
|
|
|
r.startCh <- struct{}{}
|
|
|
|
defer func() { <-r.startCh }()
|
|
|
|
|
2015-12-24 09:51:32 +00:00
|
|
|
// make sure the resolver has been setup before starting
|
|
|
|
if r.err != nil {
|
|
|
|
return r.err
|
|
|
|
}
|
2016-04-13 08:28:18 +00:00
|
|
|
|
|
|
|
if err := r.setupIPTable(); err != nil {
|
|
|
|
return fmt.Errorf("setting up IP table rules failed: %v", err)
|
|
|
|
}
|
|
|
|
|
2023-01-20 21:58:23 +00:00
|
|
|
s := &dns.Server{Handler: dns.HandlerFunc(r.serveDNS), PacketConn: r.conn}
|
2015-12-24 09:51:32 +00:00
|
|
|
r.server = s
|
|
|
|
go func() {
|
2021-05-28 00:15:56 +00:00
|
|
|
if err := s.ActivateAndServe(); err != nil {
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(context.TODO()).WithError(err).Error("[resolver] failed to start PacketConn DNS server")
|
2021-05-28 00:15:56 +00:00
|
|
|
}
|
2015-12-24 09:51:32 +00:00
|
|
|
}()
|
2016-01-22 05:23:30 +00:00
|
|
|
|
2023-01-20 21:58:23 +00:00
|
|
|
tcpServer := &dns.Server{Handler: dns.HandlerFunc(r.serveDNS), Listener: r.tcpListen}
|
2016-01-22 05:23:30 +00:00
|
|
|
r.tcpServer = tcpServer
|
|
|
|
go func() {
|
2021-05-28 00:15:56 +00:00
|
|
|
if err := tcpServer.ActivateAndServe(); err != nil {
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(context.TODO()).WithError(err).Error("[resolver] failed to start TCP DNS server")
|
2021-05-28 00:15:56 +00:00
|
|
|
}
|
2016-01-22 05:23:30 +00:00
|
|
|
}()
|
2015-12-24 09:51:32 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-01-20 21:58:23 +00:00
|
|
|
// Stop stops the name server for the container. A stopped resolver can be
|
|
|
|
// reused after running the SetupFunc again.
|
|
|
|
func (r *Resolver) Stop() {
|
2016-11-21 19:08:41 +00:00
|
|
|
r.startCh <- struct{}{}
|
|
|
|
defer func() { <-r.startCh }()
|
|
|
|
|
2015-12-24 09:51:32 +00:00
|
|
|
if r.server != nil {
|
2022-07-13 20:30:47 +00:00
|
|
|
r.server.Shutdown() //nolint:errcheck
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
2016-01-22 05:23:30 +00:00
|
|
|
if r.tcpServer != nil {
|
2022-07-13 20:30:47 +00:00
|
|
|
r.tcpServer.Shutdown() //nolint:errcheck
|
2016-01-22 05:23:30 +00:00
|
|
|
}
|
2016-01-14 17:56:32 +00:00
|
|
|
r.conn = nil
|
2016-01-22 05:23:30 +00:00
|
|
|
r.tcpServer = nil
|
2016-01-14 17:56:32 +00:00
|
|
|
r.err = fmt.Errorf("setup not done yet")
|
2022-12-16 21:26:06 +00:00
|
|
|
r.fwdSem = semaphore.NewWeighted(maxConcurrent)
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
|
|
|
|
2023-01-20 21:58:23 +00:00
|
|
|
// SetExtServers configures the external nameservers the resolver should use
|
2024-03-15 17:04:41 +00:00
|
|
|
// when forwarding queries, unless SetExtServersForSrc has configured servers
|
|
|
|
// for the DNS client making the request.
|
2023-01-20 21:58:23 +00:00
|
|
|
func (r *Resolver) SetExtServers(extDNS []extDNSEntry) {
|
2016-12-19 02:27:13 +00:00
|
|
|
l := len(extDNS)
|
2016-01-24 05:47:57 +00:00
|
|
|
if l > maxExtDNS {
|
|
|
|
l = maxExtDNS
|
|
|
|
}
|
|
|
|
for i := 0; i < l; i++ {
|
2016-12-19 02:27:13 +00:00
|
|
|
r.extDNSList[i] = extDNS[i]
|
2016-01-24 05:47:57 +00:00
|
|
|
}
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 23:13:25 +00:00
|
|
|
// SetForwardingPolicy re-configures the embedded DNS resolver to either enable or disable forwarding DNS queries to
|
|
|
|
// external servers.
|
|
|
|
func (r *Resolver) SetForwardingPolicy(policy bool) {
|
|
|
|
r.proxyDNS.Store(policy)
|
|
|
|
}
|
|
|
|
|
2024-03-15 17:04:41 +00:00
|
|
|
// SetExtServersForSrc configures the external nameservers the resolver should
|
|
|
|
// use when forwarding queries from srcAddr. If set, these servers will be used
|
|
|
|
// in preference to servers set by SetExtServers. Supplying a nil or empty extDNS
|
|
|
|
// deletes nameservers for srcAddr.
|
|
|
|
func (r *Resolver) SetExtServersForSrc(srcAddr netip.Addr, extDNS []extDNSEntry) error {
|
|
|
|
r.ipToExtDNS.set(srcAddr, extDNS)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-01-20 21:58:23 +00:00
|
|
|
// NameServer returns the IP of the DNS resolver for the containers.
|
2024-03-15 17:04:41 +00:00
|
|
|
func (r *Resolver) NameServer() netip.Addr {
|
2016-09-19 22:48:06 +00:00
|
|
|
return r.listenAddress
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
|
|
|
|
2023-01-20 21:58:23 +00:00
|
|
|
// ResolverOptions returns resolv.conf options that should be set.
|
|
|
|
func (r *Resolver) ResolverOptions() []string {
|
2015-12-24 09:51:32 +00:00
|
|
|
return []string{"ndots:0"}
|
|
|
|
}
|
|
|
|
|
2023-02-03 17:28:13 +00:00
|
|
|
//nolint:gosec // The RNG is not used in a security-sensitive context.
|
|
|
|
var (
|
|
|
|
shuffleRNG = rand.New(rand.NewSource(time.Now().Unix()))
|
|
|
|
shuffleRNGMu sync.Mutex
|
|
|
|
)
|
|
|
|
|
2016-02-21 15:39:53 +00:00
|
|
|
func shuffleAddr(addr []net.IP) []net.IP {
|
2023-02-03 17:28:13 +00:00
|
|
|
shuffleRNGMu.Lock()
|
|
|
|
defer shuffleRNGMu.Unlock()
|
2016-02-21 15:39:53 +00:00
|
|
|
for i := len(addr) - 1; i > 0; i-- {
|
2023-02-03 17:28:13 +00:00
|
|
|
r := shuffleRNG.Intn(i + 1) //nolint:gosec // gosec complains about the use of rand here. It should be fine.
|
2016-02-21 15:39:53 +00:00
|
|
|
addr[i], addr[r] = addr[r], addr[i]
|
|
|
|
}
|
|
|
|
return addr
|
|
|
|
}
|
|
|
|
|
2016-03-19 10:07:08 +00:00
|
|
|
func createRespMsg(query *dns.Msg) *dns.Msg {
|
2023-08-17 08:15:38 +00:00
|
|
|
resp := &dns.Msg{}
|
2016-03-19 10:07:08 +00:00
|
|
|
resp.SetReply(query)
|
2023-08-17 08:15:38 +00:00
|
|
|
resp.RecursionAvailable = true
|
2016-03-19 10:07:08 +00:00
|
|
|
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
2023-09-23 09:44:55 +00:00
|
|
|
func (r *Resolver) handleMXQuery(ctx context.Context, query *dns.Msg) (*dns.Msg, error) {
|
2021-10-13 10:20:02 +00:00
|
|
|
name := query.Question[0].Name
|
2023-09-23 09:44:55 +00:00
|
|
|
addrv4, _ := r.backend.ResolveName(ctx, name, types.IPv4)
|
|
|
|
addrv6, _ := r.backend.ResolveName(ctx, name, types.IPv6)
|
2017-12-20 22:32:47 +00:00
|
|
|
|
|
|
|
if addrv4 == nil && addrv6 == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// We were able to resolve the name. Respond with an empty list with
|
|
|
|
// RcodeSuccess/NOERROR so that email clients can treat it as "implicit MX"
|
|
|
|
// [RFC 5321 Section-5.1] and issue a Type A/AAAA query for the name.
|
|
|
|
|
|
|
|
resp := createRespMsg(query)
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2023-09-23 09:44:55 +00:00
|
|
|
func (r *Resolver) handleIPQuery(ctx context.Context, query *dns.Msg, ipType int) (*dns.Msg, error) {
|
2021-10-13 10:20:02 +00:00
|
|
|
var (
|
|
|
|
addr []net.IP
|
|
|
|
ipv6Miss bool
|
|
|
|
name = query.Question[0].Name
|
|
|
|
)
|
2023-09-23 09:44:55 +00:00
|
|
|
addr, ipv6Miss = r.backend.ResolveName(ctx, name, ipType)
|
2016-09-19 22:48:06 +00:00
|
|
|
|
2016-03-19 10:07:08 +00:00
|
|
|
if addr == nil && ipv6Miss {
|
|
|
|
// Send a reply without any Answer sections
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(ctx).Debugf("[resolver] lookup name %s present without IPv6 address", name)
|
2016-03-19 10:07:08 +00:00
|
|
|
resp := createRespMsg(query)
|
|
|
|
return resp, nil
|
|
|
|
}
|
2015-12-24 09:51:32 +00:00
|
|
|
if addr == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(ctx).Debugf("[resolver] lookup for %s: IP %v", name, addr)
|
2015-12-24 09:51:32 +00:00
|
|
|
|
2016-03-19 10:07:08 +00:00
|
|
|
resp := createRespMsg(query)
|
2016-02-21 15:39:53 +00:00
|
|
|
if len(addr) > 1 {
|
|
|
|
addr = shuffleAddr(addr)
|
|
|
|
}
|
2016-02-26 22:58:11 +00:00
|
|
|
if ipType == types.IPv4 {
|
2016-01-13 04:20:31 +00:00
|
|
|
for _, ip := range addr {
|
2023-08-17 08:18:20 +00:00
|
|
|
resp.Answer = append(resp.Answer, &dns.A{
|
|
|
|
Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL},
|
|
|
|
A: ip,
|
|
|
|
})
|
2016-01-13 04:20:31 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for _, ip := range addr {
|
2023-08-17 08:18:20 +00:00
|
|
|
resp.Answer = append(resp.Answer, &dns.AAAA{
|
|
|
|
Hdr: dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: respTTL},
|
|
|
|
AAAA: ip,
|
|
|
|
})
|
2016-01-13 04:20:31 +00:00
|
|
|
}
|
2016-02-21 15:39:53 +00:00
|
|
|
}
|
2015-12-24 09:51:32 +00:00
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2023-09-23 09:44:55 +00:00
|
|
|
func (r *Resolver) handlePTRQuery(ctx context.Context, query *dns.Msg) (*dns.Msg, error) {
|
2023-01-07 01:11:15 +00:00
|
|
|
ptr := query.Question[0].Name
|
|
|
|
name, after, found := strings.Cut(ptr, ptrIPv4domain)
|
|
|
|
if !found || after != "" {
|
|
|
|
name, after, found = strings.Cut(ptr, ptrIPv6domain)
|
|
|
|
}
|
|
|
|
if !found || after != "" {
|
|
|
|
// Not a known IPv4 or IPv6 PTR domain.
|
|
|
|
// Maybe the external DNS servers know what to do with the query?
|
|
|
|
return nil, nil
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
|
|
|
|
2023-09-23 09:44:55 +00:00
|
|
|
host := r.backend.ResolveIP(ctx, name)
|
2023-01-07 01:11:15 +00:00
|
|
|
if host == "" {
|
2015-12-24 09:51:32 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(ctx).Debugf("[resolver] lookup for IP %s: name %s", name, host)
|
2015-12-24 09:51:32 +00:00
|
|
|
fqdn := dns.Fqdn(host)
|
|
|
|
|
2023-08-17 08:15:38 +00:00
|
|
|
resp := createRespMsg(query)
|
2023-08-17 08:18:20 +00:00
|
|
|
resp.Answer = append(resp.Answer, &dns.PTR{
|
|
|
|
Hdr: dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL},
|
|
|
|
Ptr: fqdn,
|
|
|
|
})
|
2015-12-24 09:51:32 +00:00
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2023-09-23 09:44:55 +00:00
|
|
|
func (r *Resolver) handleSRVQuery(ctx context.Context, query *dns.Msg) (*dns.Msg, error) {
|
2021-10-13 10:20:02 +00:00
|
|
|
svc := query.Question[0].Name
|
2023-09-23 09:44:55 +00:00
|
|
|
srv, ip := r.backend.ResolveService(ctx, svc)
|
2016-09-19 22:48:06 +00:00
|
|
|
|
2016-08-12 22:40:39 +00:00
|
|
|
if len(srv) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2016-05-08 07:48:04 +00:00
|
|
|
if len(srv) != len(ip) {
|
|
|
|
return nil, fmt.Errorf("invalid reply for SRV query %s", svc)
|
|
|
|
}
|
|
|
|
|
|
|
|
resp := createRespMsg(query)
|
|
|
|
|
|
|
|
for i, r := range srv {
|
2023-08-17 08:18:20 +00:00
|
|
|
resp.Answer = append(resp.Answer, &dns.SRV{
|
|
|
|
Hdr: dns.RR_Header{Name: svc, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL},
|
|
|
|
Port: r.Port,
|
|
|
|
Target: r.Target,
|
|
|
|
})
|
|
|
|
resp.Extra = append(resp.Extra, &dns.A{
|
|
|
|
Hdr: dns.RR_Header{Name: r.Target, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL},
|
|
|
|
A: ip[i],
|
|
|
|
})
|
2016-05-08 07:48:04 +00:00
|
|
|
}
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2023-01-20 21:58:23 +00:00
|
|
|
func (r *Resolver) serveDNS(w dns.ResponseWriter, query *dns.Msg) {
|
2015-12-24 09:51:32 +00:00
|
|
|
var (
|
2022-12-08 21:33:45 +00:00
|
|
|
resp *dns.Msg
|
|
|
|
err error
|
2015-12-24 09:51:32 +00:00
|
|
|
)
|
|
|
|
|
2016-02-16 06:01:29 +00:00
|
|
|
if query == nil || len(query.Question) == 0 {
|
|
|
|
return
|
|
|
|
}
|
2016-05-08 07:48:04 +00:00
|
|
|
|
2021-10-13 10:20:02 +00:00
|
|
|
queryName := query.Question[0].Name
|
|
|
|
queryType := query.Question[0].Qtype
|
|
|
|
|
2023-09-23 09:44:55 +00:00
|
|
|
ctx, span := otel.Tracer("").Start(context.Background(), "resolver.serveDNS", trace.WithAttributes(
|
|
|
|
attribute.String("libnet.resolver.query.name", queryName),
|
|
|
|
attribute.String("libnet.resolver.query.type", dns.TypeToString[queryType]),
|
|
|
|
))
|
|
|
|
defer span.End()
|
|
|
|
|
2021-10-13 10:20:02 +00:00
|
|
|
switch queryType {
|
2016-05-08 07:48:04 +00:00
|
|
|
case dns.TypeA:
|
2023-09-23 09:44:55 +00:00
|
|
|
resp, err = r.handleIPQuery(ctx, query, types.IPv4)
|
2016-05-08 07:48:04 +00:00
|
|
|
case dns.TypeAAAA:
|
2023-09-23 09:44:55 +00:00
|
|
|
resp, err = r.handleIPQuery(ctx, query, types.IPv6)
|
2017-12-20 22:32:47 +00:00
|
|
|
case dns.TypeMX:
|
2023-09-23 09:44:55 +00:00
|
|
|
resp, err = r.handleMXQuery(ctx, query)
|
2016-05-08 07:48:04 +00:00
|
|
|
case dns.TypePTR:
|
2023-09-23 09:44:55 +00:00
|
|
|
resp, err = r.handlePTRQuery(ctx, query)
|
2016-05-08 07:48:04 +00:00
|
|
|
case dns.TypeSRV:
|
2023-09-23 09:44:55 +00:00
|
|
|
resp, err = r.handleSRVQuery(ctx, query)
|
2021-05-28 20:59:41 +00:00
|
|
|
default:
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(ctx).Debugf("[resolver] query type %s is not supported by the embedded DNS and will be forwarded to external DNS", dns.TypeToString[queryType])
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
|
|
|
|
2022-12-16 20:35:33 +00:00
|
|
|
reply := func(msg *dns.Msg) {
|
|
|
|
if err = w.WriteMsg(msg); err != nil {
|
2023-12-18 23:01:07 +00:00
|
|
|
r.log(ctx).WithError(err).Error("[resolver] failed to write response")
|
2023-09-23 09:44:55 +00:00
|
|
|
span.RecordError(err)
|
|
|
|
span.SetStatus(codes.Error, "WriteMsg failed")
|
2023-12-18 23:01:07 +00:00
|
|
|
// Make a best-effort attempt to send a failure response to the
|
|
|
|
// client so it doesn't have to wait for a timeout if the failure
|
|
|
|
// has to do with the content of msg rather than the connection.
|
|
|
|
if msg.Rcode != dns.RcodeServerFailure {
|
|
|
|
if err := w.WriteMsg(new(dns.Msg).SetRcode(query, dns.RcodeServerFailure)); err != nil {
|
|
|
|
r.log(ctx).WithError(err).Error("[resolver] writing ServFail response also failed")
|
|
|
|
span.RecordError(err)
|
|
|
|
}
|
|
|
|
}
|
2022-12-16 20:35:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-24 09:51:32 +00:00
|
|
|
if err != nil {
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(ctx).WithError(err).Errorf("[resolver] failed to handle query: %s (%s)", queryName, dns.TypeToString[queryType])
|
2022-12-16 20:35:33 +00:00
|
|
|
reply(new(dns.Msg).SetRcode(query, dns.RcodeServerFailure))
|
|
|
|
return
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
|
|
|
|
2022-12-16 20:35:33 +00:00
|
|
|
if resp != nil {
|
|
|
|
// We are the authoritative DNS server for this request so it's
|
|
|
|
// on us to truncate the response message to the size limit
|
|
|
|
// negotiated by the client.
|
|
|
|
maxSize := dns.MinMsgSize
|
|
|
|
if w.LocalAddr().Network() == "tcp" {
|
|
|
|
maxSize = dns.MaxMsgSize
|
|
|
|
} else {
|
|
|
|
if optRR := query.IsEdns0(); optRR != nil {
|
|
|
|
if udpsize := int(optRR.UDPSize()); udpsize > maxSize {
|
|
|
|
maxSize = udpsize
|
|
|
|
}
|
2022-12-16 00:21:11 +00:00
|
|
|
}
|
2016-02-21 15:39:53 +00:00
|
|
|
}
|
2022-12-16 20:35:33 +00:00
|
|
|
resp.Truncate(maxSize)
|
2023-09-23 09:44:55 +00:00
|
|
|
span.AddEvent("found local record", trace.WithAttributes(
|
|
|
|
attribute.String("libnet.resolver.resp", resp.String()),
|
|
|
|
))
|
2022-12-16 20:35:33 +00:00
|
|
|
reply(resp)
|
|
|
|
return
|
2016-02-21 15:39:53 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 23:13:25 +00:00
|
|
|
if r.proxyDNS.Load() {
|
2022-12-14 00:14:26 +00:00
|
|
|
// If the user sets ndots > 0 explicitly and the query is
|
|
|
|
// in the root domain don't forward it out. We will return
|
|
|
|
// failure and let the client retry with the search domain
|
|
|
|
// attached.
|
|
|
|
if (queryType == dns.TypeA || queryType == dns.TypeAAAA) && r.backend.NdotsSet() &&
|
|
|
|
!strings.Contains(strings.TrimSuffix(queryName, "."), ".") {
|
|
|
|
resp = createRespMsg(query)
|
|
|
|
} else {
|
2024-03-15 17:04:41 +00:00
|
|
|
resp = r.forwardExtDNS(ctx, w.LocalAddr().Network(), w.RemoteAddr(), query)
|
2022-12-08 21:33:45 +00:00
|
|
|
}
|
|
|
|
}
|
2015-12-24 09:51:32 +00:00
|
|
|
|
2022-12-16 20:03:55 +00:00
|
|
|
if resp == nil {
|
|
|
|
// We were unable to get an answer from any of the upstream DNS
|
|
|
|
// servers or the backend doesn't support proxying DNS requests.
|
|
|
|
resp = new(dns.Msg).SetRcode(query, dns.RcodeServerFailure)
|
|
|
|
}
|
2022-12-16 20:35:33 +00:00
|
|
|
reply(resp)
|
2022-12-08 21:33:45 +00:00
|
|
|
}
|
2016-01-18 12:07:19 +00:00
|
|
|
|
2023-08-17 08:43:18 +00:00
|
|
|
const defaultPort = "53"
|
|
|
|
|
2023-01-20 21:58:23 +00:00
|
|
|
func (r *Resolver) dialExtDNS(proto string, server extDNSEntry) (net.Conn, error) {
|
2023-08-17 08:43:18 +00:00
|
|
|
port := defaultPort
|
|
|
|
if server.port != 0 {
|
|
|
|
port = strconv.FormatUint(uint64(server.port), 10)
|
|
|
|
}
|
|
|
|
addr := net.JoinHostPort(server.IPStr, port)
|
|
|
|
|
|
|
|
if server.HostLoopback {
|
|
|
|
return net.DialTimeout(proto, addr, extIOTimeout)
|
|
|
|
}
|
|
|
|
|
2022-12-16 18:28:07 +00:00
|
|
|
var (
|
|
|
|
extConn net.Conn
|
|
|
|
dialErr error
|
|
|
|
)
|
2023-08-17 08:43:18 +00:00
|
|
|
err := r.backend.ExecFunc(func() {
|
2022-12-16 18:28:07 +00:00
|
|
|
extConn, dialErr = net.DialTimeout(proto, addr, extIOTimeout)
|
2023-08-17 08:43:18 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2022-12-16 18:28:07 +00:00
|
|
|
}
|
|
|
|
if dialErr != nil {
|
|
|
|
return nil, dialErr
|
|
|
|
}
|
|
|
|
|
|
|
|
return extConn, nil
|
|
|
|
}
|
|
|
|
|
2024-03-15 17:04:41 +00:00
|
|
|
func (r *Resolver) forwardExtDNS(ctx context.Context, proto string, remoteAddr net.Addr, query *dns.Msg) *dns.Msg {
|
2023-09-23 09:44:55 +00:00
|
|
|
ctx, span := otel.Tracer("").Start(ctx, "resolver.forwardExtDNS")
|
|
|
|
defer span.End()
|
|
|
|
|
2024-03-15 17:04:41 +00:00
|
|
|
for _, extDNS := range r.extDNS(netiputil.AddrPortFromNet(remoteAddr)) {
|
2022-12-08 21:33:45 +00:00
|
|
|
if extDNS.IPStr == "" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// limits the number of outstanding concurrent queries.
|
2023-09-23 09:44:55 +00:00
|
|
|
ctx, cancel := context.WithTimeout(ctx, extIOTimeout)
|
2022-12-16 21:26:06 +00:00
|
|
|
err := r.fwdSem.Acquire(ctx, 1)
|
|
|
|
cancel()
|
2023-09-23 09:44:55 +00:00
|
|
|
|
2022-12-16 21:26:06 +00:00
|
|
|
if err != nil {
|
2023-09-23 09:44:55 +00:00
|
|
|
if errors.Is(err, context.DeadlineExceeded) {
|
|
|
|
r.logInverval.Do(func() {
|
|
|
|
r.log(ctx).Errorf("[resolver] more than %v concurrent queries", maxConcurrent)
|
|
|
|
})
|
|
|
|
}
|
2022-12-16 21:26:06 +00:00
|
|
|
return new(dns.Msg).SetRcode(query, dns.RcodeRefused)
|
2022-12-08 21:33:45 +00:00
|
|
|
}
|
2022-12-16 21:26:06 +00:00
|
|
|
resp := func() *dns.Msg {
|
|
|
|
defer r.fwdSem.Release(1)
|
2023-09-23 09:44:55 +00:00
|
|
|
return r.exchange(ctx, proto, extDNS, query)
|
2022-12-16 21:26:06 +00:00
|
|
|
}()
|
2022-12-08 21:33:45 +00:00
|
|
|
if resp == nil {
|
2022-12-16 19:32:45 +00:00
|
|
|
continue
|
2022-12-08 21:33:45 +00:00
|
|
|
}
|
2022-12-16 19:32:45 +00:00
|
|
|
|
2022-12-08 21:33:45 +00:00
|
|
|
switch resp.Rcode {
|
|
|
|
case dns.RcodeServerFailure, dns.RcodeRefused:
|
|
|
|
// Server returned FAILURE: continue with the next external DNS server
|
|
|
|
// Server returned REFUSED: this can be a transitional status, so continue with the next external DNS server
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(ctx).Debugf("[resolver] external DNS %s:%s returned failure:\n%s", proto, extDNS.IPStr, resp)
|
2022-12-08 21:33:45 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
answers := 0
|
|
|
|
for _, rr := range resp.Answer {
|
|
|
|
h := rr.Header()
|
|
|
|
switch h.Rrtype {
|
|
|
|
case dns.TypeA:
|
|
|
|
answers++
|
|
|
|
ip := rr.(*dns.A).A
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(ctx).Debugf("[resolver] received A record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
|
2022-12-08 21:33:45 +00:00
|
|
|
r.backend.HandleQueryResp(h.Name, ip)
|
|
|
|
case dns.TypeAAAA:
|
|
|
|
answers++
|
|
|
|
ip := rr.(*dns.AAAA).AAAA
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(ctx).Debugf("[resolver] received AAAA record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
|
2022-12-08 21:33:45 +00:00
|
|
|
r.backend.HandleQueryResp(h.Name, ip)
|
2016-12-06 22:56:24 +00:00
|
|
|
}
|
2016-01-18 12:07:19 +00:00
|
|
|
}
|
2023-05-18 18:10:44 +00:00
|
|
|
if len(resp.Answer) == 0 {
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(ctx).Debugf("[resolver] external DNS %s:%s returned response with no answers:\n%s", proto, extDNS.IPStr, resp)
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
2022-12-08 21:33:45 +00:00
|
|
|
resp.Compress = true
|
2023-09-23 09:44:55 +00:00
|
|
|
span.AddEvent("response from upstream server", trace.WithAttributes(
|
|
|
|
attribute.String("libnet.resolver.resp", resp.String()),
|
|
|
|
))
|
2022-12-16 19:32:45 +00:00
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
2023-09-23 09:44:55 +00:00
|
|
|
span.AddEvent("no response from upstream servers")
|
2022-12-16 19:32:45 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-03-15 17:04:41 +00:00
|
|
|
func (r *Resolver) extDNS(remoteAddr netip.AddrPort) []extDNSEntry {
|
|
|
|
if res, ok := r.ipToExtDNS.get(remoteAddr.Addr()); ok {
|
|
|
|
return res[:]
|
|
|
|
}
|
|
|
|
return r.extDNSList[:]
|
|
|
|
}
|
|
|
|
|
2023-09-23 09:44:55 +00:00
|
|
|
func (r *Resolver) exchange(ctx context.Context, proto string, extDNS extDNSEntry, query *dns.Msg) *dns.Msg {
|
|
|
|
ctx, span := otel.Tracer("").Start(ctx, "resolver.exchange", trace.WithAttributes(
|
|
|
|
attribute.String("libnet.resolver.upstream.proto", proto),
|
|
|
|
attribute.String("libnet.resolver.upstream.address", extDNS.IPStr),
|
|
|
|
attribute.Bool("libnet.resolver.upstream.host-loopback", extDNS.HostLoopback)))
|
|
|
|
defer span.End()
|
|
|
|
|
2022-12-16 19:32:45 +00:00
|
|
|
extConn, err := r.dialExtDNS(proto, extDNS)
|
|
|
|
if err != nil {
|
2023-09-23 09:44:55 +00:00
|
|
|
r.log(ctx).WithError(err).Warn("[resolver] connect failed")
|
|
|
|
span.RecordError(err)
|
|
|
|
span.SetStatus(codes.Error, "dialExtDNS failed")
|
2022-12-16 19:32:45 +00:00
|
|
|
return nil
|
|
|
|
}
|
2022-12-16 20:35:33 +00:00
|
|
|
defer extConn.Close()
|
|
|
|
|
2023-09-23 09:44:55 +00:00
|
|
|
logger := r.log(ctx).WithFields(log.Fields{
|
2022-12-16 20:35:33 +00:00
|
|
|
"dns-server": extConn.RemoteAddr().Network() + ":" + extConn.RemoteAddr().String(),
|
|
|
|
"client-addr": extConn.LocalAddr().Network() + ":" + extConn.LocalAddr().String(),
|
|
|
|
"question": query.Question[0].String(),
|
|
|
|
})
|
2023-07-30 15:18:56 +00:00
|
|
|
logger.Debug("[resolver] forwarding query")
|
2022-12-16 20:35:33 +00:00
|
|
|
|
|
|
|
resp, _, err := (&dns.Client{
|
|
|
|
Timeout: extIOTimeout,
|
|
|
|
// Following the robustness principle, make a best-effort
|
|
|
|
// attempt to receive oversized response messages without
|
|
|
|
// truncating them on our end to forward verbatim to the client.
|
|
|
|
// Some DNS servers (e.g. Mikrotik RouterOS) don't support
|
|
|
|
// EDNS(0) and may send replies over UDP longer than 512 bytes
|
|
|
|
// regardless of what size limit, if any, was advertized in the
|
|
|
|
// query message. Note that ExchangeWithConn will override this
|
|
|
|
// value if it detects an EDNS OPT record in query so only
|
|
|
|
// oversized replies to non-EDNS queries will benefit.
|
|
|
|
UDPSize: dns.MaxMsgSize,
|
|
|
|
}).ExchangeWithConn(query, &dns.Conn{Conn: extConn})
|
2022-12-16 19:32:45 +00:00
|
|
|
if err != nil {
|
2024-02-20 16:01:06 +00:00
|
|
|
logger.WithError(err).Error("[resolver] failed to query external DNS server")
|
2023-09-23 09:44:55 +00:00
|
|
|
span.RecordError(err)
|
|
|
|
span.SetStatus(codes.Error, "ExchangeWithConn failed")
|
2022-12-16 19:32:45 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp == nil {
|
2022-12-16 20:35:33 +00:00
|
|
|
// Should be impossible, so make noise if it happens anyway.
|
2023-07-30 15:18:56 +00:00
|
|
|
logger.Error("[resolver] external DNS returned empty response")
|
2023-09-23 09:44:55 +00:00
|
|
|
span.SetStatus(codes.Error, "External DNS returned empty response")
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|
2022-12-08 21:33:45 +00:00
|
|
|
return resp
|
2015-12-24 09:51:32 +00:00
|
|
|
}
|