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 }