commit
f70dfe9de8
11 files changed
+369
-125
No files matched your search
@@ -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)
|
||||
}
|
||||
}
|
||||
+26
-16
@@ -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?
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
+47
-14
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
+25
-48
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
+29
-9
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+2
-1
@@ -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)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user