Source file src/net/fd_unix.go

     1  // Copyright 2009 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  //go:build unix
     6  
     7  package net
     8  
     9  import (
    10  	"context"
    11  	"internal/poll"
    12  	"os"
    13  	"runtime"
    14  	"syscall"
    15  )
    16  
    17  const (
    18  	readSyscallName     = "read"
    19  	readFromSyscallName = "recvfrom"
    20  	readMsgSyscallName  = "recvmsg"
    21  	writeSyscallName    = "write"
    22  	writeToSyscallName  = "sendto"
    23  	writeMsgSyscallName = "sendmsg"
    24  )
    25  
    26  func newFD(sysfd, family, sotype int, net string) *netFD {
    27  	return &netFD{
    28  		pfd: poll.FD{
    29  			Sysfd:         sysfd,
    30  			IsStream:      sotype == syscall.SOCK_STREAM,
    31  			ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW,
    32  		},
    33  		family: family,
    34  		sotype: sotype,
    35  		net:    net,
    36  	}
    37  }
    38  
    39  func (fd *netFD) init() error {
    40  	return fd.pfd.Init(fd.net, true)
    41  }
    42  
    43  func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa syscall.Sockaddr, ret error) {
    44  	// Do not need to call fd.writeLock here,
    45  	// because fd is not yet accessible to user,
    46  	// so no concurrent operations are possible.
    47  	switch err := connectFunc(fd.pfd.Sysfd, ra); err {
    48  	case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
    49  	case nil, syscall.EISCONN:
    50  		select {
    51  		case <-ctx.Done():
    52  			return nil, mapErr(ctx.Err())
    53  		default:
    54  		}
    55  		if err := fd.pfd.Init(fd.net, true); err != nil {
    56  			return nil, err
    57  		}
    58  		runtime.KeepAlive(fd)
    59  		return nil, nil
    60  	case syscall.EINVAL:
    61  		// On Solaris and illumos we can see EINVAL if the socket has
    62  		// already been accepted and closed by the server.  Treat this
    63  		// as a successful connection--writes to the socket will see
    64  		// EOF.  For details and a test case in C see
    65  		// https://golang.org/issue/6828.
    66  		if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
    67  			return nil, nil
    68  		}
    69  		fallthrough
    70  	default:
    71  		return nil, os.NewSyscallError("connect", err)
    72  	}
    73  	if err := fd.pfd.Init(fd.net, true); err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	ctxDone := ctx.Done()
    78  	if ctxDone != nil {
    79  		if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
    80  			fd.pfd.SetWriteDeadline(deadline)
    81  			defer fd.pfd.SetWriteDeadline(noDeadline)
    82  		}
    83  
    84  		// Load the hook function synchronously to prevent a race
    85  		// with test code that restores the old value.
    86  		testHookCanceledDial := testHookCanceledDial
    87  		stop := context.AfterFunc(ctx, func() {
    88  			// Force the runtime's poller to immediately give up
    89  			// waiting for writability, unblocking waitWrite
    90  			// below.
    91  			_ = fd.pfd.SetWriteDeadline(aLongTimeAgo)
    92  			testHookCanceledDial()
    93  		})
    94  		defer func() {
    95  			if !stop() && ret == nil {
    96  				// The context.AfterFunc has called or is about to call
    97  				// SetWriteDeadline, but the connect code below had
    98  				// returned from waitWrite already and did a successful
    99  				// connect (ret == nil). Because we've now poisoned the
   100  				// connection by making it unwritable, don't return a
   101  				// successful dial. This was issue 16523.
   102  				ret = mapErr(ctx.Err())
   103  				// The caller closes fd on error, so there's no need to
   104  				// wait for the SetWriteDeadline call to return.
   105  			}
   106  		}()
   107  	}
   108  
   109  	for {
   110  		// Performing multiple connect system calls on a
   111  		// non-blocking socket under Unix variants does not
   112  		// necessarily result in earlier errors being
   113  		// returned. Instead, once runtime-integrated network
   114  		// poller tells us that the socket is ready, get the
   115  		// SO_ERROR socket option to see if the connection
   116  		// succeeded or failed. See issue 7474 for further
   117  		// details.
   118  		if err := fd.pfd.WaitWrite(); err != nil {
   119  			select {
   120  			case <-ctxDone:
   121  				return nil, mapErr(ctx.Err())
   122  			default:
   123  			}
   124  			return nil, err
   125  		}
   126  		nerr, err := getsockoptIntFunc(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
   127  		if err != nil {
   128  			return nil, os.NewSyscallError("getsockopt", err)
   129  		}
   130  		switch err := syscall.Errno(nerr); err {
   131  		case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
   132  		case syscall.EISCONN:
   133  			return nil, nil
   134  		case syscall.Errno(0):
   135  			// The runtime poller can wake us up spuriously;
   136  			// see issues 14548 and 19289. Check that we are
   137  			// really connected; if not, wait again.
   138  			if rsa, err := syscall.Getpeername(fd.pfd.Sysfd); err == nil {
   139  				return rsa, nil
   140  			}
   141  		default:
   142  			return nil, os.NewSyscallError("connect", err)
   143  		}
   144  		runtime.KeepAlive(fd)
   145  	}
   146  }
   147  
   148  func (fd *netFD) accept() (netfd *netFD, err error) {
   149  	d, rsa, errcall, err := fd.pfd.Accept()
   150  	if err != nil {
   151  		if errcall != "" {
   152  			err = wrapSyscallError(errcall, err)
   153  		}
   154  		return nil, err
   155  	}
   156  
   157  	netfd = newFD(d, fd.family, fd.sotype, fd.net)
   158  	if err = netfd.init(); err != nil {
   159  		netfd.Close()
   160  		return nil, err
   161  	}
   162  	lsa, _ := syscall.Getsockname(netfd.pfd.Sysfd)
   163  	netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
   164  	return netfd, nil
   165  }
   166  
   167  // Defined in os package.
   168  func newUnixFile(fd int, name string) *os.File
   169  
   170  func (fd *netFD) dup() (f *os.File, err error) {
   171  	ns, call, err := fd.pfd.Dup()
   172  	if err != nil {
   173  		if call != "" {
   174  			err = os.NewSyscallError(call, err)
   175  		}
   176  		return nil, err
   177  	}
   178  
   179  	return newUnixFile(ns, fd.name()), nil
   180  }
   181  

View as plain text