Ejemplo n.º 1
0
func TestProtocolHandler(t *testing.T) {
	var (
		answers     = rand.Intn(12) + 3
		want        = answers / 2
		testHandler = dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
			res := &dns.Msg{}

			for i := 0; i < answers; i++ {
				rr := &dns.A{
					Hdr: dns.RR_Header{
						Name:   r.Question[0].Name,
						Rrtype: dns.TypeA,
						Class:  dns.ClassINET,
						Ttl:    5,
					},
					A: net.ParseIP(fmt.Sprintf("1.2.3.%d", i)),
				}
				res.Answer = append(res.Answer, rr)
			}

			err := w.WriteMsg(res)
			if err != nil {
				t.Fatalf("write response failed: %s", err)
			}
		})
	)

	w := &testWriter{
		remoteAddr: &net.UDPAddr{},
	}
	m := &dns.Msg{}
	m.SetQuestion(dns.Fqdn("app.glimpse.io"), dns.TypeA)

	protocolHandler(want, testHandler).ServeDNS(w, m)

	if have := len(w.msg.Answer); want != have {
		t.Errorf("want %d answers, have %d", want, have)
	}

	e := &errorWriter{w}
	errorHandler := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
		err := w.WriteMsg(&dns.Msg{})
		if err == nil {
			t.Fatalf("want WriteMsg() to fail with errorWriter")
		}
	})

	m = &dns.Msg{}
	m.SetQuestion(dns.Fqdn("app.glimpse.io"), dns.TypeA)
	protocolHandler(42, errorHandler).ServeDNS(e, m)
}
Ejemplo n.º 2
0
func (s *Server) systemdServe() {
	// We will usually have at least one TCP socket and one UDP socket.
	// PacketConns are UDP sockets, Listeners are TCP sockets.
	// To make things more annoying, both can (and usually will) have nil
	// entries for the file descriptors that don't match.
	pconns, err := activation.PacketConns(false)
	if err != nil {
		glog.Fatalf("Error getting systemd packet conns: %v", err)
	}

	listeners, err := activation.Listeners(false)
	if err != nil {
		glog.Fatalf("Error getting systemd listeners: %v", err)
	}

	var wg sync.WaitGroup

	for _, pconn := range pconns {
		if pconn == nil {
			continue
		}

		wg.Add(1)
		go func(c net.PacketConn) {
			defer wg.Done()
			glog.Infof("Activate on packet connection (UDP)")
			err := dns.ActivateAndServe(nil, c, dns.HandlerFunc(s.Handler))
			glog.Fatalf("Exiting UDP listener: %v", err)
		}(pconn)
	}

	for _, lis := range listeners {
		if lis == nil {
			continue
		}

		wg.Add(1)
		go func(l net.Listener) {
			defer wg.Done()
			glog.Infof("Activate on listening socket (TCP)")
			err := dns.ActivateAndServe(l, nil, dns.HandlerFunc(s.Handler))
			glog.Fatalf("Exiting TCP listener: %v", err)
		}(lis)
	}

	wg.Wait()

	// We should only get here if there were no useful sockets.
	glog.Fatalf("No systemd sockets, did you forget the .socket?")
}
Ejemplo n.º 3
0
func TestDNSLoggingHandlerError(t *testing.T) {
	var (
		b = &bytes.Buffer{}
		l = log.New(b, "glimpse-agent ", log.Lmicroseconds)
		m = &dns.Msg{}
		e = &errorWriter{
			&testWriter{
				remoteAddr: &net.UDPAddr{},
			},
		}
		errorHandler = dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
			err := w.WriteMsg(&dns.Msg{})
			if err == nil {
				t.Fatalf("want WriteMsg() to fail with errorWriter")
			}
		})
	)

	m.SetQuestion(dns.Fqdn("app.glimpse.io"), dns.TypeA)
	dnsLoggingHandler(l, errorHandler).ServeDNS(e, m)

	sp := strings.SplitN(strings.Trim(b.String(), "\n"), " ", 10)

	if want, have := "error: failed write", sp[9]; want != have {
		t.Errorf("want %#v, have %#v", want, have)
	}
}
Ejemplo n.º 4
0
func (c *MDNSClient) Start(ifi *net.Interface) (err error) {
	if c.running {
		return nil
	}

	if c.conn, err = net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}); err != nil {
		return err
	}

	multicast, err := LinkLocalMulticastListener(ifi)
	if err != nil {
		return err
	}

	handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) {
		// Don't want to handle queries here, so filter anything out that isn't a response
		if len(r.Answer) > 0 {
			c.ResponseCallback(r)
		}
	}

	c.listener = &dns.Server{Unsafe: true, PacketConn: multicast, Handler: dns.HandlerFunc(handleMDNS)}
	go c.listener.ActivateAndServe()

	actionChan := make(chan MDNSAction, MailboxSize)
	c.actionChan = actionChan
	go c.actorLoop(actionChan)
	return nil
}
Ejemplo n.º 5
0
func TestDNSLoggingHandler(t *testing.T) {
	var (
		b    = &bytes.Buffer{}
		l    = log.New(b, "glimpse-agent ", log.Lmicroseconds)
		fqdn = dns.Fqdn("db.glimpse.io")
		w    = &testWriter{
			remoteAddr: &net.UDPAddr{
				IP:   net.ParseIP("8.7.6.5"),
				Port: 4321,
			},
		}
		testHandler = dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
			res := &dns.Msg{}
			res.SetReply(req)
			res.SetRcode(req, dns.RcodeNotImplemented)

			err := w.WriteMsg(res)
			if err != nil {
				t.Fatalf("write response failed: %s", err)
			}
		})
	)

	m := &dns.Msg{}
	m.SetQuestion(fqdn, dns.TypeA)

	dnsLoggingHandler(l, testHandler).ServeDNS(w, m)

	sp := strings.SplitN(strings.Trim(b.String(), "\n"), " ", 10)

	if want, have := 9, len(sp); want != have {
		t.Fatalf("want %d fields, have %d fields", want, have)
	}

	if want, have := "DNS", sp[2]; want != have {
		t.Errorf("want %s, have %s", want, have)
	}

	if want, have := "8.7.6.5:4321", sp[4]; want != have {
		t.Errorf("want %s, have %s", want, have)
	}

	if want, have := "A", sp[5]; want != have {
		t.Errorf("want %s, have %s", want, have)
	}

	if want, have := fqdn, sp[6]; want != have {
		t.Errorf("want %s, have %s", want, have)
	}

	if want, have := dns.RcodeToString[dns.RcodeNotImplemented], sp[7]; want != have {
		t.Errorf("want %s, have %s", want, have)
	}

	if want, have := "0", sp[8]; want != have {
		t.Errorf("want %s, have %s", want, have)
	}
}
Ejemplo n.º 6
0
func main() {
	var listenports, ipranges StringList
	var remote = flag.Bool("remote", false, "Run as the remote-end of the tunnel.")
	var dnsaddr = flag.String("dns-addr", ":53", "Listen address for local DNS server.")
	flag.Var(&ipranges, "iprange", "The IP address range to use for the proxy pool. Acceptable "+
		"values are CIDR-style network addresses & single IP addresses. Can be set multiple "+
		"times for multiple ranges.")
	flag.Var(&listenports, "port", "TCP port to proxy. Can be set multiple times for multiple "+
		"ports.")
	flag.StringVar(&tunneladdr, "tunnel-addr", "", "Address for the remote-end of the tunnel.")
	flag.StringVar(&dnsserver, "upstream-dns-server", "8.8.8.8:53", "DNS server to rely our "+
		"incoming requests to.")
	flag.Parse()

	if *remote {
		log.Fatal(listenAndTunnel(tunneladdr))
	}

	addrpool.pool.OnEvicted = func(key lru.Key, value interface{}) {
		log.Printf("Evicting %s -> %v", uint32toip4(key.(uint32)), value.(string))
		addrpool.freeaddr = uint32toip4(key.(uint32))
		delete(addrpool.domains, value.(string))
	}

	// We pass IPs onto this channel as we parse them.
	ipchan := make(chan uint32)
	go func() {
		for ip := range ipchan {
			log.Printf("Adding %v", uint32toip4(ip))
			addrpool.pool.Add(ip, "--invalid--")
			for _, port := range listenports {
				go listenAndProxy(uint32toip4(ip).String() + ":" + port)
			}
		}
	}()

	for _, r := range ipranges {
		if err := parseCIDR(r, ipchan); err == nil {
			continue
		}
		if err := parseIP(r, ipchan); err == nil {
			continue
		}
		// TODO nmap-style octet addressing ip-range parsing.
		log.Fatalf("Couldn't parse address range %v", r)
	}

	close(ipchan)

	server := &dns.Server{
		Addr:    *dnsaddr,
		Net:     "udp",
		Handler: dns.HandlerFunc(resolver),
	}
	log.Fatal(server.ListenAndServe())
}
Ejemplo n.º 7
0
// NewServer creates a new Server with the given options.
func NewServer(o Options) (*Server, error) {
	if err := o.validate(); err != nil {
		return nil, err
	}

	s := Server{
		c: &dns.Client{},
		s: &dns.Server{
			Net:  o.Net,
			Addr: o.Bind,
		},
		white:   o.Whitelist != "",
		hosts:   hosts{},
		hostsRX: hostsRX{},

		privateHosts:   map[string]struct{}{},
		privateHostsRX: map[string]*regexp.Regexp{},
	}

	hostListPath := o.Whitelist
	if hostListPath == "" {
		hostListPath = o.Blacklist
	}
	s.hostsFile.path = hostListPath
	if err := s.loadHostEntries(); err != nil {
		return nil, err
	}
	if o.Poll != 0 {
		go s.monitorHostEntries(o.Poll)
	}
	s.s.Handler = dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
		// If no upstream proxy is present, drop the query:
		if len(o.Resolve) == 0 {
			dns.HandleFailed(w, r)
			return
		}

		// Filter Questions:
		if r.Question = s.filter(r.Question); len(r.Question) == 0 {
			w.WriteMsg(r)
			return
		}

		// Proxy Query:
		for _, addr := range o.Resolve {
			in, _, err := s.c.Exchange(r, addr)
			if err != nil {
				continue
			}
			w.WriteMsg(in)
			return
		}
		dns.HandleFailed(w, r)
	})
	return &s, nil
}
Ejemplo n.º 8
0
Archivo: dns.go Proyecto: pgpst/tunnel
func (d *DNS) ListenAndServe() error {
	udpErr := make(chan error)
	tcpErr := make(chan error)
	go func() {
		udpErr <- dns.ListenAndServe(d.Config.Bind, "udp", dns.HandlerFunc(d.handler))
	}()
	go func() {
		tcpErr <- dns.ListenAndServe(d.Config.Bind, "tcp", dns.HandlerFunc(d.handler))
	}()

	select {
	case err := <-udpErr:
		return err
	case err := <-tcpErr:
		return err
	}

	return nil
}
Ejemplo n.º 9
0
func TestDnsMetricsHandler(t *testing.T) {
	testHandler := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
		res := &dns.Msg{}
		res.SetReply(req)
		res.SetRcode(req, dns.RcodeNotImplemented)

		err := w.WriteMsg(res)
		if err != nil {
			t.Fatalf("write response failed: %s", err)
		}
	})

	w := &testWriter{
		remoteAddr: &net.UDPAddr{},
	}

	m := &dns.Msg{}
	m.SetQuestion(dns.Fqdn("app.glimpse.io"), dns.TypeA)

	dnsMetricsHandler(testHandler).ServeDNS(w, m)
	r := w.msg

	if want, have := dns.RcodeNotImplemented, r.Rcode; want != have {
		t.Errorf(
			"want rcode %s, have %s",
			dns.RcodeToString[want],
			dns.RcodeToString[have],
		)
	}

	e := &errorWriter{w}
	errorHandler := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
		err := w.WriteMsg(&dns.Msg{})
		if err == nil {
			t.Fatalf("want WriteMsg() to fail with errorWriter")
		}
	})

	m = &dns.Msg{}
	m.SetQuestion(dns.Fqdn("app.glimpse.io"), dns.TypeA)
	dnsMetricsHandler(errorHandler).ServeDNS(e, m)
}
Ejemplo n.º 10
0
func NewServer() Server {
	server := Server{
		records:              make(map[string][]miekgdns.RR),
		truncatedDomainNames: make(map[string]bool),
	}

	server.udpServer = &miekgdns.Server{
		Addr:    ":53",
		Net:     "udp",
		Handler: miekgdns.HandlerFunc(server.handleUDPDNSRequest),
	}

	server.tcpServer = &miekgdns.Server{
		Addr:    ":53",
		Net:     "tcp",
		Handler: miekgdns.HandlerFunc(server.handleTCPDNSRequest),
	}

	return server
}
Ejemplo n.º 11
0
func initDNS() {
	if GConf.LocalDNS.CacheSize > 0 {
		dnsCache, _ = lru.New(GConf.LocalDNS.CacheSize)
	}
	if len(GConf.LocalDNS.Listen) > 0 {
		err := dns.ListenAndServe(GConf.LocalDNS.Listen, "udp", dns.HandlerFunc(proxyDNS))
		if nil != err {
			log.Printf("Failed to start dns server:%v", err)
		}
	}
}
Ejemplo n.º 12
0
func (s *Server) classicServe() {
	glog.Infof("DNS listening on %s", s.Addr)

	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		err := dns.ListenAndServe(s.Addr, "udp", dns.HandlerFunc(s.Handler))
		glog.Fatalf("Exiting UDP: %v", err)
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		err := dns.ListenAndServe(s.Addr, "tcp", dns.HandlerFunc(s.Handler))
		glog.Fatalf("Exiting TCP: %v", err)
	}()

	wg.Wait()
}
Ejemplo n.º 13
0
func dnsDispatch() {
	udpConn, err := net.ListenUDP("udp", dnsThis)
	if err != nil {
		kilog.Critical("%v", err.Error())
		os.Exit(-1)
	}

	// hack to make Windows happy
	nameToIP["dns.msftncsi.com."] = "131.107.255.255"

	dns.ActivateAndServe(nil, udpConn, dns.HandlerFunc(dnsHandle))
}
Ejemplo n.º 14
0
func RunLocalMulticastServer() (*dns.Server, error) {
	multicast, err := LinkLocalMulticastListener(nil)
	if err != nil {
		return nil, err
	}
	server := &dns.Server{
		PacketConn:  multicast,
		Handler:     dns.HandlerFunc(minimalServer),
		ReadTimeout: 100 * time.Millisecond}
	go server.ActivateAndServe()
	return server, nil
}
Ejemplo n.º 15
0
func (s HelixServer) Start() {
	server := &dns.Server{
		Addr:         ":" + strconv.Itoa(s.Port),
		Net:          "udp",
		Handler:      dns.HandlerFunc(s.Handler),
		ReadTimeout:  10,
		WriteTimeout: 10,
	}

	log.Print("Starting server...")

	server.ListenAndServe()
}
Ejemplo n.º 16
0
// TvproxySrv is the main exported method to run the dns server
// It will forward requests we're not interested in, otherwise it'll
// intercept for us and return the external address of this server as
// specified in the config
func TvproxySrv(port string) {

	pc, err := net.ListenPacket("udp", port)
	if err != nil {
		fmt.Printf("Cannot listen on address %s", port)
		return
	}

	fmt.Printf("Starting server on %s\n", port)

	srv := &dns.Server{Addr: port, Net: "udp", PacketConn: pc, Handler: dns.HandlerFunc(interceptRequest)}
	defer srv.Shutdown()

	// periodically update the cache
	//go refreshCache()
	// start the dns server. Ctrl + C (etc) to kill
	srv.ActivateAndServe()
}
Ejemplo n.º 17
0
func (s *dnsServer) ListenAndServe() {
	var err error

	s.answerRR, err = dns.NewRR("test.blah A 1.2.3.4")
	if err != nil {
		panic(err)
	}

	s.srv = &dns.Server{
		Addr:    s.Addr,
		Net:     "udp",
		Handler: dns.HandlerFunc(s.Handler),
	}
	err = s.srv.ListenAndServe()
	if err != nil {
		panic(err)
	}
}
Ejemplo n.º 18
0
func main() {
	var addr = flag.String("addr", "127.0.0.1:5300", "listen address")
	var ip = flag.String("ip", "127.0.0.1", "resolve ipv4 address")

	flag.Parse()

	resolveIP = net.ParseIP(*ip)

	if resolveIP == nil {
		log.Fatalf("Invalid ip address: %s\n", *ip)
	}

	if resolveIP.To4() == nil {
		log.Fatalf("Invalid ipv4 address: %s\n", *ip)
	}

	server := &dns.Server{Addr: *addr, Net: "udp"}
	server.Handler = dns.HandlerFunc(handleRequest)

	log.Printf("Listening on %s, resolving to %s\n", *addr, *ip)
	log.Fatal(server.ListenAndServe())
}
Ejemplo n.º 19
0
func startUpstreamTestServer(c *C) (string, string, func()) {
	h := dns.HandlerFunc(func(w dns.ResponseWriter, req *dns.Msg) {
		res := &dns.Msg{}
		res.SetReply(req)
		switch req.Question[0].Name {
		case "long-compressed-response.":
			for i := 0; i < 25; i++ {
				res.Answer = append(res.Answer, &dns.A{
					Hdr: dns.RR_Header{
						Name:   req.Question[0].Name,
						Rrtype: dns.TypeA,
						Class:  dns.ClassINET,
					},
					A: net.IP{192, 168, 0, byte(i)},
				})
			}
			if res.Len() <= 512 {
				panic("not huge")
			}
			res.Compress = true
			if res.Len() > 512 {
				panic("too big compressed")
			}
			w.WriteMsg(res)
		}
	})
	up := make(chan struct{}, 2)
	notifyStart := func() { up <- struct{}{} }

	udpListener, err := net.ListenPacket("udp4", "127.0.0.1:0")
	c.Assert(err, IsNil)
	udp := &dns.Server{
		Net:               "udp",
		PacketConn:        udpListener,
		Handler:           h,
		NotifyStartedFunc: notifyStart,
	}
	go udp.ActivateAndServe()

	tcpListener, err := net.Listen("tcp4", "127.0.0.1:0")
	c.Assert(err, IsNil)
	tcp := &dns.Server{
		Net:               "tcp",
		Listener:          tcpListener,
		Handler:           h,
		NotifyStartedFunc: notifyStart,
	}
	go tcp.ActivateAndServe()

	for i := 0; i < 2; i++ {
		select {
		case <-up:
		case <-time.After(5 * time.Second):
			c.Fatal("timed out waiting for server to start")
		}
	}

	return udpListener.(*net.UDPConn).LocalAddr().String(), tcpListener.Addr().String(), func() {
		udp.Shutdown()
		tcp.Shutdown()
	}
}
Ejemplo n.º 20
0
func main() {
	l := lever.New("struggledns", nil)
	l.Add(lever.Param{
		Name:        "--listen-addr",
		Description: "Address to listen on for dns requests. Will bind to both tcp and udp",
		Default:     ":53",
	})
	l.Add(lever.Param{
		Name:        "--fwd-to",
		Description: "Address (ip:port) of a dns server to attempt forward requests to. Specify multiple times to make multiple request attempts. Order specified dictates precedence should more than one server respond for a request",
	})
	l.Add(lever.Param{
		Name:        "--parallel",
		Description: "If sent the query will be sent to all addresses in parallel",
		Flag:        true,
	})
	l.Add(lever.Param{
		Name:        "--timeout",
		Description: "Timeout in milliseconds for each request",
		Default:     "300",
	})
	l.Add(lever.Param{
		Name:        "--log-level",
		Description: "Minimum log level to show, either debug, info, warn, error, or fatal",
		Default:     "warn",
	})
	l.Add(lever.Param{
		Name:        "--allow-truncated",
		Description: "If we should allow truncated responses to be proxied",
		Flag:        true,
	})
	if version != "" {
		l.Add(lever.Param{
			Name:        "--version",
			Aliases:     []string{"-v"},
			Description: "Print version info",
			Flag:        true,
		})
	}
	l.Parse()

	if l.ParamFlag("--version") {
		fmt.Println(version)
		return
	}

	addr, _ := l.ParamStr("--listen-addr")
	dnsServers, _ := l.ParamStrs("--fwd-to")
	combineGroups := l.ParamFlag("--parallel")
	timeout, _ := l.ParamInt("--timeout")

	logLevel, _ := l.ParamStr("--log-level")
	llog.SetLevelFromString(logLevel)

	allowTruncated = l.ParamFlag("--allow-truncated")

	if combineGroups {
		//combine all the servers sent into one group
		dnsServerGroups = make([][]string, 1)
		var groupServers []string
		for i := range dnsServers {
			groupServers = strings.Split(dnsServers[i], ",")
			dnsServerGroups[0] = append(dnsServerGroups[0], groupServers...)
		}
	} else {
		dnsServerGroups = make([][]string, len(dnsServers))
		for i := range dnsServers {
			dnsServerGroups[i] = strings.Split(dnsServers[i], ",")
		}
	}

	client = dns.Client{
		//since this is UDP, the Dial/Write timeouts don't mean much
		//we really only care about setting the read
		DialTimeout:  time.Millisecond * 100,
		WriteTimeout: time.Millisecond * 100,
		ReadTimeout:  time.Millisecond * time.Duration(timeout),
		UDPSize:      4096,
	}

	handler := dns.HandlerFunc(handleRequest)
	go func() {
		llog.Info("listening on udp", llog.KV{"addr": addr})
		err := dns.ListenAndServe(addr, "udp", handler)
		llog.Fatal("error listening on udp", llog.KV{"err": err})
	}()
	go func() {
		llog.Info("listening on tcp", llog.KV{"addr": addr})
		err := dns.ListenAndServe(addr, "tcp", handler)
		llog.Fatal("error listening on tcp", llog.KV{"err": err})
	}()

	select {}
}
Ejemplo n.º 21
0
// TODO(alx): Settle on naming for handlers acting as middleware.
func protocolHandler(maxAnswers int, next dns.Handler) dns.Handler {
	return dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
		next.ServeDNS(&truncatingWriter{w, maxAnswers}, r)
	})
}
Ejemplo n.º 22
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, true, DefaultLocalTTL)
	require.NoError(t, err)
	err = mdnsServer.Start(nil)
	require.NoError(t, err)
	defer mdnsServer.Stop()

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

	// Implement a minimal listener for responses
	multicast, err := LinkLocalMulticastListener(nil)
	require.NoError(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 {
				recvChan <- answer
			}
			recvChan <- "ok"
		}
	}

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

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

		Debug.Printf("Waiting for response")
		for {
			select {
			case x := <-recvChan:
				switch rr := x.(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++
				case string:
					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)
	}
}