github.com/s1s1ty/go@v0.0.0-20180207192209-104445e3140f/src/net/ipsock.go (about)

     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  }