1// Copyright 2012 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package net
6
7import (
8	"context"
9	"internal/nettrace"
10	"internal/singleflight"
11	"net/netip"
12	"sync"
13)
14
15// protocols contains minimal mappings between internet protocol
16// names and numbers for platforms that don't have a complete list of
17// protocol numbers.
18//
19// See https://www.iana.org/assignments/protocol-numbers
20//
21// On Unix, this map is augmented by readProtocols via lookupProtocol.
22var protocols = map[string]int{
23	"icmp":      1,
24	"igmp":      2,
25	"tcp":       6,
26	"udp":       17,
27	"ipv6-icmp": 58,
28}
29
30// services contains minimal mappings between services names and port
31// numbers for platforms that don't have a complete list of port numbers.
32//
33// See https://www.iana.org/assignments/service-names-port-numbers
34//
35// On Unix, this map is augmented by readServices via goLookupPort.
36var services = map[string]map[string]int{
37	"udp": {
38		"domain": 53,
39	},
40	"tcp": {
41		"ftp":    21,
42		"ftps":   990,
43		"gopher": 70, // ʕ◔ϖ◔ʔ
44		"http":   80,
45		"https":  443,
46		"imap2":  143,
47		"imap3":  220,
48		"imaps":  993,
49		"pop3":   110,
50		"pop3s":  995,
51		"smtp":   25,
52		"ssh":    22,
53		"telnet": 23,
54	},
55}
56
57// dnsWaitGroup can be used by tests to wait for all DNS goroutines to
58// complete. This avoids races on the test hooks.
59var dnsWaitGroup sync.WaitGroup
60
61const maxProtoLength = len("RSVP-E2E-IGNORE") + 10 // with room to grow
62
63func lookupProtocolMap(name string) (int, error) {
64	var lowerProtocol [maxProtoLength]byte
65	n := copy(lowerProtocol[:], name)
66	lowerASCIIBytes(lowerProtocol[:n])
67	proto, found := protocols[string(lowerProtocol[:n])]
68	if !found || n != len(name) {
69		return 0, &AddrError{Err: "unknown IP protocol specified", Addr: name}
70	}
71	return proto, nil
72}
73
74// maxPortBufSize is the longest reasonable name of a service
75// (non-numeric port).
76// Currently the longest known IANA-unregistered name is
77// "mobility-header", so we use that length, plus some slop in case
78// something longer is added in the future.
79const maxPortBufSize = len("mobility-header") + 10
80
81func lookupPortMap(network, service string) (port int, error error) {
82	switch network {
83	case "tcp4", "tcp6":
84		network = "tcp"
85	case "udp4", "udp6":
86		network = "udp"
87	}
88
89	if m, ok := services[network]; ok {
90		var lowerService [maxPortBufSize]byte
91		n := copy(lowerService[:], service)
92		lowerASCIIBytes(lowerService[:n])
93		if port, ok := m[string(lowerService[:n])]; ok && n == len(service) {
94			return port, nil
95		}
96	}
97	return 0, &AddrError{Err: "unknown port", Addr: network + "/" + service}
98}
99
100// ipVersion returns the provided network's IP version: '4', '6' or 0
101// if network does not end in a '4' or '6' byte.
102func ipVersion(network string) byte {
103	if network == "" {
104		return 0
105	}
106	n := network[len(network)-1]
107	if n != '4' && n != '6' {
108		n = 0
109	}
110	return n
111}
112
113// DefaultResolver is the resolver used by the package-level Lookup
114// functions and by Dialers without a specified Resolver.
115var DefaultResolver = &Resolver{}
116
117// A Resolver looks up names and numbers.
118//
119// A nil *Resolver is equivalent to a zero Resolver.
120type Resolver struct {
121	// PreferGo controls whether Go's built-in DNS resolver is preferred
122	// on platforms where it's available. It is equivalent to setting
123	// GODEBUG=netdns=go, but scoped to just this resolver.
124	PreferGo bool
125
126	// StrictErrors controls the behavior of temporary errors
127	// (including timeout, socket errors, and SERVFAIL) when using
128	// Go's built-in resolver. For a query composed of multiple
129	// sub-queries (such as an A+AAAA address lookup, or walking the
130	// DNS search list), this option causes such errors to abort the
131	// whole query instead of returning a partial result. This is
132	// not enabled by default because it may affect compatibility
133	// with resolvers that process AAAA queries incorrectly.
134	StrictErrors bool
135
136	// Dial optionally specifies an alternate dialer for use by
137	// Go's built-in DNS resolver to make TCP and UDP connections
138	// to DNS services. The host in the address parameter will
139	// always be a literal IP address and not a host name, and the
140	// port in the address parameter will be a literal port number
141	// and not a service name.
142	// If the Conn returned is also a PacketConn, sent and received DNS
143	// messages must adhere to RFC 1035 section 4.2.1, "UDP usage".
144	// Otherwise, DNS messages transmitted over Conn must adhere
145	// to RFC 7766 section 5, "Transport Protocol Selection".
146	// If nil, the default dialer is used.
147	Dial func(ctx context.Context, network, address string) (Conn, error)
148
149	// lookupGroup merges LookupIPAddr calls together for lookups for the same
150	// host. The lookupGroup key is the LookupIPAddr.host argument.
151	// The return values are ([]IPAddr, error).
152	lookupGroup singleflight.Group
153
154	// TODO(bradfitz): optional interface impl override hook
155	// TODO(bradfitz): Timeout time.Duration?
156}
157
158func (r *Resolver) preferGo() bool     { return r != nil && r.PreferGo }
159func (r *Resolver) strictErrors() bool { return r != nil && r.StrictErrors }
160
161func (r *Resolver) getLookupGroup() *singleflight.Group {
162	if r == nil {
163		return &DefaultResolver.lookupGroup
164	}
165	return &r.lookupGroup
166}
167
168// LookupHost looks up the given host using the local resolver.
169// It returns a slice of that host's addresses.
170//
171// LookupHost uses context.Background internally; to specify the context, use
172// Resolver.LookupHost.
173func LookupHost(host string) (addrs []string, err error) {
174	return DefaultResolver.LookupHost(context.Background(), host)
175}
176
177// LookupHost looks up the given host using the local resolver.
178// It returns a slice of that host's addresses.
179func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
180	// Make sure that no matter what we do later, host=="" is rejected.
181	// parseIP, for example, does accept empty strings.
182	if host == "" {
183		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
184	}
185	if ip, _ := parseIPZone(host); ip != nil {
186		return []string{host}, nil
187	}
188	return r.lookupHost(ctx, host)
189}
190
191// LookupIP looks up host using the local resolver.
192// It returns a slice of that host's IPv4 and IPv6 addresses.
193func LookupIP(host string) ([]IP, error) {
194	addrs, err := DefaultResolver.LookupIPAddr(context.Background(), host)
195	if err != nil {
196		return nil, err
197	}
198	ips := make([]IP, len(addrs))
199	for i, ia := range addrs {
200		ips[i] = ia.IP
201	}
202	return ips, nil
203}
204
205// LookupIPAddr looks up host using the local resolver.
206// It returns a slice of that host's IPv4 and IPv6 addresses.
207func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) {
208	return r.lookupIPAddr(ctx, "ip", host)
209}
210
211// LookupIP looks up host for the given network using the local resolver.
212// It returns a slice of that host's IP addresses of the type specified by
213// network.
214// network must be one of "ip", "ip4" or "ip6".
215func (r *Resolver) LookupIP(ctx context.Context, network, host string) ([]IP, error) {
216	afnet, _, err := parseNetwork(ctx, network, false)
217	if err != nil {
218		return nil, err
219	}
220	switch afnet {
221	case "ip", "ip4", "ip6":
222	default:
223		return nil, UnknownNetworkError(network)
224	}
225	addrs, err := r.internetAddrList(ctx, afnet, host)
226	if err != nil {
227		return nil, err
228	}
229	ips := make([]IP, 0, len(addrs))
230	for _, addr := range addrs {
231		ips = append(ips, addr.(*IPAddr).IP)
232	}
233	return ips, nil
234}
235
236// LookupNetIP looks up host using the local resolver.
237// It returns a slice of that host's IP addresses of the type specified by
238// network.
239// The network must be one of "ip", "ip4" or "ip6".
240func (r *Resolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {
241	// TODO(bradfitz): make this efficient, making the internal net package
242	// type throughout be netip.Addr and only converting to the net.IP slice
243	// version at the edge. But for now (2021-10-20), this is a wrapper around
244	// the old way.
245	ips, err := r.LookupIP(ctx, network, host)
246	if err != nil {
247		return nil, err
248	}
249	ret := make([]netip.Addr, 0, len(ips))
250	for _, ip := range ips {
251		if a, ok := netip.AddrFromSlice(ip); ok {
252			ret = append(ret, a)
253		}
254	}
255	return ret, nil
256}
257
258// onlyValuesCtx is a context that uses an underlying context
259// for value lookup if the underlying context hasn't yet expired.
260type onlyValuesCtx struct {
261	context.Context
262	lookupValues context.Context
263}
264
265var _ context.Context = (*onlyValuesCtx)(nil)
266
267// Value performs a lookup if the original context hasn't expired.
268func (ovc *onlyValuesCtx) Value(key any) any {
269	select {
270	case <-ovc.lookupValues.Done():
271		return nil
272	default:
273		return ovc.lookupValues.Value(key)
274	}
275}
276
277// withUnexpiredValuesPreserved returns a context.Context that only uses lookupCtx
278// for its values, otherwise it is never canceled and has no deadline.
279// If the lookup context expires, any looked up values will return nil.
280// See Issue 28600.
281func withUnexpiredValuesPreserved(lookupCtx context.Context) context.Context {
282	return &onlyValuesCtx{Context: context.Background(), lookupValues: lookupCtx}
283}
284
285// lookupIPAddr looks up host using the local resolver and particular network.
286// It returns a slice of that host's IPv4 and IPv6 addresses.
287func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IPAddr, error) {
288	// Make sure that no matter what we do later, host=="" is rejected.
289	// parseIP, for example, does accept empty strings.
290	if host == "" {
291		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
292	}
293	if ip, zone := parseIPZone(host); ip != nil {
294		return []IPAddr{{IP: ip, Zone: zone}}, nil
295	}
296	trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
297	if trace != nil && trace.DNSStart != nil {
298		trace.DNSStart(host)
299	}
300	// The underlying resolver func is lookupIP by default but it
301	// can be overridden by tests. This is needed by net/http, so it
302	// uses a context key instead of unexported variables.
303	resolverFunc := r.lookupIP
304	if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string, string) ([]IPAddr, error)); alt != nil {
305		resolverFunc = alt
306	}
307
308	// We don't want a cancellation of ctx to affect the
309	// lookupGroup operation. Otherwise if our context gets
310	// canceled it might cause an error to be returned to a lookup
311	// using a completely different context. However we need to preserve
312	// only the values in context. See Issue 28600.
313	lookupGroupCtx, lookupGroupCancel := context.WithCancel(withUnexpiredValuesPreserved(ctx))
314
315	lookupKey := network + "\000" + host
316	dnsWaitGroup.Add(1)
317	ch, called := r.getLookupGroup().DoChan(lookupKey, func() (any, error) {
318		defer dnsWaitGroup.Done()
319		return testHookLookupIP(lookupGroupCtx, resolverFunc, network, host)
320	})
321	if !called {
322		dnsWaitGroup.Done()
323	}
324
325	select {
326	case <-ctx.Done():
327		// Our context was canceled. If we are the only
328		// goroutine looking up this key, then drop the key
329		// from the lookupGroup and cancel the lookup.
330		// If there are other goroutines looking up this key,
331		// let the lookup continue uncanceled, and let later
332		// lookups with the same key share the result.
333		// See issues 8602, 20703, 22724.
334		if r.getLookupGroup().ForgetUnshared(lookupKey) {
335			lookupGroupCancel()
336		} else {
337			go func() {
338				<-ch
339				lookupGroupCancel()
340			}()
341		}
342		ctxErr := ctx.Err()
343		err := &DNSError{
344			Err:       mapErr(ctxErr).Error(),
345			Name:      host,
346			IsTimeout: ctxErr == context.DeadlineExceeded,
347		}
348		if trace != nil && trace.DNSDone != nil {
349			trace.DNSDone(nil, false, err)
350		}
351		return nil, err
352	case r := <-ch:
353		lookupGroupCancel()
354		err := r.Err
355		if err != nil {
356			if _, ok := err.(*DNSError); !ok {
357				isTimeout := false
358				if err == context.DeadlineExceeded {
359					isTimeout = true
360				} else if terr, ok := err.(timeout); ok {
361					isTimeout = terr.Timeout()
362				}
363				err = &DNSError{
364					Err:       err.Error(),
365					Name:      host,
366					IsTimeout: isTimeout,
367				}
368			}
369		}
370		if trace != nil && trace.DNSDone != nil {
371			addrs, _ := r.Val.([]IPAddr)
372			trace.DNSDone(ipAddrsEface(addrs), r.Shared, err)
373		}
374		return lookupIPReturn(r.Val, err, r.Shared)
375	}
376}
377
378// lookupIPReturn turns the return values from singleflight.Do into
379// the return values from LookupIP.
380func lookupIPReturn(addrsi any, err error, shared bool) ([]IPAddr, error) {
381	if err != nil {
382		return nil, err
383	}
384	addrs := addrsi.([]IPAddr)
385	if shared {
386		clone := make([]IPAddr, len(addrs))
387		copy(clone, addrs)
388		addrs = clone
389	}
390	return addrs, nil
391}
392
393// ipAddrsEface returns an empty interface slice of addrs.
394func ipAddrsEface(addrs []IPAddr) []any {
395	s := make([]any, len(addrs))
396	for i, v := range addrs {
397		s[i] = v
398	}
399	return s
400}
401
402// LookupPort looks up the port for the given network and service.
403//
404// LookupPort uses context.Background internally; to specify the context, use
405// Resolver.LookupPort.
406func LookupPort(network, service string) (port int, err error) {
407	return DefaultResolver.LookupPort(context.Background(), network, service)
408}
409
410// LookupPort looks up the port for the given network and service.
411func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) {
412	port, needsLookup := parsePort(service)
413	if needsLookup {
414		switch network {
415		case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
416		case "": // a hint wildcard for Go 1.0 undocumented behavior
417			network = "ip"
418		default:
419			return 0, &AddrError{Err: "unknown network", Addr: network}
420		}
421		port, err = r.lookupPort(ctx, network, service)
422		if err != nil {
423			return 0, err
424		}
425	}
426	if 0 > port || port > 65535 {
427		return 0, &AddrError{Err: "invalid port", Addr: service}
428	}
429	return port, nil
430}
431
432// LookupCNAME returns the canonical name for the given host.
433// Callers that do not care about the canonical name can call
434// LookupHost or LookupIP directly; both take care of resolving
435// the canonical name as part of the lookup.
436//
437// A canonical name is the final name after following zero
438// or more CNAME records.
439// LookupCNAME does not return an error if host does not
440// contain DNS "CNAME" records, as long as host resolves to
441// address records.
442//
443// The returned canonical name is validated to be a properly
444// formatted presentation-format domain name.
445//
446// LookupCNAME uses context.Background internally; to specify the context, use
447// Resolver.LookupCNAME.
448func LookupCNAME(host string) (cname string, err error) {
449	return DefaultResolver.LookupCNAME(context.Background(), host)
450}
451
452// LookupCNAME returns the canonical name for the given host.
453// Callers that do not care about the canonical name can call
454// LookupHost or LookupIP directly; both take care of resolving
455// the canonical name as part of the lookup.
456//
457// A canonical name is the final name after following zero
458// or more CNAME records.
459// LookupCNAME does not return an error if host does not
460// contain DNS "CNAME" records, as long as host resolves to
461// address records.
462//
463// The returned canonical name is validated to be a properly
464// formatted presentation-format domain name.
465func (r *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) {
466	cname, err := r.lookupCNAME(ctx, host)
467	if err != nil {
468		return "", err
469	}
470	if !isDomainName(cname) {
471		return "", &DNSError{Err: errMalformedDNSRecordsDetail, Name: host}
472	}
473	return cname, nil
474}
475
476// LookupSRV tries to resolve an SRV query of the given service,
477// protocol, and domain name. The proto is "tcp" or "udp".
478// The returned records are sorted by priority and randomized
479// by weight within a priority.
480//
481// LookupSRV constructs the DNS name to look up following RFC 2782.
482// That is, it looks up _service._proto.name. To accommodate services
483// publishing SRV records under non-standard names, if both service
484// and proto are empty strings, LookupSRV looks up name directly.
485//
486// The returned service names are validated to be properly
487// formatted presentation-format domain names. If the response contains
488// invalid names, those records are filtered out and an error
489// will be returned alongside the remaining results, if any.
490func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
491	return DefaultResolver.LookupSRV(context.Background(), service, proto, name)
492}
493
494// LookupSRV tries to resolve an SRV query of the given service,
495// protocol, and domain name. The proto is "tcp" or "udp".
496// The returned records are sorted by priority and randomized
497// by weight within a priority.
498//
499// LookupSRV constructs the DNS name to look up following RFC 2782.
500// That is, it looks up _service._proto.name. To accommodate services
501// publishing SRV records under non-standard names, if both service
502// and proto are empty strings, LookupSRV looks up name directly.
503//
504// The returned service names are validated to be properly
505// formatted presentation-format domain names. If the response contains
506// invalid names, those records are filtered out and an error
507// will be returned alongside the remaining results, if any.
508func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
509	cname, addrs, err := r.lookupSRV(ctx, service, proto, name)
510	if err != nil {
511		return "", nil, err
512	}
513	if cname != "" && !isDomainName(cname) {
514		return "", nil, &DNSError{Err: "SRV header name is invalid", Name: name}
515	}
516	filteredAddrs := make([]*SRV, 0, len(addrs))
517	for _, addr := range addrs {
518		if addr == nil {
519			continue
520		}
521		if !isDomainName(addr.Target) {
522			continue
523		}
524		filteredAddrs = append(filteredAddrs, addr)
525	}
526	if len(addrs) != len(filteredAddrs) {
527		return cname, filteredAddrs, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
528	}
529	return cname, filteredAddrs, nil
530}
531
532// LookupMX returns the DNS MX records for the given domain name sorted by preference.
533//
534// The returned mail server names are validated to be properly
535// formatted presentation-format domain names. If the response contains
536// invalid names, those records are filtered out and an error
537// will be returned alongside the remaining results, if any.
538//
539// LookupMX uses context.Background internally; to specify the context, use
540// Resolver.LookupMX.
541func LookupMX(name string) ([]*MX, error) {
542	return DefaultResolver.LookupMX(context.Background(), name)
543}
544
545// LookupMX returns the DNS MX records for the given domain name sorted by preference.
546//
547// The returned mail server names are validated to be properly
548// formatted presentation-format domain names. If the response contains
549// invalid names, those records are filtered out and an error
550// will be returned alongside the remaining results, if any.
551func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) {
552	records, err := r.lookupMX(ctx, name)
553	if err != nil {
554		return nil, err
555	}
556	filteredMX := make([]*MX, 0, len(records))
557	for _, mx := range records {
558		if mx == nil {
559			continue
560		}
561		if !isDomainName(mx.Host) {
562			continue
563		}
564		filteredMX = append(filteredMX, mx)
565	}
566	if len(records) != len(filteredMX) {
567		return filteredMX, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
568	}
569	return filteredMX, nil
570}
571
572// LookupNS returns the DNS NS records for the given domain name.
573//
574// The returned name server names are validated to be properly
575// formatted presentation-format domain names. If the response contains
576// invalid names, those records are filtered out and an error
577// will be returned alongside the remaining results, if any.
578//
579// LookupNS uses context.Background internally; to specify the context, use
580// Resolver.LookupNS.
581func LookupNS(name string) ([]*NS, error) {
582	return DefaultResolver.LookupNS(context.Background(), name)
583}
584
585// LookupNS returns the DNS NS records for the given domain name.
586//
587// The returned name server names are validated to be properly
588// formatted presentation-format domain names. If the response contains
589// invalid names, those records are filtered out and an error
590// will be returned alongside the remaining results, if any.
591func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) {
592	records, err := r.lookupNS(ctx, name)
593	if err != nil {
594		return nil, err
595	}
596	filteredNS := make([]*NS, 0, len(records))
597	for _, ns := range records {
598		if ns == nil {
599			continue
600		}
601		if !isDomainName(ns.Host) {
602			continue
603		}
604		filteredNS = append(filteredNS, ns)
605	}
606	if len(records) != len(filteredNS) {
607		return filteredNS, &DNSError{Err: errMalformedDNSRecordsDetail, Name: name}
608	}
609	return filteredNS, nil
610}
611
612// LookupTXT returns the DNS TXT records for the given domain name.
613//
614// LookupTXT uses context.Background internally; to specify the context, use
615// Resolver.LookupTXT.
616func LookupTXT(name string) ([]string, error) {
617	return DefaultResolver.lookupTXT(context.Background(), name)
618}
619
620// LookupTXT returns the DNS TXT records for the given domain name.
621func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
622	return r.lookupTXT(ctx, name)
623}
624
625// LookupAddr performs a reverse lookup for the given address, returning a list
626// of names mapping to that address.
627//
628// The returned names are validated to be properly formatted presentation-format
629// domain names. If the response contains invalid names, those records are filtered
630// out and an error will be returned alongside the remaining results, if any.
631//
632// When using the host C library resolver, at most one result will be
633// returned. To bypass the host resolver, use a custom Resolver.
634//
635// LookupAddr uses context.Background internally; to specify the context, use
636// Resolver.LookupAddr.
637func LookupAddr(addr string) (names []string, err error) {
638	return DefaultResolver.LookupAddr(context.Background(), addr)
639}
640
641// LookupAddr performs a reverse lookup for the given address, returning a list
642// of names mapping to that address.
643//
644// The returned names are validated to be properly formatted presentation-format
645// domain names. If the response contains invalid names, those records are filtered
646// out and an error will be returned alongside the remaining results, if any.
647func (r *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) {
648	names, err := r.lookupAddr(ctx, addr)
649	if err != nil {
650		return nil, err
651	}
652	filteredNames := make([]string, 0, len(names))
653	for _, name := range names {
654		if isDomainName(name) {
655			filteredNames = append(filteredNames, name)
656		}
657	}
658	if len(names) != len(filteredNames) {
659		return filteredNames, &DNSError{Err: errMalformedDNSRecordsDetail, Name: addr}
660	}
661	return filteredNames, nil
662}
663
664// errMalformedDNSRecordsDetail is the DNSError detail which is returned when a Resolver.Lookup...
665// method receives DNS records which contain invalid DNS names. This may be returned alongside
666// results which have had the malformed records filtered out.
667var errMalformedDNSRecordsDetail = "DNS response contained records which contain invalid names"
668