Example #1
0
func setup(t *testing.T) (*MDNSClient, *dns.Server, error) {
	InitDefaultLogging(testing.Verbose())

	server, err := RunLocalMulticastServer()
	if err != nil {
		t.Fatalf("Unable to run test server: %s. No default multicast interface?", err)
	}

	mdnsClient, err := NewMDNSClient()
	wt.AssertNoErr(t, err)
	err = mdnsClient.Start(nil)
	wt.AssertNoErr(t, err)

	return mdnsClient, server, err
}
Example #2
0
func TestZone(t *testing.T) {
	var (
		containerID      = "deadbeef"
		otherContainerID = "cowjuice"
		successTestName  = "test1.weave."
		testAddr1        = "10.2.2.1/24"
	)

	var zone = NewZoneDb(DefaultLocalDomain)

	ip, _, _ := net.ParseCIDR(testAddr1)
	err := zone.AddRecord(containerID, successTestName, ip)
	wt.AssertNoErr(t, err)

	// Add a few more records to make the job harder
	err = zone.AddRecord("abcdef0123", "adummy.weave.", net.ParseIP("10.2.0.1"))
	wt.AssertNoErr(t, err)
	err = zone.AddRecord("0123abcdef", "zdummy.weave.", net.ParseIP("10.2.0.2"))
	wt.AssertNoErr(t, err)

	// Check that the address is now there.
	foundIP, err := zone.LookupName(successTestName)
	wt.AssertNoErr(t, err)

	if !foundIP[0].IP().Equal(ip) {
		t.Fatal("Unexpected result for", successTestName, foundIP)
	}

	// See if we can find the address by IP.
	foundName, err := zone.LookupInaddr("1.2.2.10.in-addr.arpa.")
	wt.AssertNoErr(t, err)

	if foundName[0].Name() != successTestName {
		t.Fatal("Unexpected result for", ip, foundName)
	}

	err = zone.AddRecord(containerID, successTestName, ip)
	wt.AssertErrorType(t, err, (*DuplicateError)(nil), "duplicate add")

	err = zone.AddRecord(otherContainerID, successTestName, ip)
	// Delete the record for the original container
	err = zone.DeleteRecord(containerID, ip)
	wt.AssertNoErr(t, err)

	_, 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.LookupName(successTestName)
	wt.AssertErrorType(t, err, (*LookupError)(nil), "after deleting record")

	// Delete a record that isn't there
	err = zone.DeleteRecord(containerID, net.ParseIP("0.0.0.0"))
	wt.AssertErrorType(t, err, (*LookupError)(nil), "when deleting record that doesn't exist")
}
Example #3
0
// perform a DNS query and assert the reply code, number or answers, etc
func assertExchange(t *testing.T, z string, ty uint16, minAnswers int, maxAnswers int, expErr int) *dns.Msg {
	c := new(dns.Client)
	c.UDPSize = testUDPBufSize
	m := new(dns.Msg)
	m.RecursionDesired = true
	m.SetQuestion(z, ty)
	m.SetEdns0(testUDPBufSize, false) // we don't want to play with truncation here...

	r, _, err := c.Exchange(m, fmt.Sprintf("127.0.0.1:%d", testPort))
	t.Logf("Response:\n%+v\n", r)
	wt.AssertNoErr(t, err)
	if minAnswers == 0 && maxAnswers == 0 {
		wt.AssertStatus(t, r.Rcode, expErr, "DNS response code")
	} else {
		wt.AssertStatus(t, r.Rcode, dns.RcodeSuccess, "DNS response code")
	}
	answers := len(r.Answer)
	if minAnswers >= 0 && answers < minAnswers {
		wt.Fatalf(t, "Number of answers >= %d", minAnswers)
	}
	if maxAnswers >= 0 && answers > maxAnswers {
		wt.Fatalf(t, "Number of answers <= %d", maxAnswers)
	}
	return r
}
Example #4
0
func (c *testContext) checkResponse(t *testing.T, channelOk bool, resp *Response) {
	if !channelOk {
		c.channel = nil
		return
	}
	wt.AssertNoErr(t, resp.err)
	log.Printf("Got address response %s addr %s", resp.Name(), resp.IP())
	c.receivedAddr = resp.IP()
	c.receivedCount++
}
Example #5
0
func TestAsLookup(t *testing.T) {
	mdnsClient, server, _ := setup(t)
	defer mdnsClient.Shutdown()
	defer server.Shutdown()

	ips, err := mdnsClient.LookupName(successTestName)
	wt.AssertNoErr(t, err)
	if !testAddr.Equal(ips[0].IP()) {
		t.Fatalf("Returned address incorrect %s", ips)
	}

	ips, err = mdnsClient.LookupName("foo.example.com.")
	wt.AssertErrorType(t, err, (*LookupError)(nil), "unknown hostname")

	names, err := mdnsClient.LookupInaddr(testInAddr)
	wt.AssertNoErr(t, err)
	if !(successTestName == names[0].Name()) {
		t.Fatalf("Expected name %s, got %s", successTestName, names)
	}
}
Example #6
0
func TestDeleteFor(t *testing.T) {
	var (
		id    = "foobar"
		name  = "foo.weave."
		addr1 = "10.2.2.3/24"
		addr2 = "10.2.7.8/24"
	)
	zone := NewZoneDb(DefaultLocalDomain)
	for _, addr := range []string{addr1, addr2} {
		ip, _, _ := net.ParseCIDR(addr)
		err := zone.AddRecord(id, name, ip)
		wt.AssertNoErr(t, err)
	}

	_, err := zone.LookupName(name)
	wt.AssertNoErr(t, err)

	err = zone.DeleteRecordsFor(id)
	_, err = zone.LookupName(name)
	wt.AssertErrorType(t, err, (*LookupError)(nil), "after deleting records for ident")
}
Example #7
0
func TestFieldValidator(t *testing.T) {
	testMap := map[string]string{"a": "a"}

	fv := NewFieldValidator(testMap)
	val, err := fv.Value("a")
	wt.AssertNoErr(t, err)
	wt.AssertNoErr(t, fv.Err())
	wt.AssertEqualString(t, val, "a", "")
	_, err = fv.Value("x")
	wt.AssertFalse(t, err == nil || fv.Err() == nil, "Expected error")
	_, err = fv.Value("a")
	wt.AssertFalse(t, err == nil || fv.Err() == nil, "Previous error should be retained")

	fv = NewFieldValidator(testMap)
	err = fv.CheckEqual("a", "a")
	wt.AssertNoErr(t, err)
	wt.AssertNoErr(t, fv.Err())
	err = fv.CheckEqual("a", "b")
	wt.AssertFalse(t, err == nil || fv.Err() == nil, "Expected error")
	err = fv.CheckEqual("a", "a")
	wt.AssertFalse(t, err == nil || fv.Err() == nil, "Previous error should be retained")
}
Example #8
0
func TestAddrs(t *testing.T) {
	InitDefaultLogging(testing.Verbose())
	Info.Println("TestAddrs starting")

	ip, err := addrToIPv4("10.13.12.11")
	wt.AssertNoErr(t, err)
	wt.AssertTrue(t, net.ParseIP("10.13.12.11").Equal(ip.toNetIP()), "IP")

	ip, err = raddrToIPv4("11.12.13.10.in-addr.arpa.")
	wt.AssertNoErr(t, err)
	wt.AssertTrue(t, net.ParseIP("10.13.12.11").Equal(ip.toNetIP()), "IP")

	// some malformed addresses
	ip, err = addrToIPv4("10.13.12")
	wt.AssertTrue(t, err != nil, "when parsing malformed address")
	ip, err = addrToIPv4("10.13.AA.12")
	wt.AssertTrue(t, err != nil, "when parsing malformed address")
	ip, err = raddrToIPv4("11.12.13.10.in-axxx.arpa.")
	wt.AssertTrue(t, err != nil, "when parsing malformed address")
	ip, err = raddrToIPv4("11.12.13.10.in-addr")
	wt.AssertTrue(t, err != nil, "when parsing malformed address")
}
Example #9
0
// Check that the cache keeps its intended capacity constant
func TestCacheLength(t *testing.T) {
	InitDefaultLogging(testing.Verbose())
	Info.Println("TestCacheLength starting")

	const cacheLen = 128

	l, err := NewCache(cacheLen, nil)
	wt.AssertNoErr(t, err)

	insTime := time.Now()

	t.Logf("Inserting 256 questions in the cache at '%s', with TTL from 0 to 255", insTime)
	for i := 0; i < cacheLen*2; i++ {
		name := fmt.Sprintf("name%d", i)
		questionMsg := new(dns.Msg)
		questionMsg.SetQuestion(name, dns.TypeA)
		questionMsg.RecursionDesired = true

		question := &questionMsg.Question[0]

		ip := net.ParseIP(fmt.Sprintf("10.0.1.%d", i))
		records := []ZoneRecord{Record{name, ip, 0, 0, 0}}

		reply := makeAddressReply(questionMsg, question, records)
		reply.Answer[0].Header().Ttl = uint32(i)

		l.Put(questionMsg, reply, 0, 0)
	}

	wt.AssertEqualInt(t, l.Len(), cacheLen, "cache length")

	minExpectedTime := insTime.Add(time.Duration(cacheLen) * time.Second)
	t.Logf("Checking all remaining entries expire after insert_time + %d secs='%s'", cacheLen, minExpectedTime)
	for _, entry := range l.entries {
		if entry.validUntil.Before(minExpectedTime) {
			t.Fatalf("Entry valid until %s", entry.validUntil)
		}
	}
}
Example #10
0
func TestServerSimpleQuery(t *testing.T) {
	var (
		testRecord1 = Record{"test.weave.local.", net.ParseIP("10.20.20.10"), 0, 0, 0}
		testRecord2 = Record{"test.weave.local.", net.ParseIP("10.20.20.20"), 0, 0, 0}
		testInAddr1 = "10.20.20.10.in-addr.arpa."
	)

	InitDefaultLogging(testing.Verbose())
	Info.Println("TestServerSimpleQuery starting")

	mzone := newMockedZoneWithRecords([]ZoneRecord{testRecord1, testRecord2})
	mdnsServer, err := NewMDNSServer(mzone)
	wt.AssertNoErr(t, err)
	err = mdnsServer.Start(nil)
	wt.AssertNoErr(t, err)
	defer mdnsServer.Stop()

	receivedAddrs := make([]net.IP, 0)
	receivedName := ""
	recvChan := make(chan interface{})
	receivedCount := 0

	// Implement a minimal listener for responses
	multicast, err := LinkLocalMulticastListener(nil)
	wt.AssertNoErr(t, err)

	handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) {
		// Only handle responses here
		if len(r.Answer) > 0 {
			t.Logf("Received %d answer(s)", len(r.Answer))
			for _, answer := range r.Answer {
				switch rr := answer.(type) {
				case *dns.A:
					t.Logf("... A:\n%+v", rr)
					receivedAddrs = append(receivedAddrs, rr.A)
					receivedCount++
				case *dns.PTR:
					t.Logf("... PTR:\n%+v", rr)
					receivedName = rr.Ptr
					receivedCount++
				}
			}
			recvChan <- "ok"
		}
	}

	sendQuery := func(name string, querytype uint16) {
		receivedAddrs = make([]net.IP, 0)
		receivedName = ""
		receivedCount = 0
		recvChan = make(chan interface{})

		m := new(dns.Msg)
		m.SetQuestion(name, querytype)
		m.RecursionDesired = false
		buf, err := m.Pack()
		wt.AssertNoErr(t, err)
		conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
		wt.AssertNoErr(t, err)
		Debug.Printf("Sending UDP packet to %s", ipv4Addr)
		_, err = conn.WriteTo(buf, ipv4Addr)
		wt.AssertNoErr(t, err)

		Debug.Printf("Waiting for response")
		select {
		case <-recvChan:
			return
		case <-time.After(100 * time.Millisecond):
			Debug.Printf("Timeout while waiting for response")
			return
		}
	}

	listener := &dns.Server{
		Unsafe:      true,
		PacketConn:  multicast,
		Handler:     dns.HandlerFunc(handleMDNS),
		ReadTimeout: 100 * time.Millisecond}
	go listener.ActivateAndServe()
	defer listener.Shutdown()

	time.Sleep(100 * time.Millisecond) // Allow for server to get going

	Debug.Printf("Checking that we get 2 IPs fo name '%s' [A]", testRecord1.Name())
	sendQuery(testRecord1.Name(), dns.TypeA)
	if receivedCount != 2 {
		t.Fatalf("Unexpected result count %d for %s", receivedCount, testRecord1.Name())
	}
	if !(receivedAddrs[0].Equal(testRecord1.IP()) || receivedAddrs[0].Equal(testRecord2.IP())) {
		t.Fatalf("Unexpected result %s for %s", receivedAddrs, testRecord1.Name())
	}
	if !(receivedAddrs[1].Equal(testRecord1.IP()) || receivedAddrs[1].Equal(testRecord2.IP())) {
		t.Fatalf("Unexpected result %s for %s", receivedAddrs, testRecord1.Name())
	}

	Debug.Printf("Checking that 'testfail.weave.' [A] gets no answers")
	sendQuery("testfail.weave.", dns.TypeA)
	if receivedCount != 0 {
		t.Fatalf("Unexpected result count %d for testfail.weave", receivedCount)
	}

	Debug.Printf("Checking that '%s' [PTR] gets one name", testInAddr1)
	sendQuery(testInAddr1, dns.TypePTR)
	if receivedCount != 1 {
		t.Fatalf("Expected an answer to %s, got %d answers", testInAddr1, receivedCount)
	} else if !(testRecord1.Name() == receivedName) {
		t.Fatalf("Expected answer %s to query for %s, got %s", testRecord1.Name(), testInAddr1, receivedName)
	}
}
Example #11
0
// Check that the cache entries are ok
func TestCacheEntries(t *testing.T) {
	InitDefaultLogging(testing.Verbose())
	Info.Println("TestCacheEntries starting")

	Info.Println("Checking cache consistency")

	const cacheLen = 128
	clk := clock.NewMock()

	l, err := NewCache(cacheLen, clk)
	wt.AssertNoErr(t, err)

	questionMsg := new(dns.Msg)
	questionMsg.SetQuestion("some.name", dns.TypeA)
	questionMsg.RecursionDesired = true

	question := &questionMsg.Question[0]

	t.Logf("Trying to get a name")
	resp, err := l.Get(questionMsg, minUDPSize)
	wt.AssertNoErr(t, err)
	if resp != nil {
		t.Logf("Got\n%s", resp)
		t.Fatalf("ERROR: Did not expect a reponse from Get() yet")
	}
	t.Logf("Trying to get it again")
	resp, err = l.Get(questionMsg, minUDPSize)
	wt.AssertNoErr(t, err)
	if resp != nil {
		t.Logf("Got\n%s", resp)
		t.Fatalf("ERROR: Did not expect a reponse from Get() yet")
	}

	t.Logf("Inserting the reply")
	records := []ZoneRecord{Record{"some.name", net.ParseIP("10.0.1.1"), 0, 0, 0}}
	reply1 := makeAddressReply(questionMsg, question, records)
	l.Put(questionMsg, reply1, nullTTL, 0)

	t.Logf("Checking we can Get() the reply now")
	resp, err = l.Get(questionMsg, minUDPSize)
	wt.AssertNoErr(t, err)
	wt.AssertTrue(t, resp != nil, "reponse from Get()")
	t.Logf("Received '%s'", resp.Answer[0])
	wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record")
	ttlGet1 := resp.Answer[0].Header().Ttl

	clk.Add(time.Duration(1) * time.Second)
	t.Logf("Checking that a second Get(), after 1 second, gets the same result, but with reduced TTL")
	resp, err = l.Get(questionMsg, minUDPSize)
	wt.AssertNoErr(t, err)
	wt.AssertTrue(t, resp != nil, "reponse from a second Get()")
	t.Logf("Received '%s'", resp.Answer[0])
	wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record")
	ttlGet2 := resp.Answer[0].Header().Ttl
	wt.AssertEqualInt(t, int(ttlGet1-ttlGet2), 1, "TTL difference")

	clk.Add(time.Duration(localTTL) * time.Second)
	t.Logf("Checking that a third Get(), after %d second, gets no result", localTTL)
	resp, err = l.Get(questionMsg, minUDPSize)
	wt.AssertNoErr(t, err)
	if resp != nil {
		t.Logf("Got\n%s", resp)
		t.Fatalf("ERROR: Did NOT expect a reponse from the second Get()")
	}

	t.Logf("Checking that an Remove() results in Get() returning nothing")
	records = []ZoneRecord{Record{"some.name", net.ParseIP("10.0.9.9"), 0, 0, 0}}
	replyTemp := makeAddressReply(questionMsg, question, records)
	l.Put(questionMsg, replyTemp, nullTTL, 0)
	lenBefore := l.Len()
	l.Remove(question)
	wt.AssertEqualInt(t, l.Len(), lenBefore-1, "cache length")
	l.Remove(question) // do it again: should have no effect...
	wt.AssertEqualInt(t, l.Len(), lenBefore-1, "cache length")

	resp, err = l.Get(questionMsg, minUDPSize)
	wt.AssertNoErr(t, err)
	wt.AssertTrue(t, resp == nil, "reponse from the Get() after a Remove()")

	t.Logf("Inserting a two replies for the same query")
	records = []ZoneRecord{Record{"some.name", net.ParseIP("10.0.1.2"), 0, 0, 0}}
	reply2 := makeAddressReply(questionMsg, question, records)
	l.Put(questionMsg, reply2, nullTTL, 0)
	clk.Add(time.Duration(1) * time.Second)
	records = []ZoneRecord{Record{"some.name", net.ParseIP("10.0.1.3"), 0, 0, 0}}
	reply3 := makeAddressReply(questionMsg, question, records)
	l.Put(questionMsg, reply3, nullTTL, 0)

	t.Logf("Checking we get the last one...")
	resp, err = l.Get(questionMsg, minUDPSize)
	wt.AssertNoErr(t, err)
	wt.AssertTrue(t, resp != nil, "reponse from the Get()")
	t.Logf("Received '%s'", resp.Answer[0])
	wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record")
	wt.AssertEqualString(t, resp.Answer[0].(*dns.A).A.String(), "10.0.1.3", "IP address")
	wt.AssertEqualInt(t, int(resp.Answer[0].Header().Ttl), int(localTTL), "TTL")

	clk.Add(time.Duration(localTTL-1) * time.Second)
	resp, err = l.Get(questionMsg, minUDPSize)
	wt.AssertNoErr(t, err)
	wt.AssertTrue(t, resp != nil, "reponse from the Get()")
	t.Logf("Received '%s'", resp.Answer[0])
	wt.AssertType(t, resp.Answer[0], (*dns.A)(nil), "DNS record")
	wt.AssertEqualString(t, resp.Answer[0].(*dns.A).A.String(), "10.0.1.3", "IP address")
	wt.AssertEqualInt(t, int(resp.Answer[0].Header().Ttl), 1, "TTL")

	t.Logf("Checking we get empty replies when they are expired...")
	lenBefore = l.Len()
	clk.Add(time.Duration(localTTL) * time.Second)
	resp, err = l.Get(questionMsg, minUDPSize)
	wt.AssertNoErr(t, err)
	if resp != nil {
		t.Logf("Got\n%s", resp.Answer[0])
		t.Fatalf("ERROR: Did NOT expect a reponse from the Get()")
	}
	wt.AssertEqualInt(t, l.Len(), lenBefore-1, "cache length (after getting an expired entry)")

	questionMsg2 := new(dns.Msg)
	questionMsg2.SetQuestion("some.other.name", dns.TypeA)
	questionMsg2.RecursionDesired = true
	question2 := &questionMsg2.Question[0]

	t.Logf("Trying to Get() a name")
	resp, err = l.Get(questionMsg2, minUDPSize)
	wt.AssertNoErr(t, err)
	wt.AssertNil(t, resp, "reponse from Get() yet")

	t.Logf("Checking that an Remove() between Get() and Put() does not break things")
	records = []ZoneRecord{Record{"some.name", net.ParseIP("10.0.9.9"), 0, 0, 0}}
	replyTemp2 := makeAddressReply(questionMsg2, question2, records)
	l.Remove(question2)
	l.Put(questionMsg2, replyTemp2, nullTTL, 0)
	resp, err = l.Get(questionMsg2, minUDPSize)
	wt.AssertNoErr(t, err)
	wt.AssertNotNil(t, resp, "reponse from Get()")

	questionMsg3 := new(dns.Msg)
	questionMsg3.SetQuestion("some.other.name", dns.TypeA)
	questionMsg3.RecursionDesired = true
	question3 := &questionMsg3.Question[0]

	t.Logf("Checking that a entry with CacheNoLocalReplies return an error")
	l.Put(questionMsg3, nil, nullTTL, CacheNoLocalReplies)
	resp, err = l.Get(questionMsg3, minUDPSize)
	wt.AssertNil(t, resp, "Get() response with CacheNoLocalReplies")
	wt.AssertNotNil(t, err, "Get() error with CacheNoLocalReplies")

	clk.Add(time.Second * time.Duration(negLocalTTL+1))
	t.Logf("Checking that we get an expired response after %f seconds", negLocalTTL)
	resp, err = l.Get(questionMsg3, minUDPSize)
	wt.AssertNil(t, resp, "expired Get() response with CacheNoLocalReplies")
	wt.AssertNil(t, err, "expired Get() error with CacheNoLocalReplies")

	l.Remove(question3)
	t.Logf("Checking that Put&Get with CacheNoLocalReplies with a Remove in the middle returns nothing")
	l.Put(questionMsg3, nil, nullTTL, CacheNoLocalReplies)
	l.Remove(question3)
	resp, err = l.Get(questionMsg3, minUDPSize)
	wt.AssertNil(t, resp, "Get() reponse with CacheNoLocalReplies")
	wt.AssertNil(t, err, "Get() error with CacheNoLocalReplies")
}
Example #12
0
func TestUDPDNSServer(t *testing.T) {
	setupForTest(t)
	const (
		containerID     = "foobar"
		successTestName = "test1.weave.local."
		failTestName    = "test2.weave.local."
		nonLocalName    = "weave.works."
		testAddr1       = "10.2.2.1"
	)
	testCIDR1 := testAddr1 + "/24"

	InitDefaultLogging(true)
	var zone = NewZoneDb(DefaultLocalDomain)
	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 == nonLocalName && 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." + nonLocalName}
			} else if q.Name == nonLocalName && q.Qtype == dns.TypeANY {
				m.Answer = make([]dns.RR, 512/len("mailn."+nonLocalName)+1)
				for i := range m.Answer {
					m.Answer[i] = &dns.MX{Hdr: dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 0}, Mx: fmt.Sprintf("mail%d.%s", i, nonLocalName)}
				}
			} 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(t, "127.0.0.1:0", fallbackHandler)
	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 := NewDNSServer(DNSServerConfig{UpstreamCfg: config, Port: testPort}, zone, nil)
	wt.AssertNoErr(t, err)
	defer srv.Stop()
	go srv.Start()
	time.Sleep(100 * time.Millisecond) // Allow sever goroutine to start

	var r *dns.Msg

	r = assertExchange(t, successTestName, dns.TypeA, 1, 1, 0)
	wt.AssertType(t, r.Answer[0], (*dns.A)(nil), "DNS record")
	wt.AssertEqualString(t, r.Answer[0].(*dns.A).A.String(), testAddr1, "IP address")

	assertExchange(t, failTestName, dns.TypeA, 0, 0, dns.RcodeNameError)

	r = assertExchange(t, testRDNSsuccess, dns.TypePTR, 1, 1, 0)
	wt.AssertType(t, r.Answer[0], (*dns.PTR)(nil), "DNS record")
	wt.AssertEqualString(t, r.Answer[0].(*dns.PTR).Ptr, successTestName, "IP address")

	assertExchange(t, testRDNSfail, dns.TypePTR, 0, 0, dns.RcodeNameError)

	// This should fail because we don't have information about MX records
	assertExchange(t, successTestName, dns.TypeMX, 0, 0, dns.RcodeNameError)

	// This non-local query for an MX record should succeed by being
	// passed on to the fallback server
	assertExchange(t, nonLocalName, dns.TypeMX, 1, -1, 0)

	// Now ask a query that we expect to return a lot of data.
	assertExchange(t, nonLocalName, dns.TypeANY, 5, -1, 0)

	assertExchange(t, testRDNSnonlocal, dns.TypePTR, 1, -1, 0)

	// Not testing MDNS functionality of server here (yet), since it
	// needs two servers, each listening on its own address
}
Example #13
0
func TestTCPDNSServer(t *testing.T) {
	setupForTest(t)
	const (
		numAnswers   = 512
		nonLocalName = "weave.works."
	)
	dnsAddr := fmt.Sprintf("localhost:%d", testPort)

	InitDefaultLogging(true)
	var zone = NewZoneDb(DefaultLocalDomain)

	// generate a list of `numAnswers` IP addresses
	var addrs []ZoneRecord
	bs := make([]byte, 4)
	for i := 0; i < numAnswers; i++ {
		binary.LittleEndian.PutUint32(bs, uint32(i))
		addrs = append(addrs, Record{"", net.IPv4(bs[0], bs[1], bs[2], bs[3]), 0, 0, 0})
	}

	// handler for the fallback server: it will just return a very long response
	fallbackUDPHandler := func(w dns.ResponseWriter, req *dns.Msg) {
		maxLen := getMaxReplyLen(req, protUDP)

		t.Logf("Fallback UDP server got asked: returning %d answers", numAnswers)
		q := req.Question[0]
		m := makeAddressReply(req, &q, addrs)
		mLen := m.Len()
		m.SetEdns0(uint16(maxLen), false)

		if mLen > maxLen {
			t.Logf("... truncated response (%d > %d)", mLen, maxLen)
			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)
	}

	t.Logf("Running a DNS fallback server with UDP")
	us, fallbackUDPAddr, err := runLocalUDPServer(t, "127.0.0.1:0", fallbackUDPHandler)
	wt.AssertNoErr(t, err)
	defer us.Shutdown()

	_, fallbackPort, err := net.SplitHostPort(fallbackUDPAddr)
	wt.AssertNoErr(t, err)

	t.Logf("Starting another fallback server, with TCP, 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, err := NewDNSServer(DNSServerConfig{UpstreamCfg: config, Port: testPort}, 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 = minUDPSize
	tc := new(dns.Client)
	tc.Net = "tcp"

	t.Logf("Creating DNS query message")
	m := new(dns.Msg)
	m.RecursionDesired = true
	m.SetQuestion(nonLocalName, dns.TypeA)

	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)
	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 returns a truncated reponse with UDP", dnsAddr)
	r, _, err = uc.Exchange(m, dnsAddr)
	t.Logf("UDP Response:\n%+v\n", r)
	wt.AssertNoErr(t, err)
	wt.AssertNotNil(t, r, "response")
	t.Logf("%d answers", len(r.Answer))
	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("TCP Response:\n%+v\n", r)
	wt.AssertNoErr(t, err)
	wt.AssertNotNil(t, r, "response")
	t.Logf("%d answers", len(r.Answer))
	wt.AssertFalse(t, r.MsgHdr.Truncated, "DNS truncated response flag")
	wt.AssertEqualInt(t, len(r.Answer), numAnswers, "number of answers (TCP)")

	t.Logf("Checking the WeaveDNS server at %s does not return a truncated reponse with UDP with a bigger buffer", dnsAddr)
	m.SetEdns0(testUDPBufSize, false)
	r, _, err = uc.Exchange(m, dnsAddr)
	t.Logf("UDP-large Response:\n%+v\n", r)
	wt.AssertNoErr(t, err)
	wt.AssertNotNil(t, r, "response")
	t.Logf("%d answers", len(r.Answer))
	wt.AssertNoErr(t, err)
	wt.AssertFalse(t, r.MsgHdr.Truncated, "DNS truncated response flag")
	wt.AssertEqualInt(t, len(r.Answer), numAnswers, "number of answers (UDP-long)")
}
Example #14
0
func TestHttp(t *testing.T) {
	var (
		containerID     = "deadbeef"
		testDomain      = "weave.local."
		successTestName = "test1." + testDomain
		testAddr1       = "10.2.2.1/24"
		dockerIP        = "9.8.7.6"
	)

	zone := NewZoneDb(DefaultLocalDomain)

	port := rand.Intn(10000) + 32768
	fmt.Println("Http test on port", port)
	go ListenHTTP("", nil, testDomain, zone, port)

	time.Sleep(100 * time.Millisecond) // Allow for http server to get going

	// Ask the http server to add our test address into the database
	addrParts := strings.Split(testAddr1, "/")
	addrURL := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, containerID, addrParts[0])
	resp, err := genForm("PUT", addrURL,
		url.Values{"fqdn": {successTestName}, "local_ip": {dockerIP}, "routing_prefix": {addrParts[1]}})
	wt.AssertNoErr(t, err)
	wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http response")

	// Check that the address is now there.
	ip, _, _ := net.ParseCIDR(testAddr1)
	foundIP, err := zone.LookupName(successTestName)
	wt.AssertNoErr(t, err)
	if !foundIP[0].IP().Equal(ip) {
		t.Fatalf("Unexpected result for %s: received %s, expected %s", successTestName, foundIP, ip)
	}

	// Adding exactly the same address should be OK
	resp, err = genForm("PUT", addrURL,
		url.Values{"fqdn": {successTestName}, "local_ip": {dockerIP}, "routing_prefix": {addrParts[1]}})
	wt.AssertNoErr(t, err)
	wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http success response for duplicate add")

	// Now try adding the same address again with a different ident --
	// again should be fine
	otherURL := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, "other", addrParts[0])
	resp, err = genForm("PUT", otherURL,
		url.Values{"fqdn": {successTestName}, "local_ip": {dockerIP}, "routing_prefix": {addrParts[1]}})
	wt.AssertNoErr(t, err)
	wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http response")

	// Delete the address
	resp, err = genForm("DELETE", addrURL, nil)
	wt.AssertNoErr(t, err)
	wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http response")

	// Check that the address is still resolvable.
	x, err := zone.LookupName(successTestName)
	t.Logf("Got %s", x)
	wt.AssertNoErr(t, err)

	// Delete the address record mentioning the other container
	resp, err = genForm("DELETE", otherURL, nil)
	wt.AssertNoErr(t, err)
	wt.AssertStatus(t, resp.StatusCode, http.StatusOK, "http response")

	// Check that the address is gone
	x, err = zone.LookupName(successTestName)
	t.Logf("Got %s", x)
	wt.AssertErrorType(t, err, (*LookupError)(nil), "fully-removed address")

	// Would like to shut down the http server at the end of this test
	// but it's complicated.
	// See https://groups.google.com/forum/#!topic/golang-nuts/vLHWa5sHnCE
}