Home | History | Annotate | Download | only in net
      1 // Copyright 2009 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 	"sync"
     10 )
     11 
     12 // BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the
     13 // "tcp" and "udp" networks does not listen for both IPv4 and IPv6
     14 // connections. This is due to the fact that IPv4 traffic will not be
     15 // routed to an IPv6 socket - two separate sockets are required if
     16 // both address families are to be supported.
     17 // See inet6(4) for details.
     18 
     19 type ipStackCapabilities struct {
     20 	sync.Once             // guards following
     21 	ipv4Enabled           bool
     22 	ipv6Enabled           bool
     23 	ipv4MappedIPv6Enabled bool
     24 }
     25 
     26 var ipStackCaps ipStackCapabilities
     27 
     28 // supportsIPv4 reports whether the platform supports IPv4 networking
     29 // functionality.
     30 func supportsIPv4() bool {
     31 	ipStackCaps.Once.Do(ipStackCaps.probe)
     32 	return ipStackCaps.ipv4Enabled
     33 }
     34 
     35 // supportsIPv6 reports whether the platform supports IPv6 networking
     36 // functionality.
     37 func supportsIPv6() bool {
     38 	ipStackCaps.Once.Do(ipStackCaps.probe)
     39 	return ipStackCaps.ipv6Enabled
     40 }
     41 
     42 // supportsIPv4map reports whether the platform supports mapping an
     43 // IPv4 address inside an IPv6 address at transport layer
     44 // protocols. See RFC 4291, RFC 4038 and RFC 3493.
     45 func supportsIPv4map() bool {
     46 	ipStackCaps.Once.Do(ipStackCaps.probe)
     47 	return ipStackCaps.ipv4MappedIPv6Enabled
     48 }
     49 
     50 // An addrList represents a list of network endpoint addresses.
     51 type addrList []Addr
     52 
     53 // isIPv4 reports whether addr contains an IPv4 address.
     54 func isIPv4(addr Addr) bool {
     55 	switch addr := addr.(type) {
     56 	case *TCPAddr:
     57 		return addr.IP.To4() != nil
     58 	case *UDPAddr:
     59 		return addr.IP.To4() != nil
     60 	case *IPAddr:
     61 		return addr.IP.To4() != nil
     62 	}
     63 	return false
     64 }
     65 
     66 // isNotIPv4 reports whether addr does not contain an IPv4 address.
     67 func isNotIPv4(addr Addr) bool { return !isIPv4(addr) }
     68 
     69 // forResolve returns the most appropriate address in address for
     70 // a call to ResolveTCPAddr, ResolveUDPAddr, or ResolveIPAddr.
     71 // IPv4 is preferred, unless addr contains an IPv6 literal.
     72 func (addrs addrList) forResolve(network, addr string) Addr {
     73 	var want6 bool
     74 	switch network {
     75 	case "ip":
     76 		// IPv6 literal (addr does NOT contain a port)
     77 		want6 = count(addr, ':') > 0
     78 	case "tcp", "udp":
     79 		// IPv6 literal. (addr contains a port, so look for '[')
     80 		want6 = count(addr, '[') > 0
     81 	}
     82 	if want6 {
     83 		return addrs.first(isNotIPv4)
     84 	}
     85 	return addrs.first(isIPv4)
     86 }
     87 
     88 // first returns the first address which satisfies strategy, or if
     89 // none do, then the first address of any kind.
     90 func (addrs addrList) first(strategy func(Addr) bool) Addr {
     91 	for _, addr := range addrs {
     92 		if strategy(addr) {
     93 			return addr
     94 		}
     95 	}
     96 	return addrs[0]
     97 }
     98 
     99 // partition divides an address list into two categories, using a
    100 // strategy function to assign a boolean label to each address.
    101 // The first address, and any with a matching label, are returned as
    102 // primaries, while addresses with the opposite label are returned
    103 // as fallbacks. For non-empty inputs, primaries is guaranteed to be
    104 // non-empty.
    105 func (addrs addrList) partition(strategy func(Addr) bool) (primaries, fallbacks addrList) {
    106 	var primaryLabel bool
    107 	for i, addr := range addrs {
    108 		label := strategy(addr)
    109 		if i == 0 || label == primaryLabel {
    110 			primaryLabel = label
    111 			primaries = append(primaries, addr)
    112 		} else {
    113 			fallbacks = append(fallbacks, addr)
    114 		}
    115 	}
    116 	return
    117 }
    118 
    119 // filterAddrList applies a filter to a list of IP addresses,
    120 // yielding a list of Addr objects. Known filters are nil, ipv4only,
    121 // and ipv6only. It returns every address when the filter is nil.
    122 // The result contains at least one address when error is nil.
    123 func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) Addr, originalAddr string) (addrList, error) {
    124 	var addrs addrList
    125 	for _, ip := range ips {
    126 		if filter == nil || filter(ip) {
    127 			addrs = append(addrs, inetaddr(ip))
    128 		}
    129 	}
    130 	if len(addrs) == 0 {
    131 		return nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: originalAddr}
    132 	}
    133 	return addrs, nil
    134 }
    135 
    136 // ipv4only reports whether addr is an IPv4 address.
    137 func ipv4only(addr IPAddr) bool {
    138 	return addr.IP.To4() != nil
    139 }
    140 
    141 // ipv6only reports whether addr is an IPv6 address except IPv4-mapped IPv6 address.
    142 func ipv6only(addr IPAddr) bool {
    143 	return len(addr.IP) == IPv6len && addr.IP.To4() == nil
    144 }
    145 
    146 // SplitHostPort splits a network address of the form "host:port",
    147 // "host%zone:port", "[host]:port" or "[host%zone]:port" into host or
    148 // host%zone and port.
    149 //
    150 // A literal IPv6 address in hostport must be enclosed in square
    151 // brackets, as in "[::1]:80", "[::1%lo0]:80".
    152 //
    153 // See func Dial for a description of the hostport parameter, and host
    154 // and port results.
    155 func SplitHostPort(hostport string) (host, port string, err error) {
    156 	const (
    157 		missingPort   = "missing port in address"
    158 		tooManyColons = "too many colons in address"
    159 	)
    160 	addrErr := func(addr, why string) (host, port string, err error) {
    161 		return "", "", &AddrError{Err: why, Addr: addr}
    162 	}
    163 	j, k := 0, 0
    164 
    165 	// The port starts after the last colon.
    166 	i := last(hostport, ':')
    167 	if i < 0 {
    168 		return addrErr(hostport, missingPort)
    169 	}
    170 
    171 	if hostport[0] == '[' {
    172 		// Expect the first ']' just before the last ':'.
    173 		end := byteIndex(hostport, ']')
    174 		if end < 0 {
    175 			return addrErr(hostport, "missing ']' in address")
    176 		}
    177 		switch end + 1 {
    178 		case len(hostport):
    179 			// There can't be a ':' behind the ']' now.
    180 			return addrErr(hostport, missingPort)
    181 		case i:
    182 			// The expected result.
    183 		default:
    184 			// Either ']' isn't followed by a colon, or it is
    185 			// followed by a colon that is not the last one.
    186 			if hostport[end+1] == ':' {
    187 				return addrErr(hostport, tooManyColons)
    188 			}
    189 			return addrErr(hostport, missingPort)
    190 		}
    191 		host = hostport[1:end]
    192 		j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions
    193 	} else {
    194 		host = hostport[:i]
    195 		if byteIndex(host, ':') >= 0 {
    196 			return addrErr(hostport, tooManyColons)
    197 		}
    198 	}
    199 	if byteIndex(hostport[j:], '[') >= 0 {
    200 		return addrErr(hostport, "unexpected '[' in address")
    201 	}
    202 	if byteIndex(hostport[k:], ']') >= 0 {
    203 		return addrErr(hostport, "unexpected ']' in address")
    204 	}
    205 
    206 	port = hostport[i+1:]
    207 	return host, port, nil
    208 }
    209 
    210 func splitHostZone(s string) (host, zone string) {
    211 	// The IPv6 scoped addressing zone identifier starts after the
    212 	// last percent sign.
    213 	if i := last(s, '%'); i > 0 {
    214 		host, zone = s[:i], s[i+1:]
    215 	} else {
    216 		host = s
    217 	}
    218 	return
    219 }
    220 
    221 // JoinHostPort combines host and port into a network address of the
    222 // form "host:port". If host contains a colon, as found in literal
    223 // IPv6 addresses, then JoinHostPort returns "[host]:port".
    224 //
    225 // See func Dial for a description of the host and port parameters.
    226 func JoinHostPort(host, port string) string {
    227 	// We assume that host is a literal IPv6 address if host has
    228 	// colons.
    229 	if byteIndex(host, ':') >= 0 {
    230 		return "[" + host + "]:" + port
    231 	}
    232 	return host + ":" + port
    233 }
    234 
    235 // internetAddrList resolves addr, which may be a literal IP
    236 // address or a DNS name, and returns a list of internet protocol
    237 // family addresses. The result contains at least one address when
    238 // error is nil.
    239 func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
    240 	var (
    241 		err        error
    242 		host, port string
    243 		portnum    int
    244 	)
    245 	switch net {
    246 	case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
    247 		if addr != "" {
    248 			if host, port, err = SplitHostPort(addr); err != nil {
    249 				return nil, err
    250 			}
    251 			if portnum, err = r.LookupPort(ctx, net, port); err != nil {
    252 				return nil, err
    253 			}
    254 		}
    255 	case "ip", "ip4", "ip6":
    256 		if addr != "" {
    257 			host = addr
    258 		}
    259 	default:
    260 		return nil, UnknownNetworkError(net)
    261 	}
    262 	inetaddr := func(ip IPAddr) Addr {
    263 		switch net {
    264 		case "tcp", "tcp4", "tcp6":
    265 			return &TCPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone}
    266 		case "udp", "udp4", "udp6":
    267 			return &UDPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone}
    268 		case "ip", "ip4", "ip6":
    269 			return &IPAddr{IP: ip.IP, Zone: ip.Zone}
    270 		default:
    271 			panic("unexpected network: " + net)
    272 		}
    273 	}
    274 	if host == "" {
    275 		return addrList{inetaddr(IPAddr{})}, nil
    276 	}
    277 
    278 	// Try as a literal IP address, then as a DNS name.
    279 	var ips []IPAddr
    280 	if ip := parseIPv4(host); ip != nil {
    281 		ips = []IPAddr{{IP: ip}}
    282 	} else if ip, zone := parseIPv6(host, true); ip != nil {
    283 		ips = []IPAddr{{IP: ip, Zone: zone}}
    284 		// Issue 18806: if the machine has halfway configured
    285 		// IPv6 such that it can bind on "::" (IPv6unspecified)
    286 		// but not connect back to that same address, fall
    287 		// back to dialing 0.0.0.0.
    288 		if ip.Equal(IPv6unspecified) {
    289 			ips = append(ips, IPAddr{IP: IPv4zero})
    290 		}
    291 	} else {
    292 		// Try as a DNS name.
    293 		ips, err = r.LookupIPAddr(ctx, host)
    294 		if err != nil {
    295 			return nil, err
    296 		}
    297 	}
    298 
    299 	var filter func(IPAddr) bool
    300 	if net != "" && net[len(net)-1] == '4' {
    301 		filter = ipv4only
    302 	}
    303 	if net != "" && net[len(net)-1] == '6' {
    304 		filter = ipv6only
    305 	}
    306 	return filterAddrList(filter, ips, inetaddr, host)
    307 }
    308 
    309 func loopbackIP(net string) IP {
    310 	if net != "" && net[len(net)-1] == '6' {
    311 		return IPv6loopback
    312 	}
    313 	return IP{127, 0, 0, 1}
    314 }
    315