diff --git a/common/testing_utils.go b/common/testing_utils.go index 669b6e9c..fc03ba9b 100644 --- a/common/testing_utils.go +++ b/common/testing_utils.go @@ -30,6 +30,18 @@ func AssertNoErr(t *testing.T, err error) { } } +func AssertEqualInt(t *testing.T, got, wanted int, desc string) { + if got != wanted { + t.Fatalf("%s: Expected %s %d but got %d", CallSite(), desc, wanted, got) + } +} + +func AssertEqualString(t *testing.T, got, wanted string, desc string) { + if got != wanted { + t.Fatalf("%s: Expected %s '%s' but got '%s'", CallSite(), desc, wanted, got) + } +} + func AssertStatus(t *testing.T, got int, wanted int, desc string) { if got != wanted { t.Fatalf("%s: Expected %s %d but got %d", CallSite(), desc, wanted, got) @@ -49,3 +61,10 @@ func AssertErrorType(t *testing.T, got interface{}, wanted interface{}, desc str t.Fatalf("%s: Expected %s but got %s (%s)", CallSite(), wantedT.String(), gotT.String(), desc) } } + +func AssertType(t *testing.T, got interface{}, wanted interface{}, desc string) { + gotT, wantedT := reflect.TypeOf(got), reflect.TypeOf(wanted) + if gotT != wantedT { + t.Fatalf("%s: Expected %s but got %s (%s)", CallSite(), wantedT.String(), gotT.String(), desc) + } +} diff --git a/nameserver/mdns_client.go b/nameserver/mdns_client.go index 7acf45a5..d602d1d9 100644 --- a/nameserver/mdns_client.go +++ b/nameserver/mdns_client.go @@ -26,7 +26,7 @@ var ( } ) -type ResponseA struct { +type Response struct { Name string Addr net.IP Err error @@ -34,7 +34,7 @@ type ResponseA struct { type responseInfo struct { timeout time.Time // if no answer by this time, give up - ch chan<- *ResponseA + ch chan<- *Response } // Represents one query that we have sent for one name. @@ -60,7 +60,7 @@ type MDNSClient struct { type mDNSQueryInfo struct { name string querytype uint16 - responseCh chan<- *ResponseA + responseCh chan<- *Response } func NewMDNSClient() (*MDNSClient, error) { @@ -121,7 +121,7 @@ func (c *MDNSClient) Shutdown() { } // Async -func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- *ResponseA) { +func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- *Response) { c.queryChan <- &MDNSInteraction{ code: CSendQuery, payload: mDNSQueryInfo{name, querytype, responseCh}, @@ -209,7 +209,7 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) { m.RecursionDesired = false buf, err := m.Pack() if err != nil { - q.responseCh <- &ResponseA{Err: err} + q.responseCh <- &Response{Err: err} close(q.responseCh) return } @@ -218,7 +218,7 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) { id: m.Id, } if _, err = c.conn.WriteTo(buf, c.addr); err != nil { - q.responseCh <- &ResponseA{Err: err} + q.responseCh <- &Response{Err: err} close(q.responseCh) return } @@ -235,19 +235,29 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) { func (c *MDNSClient) handleResponse(r *dns.Msg) { for _, answer := range r.Answer { + var name string + var res *Response + switch rr := answer.(type) { case *dns.A: - name := rr.Hdr.Name - if query, found := c.inflight[name]; found { - for _, resp := range query.responseInfos { - resp.ch <- &ResponseA{Name: rr.Hdr.Name, Addr: rr.A} - close(resp.ch) - } - delete(c.inflight, name) - } else { - // We've received a response that didn't match a query - // Do we want to cache it? + name = rr.Hdr.Name + res = &Response{Addr: rr.A} + case *dns.PTR: + name = rr.Hdr.Name + res = &Response{Name: rr.Ptr} + default: + return + } + + if query, found := c.inflight[name]; found { + for _, resp := range query.responseInfos { + resp.ch <- res + close(resp.ch) } + delete(c.inflight, name) + } else { + // We've received a response that didn't match a query + // Do we want to cache it? } } } diff --git a/nameserver/mdns_client_test.go b/nameserver/mdns_client_test.go index b50fcfd5..012bc107 100644 --- a/nameserver/mdns_client_test.go +++ b/nameserver/mdns_client_test.go @@ -13,9 +13,27 @@ var ( successTestName = "test1.weave." failTestName = "test2.weave." testAddr = net.ParseIP("9.8.7.6") + testInAddr = "6.7.8.9.in-addr.arpa." ) func minimalServer(w dns.ResponseWriter, req *dns.Msg) { + + sendAnswer := func(m *dns.Msg) { + buf, err := m.Pack() + 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) + + _, err = sendconn.Write(buf) + sendconn.Close() + checkFatal(err) + } + //log.Println("minimalServer received:", req) if len(req.Answer) > 0 { return // Only interested in questions. @@ -23,31 +41,24 @@ func minimalServer(w dns.ResponseWriter, req *dns.Msg) { if len(req.Question) != 1 { return // We only handle single-question messages } - if req.Question[0].Name != successTestName { - return // This is not the DNS record you are looking for + + if req.Question[0].Qtype == dns.TypeA && req.Question[0].Name == successTestName { + m := new(dns.Msg) + m.SetReply(req) + hdr := dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeA, + Class: dns.ClassINET, Ttl: 3600} + a := &dns.A{hdr, testAddr} + m.Answer = append(m.Answer, a) + sendAnswer(m) + } else if req.Question[0].Qtype == dns.TypePTR && req.Question[0].Name == testInAddr { + m := new(dns.Msg) + m.SetReply(req) + hdr := dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypePTR, + Class: dns.ClassINET, Ttl: 3600} + a := &dns.PTR{hdr, successTestName} + m.Answer = append(m.Answer, a) + sendAnswer(m) } - - m := new(dns.Msg) - m.SetReply(req) - - hdr := dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeA, - Class: dns.ClassINET, Ttl: 3600} - a := &dns.A{hdr, testAddr} - m.Answer = append(m.Answer, a) - - buf, err := m.Pack() - 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) - - _, err = sendconn.Write(buf) - sendconn.Close() - checkFatal(err) } func RunLocalMulticastServer() (*dns.Server, error) { @@ -76,14 +87,14 @@ func setup(t *testing.T) (*MDNSClient, *dns.Server, error) { type testContext struct { receivedAddr net.IP receivedCount int - channel chan *ResponseA + channel chan *Response } func newTestContext() *testContext { - return &testContext{channel: make(chan *ResponseA, 4)} + return &testContext{channel: make(chan *Response, 4)} } -func (c *testContext) checkResponse(t *testing.T, channelOk bool, resp *ResponseA) { +func (c *testContext) checkResponse(t *testing.T, channelOk bool, resp *Response) { if !channelOk { c.channel = nil return @@ -152,3 +163,24 @@ outerloop: t.Fatal("Unexpected result for", successTestName, context1.receivedAddr, context2.receivedAddr, context1.receivedCount, context2.receivedCount) } } + +func TestAsLookup(t *testing.T) { + mdnsClient, server, _ := setup(t) + defer mdnsClient.Shutdown() + defer server.Shutdown() + + ip, err := mdnsClient.LookupLocal(successTestName) + wt.AssertNoErr(t, err) + if !testAddr.Equal(ip) { + t.Fatalf("Returned address incorrect %s", ip) + } + + ip, err = mdnsClient.LookupLocal("foo.example.com.") + wt.AssertErrorType(t, err, (*LookupError)(nil), "unknown hostname") + + name, err := mdnsClient.ReverseLookupLocal(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 new file mode 100644 index 00000000..dddc03b3 --- /dev/null +++ b/nameserver/mdns_lookup.go @@ -0,0 +1,37 @@ +package nameserver + +import ( + "github.com/miekg/dns" + . "github.com/zettio/weave/common" + "net" +) + +func mdnsLookup(client *MDNSClient, name string, qtype uint16) (*Response, error) { + channel := make(chan *Response) + client.SendQuery(name, qtype, channel) + for resp := range channel { + Debug.Printf("Got response name %s addr %s", resp.Name, resp.Addr) + if err := resp.Err; err != nil { + return nil, err + } else { + return resp, nil + } + } + return nil, LookupError(name) +} + +func (client *MDNSClient) LookupLocal(name string) (net.IP, error) { + if r, e := mdnsLookup(client, name, dns.TypeA); r != nil { + return r.Addr, nil + } else { + return nil, e + } +} + +func (client *MDNSClient) ReverseLookupLocal(inaddr string) (string, error) { + if r, e := mdnsLookup(client, inaddr, dns.TypePTR); r != nil { + return r.Name, nil + } else { + return "", e + } +} diff --git a/nameserver/mdns_server.go b/nameserver/mdns_server.go index 71b9f2a1..cfab5721 100644 --- a/nameserver/mdns_server.go +++ b/nameserver/mdns_server.go @@ -50,24 +50,57 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { return err } - handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) { - // Ignore answers to other questions - if len(r.Answer) == 0 && len(r.Question) > 0 { - q := r.Question[0] - if ip, err := s.zone.LookupLocal(q.Name); err == nil { - m := makeAddressReply(r, &q, []net.IP{ip}) - if err = s.sendResponse(m); err != nil { - Warning.Printf("Error writing to %s", w) - } - } else if s.addrIsLocal(w.RemoteAddr()) { - // ignore this - it's our own query received via multicast + 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 { + return makeAddressReply(r, q, []net.IP{ip}) } else { - Debug.Printf("Failed MDNS lookup for %s", q.Name) + return nil + } + }) + + 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 { + return makePTRReply(r, q, []string{name}) + } else { + return nil + } + }) + + mux := dns.NewServeMux() + mux.HandleFunc(LOCAL_DOMAIN, handleLocal) + mux.HandleFunc(RDNS_DOMAIN, handleReverse) + + go dns.ActivateAndServe(nil, conn, mux) + return err +} + +type LookupFunc func(Lookup, *dns.Msg, *dns.Question) *dns.Msg + +func (s *MDNSServer) makeHandler(qtype uint16, lookup LookupFunc) dns.HandlerFunc { + return func(_ dns.ResponseWriter, r *dns.Msg) { + // Handle only questions, ignore answers. We might also ignore + // questions that arise locally (i.e., that come from an IP we + // think is local), but in the interest of avoiding + // complication, and easier testing, this is elided on the + // assumption that the client wouldn't ask if it already knew + // the answer, and if it does ask, it'll be happy to get an + // answer. + if len(r.Answer) == 0 && len(r.Question) > 0 { + q := &r.Question[0] + if q.Qtype == qtype { + if m := lookup(s.zone, r, q); m != nil { + Debug.Printf("Found local answer to mDNS query %s", q.Name) + if err := s.sendResponse(m); err != nil { + Warning.Printf("Error writing to %s", s.sendconn) + } + } else { + Debug.Printf("No local answer for mDNS query %s", q.Name) + } } } } - go dns.ActivateAndServe(nil, conn, dns.HandlerFunc(handleMDNS)) - return err } func (s *MDNSServer) sendResponse(m *dns.Msg) error { diff --git a/nameserver/mdns_server_test.go b/nameserver/mdns_server_test.go index 894ed120..4319b609 100644 --- a/nameserver/mdns_server_test.go +++ b/nameserver/mdns_server_test.go @@ -11,7 +11,9 @@ import ( var ( containerID = "deadbeef" + testName = "test.weave.local." testAddr1 = "10.0.2.1/24" + testInAddr1 = "1.2.0.10.in-addr.arpa." ) func sendQuery(name string, querytype uint16) error { @@ -31,10 +33,13 @@ func sendQuery(name string, querytype uint16) error { } func TestServerSimpleQuery(t *testing.T) { + // The ff can be handy for debugging (obvs) + //wt.InitDefaultLogging(true) + log.Println("TestServerSimpleQuery starting") var zone = new(ZoneDb) ip, _, _ := net.ParseCIDR(testAddr1) - zone.AddRecord(containerID, "test.weave.", ip) + zone.AddRecord(containerID, testName, ip) mdnsServer, err := NewMDNSServer(zone) wt.AssertNoErr(t, err) @@ -42,8 +47,26 @@ func TestServerSimpleQuery(t *testing.T) { wt.AssertNoErr(t, err) var receivedAddr net.IP + var receivedName string + var recvChan chan interface{} receivedCount := 0 + reset := func() { + receivedAddr = nil + receivedName = "" + receivedCount = 0 + recvChan = make(chan interface{}) + } + + wait := func() { + select { + case <-recvChan: + return + case <-time.After(100 * time.Millisecond): + return + } + } + // Implement a minimal listener for responses multicast, err := LinkLocalMulticastListener(nil) wt.AssertNoErr(t, err) @@ -56,8 +79,12 @@ func TestServerSimpleQuery(t *testing.T) { case *dns.A: receivedAddr = rr.A receivedCount++ + case *dns.PTR: + receivedName = rr.Ptr + receivedCount++ } } + recvChan <- "ok" } } @@ -67,22 +94,32 @@ func TestServerSimpleQuery(t *testing.T) { time.Sleep(100 * time.Millisecond) // Allow for server to get going - sendQuery("test.weave.", dns.TypeA) - - time.Sleep(time.Second) + reset() + sendQuery(testName, dns.TypeA) + wait() if receivedCount != 1 { - t.Fatal("Unexpected result count for test.weave", receivedCount) + t.Fatalf("Unexpected result count %d for %s", receivedCount, testName) } if !receivedAddr.Equal(ip) { - t.Fatal("Unexpected result for test.weave", receivedAddr) + t.Fatalf("Unexpected result %s for %s", receivedAddr, testName) } - receivedCount = 0 - + reset() sendQuery("testfail.weave.", dns.TypeA) + wait() if receivedCount != 0 { - t.Fatal("Unexpected result count for testfail.weave", receivedCount) + t.Fatalf("Unexpected result count %d for testfail.weave", receivedCount) + } + + reset() + sendQuery(testInAddr1, dns.TypePTR) + wait() + + if receivedCount != 1 { + t.Fatalf("Expected an answer to %s, got %d answers", testInAddr1, receivedCount) + } else if !(testName == receivedName) { + t.Fatalf("Expected answer %s to query for %s, got %s", testName, testInAddr1, receivedName) } } diff --git a/nameserver/server.go b/nameserver/server.go index 896f9e26..94a0c91a 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -9,12 +9,8 @@ import ( const ( LOCAL_DOMAIN = "weave.local." - RDNS_DOMAIN = "in-addr.arpa." ) -// +1 to also exclude a dot -var rdnsDomainLen = len(RDNS_DOMAIN) + 1 - func checkFatal(e error) { if e != nil { Error.Fatal(e) @@ -35,62 +31,45 @@ func makeDNSFailResponse(r *dns.Msg) *dns.Msg { return m } -func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { +func queryHandler(lookups []Lookup) dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] - Debug.Printf("Local query: %+v", q) + Debug.Printf("Query: %+v", q) if q.Qtype == dns.TypeA { - if ip, err := zone.LookupLocal(q.Name); err == nil { - m := makeAddressReply(r, &q, []net.IP{ip}) - w.WriteMsg(m) - } else { - Debug.Printf("Failed lookup for %s; sending mDNS query", q.Name) - // We don't know the answer; see if someone else does - channel := make(chan *ResponseA) - replies := make([]net.IP, 0) - go func() { - for resp := range channel { - Debug.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) - replies = append(replies, resp.Addr) - } - var responseMsg *dns.Msg - if len(replies) > 0 { - responseMsg = makeAddressReply(r, &q, replies) - } else { - responseMsg = makeDNSFailResponse(r) - } - w.WriteMsg(responseMsg) - }() - mdnsClient.SendQuery(q.Name, dns.TypeA, channel) + for _, lookup := range lookups { + if ip, err := lookup.LookupLocal(q.Name); err == nil { + m := makeAddressReply(r, &q, []net.IP{ip}) + w.WriteMsg(m) + return + } } + Debug.Printf("Query failed for %s", q.Name) + w.WriteMsg(makeDNSFailResponse(r)) } else { - Warning.Printf("Local query not handled: %+v", q) - m := makeDNSFailResponse(r) - w.WriteMsg(m) + Warning.Printf("Query not handled: %+v", q) + w.WriteMsg(makeDNSFailResponse(r)) } return } } -func rdnsHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { +func rdnsHandler(lookups []Lookup) dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] - Debug.Printf("Local rdns query: %+v", q) + Debug.Printf("Reverse query: %+v", q) if q.Qtype == dns.TypePTR { - if ip := net.ParseIP(q.Name[:len(q.Name)-rdnsDomainLen]); ip != nil { - ip4 := ip.To4() - revIP := []byte{ip4[3], ip4[2], ip4[1], ip4[0]} - Debug.Printf("Looking for address: %+v", revIP) - if name, err := zone.ReverseLookupLocal(revIP); err == nil { - Debug.Printf("Found name: %s", name) + for _, lookup := range lookups { + if name, err := lookup.ReverseLookupLocal(q.Name); err == nil { m := makePTRReply(r, &q, []string{name}) w.WriteMsg(m) - } else { - Debug.Printf("Failed lookup for %s; sending mDNS query", q.Name) - // We don't know the answer; see if someone else does - // TODO + return } } + Debug.Printf("Reverse query failed for %s", q.Name) + w.WriteMsg(makeDNSFailResponse(r)) + } else { + Warning.Printf("Reverse query not handled: %+v", q) + w.WriteMsg(makeDNSFailResponse(r)) } } } @@ -119,9 +98,7 @@ func notUsHandler() dns.HandlerFunc { } } -func StartServer(zone Zone, iface *net.Interface, dnsPort int, httpPort int, wait int) error { - go ListenHttp(LOCAL_DOMAIN, zone, httpPort) - +func StartServer(zone Zone, iface *net.Interface, dnsPort int, wait int) error { mdnsClient, err := NewMDNSClient() checkFatal(err) @@ -134,8 +111,8 @@ func StartServer(zone Zone, iface *net.Interface, dnsPort int, httpPort int, wai checkFatal(err) LocalServeMux := dns.NewServeMux() - LocalServeMux.HandleFunc(LOCAL_DOMAIN, queryHandler(zone, mdnsClient)) - LocalServeMux.HandleFunc(RDNS_DOMAIN, rdnsHandler(zone, mdnsClient)) + LocalServeMux.HandleFunc(LOCAL_DOMAIN, queryHandler([]Lookup{zone, mdnsClient})) + LocalServeMux.HandleFunc(RDNS_DOMAIN, rdnsHandler([]Lookup{zone, mdnsClient})) LocalServeMux.HandleFunc(".", notUsHandler()) mdnsServer, err := NewMDNSServer(zone) diff --git a/nameserver/server_test.go b/nameserver/server_test.go new file mode 100644 index 00000000..a3703d90 --- /dev/null +++ b/nameserver/server_test.go @@ -0,0 +1,78 @@ +package nameserver + +import ( + "fmt" + "github.com/miekg/dns" + wt "github.com/zettio/weave/common" + "net" + "testing" + "time" +) + +func TestDNSServer(t *testing.T) { + const ( + port = 17625 + successTestName = "test1.weave.local." + failTestName = "test2.weave.local." + nonLocalName = "weave.works." + testAddr1 = "10.0.2.1" + testRDNSsuccess = "1.2.0.10.in-addr.arpa." + testRDNSfail = "4.3.2.1.in-addr.arpa." + ) + dnsAddr := fmt.Sprintf("localhost:%d", port) + testCIDR1 := testAddr1 + "/24" + + wt.InitDefaultLogging(true) + var zone = new(ZoneDb) + ip, _, _ := net.ParseCIDR(testCIDR1) + zone.AddRecord(containerID, successTestName, ip) + + go StartServer(zone, nil, port, 0) + time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start + + c := new(dns.Client) + 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") + wt.AssertEqualInt(t, len(r.Answer), 1, "Number of answers") + wt.AssertType(t, r.Answer[0], (*dns.A)(nil), "DNS record") + wt.AssertEqualString(t, r.Answer[0].(*dns.A).A.String(), testAddr1, "IP address") + + m.SetQuestion(failTestName, dns.TypeA) + 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") + + m.SetQuestion(testRDNSsuccess, dns.TypePTR) + r, _, err = c.Exchange(m, dnsAddr) + wt.AssertNoErr(t, err) + wt.AssertStatus(t, r.Rcode, dns.RcodeSuccess, "DNS response code") + wt.AssertEqualInt(t, len(r.Answer), 1, "Number of answers") + wt.AssertType(t, r.Answer[0], (*dns.PTR)(nil), "DNS record") + wt.AssertEqualString(t, r.Answer[0].(*dns.PTR).Ptr, successTestName, "IP address") + m.SetQuestion(testRDNSfail, dns.TypePTR) + 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") + + // This should fail because we don't handle MX records + m.SetQuestion(successTestName, 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") + + // This non-local query should fail because we don't handle MX records + 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") + + // Not testing MDNS functionality of server here (yet) +} diff --git a/nameserver/zone.go b/nameserver/zone.go index 44c194b9..fd2ceaaf 100644 --- a/nameserver/zone.go +++ b/nameserver/zone.go @@ -2,16 +2,28 @@ package nameserver import ( "github.com/miekg/dns" + . "github.com/zettio/weave/common" "net" "sync" ) +const ( + RDNS_DOMAIN = "in-addr.arpa." +) + +// +1 to also exclude a dot +var rdnsDomainLen = len(RDNS_DOMAIN) + 1 + +type Lookup interface { + LookupLocal(name string) (net.IP, error) + ReverseLookupLocal(inaddr string) (string, error) +} + type Zone interface { AddRecord(ident string, name string, ip net.IP) error DeleteRecord(ident string, ip net.IP) error DeleteRecordsFor(ident string) error - LookupLocal(name string) (net.IP, error) - ReverseLookupLocal(ip net.IP) (string, error) + Lookup } type Record struct { @@ -60,15 +72,23 @@ func (zone *ZoneDb) LookupLocal(name string) (net.IP, error) { return nil, LookupError(name) } -func (zone *ZoneDb) ReverseLookupLocal(ip net.IP) (string, error) { - zone.mx.RLock() - defer zone.mx.RUnlock() - for _, r := range zone.recs { - if r.IP.Equal(ip) { - return r.Name, nil +func (zone *ZoneDb) ReverseLookupLocal(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]} + Debug.Printf("Looking for address: %+v", ip) + zone.mx.RLock() + defer zone.mx.RUnlock() + for _, r := range zone.recs { + if r.IP.Equal(ip) { + return r.Name, nil + } } + return "", LookupError(inaddr) + } else { + Warning.Printf("Asked to reverse lookup %s", inaddr) + return "", LookupError(inaddr) } - return "", LookupError(ip.String()) } func (zone *ZoneDb) AddRecord(ident string, name string, ip net.IP) error { diff --git a/nameserver/zone_test.go b/nameserver/zone_test.go index 6d99cf2a..86db36ec 100644 --- a/nameserver/zone_test.go +++ b/nameserver/zone_test.go @@ -35,7 +35,7 @@ func TestZone(t *testing.T) { } // See if we can find the address by IP. - foundName, err := zone.ReverseLookupLocal(ip) + foundName, err := zone.ReverseLookupLocal("1.2.0.10.in-addr.arpa.") wt.AssertNoErr(t, err) if foundName != successTestName { diff --git a/weavedns/main.go b/weavedns/main.go index d7e62b46..d00a90bc 100644 --- a/weavedns/main.go +++ b/weavedns/main.go @@ -63,7 +63,8 @@ func main() { } } - err := weavedns.StartServer(zone, iface, dnsPort, httpPort, wait) + go weavedns.ListenHttp(weavedns.LOCAL_DOMAIN, zone, httpPort) + err := weavedns.StartServer(zone, iface, dnsPort, wait) if err != nil { Error.Fatal("Failed to start server", err) }