| // 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 |
| } |