github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/net/fd_windows.go (about)

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package net
     6  
     7  import (
     8  	"context"
     9  	"internal/poll"
    10  	"os"
    11  	"runtime"
    12  	"syscall"
    13  	"unsafe"
    14  )
    15  
    16  // canUseConnectEx reports whether we can use the ConnectEx Windows API call
    17  // for the given network type.
    18  func canUseConnectEx(net string) bool {
    19  	switch net {
    20  	case "tcp", "tcp4", "tcp6":
    21  		return true
    22  	}
    23  	// ConnectEx windows API does not support connectionless sockets.
    24  	return false
    25  }
    26  
    27  // Network file descriptor.
    28  type netFD struct {
    29  	pfd poll.FD
    30  
    31  	// immutable until Close
    32  	family      int
    33  	sotype      int
    34  	isConnected bool
    35  	net         string
    36  	laddr       Addr
    37  	raddr       Addr
    38  }
    39  
    40  func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error) {
    41  	ret := &netFD{
    42  		pfd: poll.FD{
    43  			Sysfd:         sysfd,
    44  			IsStream:      sotype == syscall.SOCK_STREAM,
    45  			ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW,
    46  		},
    47  		family: family,
    48  		sotype: sotype,
    49  		net:    net,
    50  	}
    51  	return ret, nil
    52  }
    53  
    54  func (fd *netFD) init() error {
    55  	errcall, err := fd.pfd.Init(fd.net)
    56  	if errcall != "" {
    57  		err = wrapSyscallError(errcall, err)
    58  	}
    59  	return err
    60  }
    61  
    62  func (fd *netFD) setAddr(laddr, raddr Addr) {
    63  	fd.laddr = laddr
    64  	fd.raddr = raddr
    65  	runtime.SetFinalizer(fd, (*netFD).Close)
    66  }
    67  
    68  func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) error {
    69  	// Do not need to call fd.writeLock here,
    70  	// because fd is not yet accessible to user,
    71  	// so no concurrent operations are possible.
    72  	if err := fd.init(); err != nil {
    73  		return err
    74  	}
    75  	if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
    76  		fd.pfd.SetWriteDeadline(deadline)
    77  		defer fd.pfd.SetWriteDeadline(noDeadline)
    78  	}
    79  	if !canUseConnectEx(fd.net) {
    80  		err := connectFunc(fd.pfd.Sysfd, ra)
    81  		return os.NewSyscallError("connect", err)
    82  	}
    83  	// ConnectEx windows API requires an unconnected, previously bound socket.
    84  	if la == nil {
    85  		switch ra.(type) {
    86  		case *syscall.SockaddrInet4:
    87  			la = &syscall.SockaddrInet4{}
    88  		case *syscall.SockaddrInet6:
    89  			la = &syscall.SockaddrInet6{}
    90  		default:
    91  			panic("unexpected type in connect")
    92  		}
    93  		if err := syscall.Bind(fd.pfd.Sysfd, la); err != nil {
    94  			return os.NewSyscallError("bind", err)
    95  		}
    96  	}
    97  
    98  	// Wait for the goroutine converting context.Done into a write timeout
    99  	// to exist, otherwise our caller might cancel the context and
   100  	// cause fd.setWriteDeadline(aLongTimeAgo) to cancel a successful dial.
   101  	done := make(chan bool) // must be unbuffered
   102  	defer func() { done <- true }()
   103  	go func() {
   104  		select {
   105  		case <-ctx.Done():
   106  			// Force the runtime's poller to immediately give
   107  			// up waiting for writability.
   108  			fd.pfd.SetWriteDeadline(aLongTimeAgo)
   109  			<-done
   110  		case <-done:
   111  		}
   112  	}()
   113  
   114  	// Call ConnectEx API.
   115  	if err := fd.pfd.ConnectEx(ra); err != nil {
   116  		select {
   117  		case <-ctx.Done():
   118  			return mapErr(ctx.Err())
   119  		default:
   120  			if _, ok := err.(syscall.Errno); ok {
   121  				err = os.NewSyscallError("connectex", err)
   122  			}
   123  			return err
   124  		}
   125  	}
   126  	// Refresh socket properties.
   127  	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))))
   128  }
   129  
   130  func (fd *netFD) Close() error {
   131  	runtime.SetFinalizer(fd, nil)
   132  	return fd.pfd.Close()
   133  }
   134  
   135  func (fd *netFD) shutdown(how int) error {
   136  	err := fd.pfd.Shutdown(how)
   137  	runtime.KeepAlive(fd)
   138  	return err
   139  }
   140  
   141  func (fd *netFD) closeRead() error {
   142  	return fd.shutdown(syscall.SHUT_RD)
   143  }
   144  
   145  func (fd *netFD) closeWrite() error {
   146  	return fd.shutdown(syscall.SHUT_WR)
   147  }
   148  
   149  func (fd *netFD) Read(buf []byte) (int, error) {
   150  	n, err := fd.pfd.Read(buf)
   151  	runtime.KeepAlive(fd)
   152  	return n, wrapSyscallError("wsarecv", err)
   153  }
   154  
   155  func (fd *netFD) readFrom(buf []byte) (int, syscall.Sockaddr, error) {
   156  	n, sa, err := fd.pfd.ReadFrom(buf)
   157  	runtime.KeepAlive(fd)
   158  	return n, sa, wrapSyscallError("wsarecvfrom", err)
   159  }
   160  
   161  func (fd *netFD) Write(buf []byte) (int, error) {
   162  	n, err := fd.pfd.Write(buf)
   163  	runtime.KeepAlive(fd)
   164  	return n, wrapSyscallError("wsasend", err)
   165  }
   166  
   167  func (c *conn) writeBuffers(v *Buffers) (int64, error) {
   168  	if !c.ok() {
   169  		return 0, syscall.EINVAL
   170  	}
   171  	n, err := c.fd.writeBuffers(v)
   172  	if err != nil {
   173  		return n, &OpError{Op: "WSASend", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
   174  	}
   175  	return n, nil
   176  }
   177  
   178  func (fd *netFD) writeBuffers(buf *Buffers) (int64, error) {
   179  	n, err := fd.pfd.Writev((*[][]byte)(buf))
   180  	runtime.KeepAlive(fd)
   181  	return n, wrapSyscallError("wsasend", err)
   182  }
   183  
   184  func (fd *netFD) writeTo(buf []byte, sa syscall.Sockaddr) (int, error) {
   185  	n, err := fd.pfd.WriteTo(buf, sa)
   186  	runtime.KeepAlive(fd)
   187  	return n, wrapSyscallError("wsasendto", err)
   188  }
   189  
   190  func (fd *netFD) accept() (*netFD, error) {
   191  	s, rawsa, rsan, errcall, err := fd.pfd.Accept(func() (syscall.Handle, error) {
   192  		return sysSocket(fd.family, fd.sotype, 0)
   193  	})
   194  
   195  	if err != nil {
   196  		if errcall != "" {
   197  			err = wrapSyscallError(errcall, err)
   198  		}
   199  		return nil, err
   200  	}
   201  
   202  	// Associate our new socket with IOCP.
   203  	netfd, err := newFD(s, fd.family, fd.sotype, fd.net)
   204  	if err != nil {
   205  		poll.CloseFunc(s)
   206  		return nil, err
   207  	}
   208  	if err := netfd.init(); err != nil {
   209  		fd.Close()
   210  		return nil, err
   211  	}
   212  
   213  	// Get local and peer addr out of AcceptEx buffer.
   214  	var lrsa, rrsa *syscall.RawSockaddrAny
   215  	var llen, rlen int32
   216  	syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])),
   217  		0, rsan, rsan, &lrsa, &llen, &rrsa, &rlen)
   218  	lsa, _ := lrsa.Sockaddr()
   219  	rsa, _ := rrsa.Sockaddr()
   220  
   221  	netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
   222  	return netfd, nil
   223  }
   224  
   225  // Unimplemented functions.
   226  
   227  func (fd *netFD) dup() (*os.File, error) {
   228  	// TODO: Implement this
   229  	return nil, syscall.EWINDOWS
   230  }
   231  
   232  func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
   233  	return 0, 0, 0, nil, syscall.EWINDOWS
   234  }
   235  
   236  func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
   237  	return 0, 0, syscall.EWINDOWS
   238  }