| // 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. | 
 |  | 
 | // Internet protocol family sockets | 
 |  | 
 | package net | 
 |  | 
 | import ( | 
 | 	"context" | 
 | ) | 
 |  | 
 | // BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the | 
 | // "tcp" and "udp" networks does not listen for both IPv4 and IPv6 | 
 | // connections. This is due to the fact that IPv4 traffic will not be | 
 | // routed to an IPv6 socket - two separate sockets are required if | 
 | // both address families are to be supported. | 
 | // See inet6(4) for details. | 
 |  | 
 | var ( | 
 | 	// supportsIPv4 reports whether the platform supports IPv4 | 
 | 	// networking functionality. | 
 | 	supportsIPv4 bool | 
 |  | 
 | 	// supportsIPv6 reports whether the platform supports IPv6 | 
 | 	// networking functionality. | 
 | 	supportsIPv6 bool | 
 |  | 
 | 	// supportsIPv4map reports whether the platform supports | 
 | 	// mapping an IPv4 address inside an IPv6 address at transport | 
 | 	// layer protocols. See RFC 4291, RFC 4038 and RFC 3493. | 
 | 	supportsIPv4map bool | 
 | ) | 
 |  | 
 | // An addrList represents a list of network endpoint addresses. | 
 | type addrList []Addr | 
 |  | 
 | // isIPv4 returns true if the Addr contains an IPv4 address. | 
 | func isIPv4(addr Addr) bool { | 
 | 	switch addr := addr.(type) { | 
 | 	case *TCPAddr: | 
 | 		return addr.IP.To4() != nil | 
 | 	case *UDPAddr: | 
 | 		return addr.IP.To4() != nil | 
 | 	case *IPAddr: | 
 | 		return addr.IP.To4() != nil | 
 | 	} | 
 | 	return false | 
 | } | 
 |  | 
 | // first returns the first address which satisfies strategy, or if | 
 | // none do, then the first address of any kind. | 
 | func (addrs addrList) first(strategy func(Addr) bool) Addr { | 
 | 	for _, addr := range addrs { | 
 | 		if strategy(addr) { | 
 | 			return addr | 
 | 		} | 
 | 	} | 
 | 	return addrs[0] | 
 | } | 
 |  | 
 | // partition divides an address list into two categories, using a | 
 | // strategy function to assign a boolean label to each address. | 
 | // The first address, and any with a matching label, are returned as | 
 | // primaries, while addresses with the opposite label are returned | 
 | // as fallbacks. For non-empty inputs, primaries is guaranteed to be | 
 | // non-empty. | 
 | func (addrs addrList) partition(strategy func(Addr) bool) (primaries, fallbacks addrList) { | 
 | 	var primaryLabel bool | 
 | 	for i, addr := range addrs { | 
 | 		label := strategy(addr) | 
 | 		if i == 0 || label == primaryLabel { | 
 | 			primaryLabel = label | 
 | 			primaries = append(primaries, addr) | 
 | 		} else { | 
 | 			fallbacks = append(fallbacks, addr) | 
 | 		} | 
 | 	} | 
 | 	return | 
 | } | 
 |  | 
 | // filterAddrList applies a filter to a list of IP addresses, | 
 | // yielding a list of Addr objects. Known filters are nil, ipv4only, | 
 | // and ipv6only. It returns every address when the filter is nil. | 
 | // The result contains at least one address when error is nil. | 
 | func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) Addr, originalAddr string) (addrList, error) { | 
 | 	var addrs addrList | 
 | 	for _, ip := range ips { | 
 | 		if filter == nil || filter(ip) { | 
 | 			addrs = append(addrs, inetaddr(ip)) | 
 | 		} | 
 | 	} | 
 | 	if len(addrs) == 0 { | 
 | 		return nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: originalAddr} | 
 | 	} | 
 | 	return addrs, nil | 
 | } | 
 |  | 
 | // ipv4only reports whether addr is an IPv4 address. | 
 | func ipv4only(addr IPAddr) bool { | 
 | 	return addr.IP.To4() != nil | 
 | } | 
 |  | 
 | // ipv6only reports whether addr is an IPv6 address except IPv4-mapped IPv6 address. | 
 | func ipv6only(addr IPAddr) bool { | 
 | 	return len(addr.IP) == IPv6len && addr.IP.To4() == nil | 
 | } | 
 |  | 
 | // SplitHostPort splits a network address of the form "host:port", | 
 | // "[host]:port" or "[ipv6-host%zone]:port" into host or | 
 | // ipv6-host%zone and port. A literal address or host name for IPv6 | 
 | // must be enclosed in square brackets, as in "[::1]:80", | 
 | // "[ipv6-host]:http" or "[ipv6-host%zone]:80". | 
 | func SplitHostPort(hostport string) (host, port string, err error) { | 
 | 	const ( | 
 | 		missingPort   = "missing port in address" | 
 | 		tooManyColons = "too many colons in address" | 
 | 	) | 
 | 	addrErr := func(addr, why string) (host, port string, err error) { | 
 | 		return "", "", &AddrError{Err: why, Addr: addr} | 
 | 	} | 
 | 	j, k := 0, 0 | 
 |  | 
 | 	// The port starts after the last colon. | 
 | 	i := last(hostport, ':') | 
 | 	if i < 0 { | 
 | 		return addrErr(hostport, missingPort) | 
 | 	} | 
 |  | 
 | 	if hostport[0] == '[' { | 
 | 		// Expect the first ']' just before the last ':'. | 
 | 		end := byteIndex(hostport, ']') | 
 | 		if end < 0 { | 
 | 			return addrErr(hostport, "missing ']' in address") | 
 | 		} | 
 | 		switch end + 1 { | 
 | 		case len(hostport): | 
 | 			// There can't be a ':' behind the ']' now. | 
 | 			return addrErr(hostport, missingPort) | 
 | 		case i: | 
 | 			// The expected result. | 
 | 		default: | 
 | 			// Either ']' isn't followed by a colon, or it is | 
 | 			// followed by a colon that is not the last one. | 
 | 			if hostport[end+1] == ':' { | 
 | 				return addrErr(hostport, tooManyColons) | 
 | 			} | 
 | 			return addrErr(hostport, missingPort) | 
 | 		} | 
 | 		host = hostport[1:end] | 
 | 		j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions | 
 | 	} else { | 
 | 		host = hostport[:i] | 
 | 		if byteIndex(host, ':') >= 0 { | 
 | 			return addrErr(hostport, tooManyColons) | 
 | 		} | 
 | 		if byteIndex(host, '%') >= 0 { | 
 | 			return addrErr(hostport, "missing brackets in address") | 
 | 		} | 
 | 	} | 
 | 	if byteIndex(hostport[j:], '[') >= 0 { | 
 | 		return addrErr(hostport, "unexpected '[' in address") | 
 | 	} | 
 | 	if byteIndex(hostport[k:], ']') >= 0 { | 
 | 		return addrErr(hostport, "unexpected ']' in address") | 
 | 	} | 
 |  | 
 | 	port = hostport[i+1:] | 
 | 	return host, port, nil | 
 | } | 
 |  | 
 | func splitHostZone(s string) (host, zone string) { | 
 | 	// The IPv6 scoped addressing zone identifier starts after the | 
 | 	// last percent sign. | 
 | 	if i := last(s, '%'); i > 0 { | 
 | 		host, zone = s[:i], s[i+1:] | 
 | 	} else { | 
 | 		host = s | 
 | 	} | 
 | 	return | 
 | } | 
 |  | 
 | // JoinHostPort combines host and port into a network address of the | 
 | // form "host:port" or, if host contains a colon or a percent sign, | 
 | // "[host]:port". | 
 | func JoinHostPort(host, port string) string { | 
 | 	// If host has colons or a percent sign, have to bracket it. | 
 | 	if byteIndex(host, ':') >= 0 || byteIndex(host, '%') >= 0 { | 
 | 		return "[" + host + "]:" + port | 
 | 	} | 
 | 	return host + ":" + port | 
 | } | 
 |  | 
 | // internetAddrList resolves addr, which may be a literal IP | 
 | // address or a DNS name, and returns a list of internet protocol | 
 | // family addresses. The result contains at least one address when | 
 | // error is nil. | 
 | func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addrList, error) { | 
 | 	var ( | 
 | 		err        error | 
 | 		host, port string | 
 | 		portnum    int | 
 | 	) | 
 | 	switch net { | 
 | 	case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": | 
 | 		if addr != "" { | 
 | 			if host, port, err = SplitHostPort(addr); err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 			if portnum, err = r.LookupPort(ctx, net, port); err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 		} | 
 | 	case "ip", "ip4", "ip6": | 
 | 		if addr != "" { | 
 | 			host = addr | 
 | 		} | 
 | 	default: | 
 | 		return nil, UnknownNetworkError(net) | 
 | 	} | 
 | 	inetaddr := func(ip IPAddr) Addr { | 
 | 		switch net { | 
 | 		case "tcp", "tcp4", "tcp6": | 
 | 			return &TCPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone} | 
 | 		case "udp", "udp4", "udp6": | 
 | 			return &UDPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone} | 
 | 		case "ip", "ip4", "ip6": | 
 | 			return &IPAddr{IP: ip.IP, Zone: ip.Zone} | 
 | 		default: | 
 | 			panic("unexpected network: " + net) | 
 | 		} | 
 | 	} | 
 | 	if host == "" { | 
 | 		return addrList{inetaddr(IPAddr{})}, nil | 
 | 	} | 
 |  | 
 | 	// Try as a literal IP address, then as a DNS name. | 
 | 	var ips []IPAddr | 
 | 	if ip := parseIPv4(host); ip != nil { | 
 | 		ips = []IPAddr{{IP: ip}} | 
 | 	} else if ip, zone := parseIPv6(host, true); ip != nil { | 
 | 		ips = []IPAddr{{IP: ip, Zone: zone}} | 
 | 	} else { | 
 | 		// Try as a DNS name. | 
 | 		ips, err = r.LookupIPAddr(ctx, host) | 
 | 		if err != nil { | 
 | 			return nil, err | 
 | 		} | 
 | 	} | 
 |  | 
 | 	var filter func(IPAddr) bool | 
 | 	if net != "" && net[len(net)-1] == '4' { | 
 | 		filter = ipv4only | 
 | 	} | 
 | 	if net != "" && net[len(net)-1] == '6' { | 
 | 		filter = ipv6only | 
 | 	} | 
 | 	return filterAddrList(filter, ips, inetaddr, host) | 
 | } | 
 |  | 
 | func loopbackIP(net string) IP { | 
 | 	if net != "" && net[len(net)-1] == '6' { | 
 | 		return IPv6loopback | 
 | 	} | 
 | 	return IP{127, 0, 0, 1} | 
 | } |