From 9c7ffd9b8129874ed2986eb51dd6683438ad7f80 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Thu, 12 Feb 2015 12:51:34 +0100 Subject: [PATCH] zettio/weave#362 - improve the unit tests for the TCP server by checking if we get all the responses with TCP when a previous UDP query resulted in a truncated response. --- nameserver/server.go | 40 ++++++++--------- nameserver/server_test.go | 93 +++++++++++++++++++++++++++++++-------- weavedns/main.go | 8 ++-- 3 files changed, 96 insertions(+), 45 deletions(-) diff --git a/nameserver/server.go b/nameserver/server.go index 33581260..9a1726e5 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -21,34 +21,31 @@ func makeDNSFailResponse(r *dns.Msg) *dns.Msg { } type DNSServer struct { - config *dns.ClientConfig - zone Zone - iface *net.Interface - udpPort int - udpSrv *dns.Server - tcpPort int - tcpSrv *dns.Server + config *dns.ClientConfig + zone Zone + iface *net.Interface + port int + udpSrv *dns.Server + tcpSrv *dns.Server } // Creates a new DNS server with a given config -func NewDNSServerWithConfig(config *dns.ClientConfig, zone Zone, iface *net.Interface, udpPort int, tcpPort int) (*DNSServer, error) { +func NewDNSServerWithConfig(config *dns.ClientConfig, zone Zone, iface *net.Interface, port int) (*DNSServer, error) { return &DNSServer{ - config: config, - zone: zone, - iface: iface, - udpPort: udpPort, - tcpPort: tcpPort, + config: config, + zone: zone, + iface: iface, + port: port, }, nil } // Creates a new DNS server -func NewDNSServer(zone Zone, iface *net.Interface, udpPort int, tcpPort int) (*DNSServer, error) { +func NewDNSServer(zone Zone, iface *net.Interface, port int) (*DNSServer, error) { config, err := dns.ClientConfigFromFile("/etc/resolv.conf") if err != nil { return nil, err } - - return NewDNSServerWithConfig(config, zone, iface, udpPort, tcpPort) + return NewDNSServerWithConfig(config, zone, iface, port) } // Start the DNS server @@ -75,19 +72,18 @@ func (s *DNSServer) Start() error { err = mdnsServer.Start(s.iface) checkFatal(err) - udpAddress := fmt.Sprintf(":%d", s.udpPort) - s.udpSrv = &dns.Server{Addr: udpAddress, Net: "udp", Handler: LocalServeMux} - tcpAddress := fmt.Sprintf(":%d", s.tcpPort) - s.tcpSrv = &dns.Server{Addr: tcpAddress, Net: "tcp", Handler: LocalServeMux} + address := fmt.Sprintf(":%d", s.port) + s.udpSrv = &dns.Server{Addr: address, Net: "udp", Handler: LocalServeMux} + s.tcpSrv = &dns.Server{Addr: address, Net: "tcp", Handler: LocalServeMux} go func() { - Info.Printf("Listening for DNS on %s (UDP)", udpAddress) + Info.Printf("Listening for DNS on %s (UDP)", address) err = s.udpSrv.ListenAndServe() checkFatal(err) }() go func() { - Info.Printf("Listening for DNS on %s (TCP)", tcpAddress) + Info.Printf("Listening for DNS on %s (TCP)", address) err = s.tcpSrv.ListenAndServe() checkFatal(err) }() diff --git a/nameserver/server_test.go b/nameserver/server_test.go index a5cb356d..f117b3ff 100644 --- a/nameserver/server_test.go +++ b/nameserver/server_test.go @@ -3,7 +3,7 @@ package nameserver import ( "fmt" "github.com/miekg/dns" - "github.com/zettio/weave/common" + . "github.com/zettio/weave/common" wt "github.com/zettio/weave/testing" "net" "testing" @@ -16,7 +16,7 @@ const ( testRDNSnonlocal = "8.8.8.8.in-addr.arpa." ) -func TestDNSServer(t *testing.T) { +func TestUDPDNSServer(t *testing.T) { const ( port = 17625 successTestName = "test1.weave.local." @@ -27,7 +27,7 @@ func TestDNSServer(t *testing.T) { dnsAddr := fmt.Sprintf("localhost:%d", port) testCIDR1 := testAddr1 + "/24" - common.InitDefaultLogging(true) + InitDefaultLogging(true) var zone = new(ZoneDb) ip, _, _ := net.ParseCIDR(testCIDR1) zone.AddRecord(containerID, successTestName, ip) @@ -41,17 +41,14 @@ func TestDNSServer(t *testing.T) { wt.AssertNoErr(t, err) config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort} - srv, err := NewDNSServerWithConfig(config, zone, nil, port, port) + srv, err := NewDNSServerWithConfig(config, zone, nil, port) wt.AssertNoErr(t, err) + defer srv.Stop() go srv.Start() time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start - // Create a regular UDP client and a TCP client c := new(dns.Client) c.UDPSize = UDPBufSize - tc := new(dns.Client) - tc.Net = "tcp" - m := new(dns.Msg) m.SetQuestion(successTestName, dns.TypeA) m.RecursionDesired = true @@ -63,16 +60,6 @@ func TestDNSServer(t *testing.T) { wt.AssertType(t, r.Answer[0], (*dns.A)(nil), "DNS record") wt.AssertEqualString(t, r.Answer[0].(*dns.A).A.String(), testAddr1, "IP address") - // Retry the query with the TCP client - tr, _, err := tc.Exchange(m, dnsAddr) - wt.AssertNoErr(t, err) - wt.AssertStatus(t, tr.Rcode, dns.RcodeSuccess, "DNS response code (TCP)") - wt.AssertEqualInt(t, len(tr.Answer), 1, "Number of answers (TCP)") - wt.AssertType(t, tr.Answer[0], (*dns.A)(nil), "DNS record (TCP)") - wt.AssertEqualString(t, tr.Answer[0].(*dns.A).A.String(), testAddr1, "IP address (TCP)") - // TODO: look for some way of testing the TCP fallback for truncated responses: it seems there is - // TODO: no client lib in Go that can do that... - m.SetQuestion(failTestName, dns.TypeA) r, _, err = c.Exchange(m, dnsAddr) wt.AssertNoErr(t, err) @@ -129,6 +116,76 @@ func TestDNSServer(t *testing.T) { // needs two servers, each listening on its own address } +func TestTCPDNSServer(t *testing.T) { + const ( + port = 17625 + localTestName = "test1.weave.local." + testAddr1 = "10.0.2.1" + ) + dnsAddr := fmt.Sprintf("localhost:%d", port) + + InitDefaultLogging(true) + var zone = new(ZoneDb) + + // Insert a bunch of IPs for the same name + ansCount := 0 + for i := 0; i < 255; i++ { + for j := 0; j < 10; j++ { + ip := net.ParseIP(fmt.Sprintf("10.0.%d.%d", i, j)) + ansCount += 1 + err := zone.AddRecord(containerID, localTestName, ip) + wt.AssertNoErr(t, err) + } + } + + // Run another DNS server for fallback + s, fallbackAddr, err := RunLocalUDPServer("127.0.0.1:0") + wt.AssertNoErr(t, err) + defer s.Shutdown() + + _, fallbackPort, err := net.SplitHostPort(fallbackAddr) + wt.AssertNoErr(t, err) + + config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort} + srv, err := NewDNSServerWithConfig(config, zone, nil, port) + wt.AssertNoErr(t, err) + defer srv.Stop() + go srv.Start() + time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start + + // Create a regular UDP client and a TCP client + uc := new(dns.Client) + uc.UDPSize = UDPBufSize + tc := new(dns.Client) + tc.Net = "tcp" + + m := new(dns.Msg) + m.SetQuestion(localTestName, dns.TypeA) + m.RecursionDesired = false + + Debug.Printf("Using UDP for quering about %s", localTestName) + r, _, err := uc.Exchange(m, dnsAddr) + wt.AssertNoErr(t, err) + if len(r.Answer) > 1 { // TODO: remove this when we have proper support for multiple responses (zettio/weave#338) + wt.AssertStatus(t, r.Rcode, dns.RcodeBadTrunc, "DNS response code") + if len(r.Answer) == ansCount { + wt.Fatalf(t, "%d answers received, but we expected less than that", len(r.Answer)) + } + } else { + Debug.Println("... multiple reponses not supported (yet)") + wt.AssertStatus(t, r.Rcode, dns.RcodeSuccess, "DNS response code") + } + + Debug.Println("Using TCP for the same query") + tr, _, err := tc.Exchange(m, dnsAddr) + wt.AssertNoErr(t, err) + wt.AssertStatus(t, tr.Rcode, dns.RcodeSuccess, "DNS response code (TCP)") + if len(r.Answer) > 1 { // TODO: remove this when we have proper support for multiple responses (zettio/weave#338) + wt.AssertEqualInt(t, len(tr.Answer), ansCount, "Number of answers (TCP)") + } + wt.AssertType(t, tr.Answer[0], (*dns.A)(nil), "DNS record (TCP)") // assume they are all the same... +} + func fallbackHandler(w dns.ResponseWriter, req *dns.Msg) { m := new(dns.Msg) m.SetReply(req) diff --git a/weavedns/main.go b/weavedns/main.go index d5053686..ab9f5686 100644 --- a/weavedns/main.go +++ b/weavedns/main.go @@ -18,8 +18,7 @@ func main() { justVersion bool ifaceName string apiPath string - udpPort int - tcpPort int + dnsPort int httpPort int wait int watch bool @@ -30,8 +29,7 @@ func main() { flag.StringVar(&ifaceName, "iface", "", "name of interface to use for multicast") flag.StringVar(&apiPath, "api", "unix:///var/run/docker.sock", "Path to Docker API socket") flag.IntVar(&wait, "wait", 0, "number of seconds to wait for interface to be created and come up") - flag.IntVar(&udpPort, "udpport", 53, "UDP port to listen to DNS requests") - flag.IntVar(&tcpPort, "tcpport", 53, "TCP port to listen to DNS requests") + flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to DNS requests") flag.IntVar(&httpPort, "httpport", 6785, "port to listen to HTTP requests") flag.BoolVar(&watch, "watch", true, "watch the docker socket for container events") flag.BoolVar(&debug, "debug", false, "output debugging info to stderr") @@ -66,7 +64,7 @@ func main() { } go weavedns.ListenHttp(weavedns.LOCAL_DOMAIN, zone, httpPort) - srv, err := weavedns.NewDNSServer(zone, iface, udpPort, tcpPort) + srv, err := weavedns.NewDNSServer(zone, iface, dnsPort) if err != nil { Error.Fatal("Failed to start server", err) }