return "", 0, UnknownNetworkError(net)
}
-func resolveAddr(op, net, addr string, deadline time.Time) (Addr, error) {
+func resolveAddr(op, net, addr string, deadline time.Time) (netaddr, error) {
afnet, _, err := parseNetwork(net)
if err != nil {
return nil, err
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
}
- switch la := la.(type) {
+ switch la := la.toAddr().(type) {
case *TCPAddr:
return ListenTCP(net, la)
case *UnixAddr:
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
}
- switch la := la.(type) {
+ switch la := la.toAddr().(type) {
case *UDPAddr:
return ListenUDP(net, la)
case *IPAddr:
if err != nil {
return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: err}
}
- return dial(net, addr, localAddr, ra, noDeadline)
+ return dial(net, addr, localAddr, ra.toAddr(), noDeadline)
}
t := time.NewTimer(timeout)
defer t.Stop()
ch <- pair{nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: err}}
return
}
- resolvedAddr <- ra // in case we need it for OpError
- c, err := dial(net, addr, localAddr, ra, noDeadline)
+ resolvedAddr <- ra.toAddr() // in case we need it for OpError
+ c, err := dial(net, addr, localAddr, ra.toAddr(), noDeadline)
ch <- pair{c, err}
}()
select {
if err != nil {
return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: err}
}
- return dial(net, addr, localAddr, ra, deadline)
+ return dial(net, addr, localAddr, ra.toAddr(), deadline)
}
func newFD(sysfd, family, sotype int, net string) (*netFD, error) {
if err != nil {
return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: err}
}
- return dial(net, addr, localAddr, ra, deadline)
+ return dial(net, addr, localAddr, ra.toAddr(), deadline)
}
// operation contains superset of data necessary to perform all async IO.
return a.IP.String()
}
+func (a *IPAddr) toAddr() Addr {
+ if a == nil {
+ return nil
+ }
+ return a
+}
+
// ResolveIPAddr parses addr as an IP address of the form "host" or
// "ipv6-host%zone" and resolves the domain name on the network net,
// which must be "ip", "ip4" or "ip6".
if err != nil {
return nil, err
}
- return a.(*IPAddr), nil
+ return a.toAddr().(*IPAddr), nil
}
return ipToSockaddr(family, a.IP, 0, a.Zone)
}
-func (a *IPAddr) toAddr() sockaddr {
- if a == nil {
- return nil
- }
- return a
-}
-
// IPConn is the implementation of the Conn and PacketConn interfaces
// for IP network connections.
type IPConn struct {
package net
-import "time"
+import (
+ "errors"
+ "time"
+)
var (
// supportsIPv4 reports whether the platform supports IPv4
supportsIPv6, supportsIPv4map = probeIPv6Stack()
}
-func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) {
+// A netaddr represents a network endpoint address or a list of
+// network endpoint addresses.
+type netaddr interface {
+ // toAddr returns the address represented in Addr interface.
+ // It returns a nil interface when the address is nil.
+ toAddr() Addr
+}
+
+var errNoSuitableAddress = errors.New("no suitable address found")
+
+// firstFavoriteAddr returns an address that implemets netaddr
+// interface.
+func firstFavoriteAddr(filter func(IP) IP, addrs []string, inetaddr func(IP) netaddr) (netaddr, error) {
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)
+ addr, err := firstSupportedAddr(ipv4only, addrs, inetaddr)
+ if err != nil {
+ addr, err = firstSupportedAddr(anyaddr, addrs, inetaddr)
}
+ return addr, err
} else {
- addr = firstSupportedAddr(filter, addrs)
+ return firstSupportedAddr(filter, addrs, inetaddr)
}
- return
}
-func firstSupportedAddr(filter func(IP) IP, addrs []string) IP {
+func firstSupportedAddr(filter func(IP) IP, addrs []string, inetaddr func(IP) netaddr) (netaddr, error) {
for _, s := range addrs {
- if addr := filter(ParseIP(s)); addr != nil {
- return addr
+ if ip := filter(ParseIP(s)); ip != nil {
+ return inetaddr(ip), nil
}
}
- return nil
+ return nil, errNoSuitableAddress
}
// anyaddr returns IP addresses that we can use with the current
return host + ":" + port
}
-func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) {
+// resolveInternetAddr resolves addr that is either a literal IP
+// address or a DNS registered name and returns an internet protocol
+// family address.
+func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error) {
var (
err error
host, port, zone string
default:
return nil, UnknownNetworkError(net)
}
- inetaddr := func(net string, ip IP, port int, zone string) Addr {
+ inetaddr := func(ip IP) netaddr {
switch net {
case "tcp", "tcp4", "tcp6":
- return &TCPAddr{IP: ip, Port: port, Zone: zone}
+ return &TCPAddr{IP: ip, Port: portnum, Zone: zone}
case "udp", "udp4", "udp6":
- return &UDPAddr{IP: ip, Port: port, Zone: zone}
+ return &UDPAddr{IP: ip, Port: portnum, Zone: zone}
case "ip", "ip4", "ip6":
return &IPAddr{IP: ip, Zone: zone}
+ default:
+ panic("unexpected network: " + net)
}
- return nil
}
if host == "" {
- return inetaddr(net, nil, portnum, zone), nil
+ return inetaddr(nil), nil
}
- // Try as an IP address.
- if ip := parseIPv4(host); ip != nil {
- return inetaddr(net, ip, portnum, zone), nil
+ // Try as a literal IP address.
+ var ip IP
+ if ip = parseIPv4(host); ip != nil {
+ return inetaddr(ip), nil
}
- if ip, zone := parseIPv6(host, true); ip != nil {
- return inetaddr(net, ip, portnum, zone), nil
+ if ip, zone = parseIPv6(host, true); ip != nil {
+ return inetaddr(ip), nil
}
- // Try as a domain name.
+ // Try as a DNS registered name.
host, zone = splitHostZone(host)
addrs, err := lookupHostDeadline(host, deadline)
if err != nil {
if net != "" && net[len(net)-1] == '6' || zone != "" {
filter = ipv6only
}
- 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
+ return firstFavoriteAddr(filter, addrs, inetaddr)
}
func zoneToString(zone int) string {
type sockaddr interface {
Addr
+ netaddr
+
// family returns the platform-dependent address family
// identifier.
family() int
// interface. It returns a nil interface when the address is
// nil.
sockaddr(family int) (syscall.Sockaddr, error)
-
- // toAddr returns the address represented in sockaddr
- // interface. It returns a nil interface when the address is
- // nil.
- toAddr() sockaddr
}
// socket returns a network file descriptor that is ready for
return JoinHostPort(a.IP.String(), itoa(a.Port))
}
+func (a *TCPAddr) toAddr() Addr {
+ if a == nil {
+ return nil
+ }
+ return a
+}
+
// ResolveTCPAddr parses addr as a TCP address of the form "host:port"
// or "[ipv6-host%zone]:port" and resolves a pair of domain name and
// port name on the network net, which must be "tcp", "tcp4" or
if err != nil {
return nil, err
}
- return a.(*TCPAddr), nil
+ return a.toAddr().(*TCPAddr), nil
}
return ipToSockaddr(family, a.IP, a.Port, a.Zone)
}
-func (a *TCPAddr) toAddr() sockaddr {
- if a == nil {
- return nil
- }
- return a
-}
-
// TCPConn is an implementation of the Conn interface for TCP network
// connections.
type TCPConn struct {
return JoinHostPort(a.IP.String(), itoa(a.Port))
}
+func (a *UDPAddr) toAddr() Addr {
+ if a == nil {
+ return nil
+ }
+ return a
+}
+
// ResolveUDPAddr parses addr as a UDP address of the form "host:port"
// or "[ipv6-host%zone]:port" and resolves a pair of domain name and
// port name on the network net, which must be "udp", "udp4" or
if err != nil {
return nil, err
}
- return a.(*UDPAddr), nil
+ return a.toAddr().(*UDPAddr), nil
}
return ipToSockaddr(family, a.IP, a.Port, a.Zone)
}
-func (a *UDPAddr) toAddr() sockaddr {
- if a == nil {
- return nil
- }
- return a
-}
-
// UDPConn is the implementation of the Conn and PacketConn interfaces
// for UDP network connections.
type UDPConn struct {
return a.Name
}
+func (a *UnixAddr) toAddr() Addr {
+ if a == nil {
+ return nil
+ }
+ return a
+}
+
// ResolveUnixAddr parses addr as a Unix domain socket address.
// The string net gives the network name, "unix", "unixgram" or
// "unixpacket".
return &syscall.SockaddrUnix{Name: a.Name}, nil
}
-func (a *UnixAddr) toAddr() sockaddr {
- if a == nil {
- return nil
- }
- return a
-}
-
// UnixConn is an implementation of the Conn interface for connections
// to Unix domain sockets.
type UnixConn struct {