1
0
mirror of https://github.com/golang/go synced 2024-11-25 08:57:58 -07:00

net: protocol family adaptive address family selection

This CL will help to make an adaptive address family
selection possible when an any address family, vague
network string such as "ip", "tcp" or "udp" is passed
to Dial and Listen API.

Fixes #1769.

R=bradfitz, rsc
CC=golang-dev
https://golang.org/cl/4438066
This commit is contained in:
Mikio Hara 2011-05-16 17:03:06 -04:00 committed by Russ Cox
parent b701cf3332
commit 2ddcad96d7
11 changed files with 183 additions and 95 deletions

View File

@ -60,7 +60,7 @@ func Dial(net, addr string) (c Conn, err os.Error) {
return c, nil return c, nil
case "ip", "ip4", "ip6": case "ip", "ip4", "ip6":
var ra *IPAddr var ra *IPAddr
if ra, err = ResolveIPAddr(raddr); err != nil { if ra, err = ResolveIPAddr(net, raddr); err != nil {
goto Error goto Error
} }
c, err := DialIP(net, nil, ra) c, err := DialIP(net, nil, ra)
@ -139,12 +139,13 @@ func ListenPacket(net, laddr string) (c PacketConn, err os.Error) {
return c, nil return c, nil
} }
if i := last(net, ':'); i > 0 { var rawnet string
switch net[0:i] { if rawnet, _, err = splitNetProto(net); err != nil {
switch rawnet {
case "ip", "ip4", "ip6": case "ip", "ip4", "ip6":
var la *IPAddr var la *IPAddr
if laddr != "" { if laddr != "" {
if la, err = ResolveIPAddr(laddr); err != nil { if la, err = ResolveIPAddr(rawnet, laddr); err != nil {
return nil, err return nil, err
} }
} }

View File

@ -105,14 +105,12 @@ func TestDialGoogleIPv4(t *testing.T) {
doDial(t, "tcp", addr) doDial(t, "tcp", addr)
if addr[0] != '[' { if addr[0] != '[' {
doDial(t, "tcp4", addr) doDial(t, "tcp4", addr)
if !preferIPv4 { if supportsIPv6 {
// make sure preferIPv4 flag works. // make sure syscall.SocketDisableIPv6 flag works.
preferIPv4 = true
syscall.SocketDisableIPv6 = true syscall.SocketDisableIPv6 = true
doDial(t, "tcp", addr) doDial(t, "tcp", addr)
doDial(t, "tcp4", addr) doDial(t, "tcp4", addr)
syscall.SocketDisableIPv6 = false syscall.SocketDisableIPv6 = false
preferIPv4 = false
} }
} }
} }
@ -132,7 +130,7 @@ func TestDialGoogleIPv6(t *testing.T) {
return return
} }
// Only run tcp6 if the kernel will take it. // Only run tcp6 if the kernel will take it.
if !*ipv6 || !kernelSupportsIPv6() { if !*ipv6 || !supportsIPv6 {
return return
} }

View File

@ -62,7 +62,7 @@ func TestFileListener(t *testing.T) {
} }
testFileListener(t, "tcp", "127.0.0.1") testFileListener(t, "tcp", "127.0.0.1")
testFileListener(t, "tcp", "127.0.0.1") testFileListener(t, "tcp", "127.0.0.1")
if kernelSupportsIPv6() { if supportsIPv6 && supportsIPv4map {
testFileListener(t, "tcp", "[::ffff:127.0.0.1]") testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
testFileListener(t, "tcp", "127.0.0.1") testFileListener(t, "tcp", "127.0.0.1")
testFileListener(t, "tcp", "[::ffff:127.0.0.1]") testFileListener(t, "tcp", "[::ffff:127.0.0.1]")
@ -121,8 +121,10 @@ func TestFilePacketConn(t *testing.T) {
} }
testFilePacketConnListen(t, "udp", "127.0.0.1:0") testFilePacketConnListen(t, "udp", "127.0.0.1:0")
testFilePacketConnDial(t, "udp", "127.0.0.1:12345") testFilePacketConnDial(t, "udp", "127.0.0.1:12345")
if kernelSupportsIPv6() { if supportsIPv6 {
testFilePacketConnListen(t, "udp", "[::1]:0") testFilePacketConnListen(t, "udp", "[::1]:0")
}
if supportsIPv6 && supportsIPv4map {
testFilePacketConnDial(t, "udp", "[::ffff:127.0.0.1]:12345") testFilePacketConnDial(t, "udp", "[::ffff:127.0.0.1]:12345")
} }
if syscall.OS == "linux" { if syscall.OS == "linux" {

View File

@ -75,8 +75,12 @@ var (
// Well-known IPv6 addresses // Well-known IPv6 addresses
var ( var (
IPzero = make(IP, IPv6len) // all zeros IPv6zero = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
IPv6loopback = IP([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) IPv6unspecified = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
IPv6loopback = IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
IPv6interfacelocalallnodes = IP{0xff, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}
IPv6linklocalallnodes = IP{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}
IPv6linklocalallrouters = IP{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02}
) )
// Is p all zeros? // Is p all zeros?

View File

@ -143,3 +143,36 @@ func TestJoinHostPort(t *testing.T) {
} }
} }
} }
var ipaftests = []struct {
in IP
af4 bool
af6 bool
}{
{IPv4bcast, true, false},
{IPv4allsys, true, false},
{IPv4allrouter, true, false},
{IPv4zero, true, false},
{IPv4(224, 0, 0, 1), true, false},
{IPv4(127, 0, 0, 1), true, false},
{IPv4(240, 0, 0, 1), true, false},
{IPv6unspecified, false, true},
{IPv6loopback, false, true},
{IPv6interfacelocalallnodes, false, true},
{IPv6linklocalallnodes, false, true},
{IPv6linklocalallrouters, false, true},
{ParseIP("ff05::a:b:c:d"), false, true},
{ParseIP("fe80::1:2:3:4"), false, true},
{ParseIP("2001:db8::123:12:1"), false, true},
}
func TestIPAddrFamily(t *testing.T) {
for _, tt := range ipaftests {
if af := tt.in.To4() != nil; af != tt.af4 {
t.Errorf("verifying IPv4 address family for %#q = %v, want %v", tt.in, af, tt.af4)
}
if af := len(tt.in) == IPv6len && tt.in.To4() == nil; af != tt.af6 {
t.Errorf("verifying IPv6 address family for %#q = %v, want %v", tt.in, af, tt.af6)
}
}
}

View File

@ -75,15 +75,15 @@ func TestICMP(t *testing.T) {
err os.Error err os.Error
) )
if *srchost != "" { if *srchost != "" {
laddr, err = ResolveIPAddr(*srchost) laddr, err = ResolveIPAddr("ip4", *srchost)
if err != nil { if err != nil {
t.Fatalf(`net.ResolveIPAddr("%v") = %v, %v`, *srchost, laddr, err) t.Fatalf(`net.ResolveIPAddr("ip4", %v") = %v, %v`, *srchost, laddr, err)
} }
} }
raddr, err := ResolveIPAddr(*dsthost) raddr, err := ResolveIPAddr("ip4", *dsthost)
if err != nil { if err != nil {
t.Fatalf(`net.ResolveIPAddr("%v") = %v, %v`, *dsthost, raddr, err) t.Fatalf(`net.ResolveIPAddr("ip4", %v") = %v, %v`, *dsthost, raddr, err)
} }
c, err := ListenIP("ip4:icmp", laddr) c, err := ListenIP("ip4:icmp", laddr)

View File

@ -43,7 +43,7 @@ func (a *IPAddr) family() int {
if a == nil || len(a.IP) <= 4 { if a == nil || len(a.IP) <= 4 {
return syscall.AF_INET return syscall.AF_INET
} }
if ip := a.IP.To4(); ip != nil { if a.IP.To4() != nil {
return syscall.AF_INET return syscall.AF_INET
} }
return syscall.AF_INET6 return syscall.AF_INET6
@ -61,10 +61,11 @@ func (a *IPAddr) toAddr() sockaddr {
} }
// ResolveIPAddr parses addr as a IP address and resolves domain // ResolveIPAddr parses addr as a IP address and resolves domain
// names to numeric addresses. A literal IPv6 host address must be // names to numeric addresses on the network net, which must be
// "ip", "ip4" or "ip6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]". // enclosed in square brackets, as in "[::]".
func ResolveIPAddr(addr string) (*IPAddr, os.Error) { func ResolveIPAddr(net, addr string) (*IPAddr, os.Error) {
ip, err := hostToIP(addr) ip, err := hostToIP(net, addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -234,32 +235,36 @@ func (c *IPConn) WriteTo(b []byte, addr Addr) (n int, err os.Error) {
} }
// Convert "host" into IP address. // Convert "host" into IP address.
func hostToIP(host string) (ip IP, err os.Error) { func hostToIP(net, host string) (ip IP, err os.Error) {
var addr IP var addr IP
// Try as an IP address. // Try as an IP address.
addr = ParseIP(host) addr = ParseIP(host)
if addr == nil { if addr == nil {
filter := anyaddr
if net != "" && net[len(net)-1] == '4' {
filter = ipv4only
}
if net != "" && net[len(net)-1] == '6' {
filter = ipv6only
}
// Not an IP address. Try as a DNS name. // Not an IP address. Try as a DNS name.
addrs, err1 := LookupHost(host) addrs, err1 := LookupHost(host)
if err1 != nil { if err1 != nil {
err = err1 err = err1
goto Error goto Error
} }
addr = firstSupportedAddr(anyaddr, addrs) addr = firstFavoriteAddr(filter, addrs)
if addr == nil { if addr == nil {
// should not happen // should not happen
err = &AddrError{"LookupHost returned invalid address", addrs[0]} err = &AddrError{"LookupHost returned no suitable address", addrs[0]}
goto Error goto Error
} }
} }
return addr, nil return addr, nil
Error: Error:
return nil, err return nil, err
} }
var protocols map[string]int var protocols map[string]int
func readProtocols() { func readProtocols() {
@ -285,7 +290,7 @@ func readProtocols() {
} }
} }
func netProtoSplit(netProto string) (net string, proto int, err os.Error) { func splitNetProto(netProto string) (net string, proto int, err os.Error) {
onceReadProtocols.Do(readProtocols) onceReadProtocols.Do(readProtocols)
i := last(netProto, ':') i := last(netProto, ':')
if i < 0 { // no colon if i < 0 { // no colon
@ -307,7 +312,7 @@ func netProtoSplit(netProto string) (net string, proto int, err os.Error) {
// DialIP connects to the remote address raddr on the network net, // DialIP connects to the remote address raddr on the network net,
// which must be "ip", "ip4", or "ip6". // which must be "ip", "ip4", or "ip6".
func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) { func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) {
net, proto, err := netProtoSplit(netProto) net, proto, err := splitNetProto(netProto)
if err != nil { if err != nil {
return return
} }
@ -331,7 +336,7 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (c *IPConn, err os.Error) {
// and WriteTo methods can be used to receive and send IP // and WriteTo methods can be used to receive and send IP
// packets with per-packet addressing. // packets with per-packet addressing.
func ListenIP(netProto string, laddr *IPAddr) (c *IPConn, err os.Error) { func ListenIP(netProto string, laddr *IPAddr) (c *IPConn, err os.Error) {
net, proto, err := netProtoSplit(netProto) net, proto, err := splitNetProto(netProto)
if err != nil { if err != nil {
return return
} }

View File

@ -15,25 +15,86 @@ import (
// only dealing with IPv4 sockets? As long as the host system // only dealing with IPv4 sockets? As long as the host system
// understands IPv6, it's okay to pass IPv4 addresses to the IPv6 // understands IPv6, it's okay to pass IPv4 addresses to the IPv6
// interface. That simplifies our code and is most general. // interface. That simplifies our code and is most general.
// Unfortunately, we need to run on kernels built without IPv6 support too. // Unfortunately, we need to run on kernels built without IPv6
// So probe the kernel to figure it out. // support too. So probe the kernel to figure it out.
func kernelSupportsIPv6() bool { //
s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) // probeIPv6Stack probes both basic IPv6 capability and IPv6 IPv4-
if err != 0 { // mapping capability which is controlled by IPV6_V6ONLY socket
return false // option and/or kernel state "net.inet6.ip6.v6only".
// It returns two boolean values. If the first boolean value is
// true, kernel supports basic IPv6 functionality. If the second
// boolean value is true, kernel supports IPv6 IPv4-mapping.
func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
var probes = []struct {
s int
la TCPAddr
ok bool
}{
// IPv6 communication capability
{-1, TCPAddr{IP: ParseIP("::1")}, false},
// IPv6 IPv4-mapped address communication capability
{-1, TCPAddr{IP: IPv4(127, 0, 0, 1)}, false},
} }
defer closesocket(s) var errno int
la := &TCPAddr{IP: IPv4(127, 0, 0, 1)} for i := range probes {
sa, oserr := la.toAddr().sockaddr(syscall.AF_INET6) probes[i].s, errno = syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
if oserr != nil { if errno != 0 {
return false continue
}
defer closesocket(probes[i].s)
sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6)
if err != nil {
continue
}
errno = syscall.Bind(probes[i].s, sa)
if errno != 0 {
continue
}
probes[i].ok = true
} }
return syscall.Bind(s, sa) == 0 return probes[0].ok, probes[1].ok
} }
var preferIPv4 = !kernelSupportsIPv6() var supportsIPv6, supportsIPv4map = probeIPv6Stack()
func favoriteAddrFamily(net string, raddr, laddr sockaddr) (family int) {
// Figure out IP version.
// If network has a suffix like "tcp4", obey it.
family = syscall.AF_INET6
switch net[len(net)-1] {
case '4':
family = syscall.AF_INET
case '6':
// nothing to do
default:
// Otherwise, guess.
// If the addresses are IPv4, use 4; else 6.
if (laddr == nil || laddr.family() == syscall.AF_INET) &&
(raddr == nil || raddr.family() == syscall.AF_INET) {
family = syscall.AF_INET
}
}
return
}
func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) {
if filter == anyaddr {
// 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 { func firstSupportedAddr(filter func(IP) IP, addrs []string) IP {
for _, s := range addrs { for _, s := range addrs {
@ -44,19 +105,25 @@ func firstSupportedAddr(filter func(IP) IP, addrs []string) IP {
return nil return nil
} }
func anyaddr(x IP) IP { return x } 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 ipv4only(x IP) IP { return x.To4() }
func ipv6only(x IP) IP { func ipv6only(x IP) IP {
// Only return addresses that we can use // Only return addresses that we can use
// with the kernel's IPv6 addressing modes. // with the kernel's IPv6 addressing modes.
// If preferIPv4 is set, it means the IPv6 stack if len(x) == IPv6len && x.To4() == nil && supportsIPv6 {
// cannot take IPv4 addresses directly (we prefer return x
// to use the IPv4 stack) so reject IPv4 addresses.
if x.To4() != nil && preferIPv4 {
return nil
} }
return x return nil
} }
// TODO(rsc): if syscall.OS == "linux", we're supposd to read // TODO(rsc): if syscall.OS == "linux", we're supposd to read
@ -78,23 +145,8 @@ func internetSocket(net string, laddr, raddr sockaddr, socktype, proto int, mode
// Figure out IP version. // Figure out IP version.
// If network has a suffix like "tcp4", obey it. // If network has a suffix like "tcp4", obey it.
var oserr os.Error var oserr os.Error
family := syscall.AF_INET6
switch net[len(net)-1] {
case '4':
family = syscall.AF_INET
case '6':
// nothing to do
default:
// Otherwise, guess.
// If the addresses are IPv4 and we prefer IPv4, use 4; else 6.
if preferIPv4 &&
(laddr == nil || laddr.family() == syscall.AF_INET) &&
(raddr == nil || raddr.family() == syscall.AF_INET) {
family = syscall.AF_INET
}
}
var la, ra syscall.Sockaddr var la, ra syscall.Sockaddr
family := favoriteAddrFamily(net, raddr, laddr)
if laddr != nil { if laddr != nil {
if la, oserr = laddr.sockaddr(family); oserr != nil { if la, oserr = laddr.sockaddr(family); oserr != nil {
goto Error goto Error
@ -142,13 +194,13 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
return s, nil return s, nil
case syscall.AF_INET6: case syscall.AF_INET6:
if len(ip) == 0 { if len(ip) == 0 {
ip = IPzero ip = IPv6zero
} }
// IPv4 callers use 0.0.0.0 to mean "announce on any available address". // IPv4 callers use 0.0.0.0 to mean "announce on any available address".
// In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0", // In IPv6 mode, Linux treats that as meaning "announce on 0.0.0.0",
// which it refuses to do. Rewrite to the IPv6 all zeros. // which it refuses to do. Rewrite to the IPv6 all zeros.
if p4 := ip.To4(); p4 != nil && p4[0] == 0 && p4[1] == 0 && p4[2] == 0 && p4[3] == 0 { if ip.Equal(IPv4zero) {
ip = IPzero ip = IPv6zero
} }
if ip = ip.To16(); ip == nil { if ip = ip.To16(); ip == nil {
return nil, InvalidAddrError("non-IPv6 address") return nil, InvalidAddrError("non-IPv6 address")
@ -212,9 +264,10 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
addr = ParseIP(host) addr = ParseIP(host)
if addr == nil { if addr == nil {
filter := anyaddr filter := anyaddr
if len(net) >= 4 && net[3] == '4' { if net != "" && net[len(net)-1] == '4' {
filter = ipv4only filter = ipv4only
} else if len(net) >= 4 && net[3] == '6' { }
if net != "" && net[len(net)-1] == '6' {
filter = ipv6only filter = ipv6only
} }
// Not an IP address. Try as a DNS name. // Not an IP address. Try as a DNS name.
@ -223,22 +276,10 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) {
err = err1 err = err1
goto Error goto Error
} }
if filter == anyaddr { addr = firstFavoriteAddr(filter, addrs)
// 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)
}
if addr == nil { if addr == nil {
// should not happen // should not happen
err = &AddrError{"LookupHost returned invalid address", addrs[0]} err = &AddrError{"LookupHost returned no suitable address", addrs[0]}
goto Error goto Error
} }
} }

View File

@ -109,8 +109,10 @@ func doTest(t *testing.T, network, listenaddr, dialaddr string) {
func TestTCPServer(t *testing.T) { func TestTCPServer(t *testing.T) {
doTest(t, "tcp", "127.0.0.1", "127.0.0.1") doTest(t, "tcp", "127.0.0.1", "127.0.0.1")
if kernelSupportsIPv6() { if supportsIPv6 {
doTest(t, "tcp", "[::1]", "[::1]") doTest(t, "tcp", "[::1]", "[::1]")
}
if supportsIPv6 && supportsIPv4map {
doTest(t, "tcp", "127.0.0.1", "[::ffff:127.0.0.1]") doTest(t, "tcp", "127.0.0.1", "[::ffff:127.0.0.1]")
} }
} }
@ -186,7 +188,7 @@ func TestUDPServer(t *testing.T) {
for _, isEmpty := range []bool{false, true} { for _, isEmpty := range []bool{false, true} {
doTestPacket(t, "udp", "0.0.0.0", "127.0.0.1", isEmpty) doTestPacket(t, "udp", "0.0.0.0", "127.0.0.1", isEmpty)
doTestPacket(t, "udp", "", "127.0.0.1", isEmpty) doTestPacket(t, "udp", "", "127.0.0.1", isEmpty)
if kernelSupportsIPv6() { if supportsIPv6 && supportsIPv4map {
doTestPacket(t, "udp", "[::]", "[::ffff:127.0.0.1]", isEmpty) doTestPacket(t, "udp", "[::]", "[::ffff:127.0.0.1]", isEmpty)
doTestPacket(t, "udp", "[::]", "127.0.0.1", isEmpty) doTestPacket(t, "udp", "[::]", "127.0.0.1", isEmpty)
doTestPacket(t, "udp", "0.0.0.0", "[::ffff:127.0.0.1]", isEmpty) doTestPacket(t, "udp", "0.0.0.0", "[::ffff:127.0.0.1]", isEmpty)

View File

@ -41,7 +41,7 @@ func (a *TCPAddr) family() int {
if a == nil || len(a.IP) <= 4 { if a == nil || len(a.IP) <= 4 {
return syscall.AF_INET return syscall.AF_INET
} }
if ip := a.IP.To4(); ip != nil { if a.IP.To4() != nil {
return syscall.AF_INET return syscall.AF_INET
} }
return syscall.AF_INET6 return syscall.AF_INET6
@ -60,10 +60,11 @@ func (a *TCPAddr) toAddr() sockaddr {
// ResolveTCPAddr parses addr as a TCP address of the form // ResolveTCPAddr parses addr as a TCP address of the form
// host:port and resolves domain names or port names to // host:port and resolves domain names or port names to
// numeric addresses. A literal IPv6 host address must be // numeric addresses on the network net, which must be "tcp",
// "tcp4" or "tcp6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80". // enclosed in square brackets, as in "[::]:80".
func ResolveTCPAddr(network, addr string) (*TCPAddr, os.Error) { func ResolveTCPAddr(net, addr string) (*TCPAddr, os.Error) {
ip, port, err := hostPortToIP(network, addr) ip, port, err := hostPortToIP(net, addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -41,7 +41,7 @@ func (a *UDPAddr) family() int {
if a == nil || len(a.IP) <= 4 { if a == nil || len(a.IP) <= 4 {
return syscall.AF_INET return syscall.AF_INET
} }
if ip := a.IP.To4(); ip != nil { if a.IP.To4() != nil {
return syscall.AF_INET return syscall.AF_INET
} }
return syscall.AF_INET6 return syscall.AF_INET6
@ -60,10 +60,11 @@ func (a *UDPAddr) toAddr() sockaddr {
// ResolveUDPAddr parses addr as a UDP address of the form // ResolveUDPAddr parses addr as a UDP address of the form
// host:port and resolves domain names or port names to // host:port and resolves domain names or port names to
// numeric addresses. A literal IPv6 host address must be // numeric addresses on the network net, which must be "udp",
// "udp4" or "udp6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80". // enclosed in square brackets, as in "[::]:80".
func ResolveUDPAddr(network, addr string) (*UDPAddr, os.Error) { func ResolveUDPAddr(net, addr string) (*UDPAddr, os.Error) {
ip, port, err := hostPortToIP(network, addr) ip, port, err := hostPortToIP(net, addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }