...

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 = bytealg.CountString(addr, ':') > 0
    87  	case "tcp", "udp":
    88  		// IPv6 literal. (addr contains a port, so look for '[')
    89  		want6 = bytealg.CountString(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 := bytealg.LastIndexByteString(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 := bytealg.LastIndexByteString(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