github.com/khulnasoft-lab/khulnasoft@v26.0.1-0.20240328202558-330a6f959fe0+incompatible/libnetwork/resolver_test.go (about)

     1  package libnetwork
     2  
     3  import (
     4  	"context"
     5  	"encoding/hex"
     6  	"errors"
     7  	"net"
     8  	"syscall"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/containerd/log"
    13  	"github.com/docker/docker/internal/testutils/netnsutils"
    14  	"github.com/miekg/dns"
    15  	"github.com/sirupsen/logrus"
    16  	"gotest.tools/v3/assert"
    17  	is "gotest.tools/v3/assert/cmp"
    18  )
    19  
    20  // a simple/null address type that will be used to fake a local address for unit testing
    21  type tstaddr struct {
    22  	network string
    23  }
    24  
    25  func (a *tstaddr) Network() string {
    26  	if a.network != "" {
    27  		return a.network
    28  	}
    29  	return "tcp"
    30  }
    31  
    32  func (a *tstaddr) String() string { return "(fake)" }
    33  
    34  // a simple writer that implements dns.ResponseWriter for unit testing purposes
    35  type tstwriter struct {
    36  	network string
    37  	msg     *dns.Msg
    38  }
    39  
    40  func (w *tstwriter) WriteMsg(m *dns.Msg) (err error) {
    41  	// Assert that the message is serializable.
    42  	if _, err := m.Pack(); err != nil {
    43  		return err
    44  	}
    45  	w.msg = m
    46  	return nil
    47  }
    48  
    49  func (w *tstwriter) Write(m []byte) (int, error) { return 0, nil }
    50  
    51  func (w *tstwriter) LocalAddr() net.Addr {
    52  	return &tstaddr{network: w.network}
    53  }
    54  
    55  func (w *tstwriter) RemoteAddr() net.Addr {
    56  	return &tstaddr{network: w.network}
    57  }
    58  
    59  func (w *tstwriter) TsigStatus() error { return nil }
    60  
    61  func (w *tstwriter) TsigTimersOnly(b bool) {}
    62  
    63  func (w *tstwriter) Hijack() {}
    64  
    65  func (w *tstwriter) Close() error { return nil }
    66  
    67  func (w *tstwriter) GetResponse() *dns.Msg { return w.msg }
    68  
    69  func (w *tstwriter) ClearResponse() { w.msg = nil }
    70  
    71  func checkNonNullResponse(t *testing.T, m *dns.Msg) {
    72  	t.Helper()
    73  	if m == nil {
    74  		t.Fatal("Null DNS response found. Non Null response msg expected.")
    75  	}
    76  }
    77  
    78  func checkDNSAnswersCount(t *testing.T, m *dns.Msg, expected int) {
    79  	t.Helper()
    80  	answers := len(m.Answer)
    81  	if answers != expected {
    82  		t.Fatalf("Expected number of answers in response: %d. Found: %d", expected, answers)
    83  	}
    84  }
    85  
    86  func checkDNSResponseCode(t *testing.T, m *dns.Msg, expected int) {
    87  	t.Helper()
    88  	if m.MsgHdr.Rcode != expected {
    89  		t.Fatalf("Expected DNS response code: %d (%s). Found: %d (%s)", expected, dns.RcodeToString[expected], m.MsgHdr.Rcode, dns.RcodeToString[m.MsgHdr.Rcode])
    90  	}
    91  }
    92  
    93  func checkDNSRRType(t *testing.T, actual, expected uint16) {
    94  	t.Helper()
    95  	if actual != expected {
    96  		t.Fatalf("Expected DNS Rrtype: %d. Found: %d", expected, actual)
    97  	}
    98  }
    99  
   100  func newDNSHandlerServFailOnce(requests *int) func(w dns.ResponseWriter, r *dns.Msg) {
   101  	return func(w dns.ResponseWriter, r *dns.Msg) {
   102  		m := new(dns.Msg)
   103  		m.SetReply(r)
   104  		m.Compress = false
   105  		if *requests == 0 {
   106  			m.SetRcode(r, dns.RcodeServerFailure)
   107  		}
   108  		*requests = *requests + 1
   109  		if err := w.WriteMsg(m); err != nil {
   110  			log.G(context.TODO()).WithError(err).Error("Error writing dns response")
   111  		}
   112  	}
   113  }
   114  
   115  func waitForLocalDNSServer(t *testing.T) {
   116  	retries := 0
   117  	maxRetries := 10
   118  
   119  	for retries < maxRetries {
   120  		t.Log("Try connecting to DNS server ...")
   121  		// this test and retry mechanism only works for TCP. With UDP there is no
   122  		// connection and the test becomes inaccurate leading to unpredictable results
   123  		tconn, err := net.DialTimeout("tcp", "127.0.0.1:53", 10*time.Second)
   124  		retries = retries + 1
   125  		if err != nil {
   126  			if oerr, ok := err.(*net.OpError); ok {
   127  				// server is probably initializing
   128  				if oerr.Err == syscall.ECONNREFUSED {
   129  					continue
   130  				}
   131  			} else {
   132  				// something is wrong: we should stop for analysis
   133  				t.Fatal(err)
   134  			}
   135  		}
   136  		if tconn != nil {
   137  			tconn.Close()
   138  			break
   139  		}
   140  	}
   141  }
   142  
   143  // Packet 24 extracted from
   144  // https://gist.github.com/vojtad/3bac63b8c91b1ec50e8d8b36047317fa/raw/7d75eb3d3448381bf252ae55ea5123a132c46658/host.pcap
   145  // (https://github.com/moby/moby/issues/44575)
   146  // which is a non-compliant DNS reply > 512B (w/o EDNS(0)) to the query
   147  //
   148  //	s3.amazonaws.com. IN A
   149  const oversizedDNSReplyMsg = "\xf5\x11\x81\x80\x00\x01\x00\x20\x00\x00\x00\x00\x02\x73\x33\x09" +
   150  	"\x61\x6d\x61\x7a\x6f\x6e\x61\x77\x73\x03\x63\x6f\x6d\x00\x00\x01" +
   151  	"\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
   152  	"\x11\x9e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
   153  	"\x4c\x66\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
   154  	"\xda\x10\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
   155  	"\x01\x3e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
   156  	"\x88\x68\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
   157  	"\x66\x9e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
   158  	"\x5f\x28\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
   159  	"\x8e\x4e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x36\xe7" +
   160  	"\x84\xf0\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x34\xd8" +
   161  	"\x92\x45\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
   162  	"\x8f\xa6\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x36\xe7" +
   163  	"\xc0\xd0\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
   164  	"\xfe\x28\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
   165  	"\xaa\x3d\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
   166  	"\x4e\x56\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd9" +
   167  	"\xea\xb0\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
   168  	"\x6d\xed\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x04\x00\x04\x34\xd8" +
   169  	"\x28\x00\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x34\xd9" +
   170  	"\xe9\x78\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x34\xd9" +
   171  	"\x6e\x9e\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x34\xd9" +
   172  	"\x45\x86\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x34\xd8" +
   173  	"\x30\x38\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x36\xe7" +
   174  	"\xc6\xa8\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x00\x00\x04\x03\x05" +
   175  	"\x01\x9d\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd9" +
   176  	"\xa8\xe8\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd9" +
   177  	"\x64\xa6\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd8" +
   178  	"\x3c\x48\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd8" +
   179  	"\x35\x20\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd9" +
   180  	"\x54\xf6\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd9" +
   181  	"\x5d\x36\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x34\xd9" +
   182  	"\x30\x36\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x05\x00\x04\x36\xe7" +
   183  	"\x83\x90"
   184  
   185  // Regression test for https://github.com/moby/moby/issues/44575
   186  func TestOversizedDNSReply(t *testing.T) {
   187  	srv, err := net.ListenPacket("udp", "127.0.0.1:0")
   188  	assert.NilError(t, err)
   189  	defer srv.Close()
   190  	go func() {
   191  		buf := make([]byte, 65536)
   192  		for {
   193  			n, src, err := srv.ReadFrom(buf)
   194  			if errors.Is(err, net.ErrClosed) {
   195  				return
   196  			}
   197  			t.Logf("[<-%v]\n%s", src, hex.Dump(buf[:n]))
   198  			if n < 2 {
   199  				continue
   200  			}
   201  			resp := []byte(oversizedDNSReplyMsg)
   202  			resp[0], resp[1] = buf[0], buf[1] // Copy query ID into response.
   203  			_, err = srv.WriteTo(resp, src)
   204  			if errors.Is(err, net.ErrClosed) {
   205  				return
   206  			}
   207  			if err != nil {
   208  				t.Log(err)
   209  			}
   210  		}
   211  	}()
   212  
   213  	srvAddr := srv.LocalAddr().(*net.UDPAddr)
   214  	rsv := NewResolver("", true, noopDNSBackend{})
   215  	// The resolver logs lots of valuable info at level debug. Redirect it
   216  	// to t.Log() so the log spew is emitted only if the test fails.
   217  	rsv.logger = testLogger(t)
   218  	rsv.SetExtServers([]extDNSEntry{
   219  		{IPStr: srvAddr.IP.String(), port: uint16(srvAddr.Port), HostLoopback: true},
   220  	})
   221  
   222  	w := &tstwriter{network: srvAddr.Network()}
   223  	q := new(dns.Msg).SetQuestion("s3.amazonaws.com.", dns.TypeA)
   224  	rsv.serveDNS(w, q)
   225  	resp := w.GetResponse()
   226  	checkNonNullResponse(t, resp)
   227  	t.Log("Response: ", resp.String())
   228  	checkDNSResponseCode(t, resp, dns.RcodeSuccess)
   229  	assert.Assert(t, len(resp.Answer) >= 1)
   230  	checkDNSRRType(t, resp.Answer[0].Header().Rrtype, dns.TypeA)
   231  }
   232  
   233  func testLogger(t *testing.T) *logrus.Entry {
   234  	logger := logrus.New()
   235  	logger.SetLevel(logrus.DebugLevel)
   236  	logger.SetOutput(tlogWriter{t})
   237  	return logrus.NewEntry(logger)
   238  }
   239  
   240  type tlogWriter struct{ t *testing.T }
   241  
   242  func (w tlogWriter) Write(p []byte) (n int, err error) {
   243  	w.t.Logf("%s", p)
   244  	return len(p), nil
   245  }
   246  
   247  type noopDNSBackend struct{ DNSBackend }
   248  
   249  func (noopDNSBackend) ResolveName(_ context.Context, name string, iplen int) ([]net.IP, bool) {
   250  	return nil, false
   251  }
   252  
   253  func (noopDNSBackend) ExecFunc(f func()) error { f(); return nil }
   254  
   255  func (noopDNSBackend) NdotsSet() bool { return false }
   256  
   257  func (noopDNSBackend) HandleQueryResp(name string, ip net.IP) {}
   258  
   259  func TestReplySERVFAIL(t *testing.T) {
   260  	cases := []struct {
   261  		name     string
   262  		q        *dns.Msg
   263  		proxyDNS bool
   264  	}{
   265  		{
   266  			name: "InternalError",
   267  			q:    new(dns.Msg).SetQuestion("_sip._tcp.example.com.", dns.TypeSRV),
   268  		},
   269  		{
   270  			name: "ProxyDNS=false",
   271  			q:    new(dns.Msg).SetQuestion("example.com.", dns.TypeA),
   272  		},
   273  		{
   274  			name:     "ProxyDNS=true", // No extDNS servers configured -> no answer from any upstream
   275  			q:        new(dns.Msg).SetQuestion("example.com.", dns.TypeA),
   276  			proxyDNS: true,
   277  		},
   278  	}
   279  	for _, tt := range cases {
   280  		t.Run(tt.name, func(t *testing.T) {
   281  			rsv := NewResolver("", tt.proxyDNS, badSRVDNSBackend{})
   282  			rsv.logger = testLogger(t)
   283  			w := &tstwriter{}
   284  			rsv.serveDNS(w, tt.q)
   285  			resp := w.GetResponse()
   286  			checkNonNullResponse(t, resp)
   287  			t.Log("Response: ", resp.String())
   288  			checkDNSResponseCode(t, resp, dns.RcodeServerFailure)
   289  		})
   290  	}
   291  }
   292  
   293  type badSRVDNSBackend struct{ noopDNSBackend }
   294  
   295  func (badSRVDNSBackend) ResolveService(_ context.Context, _ string) ([]*net.SRV, []net.IP) {
   296  	return []*net.SRV{nil, nil, nil}, nil // Mismatched slice lengths
   297  }
   298  
   299  func TestProxyNXDOMAIN(t *testing.T) {
   300  	mockSOA, err := dns.NewRR(".	86367	IN	SOA	a.root-servers.net. nstld.verisign-grs.com. 2023051800 1800 900 604800 86400\n")
   301  	assert.NilError(t, err)
   302  	assert.Assert(t, mockSOA != nil)
   303  
   304  	serveStarted := make(chan struct{})
   305  	srv := &dns.Server{
   306  		Net:  "udp",
   307  		Addr: "127.0.0.1:0",
   308  		Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
   309  			msg := new(dns.Msg).SetRcode(r, dns.RcodeNameError)
   310  			msg.Ns = append(msg.Ns, dns.Copy(mockSOA))
   311  			w.WriteMsg(msg)
   312  		}),
   313  		NotifyStartedFunc: func() { close(serveStarted) },
   314  	}
   315  	serveDone := make(chan error, 1)
   316  	go func() {
   317  		defer close(serveDone)
   318  		serveDone <- srv.ListenAndServe()
   319  	}()
   320  
   321  	select {
   322  	case err := <-serveDone:
   323  		t.Fatal(err)
   324  	case <-serveStarted:
   325  	}
   326  
   327  	defer func() {
   328  		if err := srv.Shutdown(); err != nil {
   329  			t.Error(err)
   330  		}
   331  		<-serveDone
   332  	}()
   333  
   334  	// This test, by virtue of running a server and client in different
   335  	// not-locked-to-thread goroutines, happens to be a good canary for
   336  	// whether we are leaking unlocked OS threads set to the wrong network
   337  	// namespace. Make a best-effort attempt to detect that situation so we
   338  	// are not left chasing ghosts next time.
   339  	netnsutils.AssertSocketSameNetNS(t, srv.PacketConn.(*net.UDPConn))
   340  
   341  	srvAddr := srv.PacketConn.LocalAddr().(*net.UDPAddr)
   342  	rsv := NewResolver("", true, noopDNSBackend{})
   343  	rsv.SetExtServers([]extDNSEntry{
   344  		{IPStr: srvAddr.IP.String(), port: uint16(srvAddr.Port), HostLoopback: true},
   345  	})
   346  
   347  	// The resolver logs lots of valuable info at level debug. Redirect it
   348  	// to t.Log() so the log spew is emitted only if the test fails.
   349  	rsv.logger = testLogger(t)
   350  
   351  	w := &tstwriter{network: srvAddr.Network()}
   352  	q := new(dns.Msg).SetQuestion("example.net.", dns.TypeA)
   353  	rsv.serveDNS(w, q)
   354  	resp := w.GetResponse()
   355  	checkNonNullResponse(t, resp)
   356  	t.Log("Response:\n" + resp.String())
   357  	checkDNSResponseCode(t, resp, dns.RcodeNameError)
   358  	assert.Assert(t, is.Len(resp.Answer, 0))
   359  	assert.Assert(t, is.Len(resp.Ns, 1))
   360  	assert.Equal(t, resp.Ns[0].String(), mockSOA.String())
   361  }
   362  
   363  type ptrDNSBackend struct {
   364  	noopDNSBackend
   365  	zone map[string]string
   366  }
   367  
   368  func (b *ptrDNSBackend) ResolveIP(_ context.Context, name string) string {
   369  	return b.zone[name]
   370  }
   371  
   372  // Regression test for https://github.com/moby/moby/issues/46928
   373  func TestInvalidReverseDNS(t *testing.T) {
   374  	rsv := NewResolver("", false, &ptrDNSBackend{zone: map[string]string{"4.3.2.1": "sixtyfourcharslong9012345678901234567890123456789012345678901234"}})
   375  	rsv.logger = testLogger(t)
   376  
   377  	w := &tstwriter{}
   378  	q := new(dns.Msg).SetQuestion("4.3.2.1.in-addr.arpa.", dns.TypePTR)
   379  	rsv.serveDNS(w, q)
   380  	resp := w.GetResponse()
   381  	checkNonNullResponse(t, resp)
   382  	t.Log("Response: ", resp.String())
   383  	checkDNSResponseCode(t, resp, dns.RcodeServerFailure)
   384  }