github.com/volts-dev/volts@v0.0.0-20240120094013-5e9c65924106/internal/mdns/zone_test.go (about)

     1  package mdns
     2  
     3  import (
     4  	"bytes"
     5  	"net"
     6  	"reflect"
     7  	"testing"
     8  
     9  	"github.com/miekg/dns"
    10  )
    11  
    12  func makeService(t *testing.T) *MDNSService {
    13  	return makeServiceWithServiceName(t, "_http._tcp")
    14  }
    15  
    16  func makeServiceWithServiceName(t *testing.T, service string) *MDNSService {
    17  	m, err := NewMDNSService(
    18  		"hostname",
    19  		service,
    20  		"local.",
    21  		"testhost.",
    22  		80, // port
    23  		[]net.IP{net.IP([]byte{192, 168, 0, 42}), net.ParseIP("2620:0:1000:1900:b0c2:d0b2:c411:18bc")},
    24  		[]string{"Local web server"}) // TXT
    25  
    26  	if err != nil {
    27  		t.Fatalf("err: %v", err)
    28  	}
    29  
    30  	return m
    31  }
    32  
    33  func TestNewMDNSService_BadParams(t *testing.T) {
    34  	for _, test := range []struct {
    35  		testName string
    36  		hostName string
    37  		domain   string
    38  	}{
    39  		{
    40  			"NewMDNSService should fail when passed hostName that is not a legal fully-qualified domain name",
    41  			"hostname", // not legal FQDN - should be "hostname." or "hostname.local.", etc.
    42  			"local.",   // legal
    43  		},
    44  		{
    45  			"NewMDNSService should fail when passed domain that is not a legal fully-qualified domain name",
    46  			"hostname.", // legal
    47  			"local",     // should be "local."
    48  		},
    49  	} {
    50  		_, err := NewMDNSService(
    51  			"instance name",
    52  			"_http._tcp",
    53  			test.domain,
    54  			test.hostName,
    55  			80, // port
    56  			[]net.IP{net.IP([]byte{192, 168, 0, 42})},
    57  			[]string{"Local web server"}) // TXT
    58  		if err == nil {
    59  			t.Fatalf("%s: error expected, but got none", test.testName)
    60  		}
    61  	}
    62  }
    63  
    64  func TestMDNSService_BadAddr(t *testing.T) {
    65  	s := makeService(t)
    66  	q := dns.Question{
    67  		Name:  "random",
    68  		Qtype: dns.TypeANY,
    69  	}
    70  	recs := s.Records(q)
    71  	if len(recs) != 0 {
    72  		t.Fatalf("bad: %v", recs)
    73  	}
    74  }
    75  
    76  func TestMDNSService_ServiceAddr(t *testing.T) {
    77  	s := makeService(t)
    78  	q := dns.Question{
    79  		Name:  "_http._tcp.local.",
    80  		Qtype: dns.TypeANY,
    81  	}
    82  	recs := s.Records(q)
    83  	if got, want := len(recs), 5; got != want {
    84  		t.Fatalf("got %d records, want %d: %v", got, want, recs)
    85  	}
    86  
    87  	if ptr, ok := recs[0].(*dns.PTR); !ok {
    88  		t.Errorf("recs[0] should be PTR record, got: %v, all records: %v", recs[0], recs)
    89  	} else if got, want := ptr.Ptr, "hostname._http._tcp.local."; got != want {
    90  		t.Fatalf("bad PTR record %v: got %v, want %v", ptr, got, want)
    91  	}
    92  
    93  	if _, ok := recs[1].(*dns.SRV); !ok {
    94  		t.Errorf("recs[1] should be SRV record, got: %v, all reccords: %v", recs[1], recs)
    95  	}
    96  	if _, ok := recs[2].(*dns.A); !ok {
    97  		t.Errorf("recs[2] should be A record, got: %v, all records: %v", recs[2], recs)
    98  	}
    99  	if _, ok := recs[3].(*dns.AAAA); !ok {
   100  		t.Errorf("recs[3] should be AAAA record, got: %v, all records: %v", recs[3], recs)
   101  	}
   102  	if _, ok := recs[4].(*dns.TXT); !ok {
   103  		t.Errorf("recs[4] should be TXT record, got: %v, all records: %v", recs[4], recs)
   104  	}
   105  
   106  	q.Qtype = dns.TypePTR
   107  	if recs2 := s.Records(q); !reflect.DeepEqual(recs, recs2) {
   108  		t.Fatalf("PTR question should return same result as ANY question: ANY => %v, PTR => %v", recs, recs2)
   109  	}
   110  }
   111  
   112  func TestMDNSService_InstanceAddr_ANY(t *testing.T) {
   113  	s := makeService(t)
   114  	q := dns.Question{
   115  		Name:  "hostname._http._tcp.local.",
   116  		Qtype: dns.TypeANY,
   117  	}
   118  	recs := s.Records(q)
   119  	if len(recs) != 4 {
   120  		t.Fatalf("bad: %v", recs)
   121  	}
   122  	if _, ok := recs[0].(*dns.SRV); !ok {
   123  		t.Fatalf("bad: %v", recs[0])
   124  	}
   125  	if _, ok := recs[1].(*dns.A); !ok {
   126  		t.Fatalf("bad: %v", recs[1])
   127  	}
   128  	if _, ok := recs[2].(*dns.AAAA); !ok {
   129  		t.Fatalf("bad: %v", recs[2])
   130  	}
   131  	if _, ok := recs[3].(*dns.TXT); !ok {
   132  		t.Fatalf("bad: %v", recs[3])
   133  	}
   134  }
   135  
   136  func TestMDNSService_InstanceAddr_SRV(t *testing.T) {
   137  	s := makeService(t)
   138  	q := dns.Question{
   139  		Name:  "hostname._http._tcp.local.",
   140  		Qtype: dns.TypeSRV,
   141  	}
   142  	recs := s.Records(q)
   143  	if len(recs) != 3 {
   144  		t.Fatalf("bad: %v", recs)
   145  	}
   146  	srv, ok := recs[0].(*dns.SRV)
   147  	if !ok {
   148  		t.Fatalf("bad: %v", recs[0])
   149  	}
   150  	if _, ok := recs[1].(*dns.A); !ok {
   151  		t.Fatalf("bad: %v", recs[1])
   152  	}
   153  	if _, ok := recs[2].(*dns.AAAA); !ok {
   154  		t.Fatalf("bad: %v", recs[2])
   155  	}
   156  
   157  	if srv.Port != uint16(s.Port) {
   158  		t.Fatalf("bad: %v", recs[0])
   159  	}
   160  }
   161  
   162  func TestMDNSService_InstanceAddr_A(t *testing.T) {
   163  	s := makeService(t)
   164  	q := dns.Question{
   165  		Name:  "hostname._http._tcp.local.",
   166  		Qtype: dns.TypeA,
   167  	}
   168  	recs := s.Records(q)
   169  	if len(recs) != 1 {
   170  		t.Fatalf("bad: %v", recs)
   171  	}
   172  	a, ok := recs[0].(*dns.A)
   173  	if !ok {
   174  		t.Fatalf("bad: %v", recs[0])
   175  	}
   176  	if !bytes.Equal(a.A, []byte{192, 168, 0, 42}) {
   177  		t.Fatalf("bad: %v", recs[0])
   178  	}
   179  }
   180  
   181  func TestMDNSService_InstanceAddr_AAAA(t *testing.T) {
   182  	s := makeService(t)
   183  	q := dns.Question{
   184  		Name:  "hostname._http._tcp.local.",
   185  		Qtype: dns.TypeAAAA,
   186  	}
   187  	recs := s.Records(q)
   188  	if len(recs) != 1 {
   189  		t.Fatalf("bad: %v", recs)
   190  	}
   191  	a4, ok := recs[0].(*dns.AAAA)
   192  	if !ok {
   193  		t.Fatalf("bad: %v", recs[0])
   194  	}
   195  	ip6 := net.ParseIP("2620:0:1000:1900:b0c2:d0b2:c411:18bc")
   196  	if got := len(ip6); got != net.IPv6len {
   197  		t.Fatalf("test IP failed to parse (len = %d, want %d)", got, net.IPv6len)
   198  	}
   199  	if !bytes.Equal(a4.AAAA, ip6) {
   200  		t.Fatalf("bad: %v", recs[0])
   201  	}
   202  }
   203  
   204  func TestMDNSService_InstanceAddr_TXT(t *testing.T) {
   205  	s := makeService(t)
   206  	q := dns.Question{
   207  		Name:  "hostname._http._tcp.local.",
   208  		Qtype: dns.TypeTXT,
   209  	}
   210  	recs := s.Records(q)
   211  	if len(recs) != 1 {
   212  		t.Fatalf("bad: %v", recs)
   213  	}
   214  	txt, ok := recs[0].(*dns.TXT)
   215  	if !ok {
   216  		t.Fatalf("bad: %v", recs[0])
   217  	}
   218  	if got, want := txt.Txt, s.TXT; !reflect.DeepEqual(got, want) {
   219  		t.Fatalf("TXT record mismatch for %v: got %v, want %v", recs[0], got, want)
   220  	}
   221  }
   222  
   223  func TestMDNSService_HostNameQuery(t *testing.T) {
   224  	s := makeService(t)
   225  	for _, test := range []struct {
   226  		q    dns.Question
   227  		want []dns.RR
   228  	}{
   229  		{
   230  			dns.Question{Name: "testhost.", Qtype: dns.TypeA},
   231  			[]dns.RR{&dns.A{
   232  				Hdr: dns.RR_Header{
   233  					Name:   "testhost.",
   234  					Rrtype: dns.TypeA,
   235  					Class:  dns.ClassINET,
   236  					Ttl:    120,
   237  				},
   238  				A: net.IP([]byte{192, 168, 0, 42}),
   239  			}},
   240  		},
   241  		{
   242  			dns.Question{Name: "testhost.", Qtype: dns.TypeAAAA},
   243  			[]dns.RR{&dns.AAAA{
   244  				Hdr: dns.RR_Header{
   245  					Name:   "testhost.",
   246  					Rrtype: dns.TypeAAAA,
   247  					Class:  dns.ClassINET,
   248  					Ttl:    120,
   249  				},
   250  				AAAA: net.ParseIP("2620:0:1000:1900:b0c2:d0b2:c411:18bc"),
   251  			}},
   252  		},
   253  	} {
   254  		if got := s.Records(test.q); !reflect.DeepEqual(got, test.want) {
   255  			t.Errorf("hostname query failed: s.Records(%v) = %v, want %v", test.q, got, test.want)
   256  		}
   257  	}
   258  }
   259  
   260  func TestMDNSService_serviceEnum_PTR(t *testing.T) {
   261  	s := makeService(t)
   262  	q := dns.Question{
   263  		Name:  "_services._dns-sd._udp.local.",
   264  		Qtype: dns.TypePTR,
   265  	}
   266  	recs := s.Records(q)
   267  	if len(recs) != 1 {
   268  		t.Fatalf("bad: %v", recs)
   269  	}
   270  	if ptr, ok := recs[0].(*dns.PTR); !ok {
   271  		t.Errorf("recs[0] should be PTR record, got: %v, all records: %v", recs[0], recs)
   272  	} else if got, want := ptr.Ptr, "_http._tcp.local."; got != want {
   273  		t.Fatalf("bad PTR record %v: got %v, want %v", ptr, got, want)
   274  	}
   275  }