276 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			276 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package mdns
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"net"
 | 
						|
	"reflect"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/miekg/dns"
 | 
						|
)
 | 
						|
 | 
						|
func makeService(t *testing.T) *MDNSService {
 | 
						|
	return makeServiceWithServiceName(t, "_http._tcp")
 | 
						|
}
 | 
						|
 | 
						|
func makeServiceWithServiceName(t *testing.T, service string) *MDNSService {
 | 
						|
	m, err := NewMDNSService(
 | 
						|
		"hostname",
 | 
						|
		service,
 | 
						|
		"local.",
 | 
						|
		"testhost.",
 | 
						|
		80, // port
 | 
						|
		[]net.IP{net.IP([]byte{192, 168, 0, 42}), net.ParseIP("2620:0:1000:1900:b0c2:d0b2:c411:18bc")},
 | 
						|
		[]string{"Local web server"}) // TXT
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("err: %v", err)
 | 
						|
	}
 | 
						|
 | 
						|
	return m
 | 
						|
}
 | 
						|
 | 
						|
func TestNewMDNSService_BadParams(t *testing.T) {
 | 
						|
	for _, test := range []struct {
 | 
						|
		testName string
 | 
						|
		hostName string
 | 
						|
		domain   string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			"NewMDNSService should fail when passed hostName that is not a legal fully-qualified domain name",
 | 
						|
			"hostname", // not legal FQDN - should be "hostname." or "hostname.local.", etc.
 | 
						|
			"local.",   // legal
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"NewMDNSService should fail when passed domain that is not a legal fully-qualified domain name",
 | 
						|
			"hostname.", // legal
 | 
						|
			"local",     // should be "local."
 | 
						|
		},
 | 
						|
	} {
 | 
						|
		_, err := NewMDNSService(
 | 
						|
			"instance name",
 | 
						|
			"_http._tcp",
 | 
						|
			test.domain,
 | 
						|
			test.hostName,
 | 
						|
			80, // port
 | 
						|
			[]net.IP{net.IP([]byte{192, 168, 0, 42})},
 | 
						|
			[]string{"Local web server"}) // TXT
 | 
						|
		if err == nil {
 | 
						|
			t.Fatalf("%s: error expected, but got none", test.testName)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestMDNSService_BadAddr(t *testing.T) {
 | 
						|
	s := makeService(t)
 | 
						|
	q := dns.Question{
 | 
						|
		Name:  "random",
 | 
						|
		Qtype: dns.TypeANY,
 | 
						|
	}
 | 
						|
	recs := s.Records(q)
 | 
						|
	if len(recs) != 0 {
 | 
						|
		t.Fatalf("bad: %v", recs)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestMDNSService_ServiceAddr(t *testing.T) {
 | 
						|
	s := makeService(t)
 | 
						|
	q := dns.Question{
 | 
						|
		Name:  "_http._tcp.local.",
 | 
						|
		Qtype: dns.TypeANY,
 | 
						|
	}
 | 
						|
	recs := s.Records(q)
 | 
						|
	if got, want := len(recs), 5; got != want {
 | 
						|
		t.Fatalf("got %d records, want %d: %v", got, want, recs)
 | 
						|
	}
 | 
						|
 | 
						|
	if ptr, ok := recs[0].(*dns.PTR); !ok {
 | 
						|
		t.Errorf("recs[0] should be PTR record, got: %v, all records: %v", recs[0], recs)
 | 
						|
	} else if got, want := ptr.Ptr, "hostname._http._tcp.local."; got != want {
 | 
						|
		t.Fatalf("bad PTR record %v: got %v, want %v", ptr, got, want)
 | 
						|
	}
 | 
						|
 | 
						|
	if _, ok := recs[1].(*dns.SRV); !ok {
 | 
						|
		t.Errorf("recs[1] should be SRV record, got: %v, all reccords: %v", recs[1], recs)
 | 
						|
	}
 | 
						|
	if _, ok := recs[2].(*dns.A); !ok {
 | 
						|
		t.Errorf("recs[2] should be A record, got: %v, all records: %v", recs[2], recs)
 | 
						|
	}
 | 
						|
	if _, ok := recs[3].(*dns.AAAA); !ok {
 | 
						|
		t.Errorf("recs[3] should be AAAA record, got: %v, all records: %v", recs[3], recs)
 | 
						|
	}
 | 
						|
	if _, ok := recs[4].(*dns.TXT); !ok {
 | 
						|
		t.Errorf("recs[4] should be TXT record, got: %v, all records: %v", recs[4], recs)
 | 
						|
	}
 | 
						|
 | 
						|
	q.Qtype = dns.TypePTR
 | 
						|
	if recs2 := s.Records(q); !reflect.DeepEqual(recs, recs2) {
 | 
						|
		t.Fatalf("PTR question should return same result as ANY question: ANY => %v, PTR => %v", recs, recs2)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestMDNSService_InstanceAddr_ANY(t *testing.T) {
 | 
						|
	s := makeService(t)
 | 
						|
	q := dns.Question{
 | 
						|
		Name:  "hostname._http._tcp.local.",
 | 
						|
		Qtype: dns.TypeANY,
 | 
						|
	}
 | 
						|
	recs := s.Records(q)
 | 
						|
	if len(recs) != 4 {
 | 
						|
		t.Fatalf("bad: %v", recs)
 | 
						|
	}
 | 
						|
	if _, ok := recs[0].(*dns.SRV); !ok {
 | 
						|
		t.Fatalf("bad: %v", recs[0])
 | 
						|
	}
 | 
						|
	if _, ok := recs[1].(*dns.A); !ok {
 | 
						|
		t.Fatalf("bad: %v", recs[1])
 | 
						|
	}
 | 
						|
	if _, ok := recs[2].(*dns.AAAA); !ok {
 | 
						|
		t.Fatalf("bad: %v", recs[2])
 | 
						|
	}
 | 
						|
	if _, ok := recs[3].(*dns.TXT); !ok {
 | 
						|
		t.Fatalf("bad: %v", recs[3])
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestMDNSService_InstanceAddr_SRV(t *testing.T) {
 | 
						|
	s := makeService(t)
 | 
						|
	q := dns.Question{
 | 
						|
		Name:  "hostname._http._tcp.local.",
 | 
						|
		Qtype: dns.TypeSRV,
 | 
						|
	}
 | 
						|
	recs := s.Records(q)
 | 
						|
	if len(recs) != 3 {
 | 
						|
		t.Fatalf("bad: %v", recs)
 | 
						|
	}
 | 
						|
	srv, ok := recs[0].(*dns.SRV)
 | 
						|
	if !ok {
 | 
						|
		t.Fatalf("bad: %v", recs[0])
 | 
						|
	}
 | 
						|
	if _, ok := recs[1].(*dns.A); !ok {
 | 
						|
		t.Fatalf("bad: %v", recs[1])
 | 
						|
	}
 | 
						|
	if _, ok := recs[2].(*dns.AAAA); !ok {
 | 
						|
		t.Fatalf("bad: %v", recs[2])
 | 
						|
	}
 | 
						|
 | 
						|
	if srv.Port != uint16(s.Port) {
 | 
						|
		t.Fatalf("bad: %v", recs[0])
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestMDNSService_InstanceAddr_A(t *testing.T) {
 | 
						|
	s := makeService(t)
 | 
						|
	q := dns.Question{
 | 
						|
		Name:  "hostname._http._tcp.local.",
 | 
						|
		Qtype: dns.TypeA,
 | 
						|
	}
 | 
						|
	recs := s.Records(q)
 | 
						|
	if len(recs) != 1 {
 | 
						|
		t.Fatalf("bad: %v", recs)
 | 
						|
	}
 | 
						|
	a, ok := recs[0].(*dns.A)
 | 
						|
	if !ok {
 | 
						|
		t.Fatalf("bad: %v", recs[0])
 | 
						|
	}
 | 
						|
	if !bytes.Equal(a.A, []byte{192, 168, 0, 42}) {
 | 
						|
		t.Fatalf("bad: %v", recs[0])
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestMDNSService_InstanceAddr_AAAA(t *testing.T) {
 | 
						|
	s := makeService(t)
 | 
						|
	q := dns.Question{
 | 
						|
		Name:  "hostname._http._tcp.local.",
 | 
						|
		Qtype: dns.TypeAAAA,
 | 
						|
	}
 | 
						|
	recs := s.Records(q)
 | 
						|
	if len(recs) != 1 {
 | 
						|
		t.Fatalf("bad: %v", recs)
 | 
						|
	}
 | 
						|
	a4, ok := recs[0].(*dns.AAAA)
 | 
						|
	if !ok {
 | 
						|
		t.Fatalf("bad: %v", recs[0])
 | 
						|
	}
 | 
						|
	ip6 := net.ParseIP("2620:0:1000:1900:b0c2:d0b2:c411:18bc")
 | 
						|
	if got := len(ip6); got != net.IPv6len {
 | 
						|
		t.Fatalf("test IP failed to parse (len = %d, want %d)", got, net.IPv6len)
 | 
						|
	}
 | 
						|
	if !bytes.Equal(a4.AAAA, ip6) {
 | 
						|
		t.Fatalf("bad: %v", recs[0])
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestMDNSService_InstanceAddr_TXT(t *testing.T) {
 | 
						|
	s := makeService(t)
 | 
						|
	q := dns.Question{
 | 
						|
		Name:  "hostname._http._tcp.local.",
 | 
						|
		Qtype: dns.TypeTXT,
 | 
						|
	}
 | 
						|
	recs := s.Records(q)
 | 
						|
	if len(recs) != 1 {
 | 
						|
		t.Fatalf("bad: %v", recs)
 | 
						|
	}
 | 
						|
	txt, ok := recs[0].(*dns.TXT)
 | 
						|
	if !ok {
 | 
						|
		t.Fatalf("bad: %v", recs[0])
 | 
						|
	}
 | 
						|
	if got, want := txt.Txt, s.TXT; !reflect.DeepEqual(got, want) {
 | 
						|
		t.Fatalf("TXT record mismatch for %v: got %v, want %v", recs[0], got, want)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestMDNSService_HostNameQuery(t *testing.T) {
 | 
						|
	s := makeService(t)
 | 
						|
	for _, test := range []struct {
 | 
						|
		q    dns.Question
 | 
						|
		want []dns.RR
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			dns.Question{Name: "testhost.", Qtype: dns.TypeA},
 | 
						|
			[]dns.RR{&dns.A{
 | 
						|
				Hdr: dns.RR_Header{
 | 
						|
					Name:   "testhost.",
 | 
						|
					Rrtype: dns.TypeA,
 | 
						|
					Class:  dns.ClassINET,
 | 
						|
					Ttl:    120,
 | 
						|
				},
 | 
						|
				A: net.IP([]byte{192, 168, 0, 42}),
 | 
						|
			}},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			dns.Question{Name: "testhost.", Qtype: dns.TypeAAAA},
 | 
						|
			[]dns.RR{&dns.AAAA{
 | 
						|
				Hdr: dns.RR_Header{
 | 
						|
					Name:   "testhost.",
 | 
						|
					Rrtype: dns.TypeAAAA,
 | 
						|
					Class:  dns.ClassINET,
 | 
						|
					Ttl:    120,
 | 
						|
				},
 | 
						|
				AAAA: net.ParseIP("2620:0:1000:1900:b0c2:d0b2:c411:18bc"),
 | 
						|
			}},
 | 
						|
		},
 | 
						|
	} {
 | 
						|
		if got := s.Records(test.q); !reflect.DeepEqual(got, test.want) {
 | 
						|
			t.Errorf("hostname query failed: s.Records(%v) = %v, want %v", test.q, got, test.want)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestMDNSService_serviceEnum_PTR(t *testing.T) {
 | 
						|
	s := makeService(t)
 | 
						|
	q := dns.Question{
 | 
						|
		Name:  "_services._dns-sd._udp.local.",
 | 
						|
		Qtype: dns.TypePTR,
 | 
						|
	}
 | 
						|
	recs := s.Records(q)
 | 
						|
	if len(recs) != 1 {
 | 
						|
		t.Fatalf("bad: %v", recs)
 | 
						|
	}
 | 
						|
	if ptr, ok := recs[0].(*dns.PTR); !ok {
 | 
						|
		t.Errorf("recs[0] should be PTR record, got: %v, all records: %v", recs[0], recs)
 | 
						|
	} else if got, want := ptr.Ptr, "_http._tcp.local."; got != want {
 | 
						|
		t.Fatalf("bad PTR record %v: got %v, want %v", ptr, got, want)
 | 
						|
	}
 | 
						|
}
 |