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