Commit d1e3ad8b authored by Mikio Hara's avatar Mikio Hara

net: avoid multiple calling of syscall connect on Unix variants

The previous fix CL 69340044 still leaves a possibility of it.
This CL prevents the kernel, especially DragonFly BSD, from
performing unpredictable asynchronous connection establishment
on stream-based transport layer protocol sockets.

Update #7541
Update #7474

LGTM=jsing
R=jsing
CC=golang-codereviews
https://golang.org/cl/75930043
parent 4ffa0219
......@@ -75,51 +75,47 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
if err := fd.pd.PrepareWrite(); err != nil {
return err
}
for {
err := syscall.Connect(fd.sysfd, ra)
if err == nil || err == syscall.EISCONN {
break
}
switch err := syscall.Connect(fd.sysfd, ra); err {
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
case nil, syscall.EISCONN:
return nil
case syscall.EINVAL:
// On Solaris we can see EINVAL if the socket has
// already been accepted and closed by the server.
// Treat this as a successful connection--writes to
// the socket will see EOF. For details and a test
// case in C see http://golang.org/issue/6828.
if runtime.GOOS == "solaris" && err == syscall.EINVAL {
break
if runtime.GOOS == "solaris" {
return nil
}
if err != syscall.EINPROGRESS && err != syscall.EALREADY && err != syscall.EINTR {
fallthrough
default:
return err
}
for {
// Performing multiple connect system calls on a
// non-blocking socket under Unix variants does not
// necessarily result in earlier errors being
// returned. Instead, once runtime-integrated network
// poller tells us that the socket is ready, get the
// SO_ERROR socket option to see if the connection
// succeeded or failed. See issue 7474 for further
// details.
if err := fd.pd.WaitWrite(); err != nil {
return err
}
if err = fd.pd.WaitWrite(); err != nil {
nerr, err := syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
if err != nil {
return err
}
// Performing multiple connect system calls on a non-blocking
// socket under DragonFly BSD does not necessarily result in
// earlier errors being returned, particularly if we are
// connecting to localhost. Instead, once netpoll tells us that
// the socket is ready, get the SO_ERROR socket option to see
// if the connection succeeded or failed. See issue 7474 for
// further details. At some point we may want to consider
// doing the same on other Unixes.
if runtime.GOOS == "dragonfly" {
nerr, err := syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
if err != nil {
return err
}
if nerr == 0 {
return nil
}
err = syscall.Errno(nerr)
if err != syscall.EINPROGRESS && err != syscall.EALREADY && err != syscall.EINTR {
return err
}
switch err := syscall.Errno(nerr); err {
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
case syscall.Errno(0), syscall.EISCONN:
return nil
default:
return err
}
}
return nil
}
func (fd *netFD) destroy() {
......
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