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

View as plain text