| // Copyright 2009 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | //go:build unix || (js && wasm) || wasip1 || windows | 
 |  | 
 | package net | 
 |  | 
 | import ( | 
 | 	"context" | 
 | 	"internal/poll" | 
 | 	"net/netip" | 
 | 	"runtime" | 
 | 	"syscall" | 
 | ) | 
 |  | 
 | // probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication | 
 | // capabilities which are controlled by the IPV6_V6ONLY socket option | 
 | // and kernel configuration. | 
 | // | 
 | // Should we try to use the IPv4 socket interface if we're only | 
 | // dealing with IPv4 sockets? As long as the host system understands | 
 | // IPv4-mapped IPv6, it's okay to pass IPv4-mapped IPv6 addresses to | 
 | // the IPv6 interface. That simplifies our code and is most | 
 | // general. Unfortunately, we need to run on kernels built without | 
 | // IPv6 support too. So probe the kernel to figure it out. | 
 | func (p *ipStackCapabilities) probe() { | 
 | 	s, err := sysSocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) | 
 | 	switch err { | 
 | 	case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT: | 
 | 	case nil: | 
 | 		poll.CloseFunc(s) | 
 | 		p.ipv4Enabled = true | 
 | 	} | 
 | 	var probes = []struct { | 
 | 		laddr TCPAddr | 
 | 		value int | 
 | 	}{ | 
 | 		// IPv6 communication capability | 
 | 		{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1}, | 
 | 		// IPv4-mapped IPv6 address communication capability | 
 | 		{laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0}, | 
 | 	} | 
 | 	switch runtime.GOOS { | 
 | 	case "dragonfly", "openbsd": | 
 | 		// The latest DragonFly BSD and OpenBSD kernels don't | 
 | 		// support IPV6_V6ONLY=0. They always return an error | 
 | 		// and we don't need to probe the capability. | 
 | 		probes = probes[:1] | 
 | 	} | 
 | 	for i := range probes { | 
 | 		s, err := sysSocket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) | 
 | 		if err != nil { | 
 | 			continue | 
 | 		} | 
 | 		defer poll.CloseFunc(s) | 
 | 		syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value) | 
 | 		sa, err := probes[i].laddr.sockaddr(syscall.AF_INET6) | 
 | 		if err != nil { | 
 | 			continue | 
 | 		} | 
 | 		if err := syscall.Bind(s, sa); err != nil { | 
 | 			continue | 
 | 		} | 
 | 		if i == 0 { | 
 | 			p.ipv6Enabled = true | 
 | 		} else { | 
 | 			p.ipv4MappedIPv6Enabled = true | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // favoriteAddrFamily returns the appropriate address family for the | 
 | // given network, laddr, raddr and mode. | 
 | // | 
 | // If mode indicates "listen" and laddr is a wildcard, we assume that | 
 | // the user wants to make a passive-open connection with a wildcard | 
 | // address family, both AF_INET and AF_INET6, and a wildcard address | 
 | // like the following: | 
 | // | 
 | //   - A listen for a wildcard communication domain, "tcp" or | 
 | //     "udp", with a wildcard address: If the platform supports | 
 | //     both IPv6 and IPv4-mapped IPv6 communication capabilities, | 
 | //     or does not support IPv4, we use a dual stack, AF_INET6 and | 
 | //     IPV6_V6ONLY=0, wildcard address listen. The dual stack | 
 | //     wildcard address listen may fall back to an IPv6-only, | 
 | //     AF_INET6 and IPV6_V6ONLY=1, wildcard address listen. | 
 | //     Otherwise we prefer an IPv4-only, AF_INET, wildcard address | 
 | //     listen. | 
 | // | 
 | //   - A listen for a wildcard communication domain, "tcp" or | 
 | //     "udp", with an IPv4 wildcard address: same as above. | 
 | // | 
 | //   - A listen for a wildcard communication domain, "tcp" or | 
 | //     "udp", with an IPv6 wildcard address: same as above. | 
 | // | 
 | //   - A listen for an IPv4 communication domain, "tcp4" or "udp4", | 
 | //     with an IPv4 wildcard address: We use an IPv4-only, AF_INET, | 
 | //     wildcard address listen. | 
 | // | 
 | //   - A listen for an IPv6 communication domain, "tcp6" or "udp6", | 
 | //     with an IPv6 wildcard address: We use an IPv6-only, AF_INET6 | 
 | //     and IPV6_V6ONLY=1, wildcard address listen. | 
 | // | 
 | // Otherwise guess: If the addresses are IPv4 then returns AF_INET, | 
 | // or else returns AF_INET6. It also returns a boolean value what | 
 | // designates IPV6_V6ONLY option. | 
 | // | 
 | // Note that the latest DragonFly BSD and OpenBSD kernels allow | 
 | // neither "net.inet6.ip6.v6only=1" change nor IPPROTO_IPV6 level | 
 | // IPV6_V6ONLY socket option setting. | 
 | func favoriteAddrFamily(network string, laddr, raddr sockaddr, mode string) (family int, ipv6only bool) { | 
 | 	switch network[len(network)-1] { | 
 | 	case '4': | 
 | 		return syscall.AF_INET, false | 
 | 	case '6': | 
 | 		return syscall.AF_INET6, true | 
 | 	} | 
 |  | 
 | 	if mode == "listen" && (laddr == nil || laddr.isWildcard()) { | 
 | 		if supportsIPv4map() || !supportsIPv4() { | 
 | 			return syscall.AF_INET6, false | 
 | 		} | 
 | 		if laddr == nil { | 
 | 			return syscall.AF_INET, false | 
 | 		} | 
 | 		return laddr.family(), false | 
 | 	} | 
 |  | 
 | 	if (laddr == nil || laddr.family() == syscall.AF_INET) && | 
 | 		(raddr == nil || raddr.family() == syscall.AF_INET) { | 
 | 		return syscall.AF_INET, false | 
 | 	} | 
 | 	return syscall.AF_INET6, false | 
 | } | 
 |  | 
 | func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string, ctrlCtxFn func(context.Context, string, string, syscall.RawConn) error) (fd *netFD, err error) { | 
 | 	if (runtime.GOOS == "aix" || runtime.GOOS == "windows" || runtime.GOOS == "openbsd") && mode == "dial" && raddr.isWildcard() { | 
 | 		raddr = raddr.toLocal(net) | 
 | 	} | 
 | 	family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode) | 
 | 	return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr, ctrlCtxFn) | 
 | } | 
 |  | 
 | func ipToSockaddrInet4(ip IP, port int) (syscall.SockaddrInet4, error) { | 
 | 	if len(ip) == 0 { | 
 | 		ip = IPv4zero | 
 | 	} | 
 | 	ip4 := ip.To4() | 
 | 	if ip4 == nil { | 
 | 		return syscall.SockaddrInet4{}, &AddrError{Err: "non-IPv4 address", Addr: ip.String()} | 
 | 	} | 
 | 	sa := syscall.SockaddrInet4{Port: port} | 
 | 	copy(sa.Addr[:], ip4) | 
 | 	return sa, nil | 
 | } | 
 |  | 
 | func ipToSockaddrInet6(ip IP, port int, zone string) (syscall.SockaddrInet6, error) { | 
 | 	// In general, an IP wildcard address, which is either | 
 | 	// "0.0.0.0" or "::", means the entire IP addressing | 
 | 	// space. For some historical reason, it is used to | 
 | 	// specify "any available address" on some operations | 
 | 	// of IP node. | 
 | 	// | 
 | 	// When the IP node supports IPv4-mapped IPv6 address, | 
 | 	// we allow a listener to listen to the wildcard | 
 | 	// address of both IP addressing spaces by specifying | 
 | 	// IPv6 wildcard address. | 
 | 	if len(ip) == 0 || ip.Equal(IPv4zero) { | 
 | 		ip = IPv6zero | 
 | 	} | 
 | 	// We accept any IPv6 address including IPv4-mapped | 
 | 	// IPv6 address. | 
 | 	ip6 := ip.To16() | 
 | 	if ip6 == nil { | 
 | 		return syscall.SockaddrInet6{}, &AddrError{Err: "non-IPv6 address", Addr: ip.String()} | 
 | 	} | 
 | 	sa := syscall.SockaddrInet6{Port: port, ZoneId: uint32(zoneCache.index(zone))} | 
 | 	copy(sa.Addr[:], ip6) | 
 | 	return sa, nil | 
 | } | 
 |  | 
 | func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) { | 
 | 	switch family { | 
 | 	case syscall.AF_INET: | 
 | 		sa, err := ipToSockaddrInet4(ip, port) | 
 | 		if err != nil { | 
 | 			return nil, err | 
 | 		} | 
 | 		return &sa, nil | 
 | 	case syscall.AF_INET6: | 
 | 		sa, err := ipToSockaddrInet6(ip, port, zone) | 
 | 		if err != nil { | 
 | 			return nil, err | 
 | 		} | 
 | 		return &sa, nil | 
 | 	} | 
 | 	return nil, &AddrError{Err: "invalid address family", Addr: ip.String()} | 
 | } | 
 |  | 
 | func addrPortToSockaddrInet4(ap netip.AddrPort) (syscall.SockaddrInet4, error) { | 
 | 	// ipToSockaddrInet4 has special handling here for zero length slices. | 
 | 	// We do not, because netip has no concept of a generic zero IP address. | 
 | 	addr := ap.Addr() | 
 | 	if !addr.Is4() { | 
 | 		return syscall.SockaddrInet4{}, &AddrError{Err: "non-IPv4 address", Addr: addr.String()} | 
 | 	} | 
 | 	sa := syscall.SockaddrInet4{ | 
 | 		Addr: addr.As4(), | 
 | 		Port: int(ap.Port()), | 
 | 	} | 
 | 	return sa, nil | 
 | } | 
 |  | 
 | func addrPortToSockaddrInet6(ap netip.AddrPort) (syscall.SockaddrInet6, error) { | 
 | 	// ipToSockaddrInet6 has special handling here for zero length slices. | 
 | 	// We do not, because netip has no concept of a generic zero IP address. | 
 | 	// | 
 | 	// addr is allowed to be an IPv4 address, because As16 will convert it | 
 | 	// to an IPv4-mapped IPv6 address. | 
 | 	// The error message is kept consistent with ipToSockaddrInet6. | 
 | 	addr := ap.Addr() | 
 | 	if !addr.IsValid() { | 
 | 		return syscall.SockaddrInet6{}, &AddrError{Err: "non-IPv6 address", Addr: addr.String()} | 
 | 	} | 
 | 	sa := syscall.SockaddrInet6{ | 
 | 		Addr:   addr.As16(), | 
 | 		Port:   int(ap.Port()), | 
 | 		ZoneId: uint32(zoneCache.index(addr.Zone())), | 
 | 	} | 
 | 	return sa, nil | 
 | } |