Source file
src/net/lookup.go
Documentation: net
1
2
3
4
5 package net
6
7 import (
8 "context"
9 "errors"
10 "internal/nettrace"
11 "internal/singleflight"
12 "net/netip"
13 "sync"
14
15 "golang.org/x/net/dns/dnsmessage"
16 )
17
18
19
20
21
22
23
24
25 var protocols = map[string]int{
26 "icmp": 1,
27 "igmp": 2,
28 "tcp": 6,
29 "udp": 17,
30 "ipv6-icmp": 58,
31 }
32
33
34
35
36
37
38
39 var services = map[string]map[string]int{
40 "udp": {
41 "domain": 53,
42 },
43 "tcp": {
44 "ftp": 21,
45 "ftps": 990,
46 "gopher": 70,
47 "http": 80,
48 "https": 443,
49 "imap2": 143,
50 "imap3": 220,
51 "imaps": 993,
52 "pop3": 110,
53 "pop3s": 995,
54 "smtp": 25,
55 "submissions": 465,
56 "ssh": 22,
57 "telnet": 23,
58 },
59 }
60
61
62
63 var dnsWaitGroup sync.WaitGroup
64
65 const maxProtoLength = len("RSVP-E2E-IGNORE") + 10
66
67 func lookupProtocolMap(name string) (int, error) {
68 var lowerProtocol [maxProtoLength]byte
69 n := copy(lowerProtocol[:], name)
70 lowerASCIIBytes(lowerProtocol[:n])
71 proto, found := protocols[string(lowerProtocol[:n])]
72 if !found || n != len(name) {
73 return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name}
74 }
75 return proto, nil
76 }
77
78
79
80
81
82
83 const maxPortBufSize = len("mobility-header") + 10
84
85 func lookupPortMap(network, service string) (port int, error error) {
86 switch network {
87 case "ip":
88 if p, err := lookupPortMapWithNetwork("tcp", "ip", service); err == nil {
89 return p, nil
90 }
91 return lookupPortMapWithNetwork("udp", "ip", service)
92 case "tcp", "tcp4", "tcp6":
93 return lookupPortMapWithNetwork("tcp", "tcp", service)
94 case "udp", "udp4", "udp6":
95 return lookupPortMapWithNetwork("udp", "udp", service)
96 }
97 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}
98 }
99
100 func lookupPortMapWithNetwork(network, errNetwork, service string) (port int, error error) {
101 if m, ok := services[network]; ok {
102 var lowerService [maxPortBufSize]byte
103 n := copy(lowerService[:], service)
104 lowerASCIIBytes(lowerService[:n])
105 if port, ok := m[string(lowerService[:n])]; ok && n == len(service) {
106 return port, nil
107 }
108 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
109 }
110 return 0, &DNSError{Err: "unknown network", Name: errNetwork + "/" + service}
111 }
112
113
114
115 func ipVersion(network string) byte {
116 if network == "" {
117 return 0
118 }
119 n := network[len(network)-1]
120 if n != '4' && n != '6' {
121 n = 0
122 }
123 return n
124 }
125
126
127
128 var DefaultResolver = &Resolver{}
129
130
131
132
133 type Resolver struct {
134
135
136
137 PreferGo bool
138
139
140
141
142
143
144
145
146
147 StrictErrors bool
148
149
150
151
152
153
154
155
156
157
158
159
160 Dial func(ctx context.Context, network, address string) (Conn, error)
161
162
163
164
165 lookupGroup singleflight.Group
166
167
168
169 }
170
171 func (r *Resolver) preferGo() bool { return r != nil && r.PreferGo }
172 func (r *Resolver) strictErrors() bool { return r != nil && r.StrictErrors }
173
174 func (r *Resolver) getLookupGroup() *singleflight.Group {
175 if r == nil {
176 return &DefaultResolver.lookupGroup
177 }
178 return &r.lookupGroup
179 }
180
181
182
183
184
185
186 func LookupHost(host string) (addrs []string, err error) {
187 return DefaultResolver.LookupHost(context.Background(), host)
188 }
189
190
191
192 func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
193
194 if host == "" {
195 return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
196 }
197 if _, err := netip.ParseAddr(host); err == nil {
198 return []string{host}, nil
199 }
200 return r.lookupHost(ctx, host)
201 }
202
203
204
205 func LookupIP(host string) ([]IP, error) {
206 addrs, err := DefaultResolver.LookupIPAddr(context.Background(), host)
207 if err != nil {
208 return nil, err
209 }
210 ips := make([]IP, len(addrs))
211 for i, ia := range addrs {
212 ips[i] = ia.IP
213 }
214 return ips, nil
215 }
216
217
218
219 func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
220 return r.lookupIPAddr(ctx, "ip", host)
221 }
222
223
224
225
226
227 func (r *Resolver) LookupIP(ctx context.Context, network, host string) ([]IP, error) {
228 afnet, _, err := parseNetwork(ctx, network, false)
229 if err != nil {
230 return nil, err
231 }
232 switch afnet {
233 case "ip", "ip4", "ip6":
234 default:
235 return nil, UnknownNetworkError(network)
236 }
237
238 if host == "" {
239 return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
240 }
241 addrs, err := r.internetAddrList(ctx, afnet, host)
242 if err != nil {
243 return nil, err
244 }
245
246 ips := make([]IP, 0, len(addrs))
247 for _, addr := range addrs {
248 ips = append(ips, addr.(*IPAddr).IP)
249 }
250 return ips, nil
251 }
252
253
254
255
256
257 func (r *Resolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {
258
259
260
261
262 ips, err := r.LookupIP(ctx, network, host)
263 if err != nil {
264 return nil, err
265 }
266 ret := make([]netip.Addr, 0, len(ips))
267 for _, ip := range ips {
268 if a, ok := netip.AddrFromSlice(ip); ok {
269 ret = append(ret, a)
270 }
271 }
272 return ret, nil
273 }
274
275
276
277 type onlyValuesCtx struct {
278 context.Context
279 lookupValues context.Context
280 }
281
282 var _ context.Context = (*onlyValuesCtx)(nil)
283
284
285 func (ovc *onlyValuesCtx) Value(key any) any {
286 select {
287 case <-ovc.lookupValues.Done():
288 return nil
289 default:
290 return ovc.lookupValues.Value(key)
291 }
292 }
293
294
295
296
297
298 func withUnexpiredValuesPreserved(lookupCtx context.Context) context.Context {
299 return &onlyValuesCtx{Context: context.Background(), lookupValues: lookupCtx}
300 }
301
302
303
304 func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IPAddr, error) {
305
306 if host == "" {
307 return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
308 }
309 if ip, err := netip.ParseAddr(host); err == nil {
310 return []IPAddr{{IP: IP(ip.AsSlice()).To16(), Zone: ip.Zone()}}, nil
311 }
312 trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
313 if trace != nil && trace.DNSStart != nil {
314 trace.DNSStart(host)
315 }
316
317
318
319 resolverFunc := r.lookupIP
320 if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string, string) ([]IPAddr, error)); alt != nil {
321 resolverFunc = alt
322 }
323
324
325
326
327
328
329 lookupGroupCtx, lookupGroupCancel := context.WithCancel(withUnexpiredValuesPreserved(ctx))
330
331 lookupKey := network + "\000" + host
332 dnsWaitGroup.Add(1)
333 ch := r.getLookupGroup().DoChan(lookupKey, func() (any, error) {
334 return testHookLookupIP(lookupGroupCtx, resolverFunc, network, host)
335 })
336
337 dnsWaitGroupDone := func(ch <-chan singleflight.Result, cancelFn context.CancelFunc) {
338 <-ch
339 dnsWaitGroup.Done()
340 cancelFn()
341 }
342 select {
343 case <-ctx.Done():
344
345
346
347
348
349
350
351 if r.getLookupGroup().ForgetUnshared(lookupKey) {
352 lookupGroupCancel()
353 go dnsWaitGroupDone(ch, func() {})
354 } else {
355 go dnsWaitGroupDone(ch, lookupGroupCancel)
356 }
357 ctxErr := ctx.Err()
358 err := &DNSError{
359 Err: mapErr(ctxErr).Error(),
360 Name: host,
361 IsTimeout: ctxErr == context.DeadlineExceeded,
362 }
363 if trace != nil && trace.DNSDone != nil {
364 trace.DNSDone(nil, false, err)
365 }
366 return nil, err
367 case r := <-ch:
368 dnsWaitGroup.Done()
369 lookupGroupCancel()
370 err := r.Err
371 if err != nil {
372 if _, ok := err.(*DNSError); !ok {
373 isTimeout := false
374 if err == context.DeadlineExceeded {
375 isTimeout = true
376 } else if terr, ok := err.(timeout); ok {
377 isTimeout = terr.Timeout()
378 }
379 err = &DNSError{
380 Err: err.Error(),
381 Name: host,
382 IsTimeout: isTimeout,
383 }
384 }
385 }
386 if trace != nil && trace.DNSDone != nil {
387 addrs, _ := r.Val.([]IPAddr)
388 trace.DNSDone(ipAddrsEface(addrs), r.Shared, err)
389 }
390 return lookupIPReturn(r.Val, err, r.Shared)
391 }
392 }
393
394
395
396 func lookupIPReturn(addrsi any, err error, shared bool) ([]IPAddr, error) {
397 if err != nil {
398 return nil, err
399 }
400 addrs := addrsi.([]IPAddr)
401 if shared {
402 clone := make([]IPAddr, len(addrs))
403 copy(clone, addrs)
404 addrs = clone
405 }
406 return addrs, nil
407 }
408
409
410 func ipAddrsEface(addrs []IPAddr) []any {
411 s := make([]any, len(addrs))
412 for i, v := range addrs {
413 s[i] = v
414 }
415 return s
416 }
417
418
419
420
421
422 func LookupPort(network, service string) (port int, err error) {
423 return DefaultResolver.LookupPort(context.Background(), network, service)
424 }
425
426
427
428
429 func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
430 port, needsLookup := parsePort(service)
431 if needsLookup {
432 switch network {
433 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip":
434 case "":
435 network = "ip"
436 default:
437 return 0, &AddrError{Err: "unknown network", Addr: network}
438 }
439 port, err = r.lookupPort(ctx, network, service)
440 if err != nil {
441 return 0, err
442 }
443 }
444 if 0 > port || port > 65535 {
445 return 0, &AddrError{Err: "invalid port", Addr: service}
446 }
447 return port, nil
448 }
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466 func LookupCNAME(host string) (cname string, err error) {
467 return DefaultResolver.LookupCNAME(context.Background(), host)
468 }
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483 func (r *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) {
484 cname, err := r.lookupCNAME(ctx, host)
485 if err != nil {
486 return "", err
487 }
488 if !isDomainName(cname) {
489 return "", &DNSError{Err: errMalformedDNSRecordsDetail, Name: host}
490 }
491 return cname, nil
492 }
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508 func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
509 return DefaultResolver.LookupSRV(context.Background(), service, proto, name)
510 }
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526 func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
527 cname, addrs, err := r.lookupSRV(ctx, service, proto, name)
528 if err != nil {
529 return "", nil, err
530 }
531 if cname != "" && !isDomainName(cname) {
532 return "", nil, &DNSError{Err: "SRV header name is invalid", Name: name}
533 }
534 filteredAddrs := make([]*SRV, 0, len(addrs))
535 for _, addr := range addrs {
536 if addr == nil {
537 continue
538 }
539 if !isDomainName(addr.Target) {
540 continue
541 }
542 filteredAddrs = append(filteredAddrs, addr)
543 }
544 if len(addrs) != len(filteredAddrs) {
545 return cname, filteredAddrs, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
546 }
547 return cname, filteredAddrs, nil
548 }
549
550
551
552
553
554
555
556
557
558
559 func LookupMX(name string) ([]*MX, error) {
560 return DefaultResolver.LookupMX(context.Background(), name)
561 }
562
563
564
565
566
567
568
569 func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) {
570 records, err := r.lookupMX(ctx, name)
571 if err != nil {
572 return nil, err
573 }
574 filteredMX := make([]*MX, 0, len(records))
575 for _, mx := range records {
576 if mx == nil {
577 continue
578 }
579 if !isDomainName(mx.Host) {
580 continue
581 }
582 filteredMX = append(filteredMX, mx)
583 }
584 if len(records) != len(filteredMX) {
585 return filteredMX, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
586 }
587 return filteredMX, nil
588 }
589
590
591
592
593
594
595
596
597
598
599 func LookupNS(name string) ([]*NS, error) {
600 return DefaultResolver.LookupNS(context.Background(), name)
601 }
602
603
604
605
606
607
608
609 func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) {
610 records, err := r.lookupNS(ctx, name)
611 if err != nil {
612 return nil, err
613 }
614 filteredNS := make([]*NS, 0, len(records))
615 for _, ns := range records {
616 if ns == nil {
617 continue
618 }
619 if !isDomainName(ns.Host) {
620 continue
621 }
622 filteredNS = append(filteredNS, ns)
623 }
624 if len(records) != len(filteredNS) {
625 return filteredNS, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
626 }
627 return filteredNS, nil
628 }
629
630
631
632
633
634 func LookupTXT(name string) ([]string, error) {
635 return DefaultResolver.lookupTXT(context.Background(), name)
636 }
637
638
639 func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
640 return r.lookupTXT(ctx, name)
641 }
642
643
644
645
646
647
648
649
650
651
652
653
654
655 func LookupAddr(addr string) (names []string, err error) {
656 return DefaultResolver.LookupAddr(context.Background(), addr)
657 }
658
659
660
661
662
663
664
665 func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) {
666 names, err := r.lookupAddr(ctx, addr)
667 if err != nil {
668 return nil, err
669 }
670 filteredNames := make([]string, 0, len(names))
671 for _, name := range names {
672 if isDomainName(name) {
673 filteredNames = append(filteredNames, name)
674 }
675 }
676 if len(names) != len(filteredNames) {
677 return filteredNames, &DNSError{Err: errMalformedDNSRecordsDetail, Name: addr}
678 }
679 return filteredNames, nil
680 }
681
682
683
684
685 var errMalformedDNSRecordsDetail = "DNS response contained records which contain invalid names"
686
687
688
689
690 func (r *Resolver) dial(ctx context.Context, network, server string) (Conn, error) {
691
692
693
694
695
696 var c Conn
697 var err error
698 if r != nil && r.Dial != nil {
699 c, err = r.Dial(ctx, network, server)
700 } else {
701 var d Dialer
702 c, err = d.DialContext(ctx, network, server)
703 }
704 if err != nil {
705 return nil, mapErr(err)
706 }
707 return c, nil
708 }
709
710
711
712
713
714
715
716
717
718
719 func (r *Resolver) goLookupSRV(ctx context.Context, service, proto, name string) (target string, srvs []*SRV, err error) {
720 if service == "" && proto == "" {
721 target = name
722 } else {
723 target = "_" + service + "._" + proto + "." + name
724 }
725 p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV, nil)
726 if err != nil {
727 return "", nil, err
728 }
729 var cname dnsmessage.Name
730 for {
731 h, err := p.AnswerHeader()
732 if err == dnsmessage.ErrSectionDone {
733 break
734 }
735 if err != nil {
736 return "", nil, &DNSError{
737 Err: "cannot unmarshal DNS message",
738 Name: name,
739 Server: server,
740 }
741 }
742 if h.Type != dnsmessage.TypeSRV {
743 if err := p.SkipAnswer(); err != nil {
744 return "", nil, &DNSError{
745 Err: "cannot unmarshal DNS message",
746 Name: name,
747 Server: server,
748 }
749 }
750 continue
751 }
752 if cname.Length == 0 && h.Name.Length != 0 {
753 cname = h.Name
754 }
755 srv, err := p.SRVResource()
756 if err != nil {
757 return "", nil, &DNSError{
758 Err: "cannot unmarshal DNS message",
759 Name: name,
760 Server: server,
761 }
762 }
763 srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight})
764 }
765 byPriorityWeight(srvs).sort()
766 return cname.String(), srvs, nil
767 }
768
769
770 func (r *Resolver) goLookupMX(ctx context.Context, name string) ([]*MX, error) {
771 p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX, nil)
772 if err != nil {
773 return nil, err
774 }
775 var mxs []*MX
776 for {
777 h, err := p.AnswerHeader()
778 if err == dnsmessage.ErrSectionDone {
779 break
780 }
781 if err != nil {
782 return nil, &DNSError{
783 Err: "cannot unmarshal DNS message",
784 Name: name,
785 Server: server,
786 }
787 }
788 if h.Type != dnsmessage.TypeMX {
789 if err := p.SkipAnswer(); err != nil {
790 return nil, &DNSError{
791 Err: "cannot unmarshal DNS message",
792 Name: name,
793 Server: server,
794 }
795 }
796 continue
797 }
798 mx, err := p.MXResource()
799 if err != nil {
800 return nil, &DNSError{
801 Err: "cannot unmarshal DNS message",
802 Name: name,
803 Server: server,
804 }
805 }
806 mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref})
807
808 }
809 byPref(mxs).sort()
810 return mxs, nil
811 }
812
813
814 func (r *Resolver) goLookupNS(ctx context.Context, name string) ([]*NS, error) {
815 p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS, nil)
816 if err != nil {
817 return nil, err
818 }
819 var nss []*NS
820 for {
821 h, err := p.AnswerHeader()
822 if err == dnsmessage.ErrSectionDone {
823 break
824 }
825 if err != nil {
826 return nil, &DNSError{
827 Err: "cannot unmarshal DNS message",
828 Name: name,
829 Server: server,
830 }
831 }
832 if h.Type != dnsmessage.TypeNS {
833 if err := p.SkipAnswer(); err != nil {
834 return nil, &DNSError{
835 Err: "cannot unmarshal DNS message",
836 Name: name,
837 Server: server,
838 }
839 }
840 continue
841 }
842 ns, err := p.NSResource()
843 if err != nil {
844 return nil, &DNSError{
845 Err: "cannot unmarshal DNS message",
846 Name: name,
847 Server: server,
848 }
849 }
850 nss = append(nss, &NS{Host: ns.NS.String()})
851 }
852 return nss, nil
853 }
854
855
856 func (r *Resolver) goLookupTXT(ctx context.Context, name string) ([]string, error) {
857 p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT, nil)
858 if err != nil {
859 return nil, err
860 }
861 var txts []string
862 for {
863 h, err := p.AnswerHeader()
864 if err == dnsmessage.ErrSectionDone {
865 break
866 }
867 if err != nil {
868 return nil, &DNSError{
869 Err: "cannot unmarshal DNS message",
870 Name: name,
871 Server: server,
872 }
873 }
874 if h.Type != dnsmessage.TypeTXT {
875 if err := p.SkipAnswer(); err != nil {
876 return nil, &DNSError{
877 Err: "cannot unmarshal DNS message",
878 Name: name,
879 Server: server,
880 }
881 }
882 continue
883 }
884 txt, err := p.TXTResource()
885 if err != nil {
886 return nil, &DNSError{
887 Err: "cannot unmarshal DNS message",
888 Name: name,
889 Server: server,
890 }
891 }
892
893
894
895 n := 0
896 for _, s := range txt.TXT {
897 n += len(s)
898 }
899 txtJoin := make([]byte, 0, n)
900 for _, s := range txt.TXT {
901 txtJoin = append(txtJoin, s...)
902 }
903 if len(txts) == 0 {
904 txts = make([]string, 0, 1)
905 }
906 txts = append(txts, string(txtJoin))
907 }
908 return txts, nil
909 }
910
911 func parseCNAMEFromResources(resources []dnsmessage.Resource) (string, error) {
912 if len(resources) == 0 {
913 return "", errors.New("no CNAME record received")
914 }
915 c, ok := resources[0].Body.(*dnsmessage.CNAMEResource)
916 if !ok {
917 return "", errors.New("could not parse CNAME record")
918 }
919 return c.CNAME.String(), nil
920 }
921
View as plain text