github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/net/ipsock.go (about)

     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 platfrom 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 identifer 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  // mutiple 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  }