Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/fd_unix.go

Documentation: net

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

View as plain text