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 }