From: Mikio Hara Date: Mon, 24 Mar 2014 17:56:10 +0000 (+0900) Subject: net: avoid multiple calling of syscall connect on Unix variants X-Git-Tag: go1.3beta1~275 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=d1e3ad8bc1be60638c10d3b0962035bebf8b4275;p=gostls13.git 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 --- diff --git a/src/pkg/net/fd_unix.go b/src/pkg/net/fd_unix.go index 9b0c6158c8..3b67b796f3 100644 --- a/src/pkg/net/fd_unix.go +++ b/src/pkg/net/fd_unix.go @@ -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() {