...

Source file src/net/ipsock.go

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

View as plain text