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