github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/libnetwork/resolver.go (about)

     1  package libnetwork
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"net"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/docker/docker/libnetwork/types"
    12  	"github.com/miekg/dns"
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  // Resolver represents the embedded DNS server in Docker. It operates
    17  // by listening on container's loopback interface for DNS queries.
    18  type Resolver interface {
    19  	// Start starts the name server for the container
    20  	Start() error
    21  	// Stop stops the name server for the container. Stopped resolver
    22  	// can be reused after running the SetupFunc again.
    23  	Stop()
    24  	// SetupFunc provides the setup function that should be run
    25  	// in the container's network namespace.
    26  	SetupFunc(int) func()
    27  	// NameServer returns the IP of the DNS resolver for the
    28  	// containers.
    29  	NameServer() string
    30  	// SetExtServers configures the external nameservers the resolver
    31  	// should use to forward queries
    32  	SetExtServers([]extDNSEntry)
    33  	// ResolverOptions returns resolv.conf options that should be set
    34  	ResolverOptions() []string
    35  }
    36  
    37  // DNSBackend represents a backend DNS resolver used for DNS name
    38  // resolution. All the queries to the resolver are forwarded to the
    39  // backend resolver.
    40  type DNSBackend interface {
    41  	// ResolveName resolves a service name to an IPv4 or IPv6 address by searching
    42  	// the networks the sandbox is connected to. For IPv6 queries, second return
    43  	// value will be true if the name exists in docker domain but doesn't have an
    44  	// IPv6 address. Such queries shouldn't be forwarded to external nameservers.
    45  	ResolveName(name string, iplen int) ([]net.IP, bool)
    46  	// ResolveIP returns the service name for the passed in IP. IP is in reverse dotted
    47  	// notation; the format used for DNS PTR records
    48  	ResolveIP(name string) string
    49  	// ResolveService returns all the backend details about the containers or hosts
    50  	// backing a service. Its purpose is to satisfy an SRV query
    51  	ResolveService(name string) ([]*net.SRV, []net.IP)
    52  	// ExecFunc allows a function to be executed in the context of the backend
    53  	// on behalf of the resolver.
    54  	ExecFunc(f func()) error
    55  	// NdotsSet queries the backends ndots dns option settings
    56  	NdotsSet() bool
    57  	// HandleQueryResp passes the name & IP from a response to the backend. backend
    58  	// can use it to maintain any required state about the resolution
    59  	HandleQueryResp(name string, ip net.IP)
    60  }
    61  
    62  const (
    63  	dnsPort         = "53"
    64  	ptrIPv4domain   = ".in-addr.arpa."
    65  	ptrIPv6domain   = ".ip6.arpa."
    66  	respTTL         = 600
    67  	maxExtDNS       = 3 // max number of external servers to try
    68  	extIOTimeout    = 4 * time.Second
    69  	defaultRespSize = 512
    70  	maxConcurrent   = 1024
    71  	logInterval     = 2 * time.Second
    72  )
    73  
    74  type extDNSEntry struct {
    75  	IPStr        string
    76  	HostLoopback bool
    77  }
    78  
    79  // resolver implements the Resolver interface
    80  type resolver struct {
    81  	backend       DNSBackend
    82  	extDNSList    [maxExtDNS]extDNSEntry
    83  	server        *dns.Server
    84  	conn          *net.UDPConn
    85  	tcpServer     *dns.Server
    86  	tcpListen     *net.TCPListener
    87  	err           error
    88  	count         int32
    89  	tStamp        time.Time
    90  	queryLock     sync.Mutex
    91  	listenAddress string
    92  	proxyDNS      bool
    93  	resolverKey   string
    94  	startCh       chan struct{}
    95  }
    96  
    97  func init() {
    98  	rand.Seed(time.Now().Unix())
    99  }
   100  
   101  // NewResolver creates a new instance of the Resolver
   102  func NewResolver(address string, proxyDNS bool, resolverKey string, backend DNSBackend) Resolver {
   103  	return &resolver{
   104  		backend:       backend,
   105  		proxyDNS:      proxyDNS,
   106  		listenAddress: address,
   107  		resolverKey:   resolverKey,
   108  		err:           fmt.Errorf("setup not done yet"),
   109  		startCh:       make(chan struct{}, 1),
   110  	}
   111  }
   112  
   113  func (r *resolver) SetupFunc(port int) func() {
   114  	return func() {
   115  		var err error
   116  
   117  		// DNS operates primarily on UDP
   118  		addr := &net.UDPAddr{
   119  			IP:   net.ParseIP(r.listenAddress),
   120  			Port: port,
   121  		}
   122  
   123  		r.conn, err = net.ListenUDP("udp", addr)
   124  		if err != nil {
   125  			r.err = fmt.Errorf("error in opening name server socket %v", err)
   126  			return
   127  		}
   128  
   129  		// Listen on a TCP as well
   130  		tcpaddr := &net.TCPAddr{
   131  			IP:   net.ParseIP(r.listenAddress),
   132  			Port: port,
   133  		}
   134  
   135  		r.tcpListen, err = net.ListenTCP("tcp", tcpaddr)
   136  		if err != nil {
   137  			r.err = fmt.Errorf("error in opening name TCP server socket %v", err)
   138  			return
   139  		}
   140  		r.err = nil
   141  	}
   142  }
   143  
   144  func (r *resolver) Start() error {
   145  	r.startCh <- struct{}{}
   146  	defer func() { <-r.startCh }()
   147  
   148  	// make sure the resolver has been setup before starting
   149  	if r.err != nil {
   150  		return r.err
   151  	}
   152  
   153  	if err := r.setupIPTable(); err != nil {
   154  		return fmt.Errorf("setting up IP table rules failed: %v", err)
   155  	}
   156  
   157  	s := &dns.Server{Handler: r, PacketConn: r.conn}
   158  	r.server = s
   159  	go func() {
   160  		if err := s.ActivateAndServe(); err != nil {
   161  			logrus.WithError(err).Error("[resolver] failed to start PacketConn DNS server")
   162  		}
   163  	}()
   164  
   165  	tcpServer := &dns.Server{Handler: r, Listener: r.tcpListen}
   166  	r.tcpServer = tcpServer
   167  	go func() {
   168  		if err := tcpServer.ActivateAndServe(); err != nil {
   169  			logrus.WithError(err).Error("[resolver] failed to start TCP DNS server")
   170  		}
   171  	}()
   172  	return nil
   173  }
   174  
   175  func (r *resolver) Stop() {
   176  	r.startCh <- struct{}{}
   177  	defer func() { <-r.startCh }()
   178  
   179  	if r.server != nil {
   180  		r.server.Shutdown() //nolint:errcheck
   181  	}
   182  	if r.tcpServer != nil {
   183  		r.tcpServer.Shutdown() //nolint:errcheck
   184  	}
   185  	r.conn = nil
   186  	r.tcpServer = nil
   187  	r.err = fmt.Errorf("setup not done yet")
   188  	r.tStamp = time.Time{}
   189  	r.count = 0
   190  	r.queryLock = sync.Mutex{}
   191  }
   192  
   193  func (r *resolver) SetExtServers(extDNS []extDNSEntry) {
   194  	l := len(extDNS)
   195  	if l > maxExtDNS {
   196  		l = maxExtDNS
   197  	}
   198  	for i := 0; i < l; i++ {
   199  		r.extDNSList[i] = extDNS[i]
   200  	}
   201  }
   202  
   203  func (r *resolver) NameServer() string {
   204  	return r.listenAddress
   205  }
   206  
   207  func (r *resolver) ResolverOptions() []string {
   208  	return []string{"ndots:0"}
   209  }
   210  
   211  func setCommonFlags(msg *dns.Msg) {
   212  	msg.RecursionAvailable = true
   213  }
   214  
   215  func shuffleAddr(addr []net.IP) []net.IP {
   216  	for i := len(addr) - 1; i > 0; i-- {
   217  		r := rand.Intn(i + 1) //nolint:gosec // gosec complains about the use of rand here. It should be fine.
   218  		addr[i], addr[r] = addr[r], addr[i]
   219  	}
   220  	return addr
   221  }
   222  
   223  func createRespMsg(query *dns.Msg) *dns.Msg {
   224  	resp := new(dns.Msg)
   225  	resp.SetReply(query)
   226  	setCommonFlags(resp)
   227  
   228  	return resp
   229  }
   230  
   231  func (r *resolver) handleMXQuery(query *dns.Msg) (*dns.Msg, error) {
   232  	name := query.Question[0].Name
   233  	addrv4, _ := r.backend.ResolveName(name, types.IPv4)
   234  	addrv6, _ := r.backend.ResolveName(name, types.IPv6)
   235  
   236  	if addrv4 == nil && addrv6 == nil {
   237  		return nil, nil
   238  	}
   239  
   240  	// We were able to resolve the name. Respond with an empty list with
   241  	// RcodeSuccess/NOERROR so that email clients can treat it as "implicit MX"
   242  	// [RFC 5321 Section-5.1] and issue a Type A/AAAA query for the name.
   243  
   244  	resp := createRespMsg(query)
   245  	return resp, nil
   246  }
   247  
   248  func (r *resolver) handleIPQuery(query *dns.Msg, ipType int) (*dns.Msg, error) {
   249  	var (
   250  		addr     []net.IP
   251  		ipv6Miss bool
   252  		name     = query.Question[0].Name
   253  	)
   254  	addr, ipv6Miss = r.backend.ResolveName(name, ipType)
   255  
   256  	if addr == nil && ipv6Miss {
   257  		// Send a reply without any Answer sections
   258  		logrus.Debugf("[resolver] lookup name %s present without IPv6 address", name)
   259  		resp := createRespMsg(query)
   260  		return resp, nil
   261  	}
   262  	if addr == nil {
   263  		return nil, nil
   264  	}
   265  
   266  	logrus.Debugf("[resolver] lookup for %s: IP %v", name, addr)
   267  
   268  	resp := createRespMsg(query)
   269  	if len(addr) > 1 {
   270  		addr = shuffleAddr(addr)
   271  	}
   272  	if ipType == types.IPv4 {
   273  		for _, ip := range addr {
   274  			rr := new(dns.A)
   275  			rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
   276  			rr.A = ip
   277  			resp.Answer = append(resp.Answer, rr)
   278  		}
   279  	} else {
   280  		for _, ip := range addr {
   281  			rr := new(dns.AAAA)
   282  			rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: respTTL}
   283  			rr.AAAA = ip
   284  			resp.Answer = append(resp.Answer, rr)
   285  		}
   286  	}
   287  	return resp, nil
   288  }
   289  
   290  func (r *resolver) handlePTRQuery(query *dns.Msg) (*dns.Msg, error) {
   291  	var (
   292  		parts []string
   293  		ptr   = query.Question[0].Name
   294  	)
   295  
   296  	if strings.HasSuffix(ptr, ptrIPv4domain) {
   297  		parts = strings.Split(ptr, ptrIPv4domain)
   298  	} else if strings.HasSuffix(ptr, ptrIPv6domain) {
   299  		parts = strings.Split(ptr, ptrIPv6domain)
   300  	} else {
   301  		return nil, fmt.Errorf("invalid PTR query, %v", ptr)
   302  	}
   303  
   304  	host := r.backend.ResolveIP(parts[0])
   305  
   306  	if len(host) == 0 {
   307  		return nil, nil
   308  	}
   309  
   310  	logrus.Debugf("[resolver] lookup for IP %s: name %s", parts[0], host)
   311  	fqdn := dns.Fqdn(host)
   312  
   313  	resp := new(dns.Msg)
   314  	resp.SetReply(query)
   315  	setCommonFlags(resp)
   316  
   317  	rr := new(dns.PTR)
   318  	rr.Hdr = dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
   319  	rr.Ptr = fqdn
   320  	resp.Answer = append(resp.Answer, rr)
   321  	return resp, nil
   322  }
   323  
   324  func (r *resolver) handleSRVQuery(query *dns.Msg) (*dns.Msg, error) {
   325  	svc := query.Question[0].Name
   326  	srv, ip := r.backend.ResolveService(svc)
   327  
   328  	if len(srv) == 0 {
   329  		return nil, nil
   330  	}
   331  	if len(srv) != len(ip) {
   332  		return nil, fmt.Errorf("invalid reply for SRV query %s", svc)
   333  	}
   334  
   335  	resp := createRespMsg(query)
   336  
   337  	for i, r := range srv {
   338  		rr := new(dns.SRV)
   339  		rr.Hdr = dns.RR_Header{Name: svc, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
   340  		rr.Port = r.Port
   341  		rr.Target = r.Target
   342  		resp.Answer = append(resp.Answer, rr)
   343  
   344  		rr1 := new(dns.A)
   345  		rr1.Hdr = dns.RR_Header{Name: r.Target, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
   346  		rr1.A = ip[i]
   347  		resp.Extra = append(resp.Extra, rr1)
   348  	}
   349  	return resp, nil
   350  }
   351  
   352  func truncateResp(resp *dns.Msg, maxSize int, isTCP bool) {
   353  	if !isTCP {
   354  		resp.Truncated = true
   355  	}
   356  
   357  	srv := resp.Question[0].Qtype == dns.TypeSRV
   358  	// trim the Answer RRs one by one till the whole message fits
   359  	// within the reply size
   360  	for resp.Len() > maxSize {
   361  		resp.Answer = resp.Answer[:len(resp.Answer)-1]
   362  
   363  		if srv && len(resp.Extra) > 0 {
   364  			resp.Extra = resp.Extra[:len(resp.Extra)-1]
   365  		}
   366  	}
   367  }
   368  
   369  func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
   370  	var (
   371  		extConn net.Conn
   372  		resp    *dns.Msg
   373  		err     error
   374  	)
   375  
   376  	if query == nil || len(query.Question) == 0 {
   377  		return
   378  	}
   379  
   380  	queryName := query.Question[0].Name
   381  	queryType := query.Question[0].Qtype
   382  
   383  	switch queryType {
   384  	case dns.TypeA:
   385  		resp, err = r.handleIPQuery(query, types.IPv4)
   386  	case dns.TypeAAAA:
   387  		resp, err = r.handleIPQuery(query, types.IPv6)
   388  	case dns.TypeMX:
   389  		resp, err = r.handleMXQuery(query)
   390  	case dns.TypePTR:
   391  		resp, err = r.handlePTRQuery(query)
   392  	case dns.TypeSRV:
   393  		resp, err = r.handleSRVQuery(query)
   394  	default:
   395  		logrus.Debugf("[resolver] query type %s is not supported by the embedded DNS and will be forwarded to external DNS", dns.TypeToString[queryType])
   396  	}
   397  
   398  	if err != nil {
   399  		logrus.WithError(err).Errorf("[resolver] failed to handle query: %s (%s) from %s", queryName, dns.TypeToString[queryType], extConn.LocalAddr().String())
   400  		return
   401  	}
   402  
   403  	if resp == nil {
   404  		// If the backend doesn't support proxying dns request
   405  		// fail the response
   406  		if !r.proxyDNS {
   407  			resp = new(dns.Msg)
   408  			resp.SetRcode(query, dns.RcodeServerFailure)
   409  			if err := w.WriteMsg(resp); err != nil {
   410  				logrus.WithError(err).Error("[resolver] error writing dns response")
   411  			}
   412  			return
   413  		}
   414  
   415  		// If the user sets ndots > 0 explicitly and the query is
   416  		// in the root domain don't forward it out. We will return
   417  		// failure and let the client retry with the search domain
   418  		// attached
   419  		switch queryType {
   420  		case dns.TypeA, dns.TypeAAAA:
   421  			if r.backend.NdotsSet() && !strings.Contains(strings.TrimSuffix(queryName, "."), ".") {
   422  				resp = createRespMsg(query)
   423  			}
   424  		}
   425  	}
   426  
   427  	proto := w.LocalAddr().Network()
   428  	maxSize := 0
   429  	if proto == "tcp" {
   430  		maxSize = dns.MaxMsgSize - 1
   431  	} else if proto == "udp" {
   432  		optRR := query.IsEdns0()
   433  		if optRR != nil {
   434  			maxSize = int(optRR.UDPSize())
   435  		}
   436  		if maxSize < defaultRespSize {
   437  			maxSize = defaultRespSize
   438  		}
   439  	}
   440  
   441  	if resp != nil {
   442  		if resp.Len() > maxSize {
   443  			truncateResp(resp, maxSize, proto == "tcp")
   444  		}
   445  	} else {
   446  		for i := 0; i < maxExtDNS; i++ {
   447  			extDNS := &r.extDNSList[i]
   448  			if extDNS.IPStr == "" {
   449  				break
   450  			}
   451  			extConnect := func() {
   452  				addr := fmt.Sprintf("%s:%d", extDNS.IPStr, 53)
   453  				extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
   454  			}
   455  
   456  			if extDNS.HostLoopback {
   457  				extConnect()
   458  			} else {
   459  				execErr := r.backend.ExecFunc(extConnect)
   460  				if execErr != nil {
   461  					logrus.Warn(execErr)
   462  					continue
   463  				}
   464  			}
   465  			if err != nil {
   466  				logrus.WithField("retries", i).Warnf("[resolver] connect failed: %s", err)
   467  				continue
   468  			}
   469  			logrus.Debugf("[resolver] query %s (%s) from %s, forwarding to %s:%s", queryName, dns.TypeToString[queryType],
   470  				extConn.LocalAddr().String(), proto, extDNS.IPStr)
   471  
   472  			// Timeout has to be set for every IO operation.
   473  			if err := extConn.SetDeadline(time.Now().Add(extIOTimeout)); err != nil {
   474  				logrus.WithError(err).Error("[resolver] error setting conn deadline")
   475  			}
   476  			co := &dns.Conn{
   477  				Conn:    extConn,
   478  				UDPSize: uint16(maxSize),
   479  			}
   480  			defer co.Close()
   481  
   482  			// limits the number of outstanding concurrent queries.
   483  			if !r.forwardQueryStart() {
   484  				old := r.tStamp
   485  				r.tStamp = time.Now()
   486  				if r.tStamp.Sub(old) > logInterval {
   487  					logrus.Errorf("[resolver] more than %v concurrent queries from %s", maxConcurrent, extConn.LocalAddr().String())
   488  				}
   489  				continue
   490  			}
   491  
   492  			err = co.WriteMsg(query)
   493  			if err != nil {
   494  				r.forwardQueryEnd()
   495  				logrus.Debugf("[resolver] send to DNS server failed, %s", err)
   496  				continue
   497  			}
   498  
   499  			resp, err = co.ReadMsg()
   500  			// Truncated DNS replies should be sent to the client so that the
   501  			// client can retry over TCP
   502  			if err != nil && (resp == nil || !resp.Truncated) {
   503  				r.forwardQueryEnd()
   504  				logrus.WithError(err).Debugf("[resolver] failed to read from DNS server")
   505  				continue
   506  			}
   507  			r.forwardQueryEnd()
   508  
   509  			if resp == nil {
   510  				logrus.Debugf("[resolver] external DNS %s:%s returned empty response for %q", proto, extDNS.IPStr, queryName)
   511  				break
   512  			}
   513  			switch resp.Rcode {
   514  			case dns.RcodeServerFailure, dns.RcodeRefused:
   515  				// Server returned FAILURE: continue with the next external DNS server
   516  				// Server returned REFUSED: this can be a transitional status, so continue with the next external DNS server
   517  				logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), queryName)
   518  				continue
   519  			case dns.RcodeNameError:
   520  				// Server returned NXDOMAIN. Stop resolution if it's an authoritative answer (see RFC 8020: https://tools.ietf.org/html/rfc8020#section-2)
   521  				logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), queryName)
   522  				if resp.Authoritative {
   523  					break
   524  				}
   525  				continue
   526  			case dns.RcodeSuccess:
   527  				// All is well
   528  			default:
   529  				// Server gave some error. Log the error, and continue with the next external DNS server
   530  				logrus.Debugf("[resolver] external DNS %s:%s responded with %s (code %d) for %q", proto, extDNS.IPStr, statusString(resp.Rcode), resp.Rcode, queryName)
   531  				continue
   532  			}
   533  			answers := 0
   534  			for _, rr := range resp.Answer {
   535  				h := rr.Header()
   536  				switch h.Rrtype {
   537  				case dns.TypeA:
   538  					answers++
   539  					ip := rr.(*dns.A).A
   540  					logrus.Debugf("[resolver] received A record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
   541  					r.backend.HandleQueryResp(h.Name, ip)
   542  				case dns.TypeAAAA:
   543  					answers++
   544  					ip := rr.(*dns.AAAA).AAAA
   545  					logrus.Debugf("[resolver] received AAAA record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
   546  					r.backend.HandleQueryResp(h.Name, ip)
   547  				}
   548  			}
   549  			if resp.Answer == nil || answers == 0 {
   550  				logrus.Debugf("[resolver] external DNS %s:%s did not return any %s records for %q", proto, extDNS.IPStr, dns.TypeToString[queryType], queryName)
   551  			}
   552  			resp.Compress = true
   553  			break
   554  		}
   555  		if resp == nil {
   556  			return
   557  		}
   558  	}
   559  
   560  	if err = w.WriteMsg(resp); err != nil {
   561  		logrus.WithError(err).Errorf("[resolver] failed to write response")
   562  	}
   563  }
   564  
   565  func statusString(responseCode int) string {
   566  	if s, ok := dns.RcodeToString[responseCode]; ok {
   567  		return s
   568  	}
   569  	return "UNKNOWN"
   570  }
   571  
   572  func (r *resolver) forwardQueryStart() bool {
   573  	r.queryLock.Lock()
   574  	defer r.queryLock.Unlock()
   575  
   576  	if r.count == maxConcurrent {
   577  		return false
   578  	}
   579  	r.count++
   580  
   581  	return true
   582  }
   583  
   584  func (r *resolver) forwardQueryEnd() {
   585  	r.queryLock.Lock()
   586  	defer r.queryLock.Unlock()
   587  
   588  	if r.count == 0 {
   589  		logrus.Error("[resolver] invalid concurrent query count")
   590  	} else {
   591  		r.count--
   592  	}
   593  }