// perform a DNS query and assert the reply code, number or answers, etc func assertExchange(t *testing.T, z string, ty uint16, minAnswers int, maxAnswers int, expErr int) *dns.Msg { c := new(dns.Client) c.UDPSize = testUDPBufSize m := new(dns.Msg) m.RecursionDesired = true m.SetQuestion(z, ty) m.SetEdns0(testUDPBufSize, false) // we don't want to play with truncation here... r, _, err := c.Exchange(m, fmt.Sprintf("127.0.0.1:%d", testPort)) t.Logf("Response:\n%+v\n", r) wt.AssertNoErr(t, err) if minAnswers == 0 && maxAnswers == 0 { wt.AssertStatus(t, r.Rcode, expErr, "DNS response code") } else { wt.AssertStatus(t, r.Rcode, dns.RcodeSuccess, "DNS response code") } answers := len(r.Answer) if minAnswers >= 0 && answers < minAnswers { wt.Fatalf(t, "Number of answers >= %d", minAnswers) } if maxAnswers >= 0 && answers > maxAnswers { wt.Fatalf(t, "Number of answers <= %d", maxAnswers) } return r }
func TestTCPDNSServer(t *testing.T) { setupForTest(t) const ( numAnswers = 512 nonLocalName = "weave.works." ) InitDefaultLogging(testing.Verbose()) Info.Println("TestTCPDNSServer starting") zone, err := NewZoneDb(ZoneConfig{}) require.NoError(t, err) err = zone.Start() require.NoError(t, err) defer zone.Stop() // generate a list of `numAnswers` IP addresses var addrs []ZoneRecord bs := make([]byte, 4) for i := 0; i < numAnswers; i++ { binary.LittleEndian.PutUint32(bs, uint32(i)) ip := net.IPv4(bs[0], bs[1], bs[2], bs[3]) addrs = append(addrs, ZoneRecord(Record{"", ip, 0, 0, 0})) } // handler for the fallback server: it will just return a very long response fallbackUDPHandler := func(w dns.ResponseWriter, req *dns.Msg) { if len(req.Question) == 0 { return // ignore empty queries (sent when shutting down the server) } maxLen := getMaxReplyLen(req, protUDP) t.Logf("Fallback UDP server got asked: returning %d answers", numAnswers) q := req.Question[0] m := makeAddressReply(req, &q, addrs, DefaultLocalTTL) mLen := m.Len() m.SetEdns0(uint16(maxLen), false) if mLen > maxLen { t.Logf("... truncated response (%d > %d)", mLen, maxLen) m.Truncated = true } w.WriteMsg(m) } fallbackTCPHandler := func(w dns.ResponseWriter, req *dns.Msg) { if len(req.Question) == 0 { return // ignore empty queries (sent when shutting down the server) } t.Logf("Fallback TCP server got asked: returning %d answers", numAnswers) q := req.Question[0] m := makeAddressReply(req, &q, addrs, DefaultLocalTTL) w.WriteMsg(m) } t.Logf("Running a DNS fallback server with UDP") fallback, err := newMockedFallback(fallbackUDPHandler, fallbackTCPHandler) require.NoError(t, err) fallback.Start() defer fallback.Stop() t.Logf("Creating a WeaveDNS server instance, falling back to 127.0.0.1:%d", fallback.Port) srv, err := NewDNSServer(DNSServerConfig{ Zone: zone, UpstreamCfg: fallback.CliConfig, CacheDisabled: true, ListenReadTimeout: testSocketTimeout, }) require.NoError(t, err) err = srv.Start() require.NoError(t, err) go srv.ActivateAndServe() defer srv.Stop() time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start testPort, err := srv.GetPort() require.NoError(t, err) require.NotEqual(t, 0, testPort, "listen port") dnsAddr := fmt.Sprintf("127.0.0.1:%d", testPort) t.Logf("Creating a UDP and a TCP client") uc := new(dns.Client) uc.UDPSize = minUDPSize tc := new(dns.Client) tc.Net = "tcp" t.Logf("Creating DNS query message") m := new(dns.Msg) m.RecursionDesired = true m.SetQuestion(nonLocalName, dns.TypeA) t.Logf("Checking the fallback server at %s returns a truncated response with UDP", fallback.Addr) r, _, err := uc.Exchange(m, fallback.Addr) t.Logf("Got response from fallback server (UDP) with %d answers", len(r.Answer)) t.Logf("Response:\n%+v\n", r) require.NoError(t, err) require.True(t, r.MsgHdr.Truncated, "DNS truncated reponse flag") require.NotEqual(t, numAnswers, len(r.Answer), "number of answers (UDP)") t.Logf("Checking the WeaveDNS server at %s returns a truncated reponse with UDP", dnsAddr) r, _, err = uc.Exchange(m, dnsAddr) t.Logf("UDP Response:\n%+v\n", r) require.NoError(t, err) require.NotNil(t, r, "response") t.Logf("%d answers", len(r.Answer)) require.True(t, r.MsgHdr.Truncated, "DNS truncated reponse flag") require.NotEqual(t, numAnswers, len(r.Answer), "number of answers (UDP)") t.Logf("Checking the WeaveDNS server at %s does not return a truncated reponse with TCP", dnsAddr) r, _, err = tc.Exchange(m, dnsAddr) t.Logf("TCP Response:\n%+v\n", r) require.NoError(t, err) require.NotNil(t, r, "response") t.Logf("%d answers", len(r.Answer)) require.False(t, r.MsgHdr.Truncated, "DNS truncated response flag") require.Equal(t, numAnswers, len(r.Answer), "number of answers (TCP)") t.Logf("Checking the WeaveDNS server at %s does not return a truncated reponse with UDP with a bigger buffer", dnsAddr) m.SetEdns0(testUDPBufSize, false) r, _, err = uc.Exchange(m, dnsAddr) t.Logf("UDP-large Response:\n%+v\n", r) require.NoError(t, err) require.NotNil(t, r, "response") t.Logf("%d answers", len(r.Answer)) require.NoError(t, err) require.False(t, r.MsgHdr.Truncated, "DNS truncated response flag") require.Equal(t, numAnswers, len(r.Answer), "number of answers (UDP-long)") }
func TestTCPDNSServer(t *testing.T) { setupForTest(t) const ( numAnswers = 512 nonLocalName = "weave.works." ) dnsAddr := fmt.Sprintf("localhost:%d", testPort) InitDefaultLogging(true) var zone = NewZoneDb(DefaultLocalDomain) // generate a list of `numAnswers` IP addresses var addrs []ZoneRecord bs := make([]byte, 4) for i := 0; i < numAnswers; i++ { binary.LittleEndian.PutUint32(bs, uint32(i)) addrs = append(addrs, Record{"", net.IPv4(bs[0], bs[1], bs[2], bs[3]), 0, 0, 0}) } // handler for the fallback server: it will just return a very long response fallbackUDPHandler := func(w dns.ResponseWriter, req *dns.Msg) { maxLen := getMaxReplyLen(req, protUDP) t.Logf("Fallback UDP server got asked: returning %d answers", numAnswers) q := req.Question[0] m := makeAddressReply(req, &q, addrs) mLen := m.Len() m.SetEdns0(uint16(maxLen), false) if mLen > maxLen { t.Logf("... truncated response (%d > %d)", mLen, maxLen) m.Truncated = true } w.WriteMsg(m) } fallbackTCPHandler := func(w dns.ResponseWriter, req *dns.Msg) { t.Logf("Fallback TCP server got asked: returning %d answers", numAnswers) q := req.Question[0] m := makeAddressReply(req, &q, addrs) w.WriteMsg(m) } t.Logf("Running a DNS fallback server with UDP") us, fallbackUDPAddr, err := runLocalUDPServer(t, "127.0.0.1:0", fallbackUDPHandler) wt.AssertNoErr(t, err) defer us.Shutdown() _, fallbackPort, err := net.SplitHostPort(fallbackUDPAddr) wt.AssertNoErr(t, err) t.Logf("Starting another fallback server, with TCP, on the same port as the UDP server") fallbackTCPAddr := fmt.Sprintf("127.0.0.1:%s", fallbackPort) ts, fallbackTCPAddr, err := runLocalTCPServer(t, fallbackTCPAddr, fallbackTCPHandler) wt.AssertNoErr(t, err) defer ts.Shutdown() t.Logf("Creating a WeaveDNS server instance, falling back to 127.0.0.1:%s", fallbackPort) config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort} srv, err := NewDNSServer(DNSServerConfig{UpstreamCfg: config, Port: testPort}, zone, nil) wt.AssertNoErr(t, err) defer srv.Stop() go srv.Start() time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start t.Logf("Creating a UDP and a TCP client") uc := new(dns.Client) uc.UDPSize = minUDPSize tc := new(dns.Client) tc.Net = "tcp" t.Logf("Creating DNS query message") m := new(dns.Msg) m.RecursionDesired = true m.SetQuestion(nonLocalName, dns.TypeA) t.Logf("Checking the fallback server at %s returns a truncated response with UDP", fallbackUDPAddr) r, _, err := uc.Exchange(m, fallbackUDPAddr) t.Logf("Got response from fallback server (UDP) with %d answers", len(r.Answer)) t.Logf("Response:\n%+v\n", r) wt.AssertNoErr(t, err) wt.AssertTrue(t, r.MsgHdr.Truncated, "DNS truncated reponse flag") wt.AssertNotEqualInt(t, len(r.Answer), numAnswers, "number of answers (UDP)") t.Logf("Checking the WeaveDNS server at %s returns a truncated reponse with UDP", dnsAddr) r, _, err = uc.Exchange(m, dnsAddr) t.Logf("UDP Response:\n%+v\n", r) wt.AssertNoErr(t, err) wt.AssertNotNil(t, r, "response") t.Logf("%d answers", len(r.Answer)) wt.AssertTrue(t, r.MsgHdr.Truncated, "DNS truncated reponse flag") wt.AssertNotEqualInt(t, len(r.Answer), numAnswers, "number of answers (UDP)") t.Logf("Checking the WeaveDNS server at %s does not return a truncated reponse with TCP", dnsAddr) r, _, err = tc.Exchange(m, dnsAddr) t.Logf("TCP Response:\n%+v\n", r) wt.AssertNoErr(t, err) wt.AssertNotNil(t, r, "response") t.Logf("%d answers", len(r.Answer)) wt.AssertFalse(t, r.MsgHdr.Truncated, "DNS truncated response flag") wt.AssertEqualInt(t, len(r.Answer), numAnswers, "number of answers (TCP)") t.Logf("Checking the WeaveDNS server at %s does not return a truncated reponse with UDP with a bigger buffer", dnsAddr) m.SetEdns0(testUDPBufSize, false) r, _, err = uc.Exchange(m, dnsAddr) t.Logf("UDP-large Response:\n%+v\n", r) wt.AssertNoErr(t, err) wt.AssertNotNil(t, r, "response") t.Logf("%d answers", len(r.Answer)) wt.AssertNoErr(t, err) wt.AssertFalse(t, r.MsgHdr.Truncated, "DNS truncated response flag") wt.AssertEqualInt(t, len(r.Answer), numAnswers, "number of answers (UDP-long)") }