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  	if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
    78  		fd.pfd.SetWriteDeadline(deadline)
    79  		defer fd.pfd.SetWriteDeadline(noDeadline)
    80  	}
    81  
    82  	// Start the "interrupter" goroutine, if this context might be canceled.
    83  	//
    84  	// The interrupter goroutine waits for the context to be done and
    85  	// interrupts the dial (by altering the fd's write deadline, which
    86  	// wakes up waitWrite).
    87  	ctxDone := ctx.Done()
    88  	if ctxDone != nil {
    89  		// Wait for the interrupter goroutine to exit before returning
    90  		// from connect.
    91  		done := make(chan struct{})
    92  		interruptRes := make(chan error)
    93  		defer func() {
    94  			close(done)
    95  			if ctxErr := <-interruptRes; ctxErr != nil && ret == nil {
    96  				// The interrupter goroutine called SetWriteDeadline,
    97  				// but the connect code below had returned from
    98  				// waitWrite already and did a successful connect (ret
    99  				// == nil). Because we've now poisoned the connection
   100  				// by making it unwritable, don't return a successful
   101  				// dial. This was issue 16523.
   102  				ret = mapErr(ctxErr)
   103  				fd.Close() // prevent a leak
   104  			}
   105  		}()
   106  		go func() {
   107  			select {
   108  			case <-ctxDone:
   109  				// Force the runtime's poller to immediately give up
   110  				// waiting for writability, unblocking waitWrite
   111  				// below.
   112  				fd.pfd.SetWriteDeadline(aLongTimeAgo)
   113  				testHookCanceledDial()
   114  				interruptRes <- ctx.Err()
   115  			case <-done:
   116  				interruptRes <- nil
   117  			}
   118  		}()
   119  	}
   120  
   121  	for {
   122  		// Performing multiple connect system calls on a
   123  		// non-blocking socket under Unix variants does not
   124  		// necessarily result in earlier errors being
   125  		// returned. Instead, once runtime-integrated network
   126  		// poller tells us that the socket is ready, get the
   127  		// SO_ERROR socket option to see if the connection
   128  		// succeeded or failed. See issue 7474 for further
   129  		// details.
   130  		if err := fd.pfd.WaitWrite(); err != nil {
   131  			select {
   132  			case <-ctxDone:
   133  				return nil, mapErr(ctx.Err())
   134  			default:
   135  			}
   136  			return nil, err
   137  		}
   138  		nerr, err := getsockoptIntFunc(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
   139  		if err != nil {
   140  			return nil, os.NewSyscallError("getsockopt", err)
   141  		}
   142  		switch err := syscall.Errno(nerr); err {
   143  		case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
   144  		case syscall.EISCONN:
   145  			return nil, nil
   146  		case syscall.Errno(0):
   147  			// The runtime poller can wake us up spuriously;
   148  			// see issues 14548 and 19289. Check that we are
   149  			// really connected; if not, wait again.
   150  			if rsa, err := syscall.Getpeername(fd.pfd.Sysfd); err == nil {
   151  				return rsa, nil
   152  			}
   153  		default:
   154  			return nil, os.NewSyscallError("connect", err)
   155  		}
   156  		runtime.KeepAlive(fd)
   157  	}
   158  }
   159  
   160  func (fd *netFD) accept() (netfd *netFD, err error) {
   161  	d, rsa, errcall, err := fd.pfd.Accept()
   162  	if err != nil {
   163  		if errcall != "" {
   164  			err = wrapSyscallError(errcall, err)
   165  		}
   166  		return nil, err
   167  	}
   168  
   169  	if netfd, err = newFD(d, fd.family, fd.sotype, fd.net); err != nil {
   170  		poll.CloseFunc(d)
   171  		return nil, err
   172  	}
   173  	if err = netfd.init(); err != nil {
   174  		netfd.Close()
   175  		return nil, err
   176  	}
   177  	lsa, _ := syscall.Getsockname(netfd.pfd.Sysfd)
   178  	netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
   179  	return netfd, nil
   180  }
   181  
   182  // Defined in os package.
   183  func newUnixFile(fd int, name string) *os.File
   184  
   185  func (fd *netFD) dup() (f *os.File, err error) {
   186  	ns, call, err := fd.pfd.Dup()
   187  	if err != nil {
   188  		if call != "" {
   189  			err = os.NewSyscallError(call, err)
   190  		}
   191  		return nil, err
   192  	}
   193  
   194  	return newUnixFile(ns, fd.name()), nil
   195  }
   196  

View as plain text