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 { ...@@ -75,51 +75,47 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
if err := fd.pd.PrepareWrite(); err != nil { if err := fd.pd.PrepareWrite(); err != nil {
return err return err
} }
for { switch err := syscall.Connect(fd.sysfd, ra); err {
err := syscall.Connect(fd.sysfd, ra) case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
if err == nil || err == syscall.EISCONN { case nil, syscall.EISCONN:
break return nil
} case syscall.EINVAL:
// On Solaris we can see EINVAL if the socket has // On Solaris we can see EINVAL if the socket has
// already been accepted and closed by the server. // already been accepted and closed by the server.
// Treat this as a successful connection--writes to // Treat this as a successful connection--writes to
// the socket will see EOF. For details and a test // the socket will see EOF. For details and a test
// case in C see http://golang.org/issue/6828. // case in C see http://golang.org/issue/6828.
if runtime.GOOS == "solaris" && err == syscall.EINVAL { if runtime.GOOS == "solaris" {
break return nil
} }
fallthrough
if err != syscall.EINPROGRESS && err != syscall.EALREADY && err != syscall.EINTR { default:
return err return err
} }
if err = fd.pd.WaitWrite(); err != nil { 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 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) nerr, err := syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
if err != nil { if err != nil {
return err return err
} }
if nerr == 0 { switch err := syscall.Errno(nerr); err {
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
case syscall.Errno(0), syscall.EISCONN:
return nil return nil
} default:
err = syscall.Errno(nerr)
if err != syscall.EINPROGRESS && err != syscall.EALREADY && err != syscall.EINTR {
return err return err
} }
} }
}
return nil
} }
func (fd *netFD) destroy() { 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