...
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		"errors"
    11		"time"
    12	)
    13	
    14	var (
    15		// supportsIPv4 reports whether the platform supports IPv4
    16		// networking functionality.
    17		supportsIPv4 bool
    18	
    19		// supportsIPv6 reports whether the platform supports IPv6
    20		// networking functionality.
    21		supportsIPv6 bool
    22	
    23		// supportsIPv4map reports whether the platform supports
    24		// mapping an IPv4 address inside an IPv6 address at transport
    25		// layer protocols.  See RFC 4291, RFC 4038 and RFC 3493.
    26		supportsIPv4map bool
    27	)
    28	
    29	func init() {
    30		sysInit()
    31		supportsIPv4 = probeIPv4Stack()
    32		supportsIPv6, supportsIPv4map = probeIPv6Stack()
    33	}
    34	
    35	// A netaddr represents a network endpoint address or a list of
    36	// network endpoint addresses.
    37	type netaddr interface {
    38		// toAddr returns the address represented in Addr interface.
    39		// It returns a nil interface when the address is nil.
    40		toAddr() Addr
    41	}
    42	
    43	// An addrList represents a list of network endpoint addresses.
    44	type addrList []netaddr
    45	
    46	func (al addrList) toAddr() Addr {
    47		switch len(al) {
    48		case 0:
    49			return nil
    50		case 1:
    51			return al[0].toAddr()
    52		default:
    53			// For now, we'll roughly pick first one without
    54			// considering dealing with any preferences such as
    55			// DNS TTL, transport path quality, network routing
    56			// information.
    57			return al[0].toAddr()
    58		}
    59	}
    60	
    61	var errNoSuitableAddress = errors.New("no suitable address found")
    62	
    63	// firstFavoriteAddr returns an address or a list of addresses that
    64	// implement the netaddr interface. Known filters are nil, ipv4only
    65	// and ipv6only. It returns any address when filter is nil. The result
    66	// contains at least one address when error is nil.
    67	func firstFavoriteAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr) (netaddr, error) {
    68		if filter != nil {
    69			return firstSupportedAddr(filter, ips, inetaddr)
    70		}
    71		var (
    72			ipv4, ipv6, swap bool
    73			list             addrList
    74		)
    75		for _, ip := range ips {
    76			// We'll take any IP address, but since the dialing
    77			// code does not yet try multiple addresses
    78			// effectively, prefer to use an IPv4 address if
    79			// possible. This is especially relevant if localhost
    80			// resolves to [ipv6-localhost, ipv4-localhost]. Too
    81			// much code assumes localhost == ipv4-localhost.
    82			if ip4 := ipv4only(ip); ip4 != nil && !ipv4 {
    83				list = append(list, inetaddr(ip4))
    84				ipv4 = true
    85				if ipv6 {
    86					swap = true
    87				}
    88			} else if ip6 := ipv6only(ip); ip6 != nil && !ipv6 {
    89				list = append(list, inetaddr(ip6))
    90				ipv6 = true
    91			}
    92			if ipv4 && ipv6 {
    93				if swap {
    94					list[0], list[1] = list[1], list[0]
    95				}
    96				break
    97			}
    98		}
    99		switch len(list) {
   100		case 0:
   101			return nil, errNoSuitableAddress
   102		case 1:
   103			return list[0], nil
   104		default:
   105			return list, nil
   106		}
   107	}
   108	
   109	func firstSupportedAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr) (netaddr, error) {
   110		for _, ip := range ips {
   111			if ip := filter(ip); ip != nil {
   112				return inetaddr(ip), nil
   113			}
   114		}
   115		return nil, errNoSuitableAddress
   116	}
   117	
   118	// ipv4only returns IPv4 addresses that we can use with the kernel's
   119	// IPv4 addressing modes. If ip is an IPv4 address, ipv4only returns ip.
   120	// Otherwise it returns nil.
   121	func ipv4only(ip IP) IP {
   122		if supportsIPv4 && ip.To4() != nil {
   123			return ip
   124		}
   125		return nil
   126	}
   127	
   128	// ipv6only returns IPv6 addresses that we can use with the kernel's
   129	// IPv6 addressing modes.  It returns IPv4-mapped IPv6 addresses as
   130	// nils and returns other IPv6 address types as IPv6 addresses.
   131	func ipv6only(ip IP) IP {
   132		if supportsIPv6 && len(ip) == IPv6len && ip.To4() == nil {
   133			return ip
   134		}
   135		return nil
   136	}
   137	
   138	// SplitHostPort splits a network address of the form "host:port",
   139	// "[host]:port" or "[ipv6-host%zone]:port" into host or
   140	// ipv6-host%zone and port.  A literal address or host name for IPv6
   141	// must be enclosed in square brackets, as in "[::1]:80",
   142	// "[ipv6-host]:http" or "[ipv6-host%zone]:80".
   143	func SplitHostPort(hostport string) (host, port string, err error) {
   144		j, k := 0, 0
   145	
   146		// The port starts after the last colon.
   147		i := last(hostport, ':')
   148		if i < 0 {
   149			goto missingPort
   150		}
   151	
   152		if hostport[0] == '[' {
   153			// Expect the first ']' just before the last ':'.
   154			end := byteIndex(hostport, ']')
   155			if end < 0 {
   156				err = &AddrError{"missing ']' in address", hostport}
   157				return
   158			}
   159			switch end + 1 {
   160			case len(hostport):
   161				// There can't be a ':' behind the ']' now.
   162				goto missingPort
   163			case i:
   164				// The expected result.
   165			default:
   166				// Either ']' isn't followed by a colon, or it is
   167				// followed by a colon that is not the last one.
   168				if hostport[end+1] == ':' {
   169					goto tooManyColons
   170				}
   171				goto missingPort
   172			}
   173			host = hostport[1:end]
   174			j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions
   175		} else {
   176			host = hostport[:i]
   177			if byteIndex(host, ':') >= 0 {
   178				goto tooManyColons
   179			}
   180			if byteIndex(host, '%') >= 0 {
   181				goto missingBrackets
   182			}
   183		}
   184		if byteIndex(hostport[j:], '[') >= 0 {
   185			err = &AddrError{"unexpected '[' in address", hostport}
   186			return
   187		}
   188		if byteIndex(hostport[k:], ']') >= 0 {
   189			err = &AddrError{"unexpected ']' in address", hostport}
   190			return
   191		}
   192	
   193		port = hostport[i+1:]
   194		return
   195	
   196	missingPort:
   197		err = &AddrError{"missing port in address", hostport}
   198		return
   199	
   200	tooManyColons:
   201		err = &AddrError{"too many colons in address", hostport}
   202		return
   203	
   204	missingBrackets:
   205		err = &AddrError{"missing brackets in address", hostport}
   206		return
   207	}
   208	
   209	func splitHostZone(s string) (host, zone string) {
   210		// The IPv6 scoped addressing zone identifier starts after the
   211		// last percent sign.
   212		if i := last(s, '%'); i > 0 {
   213			host, zone = s[:i], s[i+1:]
   214		} else {
   215			host = s
   216		}
   217		return
   218	}
   219	
   220	// JoinHostPort combines host and port into a network address of the
   221	// form "host:port" or, if host contains a colon or a percent sign,
   222	// "[host]:port".
   223	func JoinHostPort(host, port string) string {
   224		// If host has colons or a percent sign, have to bracket it.
   225		if byteIndex(host, ':') >= 0 || byteIndex(host, '%') >= 0 {
   226			return "[" + host + "]:" + port
   227		}
   228		return host + ":" + port
   229	}
   230	
   231	// resolveInternetAddr resolves addr that is either a literal IP
   232	// address or a DNS name and returns an internet protocol family
   233	// address. It returns a list that contains a pair of different
   234	// address family addresses when addr is a DNS name and the name has
   235	// multiple address family records. The result contains at least one
   236	// address when error is nil.
   237	func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error) {
   238		var (
   239			err              error
   240			host, port, zone string
   241			portnum          int
   242		)
   243		switch net {
   244		case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
   245			if addr != "" {
   246				if host, port, err = SplitHostPort(addr); err != nil {
   247					return nil, err
   248				}
   249				if portnum, err = parsePort(net, port); err != nil {
   250					return nil, err
   251				}
   252			}
   253		case "ip", "ip4", "ip6":
   254			if addr != "" {
   255				host = addr
   256			}
   257		default:
   258			return nil, UnknownNetworkError(net)
   259		}
   260		inetaddr := func(ip IP) netaddr {
   261			switch net {
   262			case "tcp", "tcp4", "tcp6":
   263				return &TCPAddr{IP: ip, Port: portnum, Zone: zone}
   264			case "udp", "udp4", "udp6":
   265				return &UDPAddr{IP: ip, Port: portnum, Zone: zone}
   266			case "ip", "ip4", "ip6":
   267				return &IPAddr{IP: ip, Zone: zone}
   268			default:
   269				panic("unexpected network: " + net)
   270			}
   271		}
   272		if host == "" {
   273			return inetaddr(nil), nil
   274		}
   275		// Try as a literal IP address.
   276		var ip IP
   277		if ip = parseIPv4(host); ip != nil {
   278			return inetaddr(ip), nil
   279		}
   280		if ip, zone = parseIPv6(host, true); ip != nil {
   281			return inetaddr(ip), nil
   282		}
   283		// Try as a DNS name.
   284		host, zone = splitHostZone(host)
   285		ips, err := lookupIPDeadline(host, deadline)
   286		if err != nil {
   287			return nil, err
   288		}
   289		var filter func(IP) IP
   290		if net != "" && net[len(net)-1] == '4' {
   291			filter = ipv4only
   292		}
   293		if net != "" && net[len(net)-1] == '6' || zone != "" {
   294			filter = ipv6only
   295		}
   296		return firstFavoriteAddr(filter, ips, inetaddr)
   297	}
   298	
   299	func zoneToString(zone int) string {
   300		if zone == 0 {
   301			return ""
   302		}
   303		if ifi, err := InterfaceByIndex(zone); err == nil {
   304			return ifi.Name
   305		}
   306		return itod(uint(zone))
   307	}
   308	
   309	func zoneToInt(zone string) int {
   310		if zone == "" {
   311			return 0
   312		}
   313		if ifi, err := InterfaceByName(zone); err == nil {
   314			return ifi.Index
   315		}
   316		n, _, _ := dtoi(zone, 0)
   317		return n
   318	}
   319	

View as plain text