return fd.net + ":" + ls + "->" + rs
}
-func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret error) {
+func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa syscall.Sockaddr, ret error) {
// Do not need to call fd.writeLock here,
// because fd is not yet accessible to user,
// so no concurrent operations are possible.
case nil, syscall.EISCONN:
select {
case <-ctx.Done():
- return mapErr(ctx.Err())
+ return nil, mapErr(ctx.Err())
default:
}
if err := fd.pfd.Init(fd.net, true); err != nil {
- return err
+ return nil, err
}
runtime.KeepAlive(fd)
- return nil
+ return nil, nil
case syscall.EINVAL:
// On Solaris we can see EINVAL if the socket has
// already been accepted and closed by the server.
// the socket will see EOF. For details and a test
// case in C see https://golang.org/issue/6828.
if runtime.GOOS == "solaris" {
- return nil
+ return nil, nil
}
fallthrough
default:
- return os.NewSyscallError("connect", err)
+ return nil, os.NewSyscallError("connect", err)
}
if err := fd.pfd.Init(fd.net, true); err != nil {
- return err
+ return nil, err
}
if deadline, _ := ctx.Deadline(); !deadline.IsZero() {
fd.pfd.SetWriteDeadline(deadline)
if err := fd.pfd.WaitWrite(); err != nil {
select {
case <-ctx.Done():
- return mapErr(ctx.Err())
+ return nil, mapErr(ctx.Err())
default:
}
- return err
+ return nil, err
}
nerr, err := getsockoptIntFunc(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
if err != nil {
- return os.NewSyscallError("getsockopt", err)
+ return nil, os.NewSyscallError("getsockopt", err)
}
switch err := syscall.Errno(nerr); err {
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
- case syscall.Errno(0), syscall.EISCONN:
- if runtime.GOOS != "darwin" {
- return nil
- }
- // See golang.org/issue/14548.
- // On Darwin, multiple connect system calls on
- // a non-blocking socket never harm SO_ERROR.
- switch err := connectFunc(fd.pfd.Sysfd, ra); err {
- case nil, syscall.EISCONN:
- return nil
+ case syscall.EISCONN:
+ return nil, nil
+ case syscall.Errno(0):
+ // The runtime poller can wake us up spuriously;
+ // see issues 14548 and 19289. Check that we are
+ // really connected; if not, wait again.
+ if rsa, err := syscall.Getpeername(fd.pfd.Sysfd); err == nil {
+ return rsa, nil
}
default:
- return os.NewSyscallError("getsockopt", err)
+ return nil, os.NewSyscallError("getsockopt", err)
}
runtime.KeepAlive(fd)
}
runtime.SetFinalizer(fd, (*netFD).Close)
}
-func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) error {
+// Always returns nil for connected peer address result.
+func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (syscall.Sockaddr, error) {
// Do not need to call fd.writeLock here,
// because fd is not yet accessible to user,
// so no concurrent operations are possible.
if err := fd.init(); err != nil {
- return err
+ return nil, err
}
if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
fd.pfd.SetWriteDeadline(deadline)
}
if !canUseConnectEx(fd.net) {
err := connectFunc(fd.pfd.Sysfd, ra)
- return os.NewSyscallError("connect", err)
+ return nil, os.NewSyscallError("connect", err)
}
// ConnectEx windows API requires an unconnected, previously bound socket.
if la == nil {
panic("unexpected type in connect")
}
if err := syscall.Bind(fd.pfd.Sysfd, la); err != nil {
- return os.NewSyscallError("bind", err)
+ return nil, os.NewSyscallError("bind", err)
}
}
if err := fd.pfd.ConnectEx(ra); err != nil {
select {
case <-ctx.Done():
- return mapErr(ctx.Err())
+ return nil, mapErr(ctx.Err())
default:
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("connectex", err)
}
- return err
+ return nil, err
}
}
// Refresh socket properties.
- return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.pfd.Sysfd)), int32(unsafe.Sizeof(fd.pfd.Sysfd))))
+ return nil, os.NewSyscallError("setsockopt", syscall.Setsockopt(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.pfd.Sysfd)), int32(unsafe.Sizeof(fd.pfd.Sysfd))))
}
func (fd *netFD) Close() error {
}
}
}
- var rsa syscall.Sockaddr
+ var rsa syscall.Sockaddr // remote address from the user
+ var crsa syscall.Sockaddr // remote address we actually connected to
if raddr != nil {
if rsa, err = raddr.sockaddr(fd.family); err != nil {
return err
}
- if err := fd.connect(ctx, lsa, rsa); err != nil {
+ if crsa, err = fd.connect(ctx, lsa, rsa); err != nil {
return err
}
fd.isConnected = true
return err
}
}
+ // Record the local and remote addresses from the actual socket.
+ // Get the local address by calling Getsockname.
+ // For the remote address, use
+ // 1) the one returned by the connect method, if any; or
+ // 2) the one from Getpeername, if it succeeds; or
+ // 3) the one passed to us as the raddr parameter.
lsa, _ = syscall.Getsockname(fd.pfd.Sysfd)
- if rsa, _ = syscall.Getpeername(fd.pfd.Sysfd); rsa != nil {
+ if crsa != nil {
+ fd.setAddr(fd.addrFunc()(lsa), fd.addrFunc()(crsa))
+ } else if rsa, _ = syscall.Getpeername(fd.pfd.Sysfd); rsa != nil {
fd.setAddr(fd.addrFunc()(lsa), fd.addrFunc()(rsa))
} else {
fd.setAddr(fd.addrFunc()(lsa), raddr)
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin
+// +build !plan9,!windows
package net
import (
+ "context"
+ "internal/testenv"
+ "math/rand"
"runtime"
"sync"
"syscall"
ln.Close()
wg.Wait()
}
+
+// Issue 19289.
+// Test that a canceled Dial does not cause a subsequent Dial to succeed.
+func TestTCPSpuriousConnSetupCompletionWithCancel(t *testing.T) {
+ if testenv.Builder() == "" {
+ testenv.MustHaveExternalNetwork(t)
+ }
+ t.Parallel()
+ const tries = 10000
+ var wg sync.WaitGroup
+ wg.Add(tries * 2)
+ sem := make(chan bool, 5)
+ for i := 0; i < tries; i++ {
+ sem <- true
+ ctx, cancel := context.WithCancel(context.Background())
+ go func() {
+ defer wg.Done()
+ time.Sleep(time.Duration(rand.Int63n(int64(5 * time.Millisecond))))
+ cancel()
+ }()
+ go func(i int) {
+ defer wg.Done()
+ var dialer Dialer
+ // Try to connect to a real host on a port
+ // that it is not listening on.
+ _, err := dialer.DialContext(ctx, "tcp", "golang.org:3")
+ if err == nil {
+ t.Errorf("Dial to unbound port succeeded on attempt %d", i)
+ }
+ <-sem
+ }(i)
+ }
+ wg.Wait()
+}