mirror of
https://github.com/golang/go
synced 2024-11-25 12:07:56 -07:00
net: avoid TCP self-connect
Fixes #2690. R=golang-dev, bradfitz CC=golang-dev https://golang.org/cl/5650071
This commit is contained in:
parent
f7a3683928
commit
cbe7d8db24
@ -84,3 +84,34 @@ func TestDialTimeout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSelfConnect(t *testing.T) {
|
||||||
|
// Test that Dial does not honor self-connects.
|
||||||
|
// See the comment in DialTCP.
|
||||||
|
|
||||||
|
// Find a port that would be used as a local address.
|
||||||
|
l, err := Listen("tcp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
c, err := Dial("tcp", l.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
addr := c.LocalAddr().String()
|
||||||
|
c.Close()
|
||||||
|
l.Close()
|
||||||
|
|
||||||
|
// Try to connect to that address repeatedly.
|
||||||
|
n := 100000
|
||||||
|
if testing.Short() {
|
||||||
|
n = 1000
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
c, err := Dial("tcp", addr)
|
||||||
|
if err == nil {
|
||||||
|
c.Close()
|
||||||
|
t.Errorf("#%d: Dial %q succeeded", i, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -227,13 +227,43 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
|
|||||||
if raddr == nil {
|
if raddr == nil {
|
||||||
return nil, &OpError{"dial", net, nil, errMissingAddress}
|
return nil, &OpError{"dial", net, nil, errMissingAddress}
|
||||||
}
|
}
|
||||||
|
|
||||||
fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
|
fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
|
||||||
|
|
||||||
|
// TCP has a rarely used mechanism called a 'simultaneous connection' in
|
||||||
|
// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
|
||||||
|
// connect to a simultaneous Dial("tcp", addr2, addr1) run on the machine
|
||||||
|
// at addr2, without either machine executing Listen. If laddr == nil,
|
||||||
|
// it means we want the kernel to pick an appropriate originating local
|
||||||
|
// address. Some Linux kernels cycle blindly through a fixed range of
|
||||||
|
// local ports, regardless of destination port. If a kernel happens to
|
||||||
|
// pick local port 50001 as the source for a Dial("tcp", "", "localhost:50001"),
|
||||||
|
// then the Dial will succeed, having simultaneously connected to itself.
|
||||||
|
// This can only happen when we are letting the kernel pick a port (laddr == nil)
|
||||||
|
// and when there is no listener for the destination address.
|
||||||
|
// It's hard to argue this is anything other than a kernel bug. If we
|
||||||
|
// see this happen, rather than expose the buggy effect to users, we
|
||||||
|
// close the fd and try again. If it happens twice more, we relent and
|
||||||
|
// use the result. See also:
|
||||||
|
// http://golang.org/issue/2690
|
||||||
|
// http://stackoverflow.com/questions/4949858/
|
||||||
|
for i := 0; i < 2 && err == nil && laddr == nil && selfConnect(fd); i++ {
|
||||||
|
fd.Close()
|
||||||
|
fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newTCPConn(fd), nil
|
return newTCPConn(fd), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func selfConnect(fd *netFD) bool {
|
||||||
|
l := fd.laddr.(*TCPAddr)
|
||||||
|
r := fd.raddr.(*TCPAddr)
|
||||||
|
return l.Port == r.Port && l.IP.Equal(r.IP)
|
||||||
|
}
|
||||||
|
|
||||||
// TCPListener is a TCP network listener.
|
// TCPListener is a TCP network listener.
|
||||||
// Clients should typically use variables of type Listener
|
// Clients should typically use variables of type Listener
|
||||||
// instead of assuming TCP.
|
// instead of assuming TCP.
|
||||||
|
Loading…
Reference in New Issue
Block a user