github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/net/lookup.go (about) 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 5 package net 6 7 import ( 8 "context" 9 "internal/nettrace" 10 "internal/singleflight" 11 ) 12 13 // protocols contains minimal mappings between internet protocol 14 // names and numbers for platforms that don't have a complete list of 15 // protocol numbers. 16 // 17 // See http://www.iana.org/assignments/protocol-numbers 18 var protocols = map[string]int{ 19 "icmp": 1, "ICMP": 1, 20 "igmp": 2, "IGMP": 2, 21 "tcp": 6, "TCP": 6, 22 "udp": 17, "UDP": 17, 23 "ipv6-icmp": 58, "IPV6-ICMP": 58, "IPv6-ICMP": 58, 24 } 25 26 // LookupHost looks up the given host using the local resolver. 27 // It returns an array of that host's addresses. 28 func LookupHost(host string) (addrs []string, err error) { 29 // Make sure that no matter what we do later, host=="" is rejected. 30 // ParseIP, for example, does accept empty strings. 31 if host == "" { 32 return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host} 33 } 34 if ip := ParseIP(host); ip != nil { 35 return []string{host}, nil 36 } 37 return lookupHost(context.Background(), host) 38 } 39 40 // LookupIP looks up host using the local resolver. 41 // It returns an array of that host's IPv4 and IPv6 addresses. 42 func LookupIP(host string) (ips []IP, err error) { 43 // Make sure that no matter what we do later, host=="" is rejected. 44 // ParseIP, for example, does accept empty strings. 45 if host == "" { 46 return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host} 47 } 48 if ip := ParseIP(host); ip != nil { 49 return []IP{ip}, nil 50 } 51 addrs, err := lookupIPMerge(context.Background(), host) 52 if err != nil { 53 return 54 } 55 ips = make([]IP, len(addrs)) 56 for i, addr := range addrs { 57 ips[i] = addr.IP 58 } 59 return 60 } 61 62 var lookupGroup singleflight.Group 63 64 // lookupIPMerge wraps lookupIP, but makes sure that for any given 65 // host, only one lookup is in-flight at a time. The returned memory 66 // is always owned by the caller. 67 func lookupIPMerge(ctx context.Context, host string) (addrs []IPAddr, err error) { 68 addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) { 69 return testHookLookupIP(ctx, lookupIP, host) 70 }) 71 return lookupIPReturn(addrsi, err, shared) 72 } 73 74 // lookupIPReturn turns the return values from singleflight.Do into 75 // the return values from LookupIP. 76 func lookupIPReturn(addrsi interface{}, err error, shared bool) ([]IPAddr, error) { 77 if err != nil { 78 return nil, err 79 } 80 addrs := addrsi.([]IPAddr) 81 if shared { 82 clone := make([]IPAddr, len(addrs)) 83 copy(clone, addrs) 84 addrs = clone 85 } 86 return addrs, nil 87 } 88 89 // ipAddrsEface returns an empty interface slice of addrs. 90 func ipAddrsEface(addrs []IPAddr) []interface{} { 91 s := make([]interface{}, len(addrs)) 92 for i, v := range addrs { 93 s[i] = v 94 } 95 return s 96 } 97 98 // lookupIPContext looks up a hostname with a context. 99 // 100 // TODO(bradfitz): rename this function. All the other 101 // build-tag-specific lookupIP funcs also take a context now, so this 102 // name is no longer great. Maybe make this lookupIPMerge and ditch 103 // the other one, making its callers call this instead with a 104 // context.Background(). 105 func lookupIPContext(ctx context.Context, host string) (addrs []IPAddr, err error) { 106 trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace) 107 if trace != nil && trace.DNSStart != nil { 108 trace.DNSStart(host) 109 } 110 // The underlying resolver func is lookupIP by default but it 111 // can be overridden by tests. This is needed by net/http, so it 112 // uses a context key instead of unexported variables. 113 resolverFunc := lookupIP 114 if alt, _ := ctx.Value(nettrace.LookupIPAltResolverKey{}).(func(context.Context, string) ([]IPAddr, error)); alt != nil { 115 resolverFunc = alt 116 } 117 118 ch := lookupGroup.DoChan(host, func() (interface{}, error) { 119 return testHookLookupIP(ctx, resolverFunc, host) 120 }) 121 122 select { 123 case <-ctx.Done(): 124 // The DNS lookup timed out for some reason. Force 125 // future requests to start the DNS lookup again 126 // rather than waiting for the current lookup to 127 // complete. See issue 8602. 128 err := mapErr(ctx.Err()) 129 lookupGroup.Forget(host) 130 if trace != nil && trace.DNSDone != nil { 131 trace.DNSDone(nil, false, err) 132 } 133 return nil, err 134 case r := <-ch: 135 if trace != nil && trace.DNSDone != nil { 136 addrs, _ := r.Val.([]IPAddr) 137 trace.DNSDone(ipAddrsEface(addrs), r.Shared, r.Err) 138 } 139 return lookupIPReturn(r.Val, r.Err, r.Shared) 140 } 141 } 142 143 // LookupPort looks up the port for the given network and service. 144 func LookupPort(network, service string) (port int, err error) { 145 port, needsLookup := parsePort(service) 146 if needsLookup { 147 port, err = lookupPort(context.Background(), network, service) 148 if err != nil { 149 return 0, err 150 } 151 } 152 if 0 > port || port > 65535 { 153 return 0, &AddrError{Err: "invalid port", Addr: service} 154 } 155 return port, nil 156 } 157 158 // LookupCNAME returns the canonical DNS host for the given name. 159 // Callers that do not care about the canonical name can call 160 // LookupHost or LookupIP directly; both take care of resolving 161 // the canonical name as part of the lookup. 162 func LookupCNAME(name string) (cname string, err error) { 163 return lookupCNAME(context.Background(), name) 164 } 165 166 // LookupSRV tries to resolve an SRV query of the given service, 167 // protocol, and domain name. The proto is "tcp" or "udp". 168 // The returned records are sorted by priority and randomized 169 // by weight within a priority. 170 // 171 // LookupSRV constructs the DNS name to look up following RFC 2782. 172 // That is, it looks up _service._proto.name. To accommodate services 173 // publishing SRV records under non-standard names, if both service 174 // and proto are empty strings, LookupSRV looks up name directly. 175 func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { 176 return lookupSRV(context.Background(), service, proto, name) 177 } 178 179 // LookupMX returns the DNS MX records for the given domain name sorted by preference. 180 func LookupMX(name string) (mxs []*MX, err error) { 181 return lookupMX(context.Background(), name) 182 } 183 184 // LookupNS returns the DNS NS records for the given domain name. 185 func LookupNS(name string) (nss []*NS, err error) { 186 return lookupNS(context.Background(), name) 187 } 188 189 // LookupTXT returns the DNS TXT records for the given domain name. 190 func LookupTXT(name string) (txts []string, err error) { 191 return lookupTXT(context.Background(), name) 192 } 193 194 // LookupAddr performs a reverse lookup for the given address, returning a list 195 // of names mapping to that address. 196 func LookupAddr(addr string) (names []string, err error) { 197 return lookupAddr(context.Background(), addr) 198 }