dd931d9b48
Reviewed-on: https://go-review.googlesource.com/136435 gotools/: * Makefile.am (mostlyclean-local): Run chmod on check-go-dir to make sure it is writable. (check-go-tools): Likewise. (check-vet): Copy internal/objabi to check-vet-dir. * Makefile.in: Rebuild. From-SVN: r264546
257 lines
6.5 KiB
Go
257 lines
6.5 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"
|
|
"time"
|
|
"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 // handshake completed or use of association with peer
|
|
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, true)
|
|
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
|
|
}
|
|
|
|
func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
|
|
n, oobn, flags, sa, err = fd.pfd.ReadMsg(p, oob)
|
|
runtime.KeepAlive(fd)
|
|
return n, oobn, flags, sa, wrapSyscallError("wsarecvmsg", err)
|
|
}
|
|
|
|
func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
|
|
n, oobn, err = fd.pfd.WriteMsg(p, oob, sa)
|
|
runtime.KeepAlive(fd)
|
|
return n, oobn, wrapSyscallError("wsasendmsg", err)
|
|
}
|
|
|
|
// Unimplemented functions.
|
|
|
|
func (fd *netFD) dup() (*os.File, error) {
|
|
// TODO: Implement this
|
|
return nil, syscall.EWINDOWS
|
|
}
|
|
|
|
func (fd *netFD) SetDeadline(t time.Time) error {
|
|
return fd.pfd.SetDeadline(t)
|
|
}
|
|
|
|
func (fd *netFD) SetReadDeadline(t time.Time) error {
|
|
return fd.pfd.SetReadDeadline(t)
|
|
}
|
|
|
|
func (fd *netFD) SetWriteDeadline(t time.Time) error {
|
|
return fd.pfd.SetWriteDeadline(t)
|
|
}
|