diff --git a/nameserver/http_test.go b/nameserver/http_test.go index a2d14380..44f3ba8f 100644 --- a/nameserver/http_test.go +++ b/nameserver/http_test.go @@ -43,7 +43,7 @@ func TestHttp(t *testing.T) { wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http response") // Check that the address is now there. - foundIP, err := zone.LookupLocal(successTestName) + foundIP, err := zone.LookupName(successTestName) wt.AssertNoErr(t, err) ip, _, _ := net.ParseCIDR(testAddr1) if !foundIP.Equal(ip) { @@ -70,7 +70,7 @@ func TestHttp(t *testing.T) { wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http response") // Check that the address is still resolvable. - _, err = zone.LookupLocal(successTestName) + _, err = zone.LookupName(successTestName) wt.AssertNoErr(t, err) // Delete the address record mentioning the other container @@ -79,7 +79,7 @@ func TestHttp(t *testing.T) { wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http response") // Check that the address is gone - _, err = zone.LookupLocal(successTestName) + _, err = zone.LookupName(successTestName) wt.AssertErrorType(t, err, (*LookupError)(nil), "fully-removed address") // Would like to shut down the http server at the end of this test diff --git a/nameserver/mdns_client_test.go b/nameserver/mdns_client_test.go index e684f8b4..aa9ad6a5 100644 --- a/nameserver/mdns_client_test.go +++ b/nameserver/mdns_client_test.go @@ -169,16 +169,16 @@ func TestAsLookup(t *testing.T) { defer mdnsClient.Shutdown() defer server.Shutdown() - ip, err := mdnsClient.LookupLocal(successTestName) + ip, err := mdnsClient.LookupName(successTestName) wt.AssertNoErr(t, err) if !testAddr.Equal(ip) { t.Fatalf("Returned address incorrect %s", ip) } - ip, err = mdnsClient.LookupLocal("foo.example.com.") + ip, err = mdnsClient.LookupName("foo.example.com.") wt.AssertErrorType(t, err, (*LookupError)(nil), "unknown hostname") - name, err := mdnsClient.ReverseLookupLocal(testInAddr) + name, err := mdnsClient.LookupInaddr(testInAddr) wt.AssertNoErr(t, err) if !(successTestName == name) { t.Fatalf("Expected name %s, got %s", successTestName, name) diff --git a/nameserver/mdns_lookup.go b/nameserver/mdns_lookup.go index a16a4fbd..0cbe413e 100644 --- a/nameserver/mdns_lookup.go +++ b/nameserver/mdns_lookup.go @@ -23,7 +23,7 @@ func mdnsLookup(client *MDNSClient, name string, qtype uint16) (*Response, error return nil, LookupError(name) } -func (client *MDNSClient) LookupLocal(name string) (net.IP, error) { +func (client *MDNSClient) LookupName(name string) (net.IP, error) { if r, e := mdnsLookup(client, name, dns.TypeA); r != nil { return r.Addr, nil } else { @@ -31,7 +31,7 @@ func (client *MDNSClient) LookupLocal(name string) (net.IP, error) { } } -func (client *MDNSClient) ReverseLookupLocal(inaddr string) (string, error) { +func (client *MDNSClient) LookupInaddr(inaddr string) (string, error) { if r, e := mdnsLookup(client, inaddr, dns.TypePTR); r != nil { return r.Name, nil } else { diff --git a/nameserver/mdns_server.go b/nameserver/mdns_server.go index 65695d21..a67a4b3e 100644 --- a/nameserver/mdns_server.go +++ b/nameserver/mdns_server.go @@ -52,7 +52,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { handleLocal := s.makeHandler(dns.TypeA, func(zone Lookup, r *dns.Msg, q *dns.Question) *dns.Msg { - if ip, err := zone.LookupLocal(q.Name); err == nil { + if ip, err := zone.LookupName(q.Name); err == nil { return makeAddressReply(r, q, []net.IP{ip}) } else { return nil @@ -61,7 +61,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { handleReverse := s.makeHandler(dns.TypePTR, func(zone Lookup, r *dns.Msg, q *dns.Question) *dns.Msg { - if name, err := zone.ReverseLookupLocal(q.Name); err == nil { + if name, err := zone.LookupInaddr(q.Name); err == nil { return makePTRReply(r, q, []string{name}) } else { return nil diff --git a/nameserver/server.go b/nameserver/server.go index f202f3bb..f123da57 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -37,7 +37,7 @@ func queryHandler(lookups []Lookup) dns.HandlerFunc { Debug.Printf("Query: %+v", q) if q.Qtype == dns.TypeA { for _, lookup := range lookups { - if ip, err := lookup.LookupLocal(q.Name); err == nil { + if ip, err := lookup.LookupName(q.Name); err == nil { m := makeAddressReply(r, &q, []net.IP{ip}) w.WriteMsg(m) return @@ -56,7 +56,7 @@ func rdnsHandler(lookups []Lookup) dns.HandlerFunc { Debug.Printf("Reverse query: %+v", q) if q.Qtype == dns.TypePTR { for _, lookup := range lookups { - if name, err := lookup.ReverseLookupLocal(q.Name); err == nil { + if name, err := lookup.LookupInaddr(q.Name); err == nil { m := makePTRReply(r, &q, []string{name}) w.WriteMsg(m) return @@ -72,28 +72,35 @@ func rdnsHandler(lookups []Lookup) dns.HandlerFunc { } } -/* When we receive a request for a name outside of our '.weave' domain, call - the underlying lookup mechanism and return the answer(s) it gives. - Unfortunately, this means that TTLs from a real DNS server are lost - FIXME. +/* When we receive a request for a name outside of our '.weave.local.' + domain, ask the configured DNS server as a fallback. */ func notUsHandler() dns.HandlerFunc { + config, err := dns.ClientConfigFromFile("/etc/resolv.conf") + checkFatal(err) return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] Debug.Printf("[dns msgid %d] Non-local query: %+v", r.MsgHdr.Id, q) - var responseMsg *dns.Msg - if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA { - if addrs, err := net.LookupIP(q.Name); err == nil { - responseMsg = makeAddressReply(r, &q, addrs) - } else { - responseMsg = makeDNSFailResponse(r) - Debug.Printf("[dns msgid %d] Failed fallback: %s", r.MsgHdr.Id, err) + for _, server := range config.Servers { + reply, err := dns.Exchange(r, fmt.Sprintf("%s:%s", server, config.Port)) + if err != nil { + Debug.Printf("[dns msgid %d] Network error trying %s (%s)", + r.MsgHdr.Id, server, err) + continue } - } else { - Warning.Printf("[dns msgid %d] Non-local query not handled: %+v", - r.MsgHdr.Id, q) - responseMsg = makeDNSFailResponse(r) + if reply != nil && reply.Rcode != dns.RcodeSuccess { + Debug.Printf("[dns msgid %d] Failure reported by %s for query %s", + r.MsgHdr.Id, server, q.Name) + continue + } + Debug.Printf("[dns msgid %d] Given answer by %s for query %s", + r.MsgHdr.Id, server, q.Name) + w.WriteMsg(reply) + return } - w.WriteMsg(responseMsg) + Warning.Printf("[dns msgid %d] Failed lookup for external name %s", + r.MsgHdr.Id, q.Name) + w.WriteMsg(makeDNSFailResponse(r)) } } diff --git a/nameserver/server_test.go b/nameserver/server_test.go index c0050179..8ad62f06 100644 --- a/nameserver/server_test.go +++ b/nameserver/server_test.go @@ -68,12 +68,15 @@ func TestDNSServer(t *testing.T) { wt.AssertStatus(t, r.Rcode, dns.RcodeNameError, "DNS response code") wt.AssertEqualInt(t, len(r.Answer), 0, "Number of answers") - // This non-local query should fail because we don't handle MX records + // This non-local query for an MX record should succeed by being + // passed on to the configured (/etc/resolv.conf) DNS server. m.SetQuestion(nonLocalName, dns.TypeMX) r, _, err = c.Exchange(m, dnsAddr) wt.AssertNoErr(t, err) - wt.AssertStatus(t, r.Rcode, dns.RcodeNameError, "DNS response code") - wt.AssertEqualInt(t, len(r.Answer), 0, "Number of answers") + wt.AssertStatus(t, r.Rcode, dns.RcodeSuccess, "DNS response code") + if !(len(r.Answer) > 0) { + t.Fatal("Number of answers > 0") + } // Not testing MDNS functionality of server here (yet) } diff --git a/nameserver/zone.go b/nameserver/zone.go index 2a9baca0..ae07eb88 100644 --- a/nameserver/zone.go +++ b/nameserver/zone.go @@ -15,8 +15,8 @@ const ( var rdnsDomainLen = len(RDNS_DOMAIN) + 1 type Lookup interface { - LookupLocal(name string) (net.IP, error) - ReverseLookupLocal(inaddr string) (string, error) + LookupName(name string) (net.IP, error) + LookupInaddr(inaddr string) (string, error) } type Zone interface { @@ -61,7 +61,7 @@ func (zone *ZoneDb) indexOf(match func(Record) bool) int { return -1 } -func (zone *ZoneDb) LookupLocal(name string) (net.IP, error) { +func (zone *ZoneDb) LookupName(name string) (net.IP, error) { zone.mx.RLock() defer zone.mx.RUnlock() for _, r := range zone.recs { @@ -72,7 +72,7 @@ func (zone *ZoneDb) LookupLocal(name string) (net.IP, error) { return nil, LookupError(name) } -func (zone *ZoneDb) ReverseLookupLocal(inaddr string) (string, error) { +func (zone *ZoneDb) LookupInaddr(inaddr string) (string, error) { if revIP := net.ParseIP(inaddr[:len(inaddr)-rdnsDomainLen]); revIP != nil { revIP4 := revIP.To4() ip := []byte{revIP4[3], revIP4[2], revIP4[1], revIP4[0]} diff --git a/nameserver/zone_test.go b/nameserver/zone_test.go index c5298c1e..ef242ddb 100644 --- a/nameserver/zone_test.go +++ b/nameserver/zone_test.go @@ -27,7 +27,7 @@ func TestZone(t *testing.T) { wt.AssertNoErr(t, err) // Check that the address is now there. - foundIP, err := zone.LookupLocal(successTestName) + foundIP, err := zone.LookupName(successTestName) wt.AssertNoErr(t, err) if !foundIP.Equal(ip) { @@ -35,7 +35,7 @@ func TestZone(t *testing.T) { } // See if we can find the address by IP. - foundName, err := zone.ReverseLookupLocal("1.2.0.10.in-addr.arpa.") + foundName, err := zone.LookupInaddr("1.2.0.10.in-addr.arpa.") wt.AssertNoErr(t, err) if foundName != successTestName { @@ -50,14 +50,14 @@ func TestZone(t *testing.T) { err = zone.DeleteRecord(containerID, ip) wt.AssertNoErr(t, err) - _, err = zone.LookupLocal(successTestName) + _, err = zone.LookupName(successTestName) wt.AssertNoErr(t, err) err = zone.DeleteRecord(otherContainerID, ip) wt.AssertNoErr(t, err) // Check that the address is not there now. - _, err = zone.LookupLocal(successTestName) + _, err = zone.LookupName(successTestName) wt.AssertErrorType(t, err, (*LookupError)(nil), "after deleting record") // Delete a record that isn't there @@ -79,10 +79,10 @@ func TestDeleteFor(t *testing.T) { wt.AssertNoErr(t, err) } - _, err := zone.LookupLocal(name) + _, err := zone.LookupName(name) wt.AssertNoErr(t, err) err = zone.DeleteRecordsFor(id) - _, err = zone.LookupLocal(name) + _, err = zone.LookupName(name) wt.AssertErrorType(t, err, (*LookupError)(nil), "after deleting records for ident") } diff --git a/test/300_fallback_dns_test.sh b/test/300_fallback_dns_test.sh new file mode 100755 index 00000000..679d621a --- /dev/null +++ b/test/300_fallback_dns_test.sh @@ -0,0 +1,16 @@ +#! /bin/bash + +. ./config.sh + +start_suite "Resolve a non-weave address" + +run_on $HOST1 sudo $WEAVE stop-dns || true +run_on $HOST1 sudo $WEAVE launch-dns 10.0.0.2 +docker_on $HOST1 rm -f c1 || true + +run_on $HOST1 sudo $WEAVE run --with-dns 10.1.1.5/24 --name=c1 -t aanand/docker-dnsutils /bin/sh + +ok=$(docker -H tcp://$HOST1:2375 exec -i c1 dig +short -t MX weave.works) +assert "test -n \"$ok\" && echo pass" "pass" + +end_suite