Commit 3c6558ad authored by Mikio Hara's avatar Mikio Hara

net: add netaddr interface

This CL adds the netaddr interface that will carry a single network
endpoint address or a short list of IP addresses to dial helper
functions in the upcoming CLs.

This is in preparation for TCP connection setup with fast failover on
dual IP stack node as described in RFC 6555.

Update #3610
Update #5267

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13368044
parent ccfe1bfd
...@@ -82,7 +82,7 @@ func parseNetwork(net string) (afnet string, proto int, err error) { ...@@ -82,7 +82,7 @@ func parseNetwork(net string) (afnet string, proto int, err error) {
return "", 0, UnknownNetworkError(net) 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) afnet, _, err := parseNetwork(net)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -184,7 +184,7 @@ func Listen(net, laddr string) (Listener, error) { ...@@ -184,7 +184,7 @@ func Listen(net, laddr string) (Listener, error) {
if err != nil { if err != nil {
return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err} return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
} }
switch la := la.(type) { switch la := la.toAddr().(type) {
case *TCPAddr: case *TCPAddr:
return ListenTCP(net, la) return ListenTCP(net, la)
case *UnixAddr: case *UnixAddr:
...@@ -203,7 +203,7 @@ func ListenPacket(net, laddr string) (PacketConn, error) { ...@@ -203,7 +203,7 @@ func ListenPacket(net, laddr string) (PacketConn, error) {
if err != nil { if err != nil {
return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err} return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
} }
switch la := la.(type) { switch la := la.toAddr().(type) {
case *UDPAddr: case *UDPAddr:
return ListenUDP(net, la) return ListenUDP(net, la)
case *IPAddr: case *IPAddr:
......
...@@ -26,7 +26,7 @@ func resolveAndDialChannel(net, addr string, localAddr Addr, deadline time.Time) ...@@ -26,7 +26,7 @@ func resolveAndDialChannel(net, addr string, localAddr Addr, deadline time.Time)
if err != nil { if err != nil {
return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: err} 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) t := time.NewTimer(timeout)
defer t.Stop() defer t.Stop()
...@@ -45,8 +45,8 @@ func resolveAndDialChannel(net, addr string, localAddr Addr, deadline time.Time) ...@@ -45,8 +45,8 @@ func resolveAndDialChannel(net, addr string, localAddr Addr, deadline time.Time)
ch <- pair{nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: err}} ch <- pair{nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: err}}
return return
} }
resolvedAddr <- ra // in case we need it for OpError resolvedAddr <- ra.toAddr() // in case we need it for OpError
c, err := dial(net, addr, localAddr, ra, noDeadline) c, err := dial(net, addr, localAddr, ra.toAddr(), noDeadline)
ch <- pair{c, err} ch <- pair{c, err}
}() }()
select { select {
......
...@@ -41,7 +41,7 @@ func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, ...@@ -41,7 +41,7 @@ func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn,
if err != nil { if err != nil {
return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: err} 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) { func newFD(sysfd, family, sotype int, net string) (*netFD, error) {
......
...@@ -93,7 +93,7 @@ func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, ...@@ -93,7 +93,7 @@ func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn,
if err != nil { if err != nil {
return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: err} 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. // operation contains superset of data necessary to perform all async IO.
......
...@@ -23,6 +23,13 @@ func (a *IPAddr) String() string { ...@@ -23,6 +23,13 @@ func (a *IPAddr) String() string {
return a.IP.String() 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 // ResolveIPAddr parses addr as an IP address of the form "host" or
// "ipv6-host%zone" and resolves the domain name on the network net, // "ipv6-host%zone" and resolves the domain name on the network net,
// which must be "ip", "ip4" or "ip6". // which must be "ip", "ip4" or "ip6".
...@@ -43,5 +50,5 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) { ...@@ -43,5 +50,5 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return a.(*IPAddr), nil return a.toAddr().(*IPAddr), nil
} }
...@@ -57,13 +57,6 @@ func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) { ...@@ -57,13 +57,6 @@ func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
return ipToSockaddr(family, a.IP, 0, a.Zone) 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 // IPConn is the implementation of the Conn and PacketConn interfaces
// for IP network connections. // for IP network connections.
type IPConn struct { type IPConn struct {
......
...@@ -6,7 +6,10 @@ ...@@ -6,7 +6,10 @@
package net package net
import "time" import (
"errors"
"time"
)
var ( var (
// supportsIPv4 reports whether the platform supports IPv4 // supportsIPv4 reports whether the platform supports IPv4
...@@ -29,30 +32,42 @@ func init() { ...@@ -29,30 +32,42 @@ func init() {
supportsIPv6, supportsIPv4map = probeIPv6Stack() 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 { if filter == nil {
// We'll take any IP address, but since the dialing code // We'll take any IP address, but since the dialing code
// does not yet try multiple addresses, prefer to use // does not yet try multiple addresses, prefer to use
// an IPv4 address if possible. This is especially relevant // an IPv4 address if possible. This is especially relevant
// if localhost resolves to [ipv6-localhost, ipv4-localhost]. // if localhost resolves to [ipv6-localhost, ipv4-localhost].
// Too much code assumes localhost == ipv4-localhost. // Too much code assumes localhost == ipv4-localhost.
addr = firstSupportedAddr(ipv4only, addrs) addr, err := firstSupportedAddr(ipv4only, addrs, inetaddr)
if addr == nil { if err != nil {
addr = firstSupportedAddr(anyaddr, addrs) addr, err = firstSupportedAddr(anyaddr, addrs, inetaddr)
} }
return addr, err
} else { } 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 { for _, s := range addrs {
if addr := filter(ParseIP(s)); addr != nil { if ip := filter(ParseIP(s)); ip != nil {
return addr return inetaddr(ip), nil
} }
} }
return nil return nil, errNoSuitableAddress
} }
// anyaddr returns IP addresses that we can use with the current // anyaddr returns IP addresses that we can use with the current
...@@ -178,7 +193,10 @@ func JoinHostPort(host, port string) string { ...@@ -178,7 +193,10 @@ func JoinHostPort(host, port string) string {
return host + ":" + port 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 ( var (
err error err error
host, port, zone string host, port, zone string
...@@ -201,28 +219,30 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) { ...@@ -201,28 +219,30 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) {
default: default:
return nil, UnknownNetworkError(net) return nil, UnknownNetworkError(net)
} }
inetaddr := func(net string, ip IP, port int, zone string) Addr { inetaddr := func(ip IP) netaddr {
switch net { switch net {
case "tcp", "tcp4", "tcp6": case "tcp", "tcp4", "tcp6":
return &TCPAddr{IP: ip, Port: port, Zone: zone} return &TCPAddr{IP: ip, Port: portnum, Zone: zone}
case "udp", "udp4", "udp6": case "udp", "udp4", "udp6":
return &UDPAddr{IP: ip, Port: port, Zone: zone} return &UDPAddr{IP: ip, Port: portnum, Zone: zone}
case "ip", "ip4", "ip6": case "ip", "ip4", "ip6":
return &IPAddr{IP: ip, Zone: zone} return &IPAddr{IP: ip, Zone: zone}
default:
panic("unexpected network: " + net)
} }
return nil
} }
if host == "" { if host == "" {
return inetaddr(net, nil, portnum, zone), nil return inetaddr(nil), nil
} }
// Try as an IP address. // Try as a literal IP address.
if ip := parseIPv4(host); ip != nil { var ip IP
return inetaddr(net, ip, portnum, zone), nil if ip = parseIPv4(host); ip != nil {
return inetaddr(ip), nil
} }
if ip, zone := parseIPv6(host, true); ip != nil { if ip, zone = parseIPv6(host, true); ip != nil {
return inetaddr(net, ip, portnum, zone), nil return inetaddr(ip), nil
} }
// Try as a domain name. // Try as a DNS registered name.
host, zone = splitHostZone(host) host, zone = splitHostZone(host)
addrs, err := lookupHostDeadline(host, deadline) addrs, err := lookupHostDeadline(host, deadline)
if err != nil { if err != nil {
...@@ -235,12 +255,7 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) { ...@@ -235,12 +255,7 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) {
if net != "" && net[len(net)-1] == '6' || zone != "" { if net != "" && net[len(net)-1] == '6' || zone != "" {
filter = ipv6only filter = ipv6only
} }
ip := firstFavoriteAddr(filter, addrs) return firstFavoriteAddr(filter, addrs, inetaddr)
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 { func zoneToString(zone int) string {
......
...@@ -17,6 +17,8 @@ import ( ...@@ -17,6 +17,8 @@ import (
type sockaddr interface { type sockaddr interface {
Addr Addr
netaddr
// family returns the platform-dependent address family // family returns the platform-dependent address family
// identifier. // identifier.
family() int family() int
...@@ -30,11 +32,6 @@ type sockaddr interface { ...@@ -30,11 +32,6 @@ type sockaddr interface {
// interface. It returns a nil interface when the address is // interface. It returns a nil interface when the address is
// nil. // nil.
sockaddr(family int) (syscall.Sockaddr, error) 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 // socket returns a network file descriptor that is ready for
......
...@@ -24,6 +24,13 @@ func (a *TCPAddr) String() string { ...@@ -24,6 +24,13 @@ func (a *TCPAddr) String() string {
return JoinHostPort(a.IP.String(), itoa(a.Port)) 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" // 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 // 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 // port name on the network net, which must be "tcp", "tcp4" or
...@@ -42,5 +49,5 @@ func ResolveTCPAddr(net, addr string) (*TCPAddr, error) { ...@@ -42,5 +49,5 @@ func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return a.(*TCPAddr), nil return a.toAddr().(*TCPAddr), nil
} }
...@@ -52,13 +52,6 @@ func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) { ...@@ -52,13 +52,6 @@ func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
return ipToSockaddr(family, a.IP, a.Port, a.Zone) 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 // TCPConn is an implementation of the Conn interface for TCP network
// connections. // connections.
type TCPConn struct { type TCPConn struct {
......
...@@ -28,6 +28,13 @@ func (a *UDPAddr) String() string { ...@@ -28,6 +28,13 @@ func (a *UDPAddr) String() string {
return JoinHostPort(a.IP.String(), itoa(a.Port)) 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" // 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 // 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 // port name on the network net, which must be "udp", "udp4" or
...@@ -46,5 +53,5 @@ func ResolveUDPAddr(net, addr string) (*UDPAddr, error) { ...@@ -46,5 +53,5 @@ func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return a.(*UDPAddr), nil return a.toAddr().(*UDPAddr), nil
} }
...@@ -45,13 +45,6 @@ func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) { ...@@ -45,13 +45,6 @@ func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
return ipToSockaddr(family, a.IP, a.Port, a.Zone) 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 // UDPConn is the implementation of the Conn and PacketConn interfaces
// for UDP network connections. // for UDP network connections.
type UDPConn struct { type UDPConn struct {
......
...@@ -23,6 +23,13 @@ func (a *UnixAddr) String() string { ...@@ -23,6 +23,13 @@ func (a *UnixAddr) String() string {
return a.Name return a.Name
} }
func (a *UnixAddr) toAddr() Addr {
if a == nil {
return nil
}
return a
}
// ResolveUnixAddr parses addr as a Unix domain socket address. // ResolveUnixAddr parses addr as a Unix domain socket address.
// The string net gives the network name, "unix", "unixgram" or // The string net gives the network name, "unix", "unixgram" or
// "unixpacket". // "unixpacket".
......
...@@ -105,13 +105,6 @@ func (a *UnixAddr) sockaddr(family int) (syscall.Sockaddr, error) { ...@@ -105,13 +105,6 @@ func (a *UnixAddr) sockaddr(family int) (syscall.Sockaddr, error) {
return &syscall.SockaddrUnix{Name: a.Name}, nil 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 // UnixConn is an implementation of the Conn interface for connections
// to Unix domain sockets. // to Unix domain sockets.
type UnixConn struct { type UnixConn struct {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment