diff options
Diffstat (limited to 'src/net/fd_windows.go')
-rw-r--r-- | src/net/fd_windows.go | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/net/fd_windows.go b/src/net/fd_windows.go new file mode 100644 index 0000000..030b6a1 --- /dev/null +++ b/src/net/fd_windows.go @@ -0,0 +1,178 @@ +// 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" +) + +const ( + readSyscallName = "wsarecv" + readFromSyscallName = "wsarecvfrom" + readMsgSyscallName = "wsarecvmsg" + writeSyscallName = "wsasend" + writeToSyscallName = "wsasendto" + writeMsgSyscallName = "wsasendmsg" +) + +// 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 +} + +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 +} + +// 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 (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) 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 +} |