Source file src/net/fd_windows.go

     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  const (
    17  	readSyscallName     = "wsarecv"
    18  	readFromSyscallName = "wsarecvfrom"
    19  	readMsgSyscallName  = "wsarecvmsg"
    20  	writeSyscallName    = "wsasend"
    21  	writeToSyscallName  = "wsasendto"
    22  	writeMsgSyscallName = "wsasendmsg"
    23  )
    24  
    25  // canUseConnectEx reports whether we can use the ConnectEx Windows API call
    26  // for the given network type.
    27  func canUseConnectEx(net string) bool {
    28  	switch net {
    29  	case "tcp", "tcp4", "tcp6":
    30  		return true
    31  	}
    32  	// ConnectEx windows API does not support connectionless sockets.
    33  	return false
    34  }
    35  
    36  func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error) {
    37  	ret := &netFD{
    38  		pfd: poll.FD{
    39  			Sysfd:         sysfd,
    40  			IsStream:      sotype == syscall.SOCK_STREAM,
    41  			ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW,
    42  		},
    43  		family: family,
    44  		sotype: sotype,
    45  		net:    net,
    46  	}
    47  	return ret, nil
    48  }
    49  
    50  func (fd *netFD) init() error {
    51  	errcall, err := fd.pfd.Init(fd.net, true)
    52  	if errcall != "" {
    53  		err = wrapSyscallError(errcall, err)
    54  	}
    55  	return err
    56  }
    57  
    58  // Always returns nil for connected peer address result.
    59  func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (syscall.Sockaddr, error) {
    60  	// Do not need to call fd.writeLock here,
    61  	// because fd is not yet accessible to user,
    62  	// so no concurrent operations are possible.
    63  	if err := fd.init(); err != nil {
    64  		return nil, err
    65  	}
    66  	if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
    67  		fd.pfd.SetWriteDeadline(deadline)
    68  		defer fd.pfd.SetWriteDeadline(noDeadline)
    69  	}
    70  	if !canUseConnectEx(fd.net) {
    71  		err := connectFunc(fd.pfd.Sysfd, ra)
    72  		return nil, os.NewSyscallError("connect", err)
    73  	}
    74  	// ConnectEx windows API requires an unconnected, previously bound socket.
    75  	if la == nil {
    76  		switch ra.(type) {
    77  		case *syscall.SockaddrInet4:
    78  			la = &syscall.SockaddrInet4{}
    79  		case *syscall.SockaddrInet6:
    80  			la = &syscall.SockaddrInet6{}
    81  		default:
    82  			panic("unexpected type in connect")
    83  		}
    84  		if err := syscall.Bind(fd.pfd.Sysfd, la); err != nil {
    85  			return nil, os.NewSyscallError("bind", err)
    86  		}
    87  	}
    88  
    89  	// Wait for the goroutine converting context.Done into a write timeout
    90  	// to exist, otherwise our caller might cancel the context and
    91  	// cause fd.setWriteDeadline(aLongTimeAgo) to cancel a successful dial.
    92  	done := make(chan bool) // must be unbuffered
    93  	defer func() { done <- true }()
    94  	go func() {
    95  		select {
    96  		case <-ctx.Done():
    97  			// Force the runtime's poller to immediately give
    98  			// up waiting for writability.
    99  			fd.pfd.SetWriteDeadline(aLongTimeAgo)
   100  			<-done
   101  		case <-done:
   102  		}
   103  	}()
   104  
   105  	// Call ConnectEx API.
   106  	if err := fd.pfd.ConnectEx(ra); err != nil {
   107  		select {
   108  		case <-ctx.Done():
   109  			return nil, mapErr(ctx.Err())
   110  		default:
   111  			if _, ok := err.(syscall.Errno); ok {
   112  				err = os.NewSyscallError("connectex", err)
   113  			}
   114  			return nil, err
   115  		}
   116  	}
   117  	// Refresh socket properties.
   118  	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))))
   119  }
   120  
   121  func (c *conn) writeBuffers(v *Buffers) (int64, error) {
   122  	if !c.ok() {
   123  		return 0, syscall.EINVAL
   124  	}
   125  	n, err := c.fd.writeBuffers(v)
   126  	if err != nil {
   127  		return n, &OpError{Op: "wsasend", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
   128  	}
   129  	return n, nil
   130  }
   131  
   132  func (fd *netFD) writeBuffers(buf *Buffers) (int64, error) {
   133  	n, err := fd.pfd.Writev((*[][]byte)(buf))
   134  	runtime.KeepAlive(fd)
   135  	return n, wrapSyscallError("wsasend", err)
   136  }
   137  
   138  func (fd *netFD) accept() (*netFD, error) {
   139  	s, rawsa, rsan, errcall, err := fd.pfd.Accept(func() (syscall.Handle, error) {
   140  		return sysSocket(fd.family, fd.sotype, 0)
   141  	})
   142  
   143  	if err != nil {
   144  		if errcall != "" {
   145  			err = wrapSyscallError(errcall, err)
   146  		}
   147  		return nil, err
   148  	}
   149  
   150  	// Associate our new socket with IOCP.
   151  	netfd, err := newFD(s, fd.family, fd.sotype, fd.net)
   152  	if err != nil {
   153  		poll.CloseFunc(s)
   154  		return nil, err
   155  	}
   156  	if err := netfd.init(); err != nil {
   157  		fd.Close()
   158  		return nil, err
   159  	}
   160  
   161  	// Get local and peer addr out of AcceptEx buffer.
   162  	var lrsa, rrsa *syscall.RawSockaddrAny
   163  	var llen, rlen int32
   164  	syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])),
   165  		0, rsan, rsan, &lrsa, &llen, &rrsa, &rlen)
   166  	lsa, _ := lrsa.Sockaddr()
   167  	rsa, _ := rrsa.Sockaddr()
   168  
   169  	netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
   170  	return netfd, nil
   171  }
   172  
   173  // Unimplemented functions.
   174  
   175  func (fd *netFD) dup() (*os.File, error) {
   176  	// TODO: Implement this
   177  	return nil, syscall.EWINDOWS
   178  }
   179  

View as plain text