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