github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/libnetwork/resolver_test.go (about) 1 package libnetwork 2 3 import ( 4 "net" 5 "runtime" 6 "syscall" 7 "testing" 8 "time" 9 10 "github.com/miekg/dns" 11 "github.com/sirupsen/logrus" 12 "gotest.tools/v3/skip" 13 ) 14 15 // a simple/null address type that will be used to fake a local address for unit testing 16 type tstaddr struct { 17 } 18 19 func (a *tstaddr) Network() string { return "tcp" } 20 21 func (a *tstaddr) String() string { return "127.0.0.1" } 22 23 // a simple writer that implements dns.ResponseWriter for unit testing purposes 24 type tstwriter struct { 25 msg *dns.Msg 26 } 27 28 func (w *tstwriter) WriteMsg(m *dns.Msg) (err error) { 29 w.msg = m 30 return nil 31 } 32 33 func (w *tstwriter) Write(m []byte) (int, error) { return 0, nil } 34 35 func (w *tstwriter) LocalAddr() net.Addr { return new(tstaddr) } 36 37 func (w *tstwriter) RemoteAddr() net.Addr { return new(tstaddr) } 38 39 func (w *tstwriter) TsigStatus() error { return nil } 40 41 func (w *tstwriter) TsigTimersOnly(b bool) {} 42 43 func (w *tstwriter) Hijack() {} 44 45 func (w *tstwriter) Close() error { return nil } 46 47 func (w *tstwriter) GetResponse() *dns.Msg { return w.msg } 48 49 func (w *tstwriter) ClearResponse() { w.msg = nil } 50 51 func checkNonNullResponse(t *testing.T, m *dns.Msg) { 52 if m == nil { 53 t.Fatal("Null DNS response found. Non Null response msg expected.") 54 } 55 } 56 57 func checkDNSAnswersCount(t *testing.T, m *dns.Msg, expected int) { 58 answers := len(m.Answer) 59 if answers != expected { 60 t.Fatalf("Expected number of answers in response: %d. Found: %d", expected, answers) 61 } 62 } 63 64 func checkDNSResponseCode(t *testing.T, m *dns.Msg, expected int) { 65 if m.MsgHdr.Rcode != expected { 66 t.Fatalf("Expected DNS response code: %d. Found: %d", expected, m.MsgHdr.Rcode) 67 } 68 } 69 70 func checkDNSRRType(t *testing.T, actual, expected uint16) { 71 if actual != expected { 72 t.Fatalf("Expected DNS Rrtype: %d. Found: %d", expected, actual) 73 } 74 } 75 76 func TestDNSIPQuery(t *testing.T) { 77 skip.If(t, runtime.GOOS == "windows", "test only works on linux") 78 79 c, err := New() 80 if err != nil { 81 t.Fatal(err) 82 } 83 defer c.Stop() 84 85 n, err := c.NewNetwork("bridge", "dtnet1", "", nil) 86 if err != nil { 87 t.Fatal(err) 88 } 89 defer func() { 90 if err := n.Delete(); err != nil { 91 t.Fatal(err) 92 } 93 }() 94 95 ep, err := n.CreateEndpoint("testep") 96 if err != nil { 97 t.Fatal(err) 98 } 99 100 sb, err := c.NewSandbox("c1") 101 if err != nil { 102 t.Fatal(err) 103 } 104 105 defer func() { 106 if err := sb.Delete(); err != nil { 107 t.Fatal(err) 108 } 109 }() 110 111 // we need the endpoint only to populate ep_list for the sandbox as part of resolve_name 112 // it is not set as a target for name resolution and does not serve any other purpose 113 err = ep.Join(sb) 114 if err != nil { 115 t.Fatal(err) 116 } 117 118 // add service records which are used to resolve names. These are the real targets for the DNS querries 119 n.(*network).addSvcRecords("ep1", "name1", "svc1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test") 120 121 w := new(tstwriter) 122 // the unit tests right now will focus on non-proxyed DNS requests 123 r := NewResolver(resolverIPSandbox, false, sb.Key(), sb.(*sandbox)) 124 125 // test name1's IP is resolved correctly with the default A type query 126 // Also make sure DNS lookups are case insensitive 127 names := []string{"name1", "NaMe1"} 128 for _, name := range names { 129 q := new(dns.Msg) 130 q.SetQuestion(name, dns.TypeA) 131 r.(*resolver).ServeDNS(w, q) 132 resp := w.GetResponse() 133 checkNonNullResponse(t, resp) 134 t.Log("Response: ", resp.String()) 135 checkDNSResponseCode(t, resp, dns.RcodeSuccess) 136 checkDNSAnswersCount(t, resp, 1) 137 checkDNSRRType(t, resp.Answer[0].Header().Rrtype, dns.TypeA) 138 if answer, ok := resp.Answer[0].(*dns.A); ok { 139 if !answer.A.Equal(net.ParseIP("192.168.0.1")) { 140 t.Fatalf("IP response in Answer %v does not match 192.168.0.1", answer.A) 141 } 142 } else { 143 t.Fatal("Answer of type A not found") 144 } 145 w.ClearResponse() 146 } 147 148 // test MX query with name1 results in Success response with 0 answer records 149 q := new(dns.Msg) 150 q.SetQuestion("name1", dns.TypeMX) 151 r.(*resolver).ServeDNS(w, q) 152 resp := w.GetResponse() 153 checkNonNullResponse(t, resp) 154 t.Log("Response: ", resp.String()) 155 checkDNSResponseCode(t, resp, dns.RcodeSuccess) 156 checkDNSAnswersCount(t, resp, 0) 157 w.ClearResponse() 158 159 // test MX query with non existent name results in ServFail response with 0 answer records 160 // since this is a unit test env, we disable proxying DNS above which results in ServFail rather than NXDOMAIN 161 q = new(dns.Msg) 162 q.SetQuestion("nonexistent", dns.TypeMX) 163 r.(*resolver).ServeDNS(w, q) 164 resp = w.GetResponse() 165 checkNonNullResponse(t, resp) 166 t.Log("Response: ", resp.String()) 167 checkDNSResponseCode(t, resp, dns.RcodeServerFailure) 168 w.ClearResponse() 169 } 170 171 func newDNSHandlerServFailOnce(requests *int) func(w dns.ResponseWriter, r *dns.Msg) { 172 return func(w dns.ResponseWriter, r *dns.Msg) { 173 m := new(dns.Msg) 174 m.SetReply(r) 175 m.Compress = false 176 if *requests == 0 { 177 m.SetRcode(r, dns.RcodeServerFailure) 178 } 179 *requests = *requests + 1 180 if err := w.WriteMsg(m); err != nil { 181 logrus.WithError(err).Error("Error writing dns response") 182 } 183 } 184 } 185 186 func waitForLocalDNSServer(t *testing.T) { 187 retries := 0 188 maxRetries := 10 189 190 for retries < maxRetries { 191 t.Log("Try connecting to DNS server ...") 192 // this test and retry mechanism only works for TCP. With UDP there is no 193 // connection and the test becomes inaccurate leading to unpredictable results 194 tconn, err := net.DialTimeout("tcp", "127.0.0.1:53", 10*time.Second) 195 retries = retries + 1 196 if err != nil { 197 if oerr, ok := err.(*net.OpError); ok { 198 // server is probably initializing 199 if oerr.Err == syscall.ECONNREFUSED { 200 continue 201 } 202 } else { 203 // something is wrong: we should stop for analysis 204 t.Fatal(err) 205 } 206 } 207 if tconn != nil { 208 tconn.Close() 209 break 210 } 211 } 212 } 213 214 func TestDNSProxyServFail(t *testing.T) { 215 skip.If(t, runtime.GOOS == "windows", "test only works on linux") 216 217 c, err := New() 218 if err != nil { 219 t.Fatal(err) 220 } 221 defer c.Stop() 222 223 n, err := c.NewNetwork("bridge", "dtnet2", "", nil) 224 if err != nil { 225 t.Fatal(err) 226 } 227 defer func() { 228 if err := n.Delete(); err != nil { 229 t.Fatal(err) 230 } 231 }() 232 233 sb, err := c.NewSandbox("c1") 234 if err != nil { 235 t.Fatal(err) 236 } 237 238 defer func() { 239 if err := sb.Delete(); err != nil { 240 t.Fatal(err) 241 } 242 }() 243 244 var nRequests int 245 // initialize a local DNS server and configure it to fail the first query 246 dns.HandleFunc(".", newDNSHandlerServFailOnce(&nRequests)) 247 // use TCP for predictable results. Connection tests (to figure out DNS server initialization) don't work with UDP 248 server := &dns.Server{Addr: "127.0.0.1:53", Net: "tcp"} 249 srvErrCh := make(chan error, 1) 250 go func() { 251 srvErrCh <- server.ListenAndServe() 252 }() 253 defer func() { 254 server.Shutdown() //nolint:errcheck 255 if err := <-srvErrCh; err != nil { 256 t.Error(err) 257 } 258 }() 259 260 waitForLocalDNSServer(t) 261 t.Log("DNS Server can be reached") 262 263 w := new(tstwriter) 264 r := NewResolver(resolverIPSandbox, true, sb.Key(), sb.(*sandbox)) 265 q := new(dns.Msg) 266 q.SetQuestion("name1.", dns.TypeA) 267 268 var localDNSEntries []extDNSEntry 269 extTestDNSEntry := extDNSEntry{IPStr: "127.0.0.1", HostLoopback: true} 270 271 // configure two external DNS entries and point both to local DNS server thread 272 localDNSEntries = append(localDNSEntries, extTestDNSEntry) 273 localDNSEntries = append(localDNSEntries, extTestDNSEntry) 274 275 // this should generate two requests: the first will fail leading to a retry 276 r.(*resolver).SetExtServers(localDNSEntries) 277 r.(*resolver).ServeDNS(w, q) 278 if nRequests != 2 { 279 t.Fatalf("Expected 2 DNS querries. Found: %d", nRequests) 280 } 281 t.Logf("Expected number of DNS requests generated") 282 }