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

View as plain text