From 3be560038332a4cbeaecfa5efafcc377ef729946 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Tue, 10 Feb 2015 10:37:17 +0100 Subject: [PATCH 01/12] zettio/weave#362 - reorganize the DNS server code, so it looks like the mDNS server, with a proper class with Start() and so... Listen on TCP on port 53 by default --- nameserver/server.go | 131 ++++++++++++++++++++++++------------------- nameserver/util.go | 17 ++++++ weavedns/main.go | 13 ++++- 3 files changed, 100 insertions(+), 61 deletions(-) create mode 100644 nameserver/util.go diff --git a/nameserver/server.go b/nameserver/server.go index 3975053c..dc303b40 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -12,18 +12,6 @@ const ( UDPBufSize = 4096 // bigger than the default 512 ) -func checkFatal(e error) { - if e != nil { - Error.Fatal(e) - } -} - -func checkWarn(e error) { - if e != nil { - Warning.Println(e) - } -} - func makeDNSFailResponse(r *dns.Msg) *dns.Msg { m := new(dns.Msg) m.SetReply(r) @@ -32,7 +20,72 @@ func makeDNSFailResponse(r *dns.Msg) *dns.Msg { return m } -func queryHandler(lookups []Lookup) dns.HandlerFunc { +type DNSServer struct { + config *dns.ClientConfig + zone Zone + iface *net.Interface + udpPort int + tcpPort int +} + +// Creates a new DNS server +func NewDNSServer(zone Zone, iface *net.Interface, udpPort int, tcpPort int) (*DNSServer, error) { + config, err := dns.ClientConfigFromFile("/etc/resolv.conf") + if err != nil { + return nil, err + } + + return &DNSServer{ + config: config, + zone: zone, + iface: iface, + udpPort: udpPort, + tcpPort: tcpPort, + }, nil +} + +// Start the DNS server +func (s *DNSServer) Start() error { + mdnsClient, err := NewMDNSClient() + checkFatal(err) + + ifaceName := "default interface" + if s.iface != nil { + ifaceName = s.iface.Name + } + Info.Printf("Using mDNS on %s", ifaceName) + err = mdnsClient.Start(s.iface) + checkFatal(err) + + LocalServeMux := dns.NewServeMux() + LocalServeMux.HandleFunc(LOCAL_DOMAIN, s.queryHandler([]Lookup{s.zone, mdnsClient})) + LocalServeMux.HandleFunc(RDNS_DOMAIN, s.rdnsHandler([]Lookup{s.zone, mdnsClient})) + LocalServeMux.HandleFunc(".", s.notUsHandler()) + + mdnsServer, err := NewMDNSServer(s.zone) + checkFatal(err) + + err = mdnsServer.Start(s.iface) + checkFatal(err) + + go func() { + udpAddress := fmt.Sprintf(":%d", s.udpPort) + Info.Printf("Listening for DNS on %s (UDP)", udpAddress) + err = dns.ListenAndServe(udpAddress, "udp", LocalServeMux) + checkFatal(err) + }() + + go func() { + tcpAddress := fmt.Sprintf(":%d", s.tcpPort) + Info.Printf("Listening for DNS on %s (TCP)", tcpAddress) + err = dns.ListenAndServe(tcpAddress, "tcp", LocalServeMux) + checkFatal(err) + }() + + return nil +} + +func (s *DNSServer) queryHandler(lookups []Lookup) dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] Debug.Printf("Query: %+v", q) @@ -51,8 +104,8 @@ func queryHandler(lookups []Lookup) dns.HandlerFunc { } } -func rdnsHandler(config *dns.ClientConfig, lookups []Lookup) dns.HandlerFunc { - fallback := notUsHandler(config) +func (s *DNSServer) rdnsHandler(lookups []Lookup) dns.HandlerFunc { + fallback := s.notUsHandler() return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] Debug.Printf("Reverse query: %+v", q) @@ -72,17 +125,16 @@ func rdnsHandler(config *dns.ClientConfig, lookups []Lookup) dns.HandlerFunc { } } -/* When we receive a request for a name outside of our '.weave.local.' - domain, ask the configured DNS server as a fallback. -*/ -func notUsHandler(config *dns.ClientConfig) dns.HandlerFunc { +// When we receive a request for a name outside of our '.weave.local.' +// domain, ask the configured DNS server as a fallback. +func (s *DNSServer) notUsHandler() dns.HandlerFunc { dnsClient := new(dns.Client) dnsClient.UDPSize = UDPBufSize return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] Debug.Printf("[dns msgid %d] Fallback query: %+v", r.MsgHdr.Id, q) - for _, server := range config.Servers { - reply, _, err := dnsClient.Exchange(r, fmt.Sprintf("%s:%s", server, config.Port)) + for _, server := range s.config.Servers { + reply, _, err := dnsClient.Exchange(r, fmt.Sprintf("%s:%s", server, s.config.Port)) if err != nil { Debug.Printf("[dns msgid %d] Network error trying %s (%s)", r.MsgHdr.Id, server, err) @@ -103,40 +155,3 @@ func notUsHandler(config *dns.ClientConfig) dns.HandlerFunc { w.WriteMsg(makeDNSFailResponse(r)) } } - -func StartServer(zone Zone, iface *net.Interface, dnsPort int, wait int) error { - config, err := dns.ClientConfigFromFile("/etc/resolv.conf") - checkFatal(err) - return startServerWithConfig(config, zone, iface, dnsPort, wait) -} - -func startServerWithConfig(config *dns.ClientConfig, zone Zone, iface *net.Interface, dnsPort int, wait int) error { - mdnsClient, err := NewMDNSClient() - checkFatal(err) - - ifaceName := "default interface" - if iface != nil { - ifaceName = iface.Name - } - Info.Printf("Using mDNS on %s", ifaceName) - err = mdnsClient.Start(iface) - checkFatal(err) - - LocalServeMux := dns.NewServeMux() - LocalServeMux.HandleFunc(LOCAL_DOMAIN, queryHandler([]Lookup{zone, mdnsClient})) - LocalServeMux.HandleFunc(RDNS_DOMAIN, rdnsHandler(config, []Lookup{zone, mdnsClient})) - LocalServeMux.HandleFunc(".", notUsHandler(config)) - - mdnsServer, err := NewMDNSServer(zone) - checkFatal(err) - - err = mdnsServer.Start(iface) - checkFatal(err) - - address := fmt.Sprintf(":%d", dnsPort) - Info.Printf("Listening for DNS on %s", address) - err = dns.ListenAndServe(address, "udp", LocalServeMux) - checkFatal(err) - - return nil -} diff --git a/nameserver/util.go b/nameserver/util.go new file mode 100644 index 00000000..9e25dfc2 --- /dev/null +++ b/nameserver/util.go @@ -0,0 +1,17 @@ +package nameserver + +import ( + . "github.com/zettio/weave/common" +) + +func checkFatal(e error) { + if e != nil { + Error.Fatal(e) + } +} + +func checkWarn(e error) { + if e != nil { + Warning.Println(e) + } +} diff --git a/weavedns/main.go b/weavedns/main.go index 2898b96e..d5053686 100644 --- a/weavedns/main.go +++ b/weavedns/main.go @@ -18,7 +18,8 @@ func main() { justVersion bool ifaceName string apiPath string - dnsPort int + udpPort int + tcpPort int httpPort int wait int watch bool @@ -29,7 +30,8 @@ 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(&dnsPort, "dnsport", 53, "port to listen to dns requests") + 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(&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") @@ -64,7 +66,12 @@ func main() { } go weavedns.ListenHttp(weavedns.LOCAL_DOMAIN, zone, httpPort) - err := weavedns.StartServer(zone, iface, dnsPort, wait) + srv, err := weavedns.NewDNSServer(zone, iface, udpPort, tcpPort) + if err != nil { + Error.Fatal("Failed to start server", err) + } + + srv.Start() if err != nil { Error.Fatal("Failed to start server", err) } From c1f9c8bc104ad18b5d511e60b267a87d48c93225 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Tue, 10 Feb 2015 11:28:53 +0100 Subject: [PATCH 02/12] zettio/weave#362 - new constructor for creating a DNS server with a given config fix unit tests by using the new constructor --- nameserver/server.go | 19 ++++++++++++------- nameserver/server_test.go | 4 +++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/nameserver/server.go b/nameserver/server.go index dc303b40..c81c064c 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -28,13 +28,8 @@ type DNSServer struct { tcpPort int } -// Creates a new DNS server -func NewDNSServer(zone Zone, iface *net.Interface, udpPort int, tcpPort int) (*DNSServer, error) { - config, err := dns.ClientConfigFromFile("/etc/resolv.conf") - if err != nil { - return nil, err - } - +// 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) { return &DNSServer{ config: config, zone: zone, @@ -44,6 +39,16 @@ func NewDNSServer(zone Zone, iface *net.Interface, udpPort int, tcpPort int) (*D }, nil } +// Creates a new DNS server +func NewDNSServer(zone Zone, iface *net.Interface, udpPort int, tcpPort int) (*DNSServer, error) { + config, err := dns.ClientConfigFromFile("/etc/resolv.conf") + if err != nil { + return nil, err + } + + return NewDNSServerWithConfig(config, zone, iface, udpPort, tcpPort) +} + // Start the DNS server func (s *DNSServer) Start() error { mdnsClient, err := NewMDNSClient() diff --git a/nameserver/server_test.go b/nameserver/server_test.go index c42fc4d7..eeb98016 100644 --- a/nameserver/server_test.go +++ b/nameserver/server_test.go @@ -41,7 +41,9 @@ func TestDNSServer(t *testing.T) { wt.AssertNoErr(t, err) config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort} - go startServerWithConfig(config, zone, nil, port, 0) + srv, err := NewDNSServerWithConfig(config, zone, nil, port, port) + wt.AssertNoErr(t, err) + go srv.Start() time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start c := new(dns.Client) From c81769cc9a7a761c66cc2844cc0608e5e8bb7c62 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Tue, 10 Feb 2015 13:05:08 +0100 Subject: [PATCH 03/12] zettio/weave#362 - implement a graceful shutdown --- nameserver/server.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/nameserver/server.go b/nameserver/server.go index c81c064c..33581260 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -25,7 +25,9 @@ type DNSServer struct { zone Zone iface *net.Interface udpPort int + udpSrv *dns.Server tcpPort int + tcpSrv *dns.Server } // Creates a new DNS server with a given config @@ -73,23 +75,37 @@ 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} + go func() { - udpAddress := fmt.Sprintf(":%d", s.udpPort) Info.Printf("Listening for DNS on %s (UDP)", udpAddress) - err = dns.ListenAndServe(udpAddress, "udp", LocalServeMux) + err = s.udpSrv.ListenAndServe() checkFatal(err) }() go func() { - tcpAddress := fmt.Sprintf(":%d", s.tcpPort) Info.Printf("Listening for DNS on %s (TCP)", tcpAddress) - err = dns.ListenAndServe(tcpAddress, "tcp", LocalServeMux) + err = s.tcpSrv.ListenAndServe() checkFatal(err) }() return nil } +// Perform a graceful shutdown +func (s *DNSServer) Stop() error { + if err := s.tcpSrv.Shutdown(); err != nil { + return err + } + if err := s.udpSrv.Shutdown(); err != nil { + return err + } + return nil +} + func (s *DNSServer) queryHandler(lookups []Lookup) dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] From bf88ed8d9653024bb5cfa790c550825c20840827 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Wed, 11 Feb 2015 11:46:18 +0100 Subject: [PATCH 04/12] zettio/weave#362 - unit test for TCP requests to our DNS server --- nameserver/server_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/nameserver/server_test.go b/nameserver/server_test.go index eeb98016..a5cb356d 100644 --- a/nameserver/server_test.go +++ b/nameserver/server_test.go @@ -46,11 +46,16 @@ func TestDNSServer(t *testing.T) { 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 + r, _, err := c.Exchange(m, dnsAddr) wt.AssertNoErr(t, err) wt.AssertStatus(t, r.Rcode, dns.RcodeSuccess, "DNS response code") @@ -58,6 +63,16 @@ 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) From 9c7ffd9b8129874ed2986eb51dd6683438ad7f80 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Thu, 12 Feb 2015 12:51:34 +0100 Subject: [PATCH 05/12] 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) } From ef72d65aa4b796751b033ca6beceb0b6c6be990b Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Fri, 13 Feb 2015 12:52:28 +0100 Subject: [PATCH 06/12] zettio/weave#362 - use for fallback queries the same protocol clients have used for querying us. --- nameserver/server.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/nameserver/server.go b/nameserver/server.go index 9a1726e5..fd15bb3d 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -61,10 +61,15 @@ func (s *DNSServer) Start() error { err = mdnsClient.Start(s.iface) checkFatal(err) - LocalServeMux := dns.NewServeMux() - LocalServeMux.HandleFunc(LOCAL_DOMAIN, s.queryHandler([]Lookup{s.zone, mdnsClient})) - LocalServeMux.HandleFunc(RDNS_DOMAIN, s.rdnsHandler([]Lookup{s.zone, mdnsClient})) - LocalServeMux.HandleFunc(".", s.notUsHandler()) + // create two DNS request multiplexerers, depending on the protocol used by clients + // (we use the same protocol for asking upstream servers) + mux := func(client *dns.Client) *dns.ServeMux { + m := dns.NewServeMux() + m.HandleFunc(LOCAL_DOMAIN, s.queryHandler([]Lookup{s.zone, mdnsClient})) + m.HandleFunc(RDNS_DOMAIN, s.rdnsHandler([]Lookup{s.zone, mdnsClient}, client)) + m.HandleFunc(".", s.notUsHandler(client)) + return m + } mdnsServer, err := NewMDNSServer(s.zone) checkFatal(err) @@ -73,8 +78,8 @@ func (s *DNSServer) Start() error { checkFatal(err) 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} + s.udpSrv = &dns.Server{Addr: address, Net: "udp", Handler: mux(&dns.Client{Net: "udp", UDPSize: UDPBufSize})} + s.tcpSrv = &dns.Server{Addr: address, Net: "tcp", Handler: mux(&dns.Client{Net: "tcp"})} go func() { Info.Printf("Listening for DNS on %s (UDP)", address) @@ -121,8 +126,8 @@ func (s *DNSServer) queryHandler(lookups []Lookup) dns.HandlerFunc { } } -func (s *DNSServer) rdnsHandler(lookups []Lookup) dns.HandlerFunc { - fallback := s.notUsHandler() +func (s *DNSServer) rdnsHandler(lookups []Lookup, client *dns.Client) dns.HandlerFunc { + fallback := s.notUsHandler(client) return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] Debug.Printf("Reverse query: %+v", q) @@ -144,9 +149,7 @@ func (s *DNSServer) rdnsHandler(lookups []Lookup) dns.HandlerFunc { // When we receive a request for a name outside of our '.weave.local.' // domain, ask the configured DNS server as a fallback. -func (s *DNSServer) notUsHandler() dns.HandlerFunc { - dnsClient := new(dns.Client) - dnsClient.UDPSize = UDPBufSize +func (s *DNSServer) notUsHandler(dnsClient *dns.Client) dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] Debug.Printf("[dns msgid %d] Fallback query: %+v", r.MsgHdr.Id, q) From 5dd475b2e5588725221e92c02c97a807d176af14 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Thu, 19 Feb 2015 21:56:47 +0100 Subject: [PATCH 07/12] zettio/weave#362 - fix DNSServer initialization removed a couple of goroutines spawns... --- nameserver/server.go | 11 +---------- nameserver/server_test.go | 12 ++++++------ weavedns/main.go | 10 ++++++---- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/nameserver/server.go b/nameserver/server.go index fd15bb3d..984e00fb 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -30,22 +30,13 @@ type DNSServer struct { } // Creates a new DNS server with a given config -func NewDNSServerWithConfig(config *dns.ClientConfig, zone Zone, iface *net.Interface, port int) (*DNSServer, error) { +func NewDNSServer(config *dns.ClientConfig, zone Zone, iface *net.Interface, port int) *DNSServer { return &DNSServer{ config: config, zone: zone, iface: iface, port: port, - }, nil -} - -// Creates a new DNS server -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, port) } // Start the DNS server diff --git a/nameserver/server_test.go b/nameserver/server_test.go index f117b3ff..0f54bc63 100644 --- a/nameserver/server_test.go +++ b/nameserver/server_test.go @@ -41,10 +41,10 @@ func TestUDPDNSServer(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) - wt.AssertNoErr(t, err) + srv := NewDNSServer(config, zone, nil, port) defer srv.Stop() - go srv.Start() + err = srv.Start() + wt.AssertNoErr(t, err) time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start c := new(dns.Client) @@ -147,10 +147,10 @@ func TestTCPDNSServer(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) - wt.AssertNoErr(t, err) + srv := NewDNSServer(config, zone, nil, port) defer srv.Stop() - go srv.Start() + err = srv.Start() + wt.AssertNoErr(t, err) time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start // Create a regular UDP client and a TCP client diff --git a/weavedns/main.go b/weavedns/main.go index ab9f5686..aa84511c 100644 --- a/weavedns/main.go +++ b/weavedns/main.go @@ -9,6 +9,7 @@ import ( "io" "net" "os" + "github.com/miekg/dns" ) var version = "(unreleased version)" @@ -63,13 +64,14 @@ func main() { } } - go weavedns.ListenHttp(weavedns.LOCAL_DOMAIN, zone, httpPort) - srv, err := weavedns.NewDNSServer(zone, iface, dnsPort) + config, err := dns.ClientConfigFromFile("/etc/resolv.conf") if err != nil { - Error.Fatal("Failed to start server", err) + Error.Fatal("Failed obtain DNS client configuration", err) } - srv.Start() + go weavedns.ListenHttp(weavedns.LOCAL_DOMAIN, zone, httpPort) + srv := weavedns.NewDNSServer(config, zone, iface, dnsPort) + err := srv.Start() if err != nil { Error.Fatal("Failed to start server", err) } From 3de924fa2f5d300ed9d4c04feeea182d89a036a4 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Thu, 19 Feb 2015 22:04:42 +0100 Subject: [PATCH 08/12] zettio/weave#362 - moved `Check*()` to `common` --- common/errors.go | 13 +++++++++++++ nameserver/mdns_client_test.go | 7 ++++--- nameserver/server.go | 12 ++++++------ nameserver/util.go | 17 ----------------- 4 files changed, 23 insertions(+), 26 deletions(-) create mode 100644 common/errors.go delete mode 100644 nameserver/util.go diff --git a/common/errors.go b/common/errors.go new file mode 100644 index 00000000..23072055 --- /dev/null +++ b/common/errors.go @@ -0,0 +1,13 @@ +package common + +func CheckFatal(e error) { + if e != nil { + Error.Fatal(e) + } +} + +func CheckWarn(e error) { + if e != nil { + Warning.Println(e) + } +} diff --git a/nameserver/mdns_client_test.go b/nameserver/mdns_client_test.go index aa9ad6a5..c21c1f18 100644 --- a/nameserver/mdns_client_test.go +++ b/nameserver/mdns_client_test.go @@ -2,6 +2,7 @@ package nameserver import ( "github.com/miekg/dns" + . "github.com/zettio/weave/common" wt "github.com/zettio/weave/testing" "log" "net" @@ -20,18 +21,18 @@ func minimalServer(w dns.ResponseWriter, req *dns.Msg) { sendAnswer := func(m *dns.Msg) { buf, err := m.Pack() - checkFatal(err) + CheckFatal(err) if buf == nil { log.Fatal("Nil buffer") } //log.Println("minimalServer sending:", buf) // This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work sendconn, err := net.DialUDP("udp", nil, ipv4Addr) - checkFatal(err) + CheckFatal(err) _, err = sendconn.Write(buf) sendconn.Close() - checkFatal(err) + CheckFatal(err) } //log.Println("minimalServer received:", req) diff --git a/nameserver/server.go b/nameserver/server.go index 984e00fb..1291a06d 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -42,7 +42,7 @@ func NewDNSServer(config *dns.ClientConfig, zone Zone, iface *net.Interface, por // Start the DNS server func (s *DNSServer) Start() error { mdnsClient, err := NewMDNSClient() - checkFatal(err) + CheckFatal(err) ifaceName := "default interface" if s.iface != nil { @@ -50,7 +50,7 @@ func (s *DNSServer) Start() error { } Info.Printf("Using mDNS on %s", ifaceName) err = mdnsClient.Start(s.iface) - checkFatal(err) + CheckFatal(err) // create two DNS request multiplexerers, depending on the protocol used by clients // (we use the same protocol for asking upstream servers) @@ -63,10 +63,10 @@ func (s *DNSServer) Start() error { } mdnsServer, err := NewMDNSServer(s.zone) - checkFatal(err) + CheckFatal(err) err = mdnsServer.Start(s.iface) - checkFatal(err) + CheckFatal(err) address := fmt.Sprintf(":%d", s.port) s.udpSrv = &dns.Server{Addr: address, Net: "udp", Handler: mux(&dns.Client{Net: "udp", UDPSize: UDPBufSize})} @@ -75,13 +75,13 @@ func (s *DNSServer) Start() error { go func() { Info.Printf("Listening for DNS on %s (UDP)", address) err = s.udpSrv.ListenAndServe() - checkFatal(err) + CheckFatal(err) }() go func() { Info.Printf("Listening for DNS on %s (TCP)", address) err = s.tcpSrv.ListenAndServe() - checkFatal(err) + CheckFatal(err) }() return nil diff --git a/nameserver/util.go b/nameserver/util.go deleted file mode 100644 index 9e25dfc2..00000000 --- a/nameserver/util.go +++ /dev/null @@ -1,17 +0,0 @@ -package nameserver - -import ( - . "github.com/zettio/weave/common" -) - -func checkFatal(e error) { - if e != nil { - Error.Fatal(e) - } -} - -func checkWarn(e error) { - if e != nil { - Warning.Println(e) - } -} From f8b3c86498d24da5c13b5aef4a6cf252fa2a0973 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Fri, 20 Feb 2015 13:07:30 +0100 Subject: [PATCH 09/12] zettio/weave#362 - properly test the TCP-retry mechanism by creating fallback servers that return truncated responses with UDP but not with TCP --- nameserver/server_test.go | 173 ++++++++++++++++++++++++-------------- testing/util.go | 18 ++++ 2 files changed, 128 insertions(+), 63 deletions(-) diff --git a/nameserver/server_test.go b/nameserver/server_test.go index 0f54bc63..f990ab3d 100644 --- a/nameserver/server_test.go +++ b/nameserver/server_test.go @@ -1,6 +1,7 @@ package nameserver import ( + "encoding/binary" "fmt" "github.com/miekg/dns" . "github.com/zettio/weave/common" @@ -32,8 +33,32 @@ func TestUDPDNSServer(t *testing.T) { ip, _, _ := net.ParseCIDR(testCIDR1) zone.AddRecord(containerID, successTestName, ip) + fallbackHandler := func(w dns.ResponseWriter, req *dns.Msg) { + m := new(dns.Msg) + m.SetReply(req) + if len(req.Question) == 1 { + q := req.Question[0] + if q.Name == "weave.works." && q.Qtype == dns.TypeMX { + m.Answer = make([]dns.RR, 1) + m.Answer[0] = &dns.MX{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0}, Mx: "mail.weave.works."} + } else if q.Name == "weave.works." && q.Qtype == dns.TypeANY { + const N = 10 + m.Extra = make([]dns.RR, N) + for i, _ := range m.Extra { + m.Extra[i] = &dns.TXT{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}, Txt: []string{"Lots and lots and lots and lots and lots and lots and lots and lots and lots of data"}} + } + } else if q.Name == testRDNSnonlocal && q.Qtype == dns.TypePTR { + m.Answer = make([]dns.RR, 1) + m.Answer[0] = &dns.PTR{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 0}, Ptr: "ns1.google.com."} + } else if q.Name == testRDNSfail && q.Qtype == dns.TypePTR { + m.Rcode = dns.RcodeNameError + } + } + w.WriteMsg(m) + } + // Run another DNS server for fallback - s, fallbackAddr, err := RunLocalUDPServer("127.0.0.1:0") + s, fallbackAddr, err := RunLocalUDPServer(t, "127.0.0.1:0", fallbackHandler) wt.AssertNoErr(t, err) defer s.Shutdown() @@ -118,34 +143,52 @@ func TestUDPDNSServer(t *testing.T) { func TestTCPDNSServer(t *testing.T) { const ( - port = 17625 - localTestName = "test1.weave.local." - testAddr1 = "10.0.2.1" + port = 17625 + numAnswers = 512 + nonLocalName = "weave.works." ) 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) - } + var addrs []net.IP + bs := make([]byte, 4) + for i := 0; i < numAnswers; i++ { + binary.LittleEndian.PutUint32(bs, uint32(i)) + addrs = append(addrs, net.IPv4(bs[0], bs[1], bs[2], bs[3])) + } + + // handler for the fallback server: it will just return a very long response + fallbackUDPHandler := func(w dns.ResponseWriter, req *dns.Msg) { + t.Logf("Fallback UDP server got asked: returning %d answers", numAnswers) + q := req.Question[0] + m := makeAddressReply(req, &q, addrs) + 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) } // Run another DNS server for fallback - s, fallbackAddr, err := RunLocalUDPServer("127.0.0.1:0") + us, fallbackUdpAddr, err := RunLocalUDPServer(t, "127.0.0.1:0", fallbackUDPHandler) wt.AssertNoErr(t, err) - defer s.Shutdown() + defer us.Shutdown() - _, fallbackPort, err := net.SplitHostPort(fallbackAddr) + _, fallbackPort, err := net.SplitHostPort(fallbackUdpAddr) wt.AssertNoErr(t, err) + // start the TCP server 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 := NewDNSServer(config, zone, nil, port) defer srv.Stop() @@ -153,74 +196,78 @@ func TestTCPDNSServer(t *testing.T) { wt.AssertNoErr(t, err) time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start - // Create a regular UDP client and a TCP client + t.Logf("Creating a UDP and a TCP client") uc := new(dns.Client) uc.UDPSize = UDPBufSize tc := new(dns.Client) tc.Net = "tcp" + t.Logf("Creating DNS query message") m := new(dns.Msg) - m.SetQuestion(localTestName, dns.TypeA) - m.RecursionDesired = false + m.RecursionDesired = true + m.SetQuestion(nonLocalName, dns.TypeA) - Debug.Printf("Using UDP for quering about %s", localTestName) - r, _, err := uc.Exchange(m, dnsAddr) + 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) - 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") - } + wt.AssertTrue(t, r.MsgHdr.Truncated, "DNS truncated reponse flag") + wt.AssertNotEqualInt(t, len(r.Answer), numAnswers, "number of answers (UDP)") - Debug.Println("Using TCP for the same query") - tr, _, err := tc.Exchange(m, dnsAddr) + t.Logf("Checking the WeaveDNS server at %s returns a truncated reponse with UDP", dnsAddr) + r, _, err = uc.Exchange(m, dnsAddr) + t.Logf("Got response from WeaveDNS (UDP) with %d answers", len(r.Answer)) + t.Logf("Response:\n%+v\n", r) 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... + 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("Got response from WeaveDNS (TCP) with %d answers", len(r.Answer)) + t.Logf("Response:\n%+v\n", r) + wt.AssertNoErr(t, err) + wt.AssertFalse(t, r.MsgHdr.Truncated, "DNS truncated response flag") + wt.AssertEqualInt(t, len(r.Answer), numAnswers, "number of answers (TCP)") } -func fallbackHandler(w dns.ResponseWriter, req *dns.Msg) { - m := new(dns.Msg) - m.SetReply(req) - if len(req.Question) == 1 { - q := req.Question[0] - if q.Name == "weave.works." && q.Qtype == dns.TypeMX { - m.Answer = make([]dns.RR, 1) - m.Answer[0] = &dns.MX{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0}, Mx: "mail.weave.works."} - } else if q.Name == "weave.works." && q.Qtype == dns.TypeANY { - const N = 10 - m.Extra = make([]dns.RR, N) - for i, _ := range m.Extra { - m.Extra[i] = &dns.TXT{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}, Txt: []string{"Lots and lots and lots and lots and lots and lots and lots and lots and lots of data"}} - } - } else if q.Name == testRDNSnonlocal && q.Qtype == dns.TypePTR { - m.Answer = make([]dns.RR, 1) - m.Answer[0] = &dns.PTR{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: 0}, Ptr: "ns1.google.com."} - } else if q.Name == testRDNSfail && q.Qtype == dns.TypePTR { - m.Rcode = dns.RcodeNameError - } - } - w.WriteMsg(m) -} - -func RunLocalUDPServer(laddr string) (*dns.Server, string, error) { +func RunLocalUDPServer(t *testing.T, laddr string, handler dns.HandlerFunc) (*dns.Server, string, error) { + t.Logf("Starting fallback UDP server at %s", laddr) pc, err := net.ListenPacket("udp", laddr) if err != nil { return nil, "", err } - server := &dns.Server{PacketConn: pc, Handler: dns.HandlerFunc(fallbackHandler)} + server := &dns.Server{PacketConn: pc, Handler: handler} go func() { server.ActivateAndServe() pc.Close() }() + t.Logf("Fallback UDP server listening at %s", pc.LocalAddr()) return server, pc.LocalAddr().String(), nil } + +func RunLocalTCPServer(t *testing.T, laddr string, handler dns.HandlerFunc) (*dns.Server, string, error) { + t.Logf("Starting fallback TCP server at %s", laddr) + laddrTcp, err := net.ResolveTCPAddr("tcp", laddr) + if err != nil { + return nil, "", err + } + + l, err := net.ListenTCP("tcp", laddrTcp) + if err != nil { + return nil, "", err + } + server := &dns.Server{Listener: l, Handler: handler} + + go func() { + server.ActivateAndServe() + l.Close() + }() + + + t.Logf("Fallback TCP server listening at %s", l.Addr().String()) + return server, l.Addr().String(), nil +} diff --git a/testing/util.go b/testing/util.go index 2222fd85..7517674e 100644 --- a/testing/util.go +++ b/testing/util.go @@ -7,6 +7,18 @@ import ( "time" ) +func AssertTrue(t *testing.T, cond bool, desc string) { + if !cond { + Fatalf(t, "Expected %s to be true", desc) + } +} + +func AssertFalse(t *testing.T, cond bool, desc string) { + if cond { + Fatalf(t, "Expected %s to be false", desc) + } +} + func AssertNoErr(t *testing.T, err error) { if err != nil { Fatalf(t, "Unexpected error: %s", err) @@ -25,6 +37,12 @@ func AssertEqualInt(t *testing.T, got, wanted int, desc string) { } } +func AssertNotEqualInt(t *testing.T, got, wanted int, desc string) { + if got == wanted { + Fatalf(t, "Expected %s %d to be different to %d", desc, wanted, got) + } +} + func AssertEqualString(t *testing.T, got, wanted string, desc string) { if got != wanted { Fatalf(t, "Expected %s '%s' but got '%s'", desc, wanted, got) From ea1c3e8219dcdd2e907e45090b368405e6b7bf22 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Fri, 20 Feb 2015 13:19:55 +0100 Subject: [PATCH 10/12] zettio/weave#362 - fix: `=` instead of `:=` --- weavedns/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weavedns/main.go b/weavedns/main.go index aa84511c..035515c2 100644 --- a/weavedns/main.go +++ b/weavedns/main.go @@ -71,7 +71,7 @@ func main() { go weavedns.ListenHttp(weavedns.LOCAL_DOMAIN, zone, httpPort) srv := weavedns.NewDNSServer(config, zone, iface, dnsPort) - err := srv.Start() + err = srv.Start() if err != nil { Error.Fatal("Failed to start server", err) } From 419138ec6a75a43ba1e08148f8419f8d47374934 Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Fri, 20 Feb 2015 16:40:34 +0100 Subject: [PATCH 11/12] zettio/weave#362 - use a WaitGroup for the TCP/UDP goroutines: otherwise, WeaveDNS server just quits --- nameserver/server.go | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/nameserver/server.go b/nameserver/server.go index 1291a06d..c2a680e8 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -5,6 +5,7 @@ import ( "github.com/miekg/dns" . "github.com/zettio/weave/common" "net" + "sync" ) const ( @@ -41,6 +42,8 @@ func NewDNSServer(config *dns.ClientConfig, zone Zone, iface *net.Interface, por // Start the DNS server func (s *DNSServer) Start() error { + Info.Printf("WeaveDNS server starting...") + mdnsClient, err := NewMDNSClient() CheckFatal(err) @@ -72,18 +75,31 @@ func (s *DNSServer) Start() error { s.udpSrv = &dns.Server{Addr: address, Net: "udp", Handler: mux(&dns.Client{Net: "udp", UDPSize: UDPBufSize})} s.tcpSrv = &dns.Server{Addr: address, Net: "tcp", Handler: mux(&dns.Client{Net: "tcp"})} + wg := new(sync.WaitGroup) + wg.Add(2) + go func() { - Info.Printf("Listening for DNS on %s (UDP)", address) + defer wg.Done() + + Debug.Printf("Listening for DNS on %s (UDP)", address) err = s.udpSrv.ListenAndServe() CheckFatal(err) + Debug.Printf("DNS UDP server exiting...") }() go func() { - Info.Printf("Listening for DNS on %s (TCP)", address) + defer wg.Done() + + Debug.Printf("Listening for DNS on %s (TCP)", address) err = s.tcpSrv.ListenAndServe() CheckFatal(err) + Debug.Printf("DNS TCP server exiting...") }() + // Waiting for all goroutines to finish (otherwise they die as main routine dies) + wg.Wait() + + Info.Printf("WeaveDNS server exiting...") return nil } From c57502368176d972f5e519250881f6980e08494e Mon Sep 17 00:00:00 2001 From: Alvaro Saurin Date: Mon, 23 Feb 2015 11:22:28 +0100 Subject: [PATCH 12/12] zettio/weave#362 - command line arg for using a specific fallback DNS server --- nameserver/server_test.go | 7 ++----- weavedns/main.go | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/nameserver/server_test.go b/nameserver/server_test.go index f990ab3d..bde9d722 100644 --- a/nameserver/server_test.go +++ b/nameserver/server_test.go @@ -68,8 +68,7 @@ func TestUDPDNSServer(t *testing.T) { config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort} srv := NewDNSServer(config, zone, nil, port) defer srv.Stop() - err = srv.Start() - wt.AssertNoErr(t, err) + go srv.Start() time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start c := new(dns.Client) @@ -192,8 +191,7 @@ func TestTCPDNSServer(t *testing.T) { config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort} srv := NewDNSServer(config, zone, nil, port) defer srv.Stop() - err = srv.Start() - wt.AssertNoErr(t, err) + go srv.Start() time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start t.Logf("Creating a UDP and a TCP client") @@ -267,7 +265,6 @@ func RunLocalTCPServer(t *testing.T, laddr string, handler dns.HandlerFunc) (*dn l.Close() }() - t.Logf("Fallback TCP server listening at %s", l.Addr().String()) return server, l.Addr().String(), nil } diff --git a/weavedns/main.go b/weavedns/main.go index 035515c2..1c788b77 100644 --- a/weavedns/main.go +++ b/weavedns/main.go @@ -3,13 +3,13 @@ package main import ( "flag" "fmt" + "github.com/miekg/dns" . "github.com/zettio/weave/common" weavedns "github.com/zettio/weave/nameserver" weavenet "github.com/zettio/weave/net" "io" "net" "os" - "github.com/miekg/dns" ) var version = "(unreleased version)" @@ -19,11 +19,13 @@ func main() { justVersion bool ifaceName string apiPath string + fallback string dnsPort int httpPort int wait int watch bool debug bool + err error ) flag.BoolVar(&justVersion, "version", false, "print version and exit") @@ -32,6 +34,7 @@ func main() { flag.IntVar(&wait, "wait", 0, "number of seconds to wait for interface to be created and come up") flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to DNS requests") flag.IntVar(&httpPort, "httpport", 6785, "port to listen to HTTP requests") + flag.StringVar(&fallback, "fallback", "", "Fallback server and port (ie, '8.8.8.8:53')") flag.BoolVar(&watch, "watch", true, "watch the docker socket for container events") flag.BoolVar(&debug, "debug", false, "output debugging info to stderr") flag.Parse() @@ -64,9 +67,19 @@ func main() { } } - config, err := dns.ClientConfigFromFile("/etc/resolv.conf") - if err != nil { - Error.Fatal("Failed obtain DNS client configuration", err) + var config *dns.ClientConfig + if len(fallback) > 0 { + fallbackHost, fallbackPort, err := net.SplitHostPort(fallback) + if err != nil { + Error.Fatal("Fould not parse fallback host and port", err) + } + config = &dns.ClientConfig{Servers: []string{fallbackHost}, Port: fallbackPort} + Debug.Printf("DNS fallback at %s:%s", fallbackHost, fallbackPort) + } else { + config, err = dns.ClientConfigFromFile("/etc/resolv.conf") + if err != nil { + Error.Fatal("Failed obtain DNS client configuration", err) + } } go weavedns.ListenHttp(weavedns.LOCAL_DOMAIN, zone, httpPort)