]> Cypherpunks repositories - gostls13.git/commitdiff
net: avoid multiple calling of syscall connect on Unix variants
authorMikio Hara <mikioh.mikioh@gmail.com>
Mon, 24 Mar 2014 17:56:10 +0000 (02:56 +0900)
committerMikio Hara <mikioh.mikioh@gmail.com>
Mon, 24 Mar 2014 17:56:10 +0000 (02:56 +0900)
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

src/pkg/net/fd_unix.go

index 9b0c6158c841e270cf16854a7e85a525bf1ea0bf..3b67b796f34ffe62e57a786e0de9375a2d77d924 100644 (file)
@@ -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() {