// 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 "time" var supportsIPv6, supportsIPv4map bool func init() { sysInit() supportsIPv6, supportsIPv4map = probeIPv6Stack() } func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) { if filter == nil { // We'll take any IP address, but since the dialing code // does not yet try multiple addresses, prefer to use // an IPv4 address if possible. This is especially relevant // if localhost resolves to [ipv6-localhost, ipv4-localhost]. // Too much code assumes localhost == ipv4-localhost. addr = firstSupportedAddr(ipv4only, addrs) if addr == nil { addr = firstSupportedAddr(anyaddr, addrs) } } else { addr = firstSupportedAddr(filter, addrs) } return } func firstSupportedAddr(filter func(IP) IP, addrs []string) IP { for _, s := range addrs { if addr := filter(ParseIP(s)); addr != nil { return addr } } return nil } func anyaddr(x IP) IP { if x4 := x.To4(); x4 != nil { return x4 } if supportsIPv6 { return x } return nil } func ipv4only(x IP) IP { return x.To4() } func ipv6only(x IP) IP { // Only return addresses that we can use // with the kernel's IPv6 addressing modes. if len(x) == IPv6len && x.To4() == nil && supportsIPv6 { return x } return nil } type InvalidAddrError string func (e InvalidAddrError) Error() string { return string(e) } func (e InvalidAddrError) Timeout() bool { return false } func (e InvalidAddrError) Temporary() bool { return false } // SplitHostPort splits a network address of the form // "host:port" or "[host]:port" into host and port. // The latter form must be used when host contains a colon. func SplitHostPort(hostport string) (host, port string, err error) { host, port, _, err = splitHostPort(hostport) return } func splitHostPort(hostport string) (host, port, zone string, err error) { j, k := 0, 0 // The port starts after the last colon. i := last(hostport, ':') if i < 0 { goto missingPort } if hostport[0] == '[' { // Expect the first ']' just before the last ':'. end := byteIndex(hostport, ']') if end < 0 { err = &AddrError{"missing ']' in address", hostport} return } switch end + 1 { case len(hostport): // There can't be a ':' behind the ']' now. goto 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] == ':' { goto tooManyColons } goto 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 { goto tooManyColons } } if byteIndex(hostport[j:], '[') >= 0 { err = &AddrError{"unexpected '[' in address", hostport} return } if byteIndex(hostport[k:], ']') >= 0 { err = &AddrError{"unexpected ']' in address", hostport} return } port = hostport[i+1:] return missingPort: err = &AddrError{"missing port in address", hostport} return tooManyColons: err = &AddrError{"too many colons in address", hostport} return } // JoinHostPort combines host and port into a network address // of the form "host:port" or, if host contains a colon, "[host]:port". func JoinHostPort(host, port string) string { // If host has colons, have to bracket it. if byteIndex(host, ':') >= 0 { return "[" + host + "]:" + port } return host + ":" + port } func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) { var ( err error host, port, zone string portnum int ) switch net { case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": if addr != "" { if host, port, zone, err = splitHostPort(addr); err != nil { return nil, err } if portnum, err = parsePort(net, port); err != nil { return nil, err } } case "ip", "ip4", "ip6": if addr != "" { host = addr } default: return nil, UnknownNetworkError(net) } inetaddr := func(net string, ip IP, port int, zone string) Addr { switch net { case "tcp", "tcp4", "tcp6": return &TCPAddr{IP: ip, Port: port, Zone: zone} case "udp", "udp4", "udp6": return &UDPAddr{IP: ip, Port: port, Zone: zone} case "ip", "ip4", "ip6": return &IPAddr{IP: ip, Zone: zone} } return nil } if host == "" { return inetaddr(net, nil, portnum, zone), nil } // Try as an IP address. if ip := ParseIP(host); ip != nil { return inetaddr(net, ip, portnum, zone), nil } var filter func(IP) IP if net != "" && net[len(net)-1] == '4' { filter = ipv4only } if net != "" && net[len(net)-1] == '6' { filter = ipv6only } // Try as a DNS name. addrs, err := lookupHostDeadline(host, deadline) if err != nil { return nil, err } ip := firstFavoriteAddr(filter, addrs) if ip == nil { // should not happen return nil, &AddrError{"LookupHost returned no suitable address", addrs[0]} } return inetaddr(net, ip, portnum, zone), nil } func zoneToString(zone int) string { if zone == 0 { return "" } if ifi, err := InterfaceByIndex(zone); err == nil { return ifi.Name } return itod(uint(zone)) } func zoneToInt(zone string) int { if zone == "" { return 0 } if ifi, err := InterfaceByName(zone); err == nil { return ifi.Index } n, _, _ := dtoi(zone, 0) return n }