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