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