mirror of
https://github.com/golang/go
synced 2024-11-13 20:30:27 -07:00
bf0f692202
In some cases the netpoll code can cause a spurious wakeup. This is normally harmless, as the woken up code simply retries the operation. However, for connect, the test we were using to see whether the connect had succeeded (setsockopt(SO_ERROR)) was not reliable in the case of a spurious wakeup. Change to using a reliable test (getpeername). On Darwin we used a different technique: a second call to connect; change Darwin to use getpeername as well. Return the result of getpeername to avoid having to call it twice. Fixes #19289. Change-Id: I119ec8e7a41f482f1e590d4c65a37f6103fa22d9 Reviewed-on: https://go-review.googlesource.com/45815 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
240 lines
6.0 KiB
Go
240 lines
6.0 KiB
Go
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package net
|
|
|
|
import (
|
|
"context"
|
|
"internal/poll"
|
|
"os"
|
|
"runtime"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
// canUseConnectEx reports whether we can use the ConnectEx Windows API call
|
|
// for the given network type.
|
|
func canUseConnectEx(net string) bool {
|
|
switch net {
|
|
case "tcp", "tcp4", "tcp6":
|
|
return true
|
|
}
|
|
// ConnectEx windows API does not support connectionless sockets.
|
|
return false
|
|
}
|
|
|
|
// Network file descriptor.
|
|
type netFD struct {
|
|
pfd poll.FD
|
|
|
|
// immutable until Close
|
|
family int
|
|
sotype int
|
|
isConnected bool
|
|
net string
|
|
laddr Addr
|
|
raddr Addr
|
|
}
|
|
|
|
func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error) {
|
|
ret := &netFD{
|
|
pfd: poll.FD{
|
|
Sysfd: sysfd,
|
|
IsStream: sotype == syscall.SOCK_STREAM,
|
|
ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW,
|
|
},
|
|
family: family,
|
|
sotype: sotype,
|
|
net: net,
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func (fd *netFD) init() error {
|
|
errcall, err := fd.pfd.Init(fd.net)
|
|
if errcall != "" {
|
|
err = wrapSyscallError(errcall, err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (fd *netFD) setAddr(laddr, raddr Addr) {
|
|
fd.laddr = laddr
|
|
fd.raddr = raddr
|
|
runtime.SetFinalizer(fd, (*netFD).Close)
|
|
}
|
|
|
|
// 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 nil, err
|
|
}
|
|
if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
|
|
fd.pfd.SetWriteDeadline(deadline)
|
|
defer fd.pfd.SetWriteDeadline(noDeadline)
|
|
}
|
|
if !canUseConnectEx(fd.net) {
|
|
err := connectFunc(fd.pfd.Sysfd, ra)
|
|
return nil, os.NewSyscallError("connect", err)
|
|
}
|
|
// ConnectEx windows API requires an unconnected, previously bound socket.
|
|
if la == nil {
|
|
switch ra.(type) {
|
|
case *syscall.SockaddrInet4:
|
|
la = &syscall.SockaddrInet4{}
|
|
case *syscall.SockaddrInet6:
|
|
la = &syscall.SockaddrInet6{}
|
|
default:
|
|
panic("unexpected type in connect")
|
|
}
|
|
if err := syscall.Bind(fd.pfd.Sysfd, la); err != nil {
|
|
return nil, os.NewSyscallError("bind", err)
|
|
}
|
|
}
|
|
|
|
// Wait for the goroutine converting context.Done into a write timeout
|
|
// to exist, otherwise our caller might cancel the context and
|
|
// cause fd.setWriteDeadline(aLongTimeAgo) to cancel a successful dial.
|
|
done := make(chan bool) // must be unbuffered
|
|
defer func() { done <- true }()
|
|
go func() {
|
|
select {
|
|
case <-ctx.Done():
|
|
// Force the runtime's poller to immediately give
|
|
// up waiting for writability.
|
|
fd.pfd.SetWriteDeadline(aLongTimeAgo)
|
|
<-done
|
|
case <-done:
|
|
}
|
|
}()
|
|
|
|
// Call ConnectEx API.
|
|
if err := fd.pfd.ConnectEx(ra); err != nil {
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil, mapErr(ctx.Err())
|
|
default:
|
|
if _, ok := err.(syscall.Errno); ok {
|
|
err = os.NewSyscallError("connectex", err)
|
|
}
|
|
return nil, err
|
|
}
|
|
}
|
|
// Refresh socket properties.
|
|
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 {
|
|
runtime.SetFinalizer(fd, nil)
|
|
return fd.pfd.Close()
|
|
}
|
|
|
|
func (fd *netFD) shutdown(how int) error {
|
|
err := fd.pfd.Shutdown(how)
|
|
runtime.KeepAlive(fd)
|
|
return err
|
|
}
|
|
|
|
func (fd *netFD) closeRead() error {
|
|
return fd.shutdown(syscall.SHUT_RD)
|
|
}
|
|
|
|
func (fd *netFD) closeWrite() error {
|
|
return fd.shutdown(syscall.SHUT_WR)
|
|
}
|
|
|
|
func (fd *netFD) Read(buf []byte) (int, error) {
|
|
n, err := fd.pfd.Read(buf)
|
|
runtime.KeepAlive(fd)
|
|
return n, wrapSyscallError("wsarecv", err)
|
|
}
|
|
|
|
func (fd *netFD) readFrom(buf []byte) (int, syscall.Sockaddr, error) {
|
|
n, sa, err := fd.pfd.ReadFrom(buf)
|
|
runtime.KeepAlive(fd)
|
|
return n, sa, wrapSyscallError("wsarecvfrom", err)
|
|
}
|
|
|
|
func (fd *netFD) Write(buf []byte) (int, error) {
|
|
n, err := fd.pfd.Write(buf)
|
|
runtime.KeepAlive(fd)
|
|
return n, wrapSyscallError("wsasend", err)
|
|
}
|
|
|
|
func (c *conn) writeBuffers(v *Buffers) (int64, error) {
|
|
if !c.ok() {
|
|
return 0, syscall.EINVAL
|
|
}
|
|
n, err := c.fd.writeBuffers(v)
|
|
if err != nil {
|
|
return n, &OpError{Op: "wsasend", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func (fd *netFD) writeBuffers(buf *Buffers) (int64, error) {
|
|
n, err := fd.pfd.Writev((*[][]byte)(buf))
|
|
runtime.KeepAlive(fd)
|
|
return n, wrapSyscallError("wsasend", err)
|
|
}
|
|
|
|
func (fd *netFD) writeTo(buf []byte, sa syscall.Sockaddr) (int, error) {
|
|
n, err := fd.pfd.WriteTo(buf, sa)
|
|
runtime.KeepAlive(fd)
|
|
return n, wrapSyscallError("wsasendto", err)
|
|
}
|
|
|
|
func (fd *netFD) accept() (*netFD, error) {
|
|
s, rawsa, rsan, errcall, err := fd.pfd.Accept(func() (syscall.Handle, error) {
|
|
return sysSocket(fd.family, fd.sotype, 0)
|
|
})
|
|
|
|
if err != nil {
|
|
if errcall != "" {
|
|
err = wrapSyscallError(errcall, err)
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
// Associate our new socket with IOCP.
|
|
netfd, err := newFD(s, fd.family, fd.sotype, fd.net)
|
|
if err != nil {
|
|
poll.CloseFunc(s)
|
|
return nil, err
|
|
}
|
|
if err := netfd.init(); err != nil {
|
|
fd.Close()
|
|
return nil, err
|
|
}
|
|
|
|
// Get local and peer addr out of AcceptEx buffer.
|
|
var lrsa, rrsa *syscall.RawSockaddrAny
|
|
var llen, rlen int32
|
|
syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])),
|
|
0, rsan, rsan, &lrsa, &llen, &rrsa, &rlen)
|
|
lsa, _ := lrsa.Sockaddr()
|
|
rsa, _ := rrsa.Sockaddr()
|
|
|
|
netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
|
|
return netfd, nil
|
|
}
|
|
|
|
// Unimplemented functions.
|
|
|
|
func (fd *netFD) dup() (*os.File, error) {
|
|
// TODO: Implement this
|
|
return nil, syscall.EWINDOWS
|
|
}
|
|
|
|
func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
|
|
return 0, 0, 0, nil, syscall.EWINDOWS
|
|
}
|
|
|
|
func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
|
|
return 0, 0, syscall.EWINDOWS
|
|
}
|