...
Run Format

Source file src/net/ipsock.go

     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	// Internet protocol family sockets
     6	
     7	package net
     8	
     9	import (
    10		"context"
    11	)
    12	
    13	// BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the
    14	// "tcp" and "udp" networks does not listen for both IPv4 and IPv6
    15	// connections. This is due to the fact that IPv4 traffic will not be
    16	// routed to an IPv6 socket - two separate sockets are required if
    17	// both address families are to be supported.
    18	// See inet6(4) for details.
    19	
    20	var (
    21		// supportsIPv4 reports whether the platform supports IPv4
    22		// networking functionality.
    23		supportsIPv4 bool
    24	
    25		// supportsIPv6 reports whether the platform supports IPv6
    26		// networking functionality.
    27		supportsIPv6 bool
    28	
    29		// supportsIPv4map reports whether the platform supports
    30		// mapping an IPv4 address inside an IPv6 address at transport
    31		// layer protocols. See RFC 4291, RFC 4038 and RFC 3493.
    32		supportsIPv4map bool
    33	)
    34	
    35	// An addrList represents a list of network endpoint addresses.
    36	type addrList []Addr
    37	
    38	// isIPv4 returns true if the Addr contains an IPv4 address.
    39	func isIPv4(addr Addr) bool {
    40		switch addr := addr.(type) {
    41		case *TCPAddr:
    42			return addr.IP.To4() != nil
    43		case *UDPAddr:
    44			return addr.IP.To4() != nil
    45		case *IPAddr:
    46			return addr.IP.To4() != nil
    47		}
    48		return false
    49	}
    50	
    51	// first returns the first address which satisfies strategy, or if
    52	// none do, then the first address of any kind.
    53	func (addrs addrList) first(strategy func(Addr) bool) Addr {
    54		for _, addr := range addrs {
    55			if strategy(addr) {
    56				return addr
    57			}
    58		}
    59		return addrs[0]
    60	}
    61	
    62	// partition divides an address list into two categories, using a
    63	// strategy function to assign a boolean label to each address.
    64	// The first address, and any with a matching label, are returned as
    65	// primaries, while addresses with the opposite label are returned
    66	// as fallbacks. For non-empty inputs, primaries is guaranteed to be
    67	// non-empty.
    68	func (addrs addrList) partition(strategy func(Addr) bool) (primaries, fallbacks addrList) {
    69		var primaryLabel bool
    70		for i, addr := range addrs {
    71			label := strategy(addr)
    72			if i == 0 || label == primaryLabel {
    73				primaryLabel = label
    74				primaries = append(primaries, addr)
    75			} else {
    76				fallbacks = append(fallbacks, addr)
    77			}
    78		}
    79		return
    80	}
    81	
    82	// filterAddrList applies a filter to a list of IP addresses,
    83	// yielding a list of Addr objects. Known filters are nil, ipv4only,
    84	// and ipv6only. It returns every address when the filter is nil.
    85	// The result contains at least one address when error is nil.
    86	func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) Addr, originalAddr string) (addrList, error) {
    87		var addrs addrList
    88		for _, ip := range ips {
    89			if filter == nil || filter(ip) {
    90				addrs = append(addrs, inetaddr(ip))
    91			}
    92		}
    93		if len(addrs) == 0 {
    94			return nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: originalAddr}
    95		}
    96		return addrs, nil
    97	}
    98	
    99	// ipv4only reports whether addr is an IPv4 address.
   100	func ipv4only(addr IPAddr) bool {
   101		return addr.IP.To4() != nil
   102	}
   103	
   104	// ipv6only reports whether addr is an IPv6 address except IPv4-mapped IPv6 address.
   105	func ipv6only(addr IPAddr) bool {
   106		return len(addr.IP) == IPv6len && addr.IP.To4() == nil
   107	}
   108	
   109	// SplitHostPort splits a network address of the form "host:port",
   110	// "[host]:port" or "[ipv6-host%zone]:port" into host or
   111	// ipv6-host%zone and port. A literal address or host name for IPv6
   112	// must be enclosed in square brackets, as in "[::1]:80",
   113	// "[ipv6-host]:http" or "[ipv6-host%zone]:80".
   114	func SplitHostPort(hostport string) (host, port string, err error) {
   115		const (
   116			missingPort   = "missing port in address"
   117			tooManyColons = "too many colons in address"
   118		)
   119		addrErr := func(addr, why string) (host, port string, err error) {
   120			return "", "", &AddrError{Err: why, Addr: addr}
   121		}
   122		j, k := 0, 0
   123	
   124		// The port starts after the last colon.
   125		i := last(hostport, ':')
   126		if i < 0 {
   127			return addrErr(hostport, missingPort)
   128		}
   129	
   130		if hostport[0] == '[' {
   131			// Expect the first ']' just before the last ':'.
   132			end := byteIndex(hostport, ']')
   133			if end < 0 {
   134				return addrErr(hostport, "missing ']' in address")
   135			}
   136			switch end + 1 {
   137			case len(hostport):
   138				// There can't be a ':' behind the ']' now.
   139				return addrErr(hostport, missingPort)
   140			case i:
   141				// The expected result.
   142			default:
   143				// Either ']' isn't followed by a colon, or it is
   144				// followed by a colon that is not the last one.
   145				if hostport[end+1] == ':' {
   146					return addrErr(hostport, tooManyColons)
   147				}
   148				return addrErr(hostport, missingPort)
   149			}
   150			host = hostport[1:end]
   151			j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions
   152		} else {
   153			host = hostport[:i]
   154			if byteIndex(host, ':') >= 0 {
   155				return addrErr(hostport, tooManyColons)
   156			}
   157			if byteIndex(host, '%') >= 0 {
   158				return addrErr(hostport, "missing brackets in address")
   159			}
   160		}
   161		if byteIndex(hostport[j:], '[') >= 0 {
   162			return addrErr(hostport, "unexpected '[' in address")
   163		}
   164		if byteIndex(hostport[k:], ']') >= 0 {
   165			return addrErr(hostport, "unexpected ']' in address")
   166		}
   167	
   168		port = hostport[i+1:]
   169		return host, port, nil
   170	}
   171	
   172	func splitHostZone(s string) (host, zone string) {
   173		// The IPv6 scoped addressing zone identifier starts after the
   174		// last percent sign.
   175		if i := last(s, '%'); i > 0 {
   176			host, zone = s[:i], s[i+1:]
   177		} else {
   178			host = s
   179		}
   180		return
   181	}
   182	
   183	// JoinHostPort combines host and port into a network address of the
   184	// form "host:port" or, if host contains a colon or a percent sign,
   185	// "[host]:port".
   186	func JoinHostPort(host, port string) string {
   187		// If host has colons or a percent sign, have to bracket it.
   188		if byteIndex(host, ':') >= 0 || byteIndex(host, '%') >= 0 {
   189			return "[" + host + "]:" + port
   190		}
   191		return host + ":" + port
   192	}
   193	
   194	// internetAddrList resolves addr, which may be a literal IP
   195	// address or a DNS name, and returns a list of internet protocol
   196	// family addresses. The result contains at least one address when
   197	// error is nil.
   198	func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
   199		var (
   200			err        error
   201			host, port string
   202			portnum    int
   203		)
   204		switch net {
   205		case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
   206			if addr != "" {
   207				if host, port, err = SplitHostPort(addr); err != nil {
   208					return nil, err
   209				}
   210				if portnum, err = r.LookupPort(ctx, net, port); err != nil {
   211					return nil, err
   212				}
   213			}
   214		case "ip", "ip4", "ip6":
   215			if addr != "" {
   216				host = addr
   217			}
   218		default:
   219			return nil, UnknownNetworkError(net)
   220		}
   221		inetaddr := func(ip IPAddr) Addr {
   222			switch net {
   223			case "tcp", "tcp4", "tcp6":
   224				return &TCPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone}
   225			case "udp", "udp4", "udp6":
   226				return &UDPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone}
   227			case "ip", "ip4", "ip6":
   228				return &IPAddr{IP: ip.IP, Zone: ip.Zone}
   229			default:
   230				panic("unexpected network: " + net)
   231			}
   232		}
   233		if host == "" {
   234			return addrList{inetaddr(IPAddr{})}, nil
   235		}
   236	
   237		// Try as a literal IP address, then as a DNS name.
   238		var ips []IPAddr
   239		if ip := parseIPv4(host); ip != nil {
   240			ips = []IPAddr{{IP: ip}}
   241		} else if ip, zone := parseIPv6(host, true); ip != nil {
   242			ips = []IPAddr{{IP: ip, Zone: zone}}
   243		} else {
   244			// Try as a DNS name.
   245			ips, err = r.LookupIPAddr(ctx, host)
   246			if err != nil {
   247				return nil, err
   248			}
   249		}
   250	
   251		var filter func(IPAddr) bool
   252		if net != "" && net[len(net)-1] == '4' {
   253			filter = ipv4only
   254		}
   255		if net != "" && net[len(net)-1] == '6' {
   256			filter = ipv6only
   257		}
   258		return filterAddrList(filter, ips, inetaddr, host)
   259	}
   260	
   261	func loopbackIP(net string) IP {
   262		if net != "" && net[len(net)-1] == '6' {
   263			return IPv6loopback
   264		}
   265		return IP{127, 0, 0, 1}
   266	}
   267	

View as plain text