1
0
mirror of https://github.com/golang/go synced 2024-10-03 14:31:22 -06:00

net: protocol specific listen functions return a proper local socket address

When a nil listener address is passed to some protocol specific
listen function, it will create an unnamed, unbound socket because
of the nil listener address. Other listener functions may return
invalid address error.

This CL allows to pass a nil listener address to all protocol
specific listen functions to fix above inconsistency. Also make it
possible to return a proper local socket address in case of a nil
listner address.

Fixes #4190.
Fixes #3847.

R=rsc, iant
CC=golang-dev
https://golang.org/cl/6525048
This commit is contained in:
Mikio Hara 2012-11-13 12:56:28 +09:00
parent 0ae80785e6
commit 677c6e6ee7
8 changed files with 130 additions and 28 deletions

View File

@ -206,3 +206,33 @@ func parseICMPEchoReply(b []byte) (id, seqnum int) {
seqnum = int(b[6])<<8 | int(b[7]) seqnum = int(b[6])<<8 | int(b[7])
return return
} }
var ipConnLocalNameTests = []struct {
net string
laddr *IPAddr
}{
{"ip4:icmp", &IPAddr{IP: IPv4(127, 0, 0, 1)}},
{"ip4:icmp", &IPAddr{}},
{"ip4:icmp", nil},
}
func TestIPConnLocalName(t *testing.T) {
if os.Getuid() != 0 {
t.Logf("skipping test; must be root")
return
}
for _, tt := range ipConnLocalNameTests {
c, err := ListenIP(tt.net, tt.laddr)
if err != nil {
t.Errorf("ListenIP failed: %v", err)
return
}
defer c.Close()
la := c.LocalAddr()
if la == nil {
t.Error("IPConn.LocalAddr failed")
return
}
}
}

View File

@ -175,7 +175,7 @@ func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn,
switch net { switch net {
case "ip", "ip4", "ip6": case "ip", "ip4", "ip6":
default: default:
return nil, UnknownNetworkError(net) return nil, UnknownNetworkError(netProto)
} }
if raddr == nil { if raddr == nil {
return nil, &OpError{"dial", netProto, nil, errMissingAddress} return nil, &OpError{"dial", netProto, nil, errMissingAddress}
@ -199,7 +199,7 @@ func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
switch net { switch net {
case "ip", "ip4", "ip6": case "ip", "ip4", "ip6":
default: default:
return nil, UnknownNetworkError(net) return nil, UnknownNetworkError(netProto)
} }
fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP) fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP)
if err != nil { if err != nil {

View File

@ -116,3 +116,33 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool) {
sem <- true sem <- true
} }
} }
var tcpListenerNameTests = []struct {
net string
laddr *TCPAddr
}{
{"tcp4", &TCPAddr{IP: IPv4(127, 0, 0, 1)}},
{"tcp4", &TCPAddr{}},
{"tcp4", nil},
}
func TestTCPListenerName(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
for _, tt := range tcpListenerNameTests {
ln, err := ListenTCP(tt.net, tt.laddr)
if err != nil {
t.Errorf("ListenTCP failed: %v", err)
return
}
defer ln.Close()
la := ln.Addr()
if a, ok := la.(*TCPAddr); !ok || a.Port == 0 {
t.Errorf("got %v; expected a proper address with non-zero port number", la)
return
}
}
}

View File

@ -89,7 +89,7 @@ func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err error) {
return nil, UnknownNetworkError(net) return nil, UnknownNetworkError(net)
} }
if laddr == nil { if laddr == nil {
return nil, &OpError{"listen", net, nil, errMissingAddress} laddr = &TCPAddr{}
} }
l1, err := listenPlan9(net, laddr) l1, err := listenPlan9(net, laddr)
if err != nil { if err != nil {

View File

@ -143,14 +143,18 @@ func (c *TCPConn) SetNoDelay(noDelay bool) error {
// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used // which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
// as the local address for the connection. // as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) { func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
switch net {
case "tcp", "tcp4", "tcp6":
default:
return nil, UnknownNetworkError(net)
}
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
return dialTCP(net, laddr, raddr, noDeadline) return dialTCP(net, laddr, raddr, noDeadline)
} }
func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) { func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
// TCP has a rarely used mechanism called a 'simultaneous connection' in // TCP has a rarely used mechanism called a 'simultaneous connection' in
@ -224,25 +228,6 @@ type TCPListener struct {
fd *netFD fd *netFD
} }
// ListenTCP announces on the TCP address laddr and returns a TCP listener.
// Net must be "tcp", "tcp4", or "tcp6".
// If laddr has a port of 0, it means to listen on some available port.
// The caller can use l.Addr() to retrieve the chosen address.
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
if err != nil {
return nil, err
}
err = syscall.Listen(fd.sysfd, listenerBacklog)
if err != nil {
closesocket(fd.sysfd)
return nil, &OpError{"listen", net, laddr, err}
}
l := new(TCPListener)
l.fd = fd
return l, nil
}
// AcceptTCP accepts the next incoming call and returns the new connection // AcceptTCP accepts the next incoming call and returns the new connection
// and the remote address. // and the remote address.
func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) { func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
@ -291,3 +276,30 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
// It is the caller's responsibility to close f when finished. // It is the caller's responsibility to close f when finished.
// Closing l does not affect f, and closing f does not affect l. // Closing l does not affect f, and closing f does not affect l.
func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() } func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
// ListenTCP announces on the TCP address laddr and returns a TCP listener.
// Net must be "tcp", "tcp4", or "tcp6".
// If laddr has a port of 0, it means to listen on some available port.
// The caller can use l.Addr() to retrieve the chosen address.
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
switch net {
case "tcp", "tcp4", "tcp6":
default:
return nil, UnknownNetworkError(net)
}
if laddr == nil {
laddr = &TCPAddr{}
}
fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
if err != nil {
return nil, err
}
err = syscall.Listen(fd.sysfd, listenerBacklog)
if err != nil {
closesocket(fd.sysfd)
return nil, &OpError{"listen", net, laddr, err}
}
l := new(TCPListener)
l.fd = fd
return l, nil
}

View File

@ -87,3 +87,33 @@ func testWriteToPacketConn(t *testing.T, raddr string) {
t.Fatal("Write should fail") t.Fatal("Write should fail")
} }
} }
var udpConnLocalNameTests = []struct {
net string
laddr *UDPAddr
}{
{"udp4", &UDPAddr{IP: IPv4(127, 0, 0, 1)}},
{"udp4", &UDPAddr{}},
{"udp4", nil},
}
func TestUDPConnLocalName(t *testing.T) {
if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
for _, tt := range udpConnLocalNameTests {
c, err := ListenUDP(tt.net, tt.laddr)
if err != nil {
t.Errorf("ListenUDP failed: %v", err)
return
}
defer c.Close()
la := c.LocalAddr()
if a, ok := la.(*UDPAddr); !ok || a.Port == 0 {
t.Errorf("got %v; expected a proper address with non-zero port number", la)
return
}
}
}

View File

@ -184,7 +184,7 @@ func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err error) {
return nil, UnknownNetworkError(net) return nil, UnknownNetworkError(net)
} }
if laddr == nil { if laddr == nil {
return nil, &OpError{"listen", net, nil, errMissingAddress} laddr = &UDPAddr{}
} }
l, err := listenPlan9(net, laddr) l, err := listenPlan9(net, laddr)
if err != nil { if err != nil {

View File

@ -193,7 +193,7 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
return nil, UnknownNetworkError(net) return nil, UnknownNetworkError(net)
} }
if laddr == nil { if laddr == nil {
return nil, &OpError{"listen", net, nil, errMissingAddress} laddr = &UDPAddr{}
} }
fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
if err != nil { if err != nil {