ipsock_posix.mx raw

   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  //go:build unix || js || wasip1 || windows
   6  
   7  package net
   8  
   9  import (
  10  	"context"
  11  	"internal/poll"
  12  	"net/netip"
  13  	"runtime"
  14  	"syscall"
  15  	_ "unsafe" // for linkname
  16  )
  17  
  18  // probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
  19  // capabilities which are controlled by the IPV6_V6ONLY socket option
  20  // and kernel configuration.
  21  //
  22  // Should we try to use the IPv4 socket interface if we're only
  23  // dealing with IPv4 sockets? As long as the host system understands
  24  // IPv4-mapped IPv6, it's okay to pass IPv4-mapped IPv6 addresses to
  25  // the IPv6 interface. That simplifies our code and is most
  26  // general. Unfortunately, we need to run on kernels built without
  27  // IPv6 support too. So probe the kernel to figure it out.
  28  func (p *ipStackCapabilities) probe() {
  29  	switch runtime.GOOS {
  30  	case "js", "wasip1":
  31  		// Both ipv4 and ipv6 are faked; see net_fake.go.
  32  		p.ipv4Enabled = true
  33  		p.ipv6Enabled = true
  34  		p.ipv4MappedIPv6Enabled = true
  35  		return
  36  	}
  37  
  38  	s, err := sysSocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
  39  	switch err {
  40  	case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT:
  41  	case nil:
  42  		poll.CloseFunc(s)
  43  		p.ipv4Enabled = true
  44  	}
  45  	var probes = []struct {
  46  		laddr TCPAddr
  47  		value int
  48  	}{
  49  		// IPv6 communication capability
  50  		{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
  51  		// IPv4-mapped IPv6 address communication capability
  52  		{laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0},
  53  	}
  54  	switch runtime.GOOS {
  55  	case "dragonfly", "openbsd":
  56  		// The latest DragonFly BSD and OpenBSD kernels don't
  57  		// support IPV6_V6ONLY=0. They always return an error
  58  		// and we don't need to probe the capability.
  59  		probes = probes[:1]
  60  	}
  61  	for i := range probes {
  62  		s, err := sysSocket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
  63  		if err != nil {
  64  			continue
  65  		}
  66  		defer poll.CloseFunc(s)
  67  		syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value)
  68  		sa, err := probes[i].laddr.sockaddr(syscall.AF_INET6)
  69  		if err != nil {
  70  			continue
  71  		}
  72  		if err := syscall.Bind(s, sa); err != nil {
  73  			continue
  74  		}
  75  		if i == 0 {
  76  			p.ipv6Enabled = true
  77  		} else {
  78  			p.ipv4MappedIPv6Enabled = true
  79  		}
  80  	}
  81  }
  82  
  83  // favoriteAddrFamily returns the appropriate address family for the
  84  // given network, laddr, raddr and mode.
  85  //
  86  // If mode indicates "listen" and laddr is a wildcard, we assume that
  87  // the user wants to make a passive-open connection with a wildcard
  88  // address family, both AF_INET and AF_INET6, and a wildcard address
  89  // like the following:
  90  //
  91  //   - A listen for a wildcard communication domain, "tcp" or
  92  //     "udp", with a wildcard address: If the platform supports
  93  //     both IPv6 and IPv4-mapped IPv6 communication capabilities,
  94  //     or does not support IPv4, we use a dual stack, AF_INET6 and
  95  //     IPV6_V6ONLY=0, wildcard address listen. The dual stack
  96  //     wildcard address listen may fall back to an IPv6-only,
  97  //     AF_INET6 and IPV6_V6ONLY=1, wildcard address listen.
  98  //     Otherwise we prefer an IPv4-only, AF_INET, wildcard address
  99  //     listen.
 100  //
 101  //   - A listen for a wildcard communication domain, "tcp" or
 102  //     "udp", with an IPv4 wildcard address: same as above.
 103  //
 104  //   - A listen for a wildcard communication domain, "tcp" or
 105  //     "udp", with an IPv6 wildcard address: same as above.
 106  //
 107  //   - A listen for an IPv4 communication domain, "tcp4" or "udp4",
 108  //     with an IPv4 wildcard address: We use an IPv4-only, AF_INET,
 109  //     wildcard address listen.
 110  //
 111  //   - A listen for an IPv6 communication domain, "tcp6" or "udp6",
 112  //     with an IPv6 wildcard address: We use an IPv6-only, AF_INET6
 113  //     and IPV6_V6ONLY=1, wildcard address listen.
 114  //
 115  // Otherwise guess: If the addresses are IPv4 then returns AF_INET,
 116  // or else returns AF_INET6. It also returns a boolean value what
 117  // designates IPV6_V6ONLY option.
 118  //
 119  // Note that the latest DragonFly BSD and OpenBSD kernels allow
 120  // neither "net.inet6.ip6.v6only=1" change nor IPPROTO_IPV6 level
 121  // IPV6_V6ONLY socket option setting.
 122  //
 123  // favoriteAddrFamily should be an internal detail,
 124  // but widely used packages access it using linkname.
 125  // Notable members of the hall of shame include:
 126  //   - github.com/database64128/tfo-go/v2
 127  //   - github.com/metacubex/tfo-go
 128  //   - github.com/sagernet/tfo-go
 129  //
 130  // Do not remove or change the type signature.
 131  // See go.dev/issue/67401.
 132  //
 133  //go:linkname favoriteAddrFamily
 134  func favoriteAddrFamily(network []byte, laddr, raddr sockaddr, mode []byte) (family int, ipv6only bool) {
 135  	switch network[len(network)-1] {
 136  	case '4':
 137  		return syscall.AF_INET, false
 138  	case '6':
 139  		return syscall.AF_INET6, true
 140  	}
 141  
 142  	if mode == "listen" && (laddr == nil || laddr.isWildcard()) {
 143  		if supportsIPv4map() || !supportsIPv4() {
 144  			return syscall.AF_INET6, false
 145  		}
 146  		if laddr == nil {
 147  			return syscall.AF_INET, false
 148  		}
 149  		return laddr.family(), false
 150  	}
 151  
 152  	if (laddr == nil || laddr.family() == syscall.AF_INET) &&
 153  		(raddr == nil || raddr.family() == syscall.AF_INET) {
 154  		return syscall.AF_INET, false
 155  	}
 156  	return syscall.AF_INET6, false
 157  }
 158  
 159  func internetSocket(ctx context.Context, net []byte, laddr, raddr sockaddr, sotype, proto int, mode []byte, ctrlCtxFn func(context.Context, []byte, []byte, syscall.RawConn) error) (fd *netFD, err error) {
 160  	switch runtime.GOOS {
 161  	case "aix", "windows", "openbsd", "js", "wasip1":
 162  		if mode == "dial" && raddr.isWildcard() {
 163  			raddr = raddr.toLocal(net)
 164  		}
 165  	}
 166  	family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
 167  	return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr, ctrlCtxFn)
 168  }
 169  
 170  func ipToSockaddrInet4(ip IP, port int) (syscall.SockaddrInet4, error) {
 171  	if len(ip) == 0 {
 172  		ip = IPv4zero
 173  	}
 174  	ip4 := ip.To4()
 175  	if ip4 == nil {
 176  		return syscall.SockaddrInet4{}, &AddrError{Err: "non-IPv4 address", Addr: ip.String()}
 177  	}
 178  	sa := syscall.SockaddrInet4{Port: port}
 179  	copy(sa.Addr[:], ip4)
 180  	return sa, nil
 181  }
 182  
 183  func ipToSockaddrInet6(ip IP, port int, zone []byte) (syscall.SockaddrInet6, error) {
 184  	// In general, an IP wildcard address, which is either
 185  	// "0.0.0.0" or "::", means the entire IP addressing
 186  	// space. For some historical reason, it is used to
 187  	// specify "any available address" on some operations
 188  	// of IP node.
 189  	//
 190  	// When the IP node supports IPv4-mapped IPv6 address,
 191  	// we allow a listener to listen to the wildcard
 192  	// address of both IP addressing spaces by specifying
 193  	// IPv6 wildcard address.
 194  	if len(ip) == 0 || ip.Equal(IPv4zero) {
 195  		ip = IPv6zero
 196  	}
 197  	// We accept any IPv6 address including IPv4-mapped
 198  	// IPv6 address.
 199  	ip6 := ip.To16()
 200  	if ip6 == nil {
 201  		return syscall.SockaddrInet6{}, &AddrError{Err: "non-IPv6 address", Addr: ip.String()}
 202  	}
 203  	sa := syscall.SockaddrInet6{Port: port, ZoneId: uint32(zoneCache.index(zone))}
 204  	copy(sa.Addr[:], ip6)
 205  	return sa, nil
 206  }
 207  
 208  // ipToSockaddr should be an internal detail,
 209  // but widely used packages access it using linkname.
 210  // Notable members of the hall of shame include:
 211  //   - github.com/database64128/tfo-go/v2
 212  //   - github.com/metacubex/tfo-go
 213  //   - github.com/sagernet/tfo-go
 214  //
 215  // Do not remove or change the type signature.
 216  // See go.dev/issue/67401.
 217  //
 218  //go:linkname ipToSockaddr
 219  func ipToSockaddr(family int, ip IP, port int, zone []byte) (syscall.Sockaddr, error) {
 220  	switch family {
 221  	case syscall.AF_INET:
 222  		sa, err := ipToSockaddrInet4(ip, port)
 223  		if err != nil {
 224  			return nil, err
 225  		}
 226  		return &sa, nil
 227  	case syscall.AF_INET6:
 228  		sa, err := ipToSockaddrInet6(ip, port, zone)
 229  		if err != nil {
 230  			return nil, err
 231  		}
 232  		return &sa, nil
 233  	}
 234  	return nil, &AddrError{Err: "invalid address family", Addr: ip.String()}
 235  }
 236  
 237  func addrPortToSockaddrInet4(ap netip.AddrPort) (syscall.SockaddrInet4, error) {
 238  	// ipToSockaddrInet4 has special handling here for zero length slices.
 239  	// We do not, because netip has no concept of a generic zero IP address.
 240  	//
 241  	// addr is allowed to be an IPv4-mapped IPv6 address.
 242  	// As4 will unmap it to an IPv4 address.
 243  	// The error message is kept consistent with ipToSockaddrInet4.
 244  	addr := ap.Addr()
 245  	if !addr.Is4() && !addr.Is4In6() {
 246  		return syscall.SockaddrInet4{}, &AddrError{Err: "non-IPv4 address", Addr: addr.String()}
 247  	}
 248  	sa := syscall.SockaddrInet4{
 249  		Addr: addr.As4(),
 250  		Port: int(ap.Port()),
 251  	}
 252  	return sa, nil
 253  }
 254  
 255  func addrPortToSockaddrInet6(ap netip.AddrPort) (syscall.SockaddrInet6, error) {
 256  	// ipToSockaddrInet6 has special handling here for zero length slices.
 257  	// We do not, because netip has no concept of a generic zero IP address.
 258  	//
 259  	// addr is allowed to be an IPv4 address, because As16 will convert it
 260  	// to an IPv4-mapped IPv6 address.
 261  	// The error message is kept consistent with ipToSockaddrInet6.
 262  	addr := ap.Addr()
 263  	if !addr.IsValid() {
 264  		return syscall.SockaddrInet6{}, &AddrError{Err: "non-IPv6 address", Addr: addr.String()}
 265  	}
 266  	sa := syscall.SockaddrInet6{
 267  		Addr:   addr.As16(),
 268  		Port:   int(ap.Port()),
 269  		ZoneId: uint32(zoneCache.index(addr.Zone())),
 270  	}
 271  	return sa, nil
 272  }
 273