github.com/pwn-term/docker@v0.0.0-20210616085119-6e977cce2565/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/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  		s.ActivateAndServe()
   161  	}()
   162  
   163  	tcpServer := &dns.Server{Handler: r, Listener: r.tcpListen}
   164  	r.tcpServer = tcpServer
   165  	go func() {
   166  		tcpServer.ActivateAndServe()
   167  	}()
   168  	return nil
   169  }
   170  
   171  func (r *resolver) Stop() {
   172  	r.startCh <- struct{}{}
   173  	defer func() { <-r.startCh }()
   174  
   175  	if r.server != nil {
   176  		r.server.Shutdown()
   177  	}
   178  	if r.tcpServer != nil {
   179  		r.tcpServer.Shutdown()
   180  	}
   181  	r.conn = nil
   182  	r.tcpServer = nil
   183  	r.err = fmt.Errorf("setup not done yet")
   184  	r.tStamp = time.Time{}
   185  	r.count = 0
   186  	r.queryLock = sync.Mutex{}
   187  }
   188  
   189  func (r *resolver) SetExtServers(extDNS []extDNSEntry) {
   190  	l := len(extDNS)
   191  	if l > maxExtDNS {
   192  		l = maxExtDNS
   193  	}
   194  	for i := 0; i < l; i++ {
   195  		r.extDNSList[i] = extDNS[i]
   196  	}
   197  }
   198  
   199  func (r *resolver) NameServer() string {
   200  	return r.listenAddress
   201  }
   202  
   203  func (r *resolver) ResolverOptions() []string {
   204  	return []string{"ndots:0"}
   205  }
   206  
   207  func setCommonFlags(msg *dns.Msg) {
   208  	msg.RecursionAvailable = true
   209  }
   210  
   211  func shuffleAddr(addr []net.IP) []net.IP {
   212  	for i := len(addr) - 1; i > 0; i-- {
   213  		r := rand.Intn(i + 1)
   214  		addr[i], addr[r] = addr[r], addr[i]
   215  	}
   216  	return addr
   217  }
   218  
   219  func createRespMsg(query *dns.Msg) *dns.Msg {
   220  	resp := new(dns.Msg)
   221  	resp.SetReply(query)
   222  	setCommonFlags(resp)
   223  
   224  	return resp
   225  }
   226  
   227  func (r *resolver) handleMXQuery(name string, query *dns.Msg) (*dns.Msg, error) {
   228  	addrv4, _ := r.backend.ResolveName(name, types.IPv4)
   229  	addrv6, _ := r.backend.ResolveName(name, types.IPv6)
   230  
   231  	if addrv4 == nil && addrv6 == nil {
   232  		return nil, nil
   233  	}
   234  
   235  	// We were able to resolve the name. Respond with an empty list with
   236  	// RcodeSuccess/NOERROR so that email clients can treat it as "implicit MX"
   237  	// [RFC 5321 Section-5.1] and issue a Type A/AAAA query for the name.
   238  
   239  	resp := createRespMsg(query)
   240  	return resp, nil
   241  }
   242  
   243  func (r *resolver) handleIPQuery(name string, query *dns.Msg, ipType int) (*dns.Msg, error) {
   244  	var addr []net.IP
   245  	var ipv6Miss bool
   246  	addr, ipv6Miss = r.backend.ResolveName(name, ipType)
   247  
   248  	if addr == nil && ipv6Miss {
   249  		// Send a reply without any Answer sections
   250  		logrus.Debugf("[resolver] lookup name %s present without IPv6 address", name)
   251  		resp := createRespMsg(query)
   252  		return resp, nil
   253  	}
   254  	if addr == nil {
   255  		return nil, nil
   256  	}
   257  
   258  	logrus.Debugf("[resolver] lookup for %s: IP %v", name, addr)
   259  
   260  	resp := createRespMsg(query)
   261  	if len(addr) > 1 {
   262  		addr = shuffleAddr(addr)
   263  	}
   264  	if ipType == types.IPv4 {
   265  		for _, ip := range addr {
   266  			rr := new(dns.A)
   267  			rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
   268  			rr.A = ip
   269  			resp.Answer = append(resp.Answer, rr)
   270  		}
   271  	} else {
   272  		for _, ip := range addr {
   273  			rr := new(dns.AAAA)
   274  			rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: respTTL}
   275  			rr.AAAA = ip
   276  			resp.Answer = append(resp.Answer, rr)
   277  		}
   278  	}
   279  	return resp, nil
   280  }
   281  
   282  func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error) {
   283  	var parts []string
   284  
   285  	if strings.HasSuffix(ptr, ptrIPv4domain) {
   286  		parts = strings.Split(ptr, ptrIPv4domain)
   287  	} else if strings.HasSuffix(ptr, ptrIPv6domain) {
   288  		parts = strings.Split(ptr, ptrIPv6domain)
   289  	} else {
   290  		return nil, fmt.Errorf("invalid PTR query, %v", ptr)
   291  	}
   292  
   293  	host := r.backend.ResolveIP(parts[0])
   294  
   295  	if len(host) == 0 {
   296  		return nil, nil
   297  	}
   298  
   299  	logrus.Debugf("[resolver] lookup for IP %s: name %s", parts[0], host)
   300  	fqdn := dns.Fqdn(host)
   301  
   302  	resp := new(dns.Msg)
   303  	resp.SetReply(query)
   304  	setCommonFlags(resp)
   305  
   306  	rr := new(dns.PTR)
   307  	rr.Hdr = dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
   308  	rr.Ptr = fqdn
   309  	resp.Answer = append(resp.Answer, rr)
   310  	return resp, nil
   311  }
   312  
   313  func (r *resolver) handleSRVQuery(svc string, query *dns.Msg) (*dns.Msg, error) {
   314  
   315  	srv, ip := r.backend.ResolveService(svc)
   316  
   317  	if len(srv) == 0 {
   318  		return nil, nil
   319  	}
   320  	if len(srv) != len(ip) {
   321  		return nil, fmt.Errorf("invalid reply for SRV query %s", svc)
   322  	}
   323  
   324  	resp := createRespMsg(query)
   325  
   326  	for i, r := range srv {
   327  		rr := new(dns.SRV)
   328  		rr.Hdr = dns.RR_Header{Name: svc, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
   329  		rr.Port = r.Port
   330  		rr.Target = r.Target
   331  		resp.Answer = append(resp.Answer, rr)
   332  
   333  		rr1 := new(dns.A)
   334  		rr1.Hdr = dns.RR_Header{Name: r.Target, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
   335  		rr1.A = ip[i]
   336  		resp.Extra = append(resp.Extra, rr1)
   337  	}
   338  	return resp, nil
   339  
   340  }
   341  
   342  func truncateResp(resp *dns.Msg, maxSize int, isTCP bool) {
   343  	if !isTCP {
   344  		resp.Truncated = true
   345  	}
   346  
   347  	srv := resp.Question[0].Qtype == dns.TypeSRV
   348  	// trim the Answer RRs one by one till the whole message fits
   349  	// within the reply size
   350  	for resp.Len() > maxSize {
   351  		resp.Answer = resp.Answer[:len(resp.Answer)-1]
   352  
   353  		if srv && len(resp.Extra) > 0 {
   354  			resp.Extra = resp.Extra[:len(resp.Extra)-1]
   355  		}
   356  	}
   357  }
   358  
   359  func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
   360  	var (
   361  		extConn net.Conn
   362  		resp    *dns.Msg
   363  		err     error
   364  	)
   365  
   366  	if query == nil || len(query.Question) == 0 {
   367  		return
   368  	}
   369  
   370  	name := query.Question[0].Name
   371  	switch query.Question[0].Qtype {
   372  	case dns.TypeA:
   373  		resp, err = r.handleIPQuery(name, query, types.IPv4)
   374  	case dns.TypeAAAA:
   375  		resp, err = r.handleIPQuery(name, query, types.IPv6)
   376  	case dns.TypeMX:
   377  		resp, err = r.handleMXQuery(name, query)
   378  	case dns.TypePTR:
   379  		resp, err = r.handlePTRQuery(name, query)
   380  	case dns.TypeSRV:
   381  		resp, err = r.handleSRVQuery(name, query)
   382  	}
   383  
   384  	if err != nil {
   385  		logrus.Error(err)
   386  		return
   387  	}
   388  
   389  	if resp == nil {
   390  		// If the backend doesn't support proxying dns request
   391  		// fail the response
   392  		if !r.proxyDNS {
   393  			resp = new(dns.Msg)
   394  			resp.SetRcode(query, dns.RcodeServerFailure)
   395  			w.WriteMsg(resp)
   396  			return
   397  		}
   398  
   399  		// If the user sets ndots > 0 explicitly and the query is
   400  		// in the root domain don't forward it out. We will return
   401  		// failure and let the client retry with the search domain
   402  		// attached
   403  		switch query.Question[0].Qtype {
   404  		case dns.TypeA:
   405  			fallthrough
   406  		case dns.TypeAAAA:
   407  			if r.backend.NdotsSet() && !strings.Contains(strings.TrimSuffix(name, "."), ".") {
   408  				resp = createRespMsg(query)
   409  			}
   410  		}
   411  	}
   412  
   413  	proto := w.LocalAddr().Network()
   414  	maxSize := 0
   415  	if proto == "tcp" {
   416  		maxSize = dns.MaxMsgSize - 1
   417  	} else if proto == "udp" {
   418  		optRR := query.IsEdns0()
   419  		if optRR != nil {
   420  			maxSize = int(optRR.UDPSize())
   421  		}
   422  		if maxSize < defaultRespSize {
   423  			maxSize = defaultRespSize
   424  		}
   425  	}
   426  
   427  	if resp != nil {
   428  		if resp.Len() > maxSize {
   429  			truncateResp(resp, maxSize, proto == "tcp")
   430  		}
   431  	} else {
   432  		for i := 0; i < maxExtDNS; i++ {
   433  			extDNS := &r.extDNSList[i]
   434  			if extDNS.IPStr == "" {
   435  				break
   436  			}
   437  			extConnect := func() {
   438  				addr := fmt.Sprintf("%s:%d", extDNS.IPStr, 53)
   439  				extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
   440  			}
   441  
   442  			if extDNS.HostLoopback {
   443  				extConnect()
   444  			} else {
   445  				execErr := r.backend.ExecFunc(extConnect)
   446  				if execErr != nil {
   447  					logrus.Warn(execErr)
   448  					continue
   449  				}
   450  			}
   451  			if err != nil {
   452  				logrus.Warnf("[resolver] connect failed: %s", err)
   453  				continue
   454  			}
   455  			queryType := dns.TypeToString[query.Question[0].Qtype]
   456  			logrus.Debugf("[resolver] query %s (%s) from %s, forwarding to %s:%s", name, queryType,
   457  				extConn.LocalAddr().String(), proto, extDNS.IPStr)
   458  
   459  			// Timeout has to be set for every IO operation.
   460  			extConn.SetDeadline(time.Now().Add(extIOTimeout))
   461  			co := &dns.Conn{
   462  				Conn:    extConn,
   463  				UDPSize: uint16(maxSize),
   464  			}
   465  			defer co.Close()
   466  
   467  			// limits the number of outstanding concurrent queries.
   468  			if !r.forwardQueryStart() {
   469  				old := r.tStamp
   470  				r.tStamp = time.Now()
   471  				if r.tStamp.Sub(old) > logInterval {
   472  					logrus.Errorf("[resolver] more than %v concurrent queries from %s", maxConcurrent, extConn.LocalAddr().String())
   473  				}
   474  				continue
   475  			}
   476  
   477  			err = co.WriteMsg(query)
   478  			if err != nil {
   479  				r.forwardQueryEnd()
   480  				logrus.Debugf("[resolver] send to DNS server failed, %s", err)
   481  				continue
   482  			}
   483  
   484  			resp, err = co.ReadMsg()
   485  			// Truncated DNS replies should be sent to the client so that the
   486  			// client can retry over TCP
   487  			if err != nil && (resp == nil || !resp.Truncated) {
   488  				r.forwardQueryEnd()
   489  				logrus.Debugf("[resolver] read from DNS server failed, %s", err)
   490  				continue
   491  			}
   492  			r.forwardQueryEnd()
   493  
   494  			if resp == nil {
   495  				logrus.Debugf("[resolver] external DNS %s:%s returned empty response for %q", proto, extDNS.IPStr, name)
   496  				break
   497  			}
   498  			switch resp.Rcode {
   499  			case dns.RcodeServerFailure, dns.RcodeRefused:
   500  				// Server returned FAILURE: continue with the next external DNS server
   501  				// Server returned REFUSED: this can be a transitional status, so continue with the next external DNS server
   502  				logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), name)
   503  				continue
   504  			case dns.RcodeNameError:
   505  				// Server returned NXDOMAIN. Stop resolution if it's an authoritative answer (see RFC 8020: https://tools.ietf.org/html/rfc8020#section-2)
   506  				logrus.Debugf("[resolver] external DNS %s:%s responded with %s for %q", proto, extDNS.IPStr, statusString(resp.Rcode), name)
   507  				if resp.Authoritative {
   508  					break
   509  				}
   510  				continue
   511  			case dns.RcodeSuccess:
   512  				// All is well
   513  			default:
   514  				// Server gave some error. Log the error, and continue with the next external DNS server
   515  				logrus.Debugf("[resolver] external DNS %s:%s responded with %s (code %d) for %q", proto, extDNS.IPStr, statusString(resp.Rcode), resp.Rcode, name)
   516  				continue
   517  			}
   518  			answers := 0
   519  			for _, rr := range resp.Answer {
   520  				h := rr.Header()
   521  				switch h.Rrtype {
   522  				case dns.TypeA:
   523  					answers++
   524  					ip := rr.(*dns.A).A
   525  					logrus.Debugf("[resolver] received A record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
   526  					r.backend.HandleQueryResp(h.Name, ip)
   527  				case dns.TypeAAAA:
   528  					answers++
   529  					ip := rr.(*dns.AAAA).AAAA
   530  					logrus.Debugf("[resolver] received AAAA record %q for %q from %s:%s", ip, h.Name, proto, extDNS.IPStr)
   531  					r.backend.HandleQueryResp(h.Name, ip)
   532  				}
   533  			}
   534  			if resp.Answer == nil || answers == 0 {
   535  				logrus.Debugf("[resolver] external DNS %s:%s did not return any %s records for %q", proto, extDNS.IPStr, queryType, name)
   536  			}
   537  			resp.Compress = true
   538  			break
   539  		}
   540  		if resp == nil {
   541  			return
   542  		}
   543  	}
   544  
   545  	if err = w.WriteMsg(resp); err != nil {
   546  		logrus.Errorf("[resolver] error writing resolver resp, %s", err)
   547  	}
   548  }
   549  
   550  func statusString(responseCode int) string {
   551  	if s, ok := dns.RcodeToString[responseCode]; ok {
   552  		return s
   553  	}
   554  	return "UNKNOWN"
   555  }
   556  
   557  func (r *resolver) forwardQueryStart() bool {
   558  	r.queryLock.Lock()
   559  	defer r.queryLock.Unlock()
   560  
   561  	if r.count == maxConcurrent {
   562  		return false
   563  	}
   564  	r.count++
   565  
   566  	return true
   567  }
   568  
   569  func (r *resolver) forwardQueryEnd() {
   570  	r.queryLock.Lock()
   571  	defer r.queryLock.Unlock()
   572  
   573  	if r.count == 0 {
   574  		logrus.Error("[resolver] invalid concurrent query count")
   575  	} else {
   576  		r.count--
   577  	}
   578  }