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) {
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
......@@ -184,7 +184,7 @@ func Listen(net, laddr string) (Listener, error) {
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:
......@@ -203,7 +203,7 @@ func ListenPacket(net, laddr string) (PacketConn, error) {
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:
......
......@@ -26,7 +26,7 @@ func resolveAndDialChannel(net, addr string, localAddr Addr, deadline time.Time)
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()
......@@ -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}}
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 {
......
......@@ -41,7 +41,7 @@ func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn,
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) {
......
......@@ -93,7 +93,7 @@ func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn,
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.
......
......@@ -23,6 +23,13 @@ func (a *IPAddr) String() 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
// "ipv6-host%zone" and resolves the domain name on the network net,
// which must be "ip", "ip4" or "ip6".
......@@ -43,5 +50,5 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) {
if err != nil {
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) {
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 {
......
......@@ -6,7 +6,10 @@
package net
import "time"
import (
"errors"
"time"
)
var (
// supportsIPv4 reports whether the platform supports IPv4
......@@ -29,30 +32,42 @@ func init() {
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
......@@ -178,7 +193,10 @@ func JoinHostPort(host, port string) string {
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
......@@ -201,28 +219,30 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) {
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 {
......@@ -235,12 +255,7 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) {
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 {
......
......@@ -17,6 +17,8 @@ import (
type sockaddr interface {
Addr
netaddr
// family returns the platform-dependent address family
// identifier.
family() int
......@@ -30,11 +32,6 @@ type sockaddr interface {
// 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
......
......@@ -24,6 +24,13 @@ func (a *TCPAddr) String() string {
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
......@@ -42,5 +49,5 @@ func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
if err != nil {
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) {
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 {
......
......@@ -28,6 +28,13 @@ func (a *UDPAddr) String() string {
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
......@@ -46,5 +53,5 @@ func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
if err != nil {
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) {
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 {
......
......@@ -23,6 +23,13 @@ func (a *UnixAddr) String() string {
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".
......
......@@ -105,13 +105,6 @@ func (a *UnixAddr) sockaddr(family int) (syscall.Sockaddr, error) {
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 {
......
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