Skip to content
Snippets Groups Projects
fd_unix.go 5.45 KiB
Newer Older
  • Learn to ignore specific revisions
  • // Copyright 2009 The Go Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    
    package net
    
    import (
    
    	"runtime"
    
    const (
    	readSyscallName     = "read"
    	readFromSyscallName = "recvfrom"
    	readMsgSyscallName  = "recvmsg"
    	writeSyscallName    = "write"
    	writeToSyscallName  = "sendto"
    	writeMsgSyscallName = "sendmsg"
    )
    
    func newFD(sysfd, family, sotype int, net string) (*netFD, error) {
    
    	ret := &netFD{
    		pfd: poll.FD{
    			Sysfd:         sysfd,
    			IsStream:      sotype == syscall.SOCK_STREAM,
    			ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW,
    		},
    		family: family,
    		sotype: sotype,
    		net:    net,
    	}
    	return ret, nil
    
    	return fd.pfd.Init(fd.net, true)
    
    func (fd *netFD) name() string {
    
    	var ls, rs string
    
    	if fd.laddr != nil {
    		ls = fd.laddr.String()
    
    Russ Cox's avatar
    Russ Cox committed
    	}
    
    	if fd.raddr != nil {
    		rs = fd.raddr.String()
    
    Russ Cox's avatar
    Russ Cox committed
    	}
    
    	return fd.net + ":" + ls + "->" + rs
    
    func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa syscall.Sockaddr, ret error) {
    
    	// Do not need to call fd.writeLock here,
    	// because fd is not yet accessible to user,
    	// so no concurrent operations are possible.
    
    	switch err := connectFunc(fd.pfd.Sysfd, ra); err {
    
    	case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
    	case nil, syscall.EISCONN:
    
    		select {
    		case <-ctx.Done():
    
    			return nil, mapErr(ctx.Err())
    
    		if err := fd.pfd.Init(fd.net, true); err != nil {
    
    		// On Solaris and illumos we can see EINVAL if the socket has
    		// already been accepted and closed by the server.  Treat this
    		// as a successful connection--writes to the socket will see
    		// EOF.  For details and a test case in C see
    		// https://golang.org/issue/6828.
    		if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
    
    		return nil, os.NewSyscallError("connect", err)
    
    	if err := fd.pfd.Init(fd.net, true); err != nil {
    
    	if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
    
    		fd.pfd.SetWriteDeadline(deadline)
    		defer fd.pfd.SetWriteDeadline(noDeadline)
    
    	// Start the "interrupter" goroutine, if this context might be canceled.
    	//
    	// The interrupter goroutine waits for the context to be done and
    	// interrupts the dial (by altering the fd's write deadline, which
    	// wakes up waitWrite).
    
    	ctxDone := ctx.Done()
    	if ctxDone != nil {
    
    		// Wait for the interrupter goroutine to exit before returning
    		// from connect.
    		done := make(chan struct{})
    		interruptRes := make(chan error)
    		defer func() {
    			close(done)
    			if ctxErr := <-interruptRes; ctxErr != nil && ret == nil {
    
    				// The interrupter goroutine called SetWriteDeadline,
    
    				// but the connect code below had returned from
    				// waitWrite already and did a successful connect (ret
    				// == nil). Because we've now poisoned the connection
    				// by making it unwritable, don't return a successful
    				// dial. This was issue 16523.
    
    				// Force the runtime's poller to immediately give up
    				// waiting for writability, unblocking waitWrite
    				// below.
    
    				fd.pfd.SetWriteDeadline(aLongTimeAgo)
    
    				testHookCanceledDial()
    				interruptRes <- ctx.Err()
    			case <-done:
    				interruptRes <- nil
    			}
    		}()
    	}
    
    	for {
    		// Performing multiple connect system calls on a
    		// non-blocking socket under Unix variants does not
    		// necessarily result in earlier errors being
    		// returned. Instead, once runtime-integrated network
    		// poller tells us that the socket is ready, get the
    		// SO_ERROR socket option to see if the connection
    		// succeeded or failed. See issue 7474 for further
    		// details.
    
    		if err := fd.pfd.WaitWrite(); err != nil {
    
    			case <-ctxDone:
    
    				return nil, mapErr(ctx.Err())
    
    Russ Cox's avatar
    Russ Cox committed
    		}
    
    		nerr, err := getsockoptIntFunc(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
    
    			return nil, os.NewSyscallError("getsockopt", err)
    
    		switch err := syscall.Errno(nerr); err {
    		case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
    
    		case syscall.EISCONN:
    			return nil, nil
    		case syscall.Errno(0):
    			// The runtime poller can wake us up spuriously;
    			// see issues 14548 and 19289. Check that we are
    			// really connected; if not, wait again.
    			if rsa, err := syscall.Getpeername(fd.pfd.Sysfd); err == nil {
    				return rsa, nil
    
    			return nil, os.NewSyscallError("connect", err)
    
    func (fd *netFD) accept() (netfd *netFD, err error) {
    
    	d, rsa, errcall, err := fd.pfd.Accept()
    	if err != nil {
    		if errcall != "" {
    			err = wrapSyscallError(errcall, err)
    
    Russ Cox's avatar
    Russ Cox committed
    		}
    
    Russ Cox's avatar
    Russ Cox committed
    
    
    	if netfd, err = newFD(d, fd.family, fd.sotype, fd.net); err != nil {
    		poll.CloseFunc(d)
    
    	lsa, _ := syscall.Getsockname(netfd.pfd.Sysfd)
    
    	netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
    
    // Defined in os package.
    func newUnixFile(fd uintptr, name string) *os.File
    
    
    func (fd *netFD) dup() (f *os.File, err error) {
    
    Russ Cox's avatar
    Russ Cox committed
    	if err != nil {
    
    		if call != "" {
    			err = os.NewSyscallError(call, err)
    		}
    
    		return nil, err
    
    	return newUnixFile(uintptr(ns), fd.name()), nil