fd_unix.mx raw

   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  		return nil, os.NewSyscallError("connect", err)
  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  	// Moxie: no goroutines. Check context before connecting.
  83  	if ctx.Err() != nil {
  84  		return nil, mapErr(ctx.Err())
  85  	}
  86  
  87  	for {
  88  		// Performing multiple connect system calls on a
  89  		// non-blocking socket under Unix variants does not
  90  		// necessarily result in earlier errors being
  91  		// returned. Instead, once runtime-integrated network
  92  		// poller tells us that the socket is ready, get the
  93  		// SO_ERROR socket option to see if the connection
  94  		// succeeded or failed. See issue 7474 for further
  95  		// details.
  96  		if err := fd.pfd.WaitWrite(); err != nil {
  97  			select {
  98  			case <-ctx.Done():
  99  				return nil, mapErr(ctx.Err())
 100  			default:
 101  			}
 102  			return nil, err
 103  		}
 104  		nerr, err := getsockoptIntFunc(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
 105  		if err != nil {
 106  			return nil, os.NewSyscallError("getsockopt", err)
 107  		}
 108  		switch err := syscall.Errno(nerr); err {
 109  		case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
 110  		case syscall.EISCONN:
 111  			return nil, nil
 112  		case syscall.Errno(0):
 113  			// The runtime poller can wake us up spuriously;
 114  			// see issues 14548 and 19289. Check that we are
 115  			// really connected; if not, wait again.
 116  			if rsa, err := syscall.Getpeername(fd.pfd.Sysfd); err == nil {
 117  				return rsa, nil
 118  			}
 119  		default:
 120  			return nil, os.NewSyscallError("connect", err)
 121  		}
 122  		runtime.KeepAlive(fd)
 123  	}
 124  }
 125  
 126  func (fd *netFD) accept() (netfd *netFD, err error) {
 127  	d, rsa, errcall, err := fd.pfd.Accept()
 128  	if err != nil {
 129  		if errcall != "" {
 130  			err = wrapSyscallError(errcall, err)
 131  		}
 132  		return nil, err
 133  	}
 134  
 135  	if netfd, err = newFD(d, fd.family, fd.sotype, fd.net); err != nil {
 136  		poll.CloseFunc(d)
 137  		return nil, err
 138  	}
 139  	if err = netfd.init(); err != nil {
 140  		netfd.Close()
 141  		return nil, err
 142  	}
 143  	lsa, _ := syscall.Getsockname(netfd.pfd.Sysfd)
 144  	netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
 145  	return netfd, nil
 146  }
 147  
 148  // Defined in os package.
 149  func newUnixFile(fd int, name string) *os.File
 150  
 151  func (fd *netFD) dup() (f *os.File, err error) {
 152  	ns, call, err := fd.pfd.Dup()
 153  	if err != nil {
 154  		if call != "" {
 155  			err = os.NewSyscallError(call, err)
 156  		}
 157  		return nil, err
 158  	}
 159  
 160  	return newUnixFile(ns, fd.name()), nil
 161  }
 162