diff --git a/nameserver/mdns_server.go b/nameserver/mdns_server.go index a67a4b3e..18ddb82d 100644 --- a/nameserver/mdns_server.go +++ b/nameserver/mdns_server.go @@ -35,7 +35,7 @@ func (s *MDNSServer) addrIsLocal(testaddr net.Addr) bool { return false } -func (s *MDNSServer) Start(ifi *net.Interface) error { +func (s *MDNSServer) Start(ifi *net.Interface, localDomain string) error { conn, err := LinkLocalMulticastListener(ifi) if err != nil { return err @@ -69,7 +69,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { }) mux := dns.NewServeMux() - mux.HandleFunc(LOCAL_DOMAIN, handleLocal) + mux.HandleFunc(localDomain, handleLocal) mux.HandleFunc(RDNS_DOMAIN, handleReverse) go dns.ActivateAndServe(nil, conn, mux) diff --git a/nameserver/mdns_server_test.go b/nameserver/mdns_server_test.go index 2ab473a9..bd7da6dc 100644 --- a/nameserver/mdns_server_test.go +++ b/nameserver/mdns_server_test.go @@ -44,7 +44,7 @@ func TestServerSimpleQuery(t *testing.T) { mdnsServer, err := NewMDNSServer(zone) wt.AssertNoErr(t, err) - err = mdnsServer.Start(nil) + err = mdnsServer.Start(nil, DEFAULT_LOCAL_DOMAIN) wt.AssertNoErr(t, err) var receivedAddr net.IP diff --git a/nameserver/server.go b/nameserver/server.go index c2a680e8..44e3102c 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -9,8 +9,11 @@ import ( ) const ( - LOCAL_DOMAIN = "weave.local." - UDPBufSize = 4096 // bigger than the default 512 + DEFAULT_LOCAL_DOMAIN = "weave.local." // The default name used for the local domain + DEFAULT_SERVER_PORT = 53 // The default server port + DEFAULT_CLI_CFG_FILE = "/etc/resolv.conf" // default "resolv.conf" file to try to load + DEFAULT_UDP_BUFLEN = 4096 // bigger than the default 512 + DEFAULT_IFACE_NAME = "default interface" ) func makeDNSFailResponse(r *dns.Msg) *dns.Msg { @@ -21,59 +24,95 @@ func makeDNSFailResponse(r *dns.Msg) *dns.Msg { return m } -type DNSServer struct { - config *dns.ClientConfig - zone Zone - iface *net.Interface - port int - udpSrv *dns.Server - tcpSrv *dns.Server +type DNSServerConfig struct { + // (Optional) client config file for resolving upstream servers + UpstreamCfgFile string + // (Optional) DNS client config for the fallback server(s) + UpstreamCfg *dns.ClientConfig + // (Optional) port number (for TCP and UDP) + Port int + // (Optional) local domain (ie, "weave.local.") + LocalDomain string } -// Creates a new DNS server with a given config -func NewDNSServer(config *dns.ClientConfig, zone Zone, iface *net.Interface, port int) *DNSServer { - return &DNSServer{ - config: config, +type DNSServer struct { + udpSrv *dns.Server + tcpSrv *dns.Server + mdnsCli *MDNSClient + mdnsSrv *MDNSServer + zone Zone + iface *net.Interface + upstream *dns.ClientConfig + + Domain string // the local domain + IfaceName string // the interface where mDNS is working on + ListenAddr string // the address the server is listening at +} + +// Creates a new DNS server +func NewDNSServer(config DNSServerConfig, zone Zone, iface *net.Interface) (s *DNSServer, err error) { + s = &DNSServer{ zone: zone, iface: iface, - port: port, + + Domain: DEFAULT_LOCAL_DOMAIN, + IfaceName: DEFAULT_IFACE_NAME, + ListenAddr: fmt.Sprintf(":%d", DEFAULT_SERVER_PORT), } -} -// Start the DNS server -func (s *DNSServer) Start() error { - Info.Printf("WeaveDNS server starting...") - - mdnsClient, err := NewMDNSClient() - CheckFatal(err) - - ifaceName := "default interface" - if s.iface != nil { - ifaceName = s.iface.Name + // fill empty parameters with defaults... + if config.Port != 0 { + s.ListenAddr = fmt.Sprintf(":%d", config.Port) + } + if len(config.LocalDomain) > 0 { + s.Domain = config.LocalDomain + } + if config.UpstreamCfg != nil { + s.upstream = config.UpstreamCfg + } else { + cfgFile := DEFAULT_CLI_CFG_FILE + if len(config.UpstreamCfgFile) > 0 { + cfgFile = config.UpstreamCfgFile + } + if s.upstream, err = dns.ClientConfigFromFile(cfgFile); err != nil { + return nil, err + } + } + s.mdnsCli, err = NewMDNSClient() + if err != nil { + return + } + s.mdnsSrv, err = NewMDNSServer(s.zone) + if err != nil { + return + } + if iface != nil { + s.IfaceName = iface.Name } - Info.Printf("Using mDNS on %s", ifaceName) - err = mdnsClient.Start(s.iface) - CheckFatal(err) // 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.Domain, s.queryHandler([]Lookup{s.zone, s.mdnsCli})) + m.HandleFunc(RDNS_DOMAIN, s.rdnsHandler([]Lookup{s.zone, s.mdnsCli}, client)) m.HandleFunc(".", s.notUsHandler(client)) return m } - mdnsServer, err := NewMDNSServer(s.zone) - CheckFatal(err) + s.udpSrv = &dns.Server{Addr: s.ListenAddr, Net: "udp", Handler: mux(&dns.Client{Net: "udp", UDPSize: DEFAULT_UDP_BUFLEN})} + s.tcpSrv = &dns.Server{Addr: s.ListenAddr, Net: "tcp", Handler: mux(&dns.Client{Net: "tcp"})} - err = mdnsServer.Start(s.iface) - CheckFatal(err) + return +} - address := fmt.Sprintf(":%d", s.port) - 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"})} +// Start the DNS server +func (s *DNSServer) Start() error { + Info.Printf("Using mDNS on %s", s.IfaceName) + err := s.mdnsCli.Start(s.iface) + CheckFatal(err) + err = s.mdnsSrv.Start(s.iface, s.Domain) + CheckFatal(err) wg := new(sync.WaitGroup) wg.Add(2) @@ -81,7 +120,7 @@ func (s *DNSServer) Start() error { go func() { defer wg.Done() - Debug.Printf("Listening for DNS on %s (UDP)", address) + Debug.Printf("Listening for DNS on %s (UDP)", s.ListenAddr) err = s.udpSrv.ListenAndServe() CheckFatal(err) Debug.Printf("DNS UDP server exiting...") @@ -90,7 +129,7 @@ func (s *DNSServer) Start() error { go func() { defer wg.Done() - Debug.Printf("Listening for DNS on %s (TCP)", address) + Debug.Printf("Listening for DNS on %s (TCP)", s.ListenAddr) err = s.tcpSrv.ListenAndServe() CheckFatal(err) Debug.Printf("DNS TCP server exiting...") @@ -111,6 +150,7 @@ func (s *DNSServer) Stop() error { if err := s.udpSrv.Shutdown(); err != nil { return err } + // TODO: shutdown the mDNS client/server return nil } @@ -160,8 +200,8 @@ 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) - for _, server := range s.config.Servers { - reply, _, err := dnsClient.Exchange(r, fmt.Sprintf("%s:%s", server, s.config.Port)) + for _, server := range s.upstream.Servers { + reply, _, err := dnsClient.Exchange(r, fmt.Sprintf("%s:%s", server, s.upstream.Port)) if err != nil { Debug.Printf("[dns msgid %d] Network error trying %s (%s)", r.MsgHdr.Id, server, err) diff --git a/nameserver/server_test.go b/nameserver/server_test.go index bde9d722..bc376ec1 100644 --- a/nameserver/server_test.go +++ b/nameserver/server_test.go @@ -25,7 +25,7 @@ func TestUDPDNSServer(t *testing.T) { nonLocalName = "weave.works." testAddr1 = "10.0.2.1" ) - dnsAddr := fmt.Sprintf("localhost:%d", port) + dnsAddr := fmt.Sprintf("127.0.0.1:%d", port) testCIDR1 := testAddr1 + "/24" InitDefaultLogging(true) @@ -66,13 +66,14 @@ func TestUDPDNSServer(t *testing.T) { wt.AssertNoErr(t, err) config := &dns.ClientConfig{Servers: []string{"127.0.0.1"}, Port: fallbackPort} - srv := NewDNSServer(config, zone, nil, port) + srv, err := NewDNSServer(DNSServerConfig{UpstreamCfg: config, Port: port}, zone, nil) + wt.AssertNoErr(t, err) defer srv.Stop() go srv.Start() time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start c := new(dns.Client) - c.UDPSize = UDPBufSize + c.UDPSize = DEFAULT_UDP_BUFLEN m := new(dns.Msg) m.SetQuestion(successTestName, dns.TypeA) m.RecursionDesired = true @@ -189,14 +190,15 @@ func TestTCPDNSServer(t *testing.T) { 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) + srv, err := NewDNSServer(DNSServerConfig{UpstreamCfg: config, Port: port}, 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 = UDPBufSize + uc.UDPSize = DEFAULT_UDP_BUFLEN tc := new(dns.Client) tc.Net = "tcp" diff --git a/test/220_dns_custom_test.sh b/test/220_dns_custom_test.sh new file mode 100755 index 00000000..29f27dfd --- /dev/null +++ b/test/220_dns_custom_test.sh @@ -0,0 +1,25 @@ +#! /bin/bash + +. ./config.sh + +C1=10.2.56.34 +C2=10.2.54.91 + +start_suite "Resolve names in custom domain" + +run_on $HOST1 sudo $WEAVE stop || true +run_on $HOST1 sudo $WEAVE stop-dns || true +run_on $HOST1 sudo $WEAVE launch-dns 10.0.0.2/8 --localDomain foo.bar. + +docker_on $HOST1 rm -f c1 c2 || true + +run_on $HOST1 sudo $WEAVE run $C2/24 -t --name=c2 -h seetwo.foo.bar ubuntu +run_on $HOST1 sudo $WEAVE run --with-dns $C1/24 -t --name=c1 aanand/docker-dnsutils /bin/sh + +ok=$(docker -H tcp://$HOST1:2375 exec -i c1 sh -c "dig +short seetwo.foo.bar.") +assert "echo $ok" "$C2" + +ok=$(docker -H tcp://$HOST1:2375 exec -i c1 sh -c "dig +short -x $C2") +assert "echo $ok" "seetwo.foo.bar." + +end_suite diff --git a/weavedns/main.go b/weavedns/main.go index 1c788b77..235aa71d 100644 --- a/weavedns/main.go +++ b/weavedns/main.go @@ -19,6 +19,7 @@ func main() { justVersion bool ifaceName string apiPath string + localDomain string fallback string dnsPort int httpPort int @@ -31,10 +32,11 @@ func main() { flag.BoolVar(&justVersion, "version", false, "print version and exit") 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.StringVar(&localDomain, "localDomain", "", "local domain (ie, 'weave.local.')") 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.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() @@ -67,25 +69,27 @@ func main() { } } - var config *dns.ClientConfig + srvConfig := weavedns.DNSServerConfig{ + Port: dnsPort, + LocalDomain: localDomain, + } + 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} + srvConfig.UpstreamCfg = &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) - srv := weavedns.NewDNSServer(config, zone, iface, dnsPort) + + go weavedns.ListenHttp(localDomain, zone, httpPort) + srv, err := weavedns.NewDNSServer(srvConfig, zone, iface) + if err != nil { + Error.Fatal("Failed to initialize the WeaveDNS server", err) + } err = srv.Start() if err != nil { - Error.Fatal("Failed to start server", err) + Error.Fatal("Failed to start the WeaveDNS server", err) } }