mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			282 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
// 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.
 | 
						|
 | 
						|
// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 | 
						|
 | 
						|
package net
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"internal/poll"
 | 
						|
	"os"
 | 
						|
	"runtime"
 | 
						|
	"syscall"
 | 
						|
	"time"
 | 
						|
)
 | 
						|
 | 
						|
// Network file descriptor.
 | 
						|
type netFD struct {
 | 
						|
	pfd poll.FD
 | 
						|
 | 
						|
	// immutable until Close
 | 
						|
	family      int
 | 
						|
	sotype      int
 | 
						|
	isConnected bool // handshake completed or use of association with peer
 | 
						|
	net         string
 | 
						|
	laddr       Addr
 | 
						|
	raddr       Addr
 | 
						|
}
 | 
						|
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) init() error {
 | 
						|
	return fd.pfd.Init(fd.net, true)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) setAddr(laddr, raddr Addr) {
 | 
						|
	fd.laddr = laddr
 | 
						|
	fd.raddr = raddr
 | 
						|
	runtime.SetFinalizer(fd, (*netFD).Close)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) name() string {
 | 
						|
	var ls, rs string
 | 
						|
	if fd.laddr != nil {
 | 
						|
		ls = fd.laddr.String()
 | 
						|
	}
 | 
						|
	if fd.raddr != nil {
 | 
						|
		rs = fd.raddr.String()
 | 
						|
	}
 | 
						|
	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())
 | 
						|
		default:
 | 
						|
		}
 | 
						|
		if err := fd.pfd.Init(fd.net, true); err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		runtime.KeepAlive(fd)
 | 
						|
		return nil, nil
 | 
						|
	case syscall.EINVAL:
 | 
						|
		// On Solaris 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" {
 | 
						|
			return nil, nil
 | 
						|
		}
 | 
						|
		fallthrough
 | 
						|
	default:
 | 
						|
		return nil, os.NewSyscallError("connect", err)
 | 
						|
	}
 | 
						|
	if err := fd.pfd.Init(fd.net, true); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if deadline, _ := ctx.Deadline(); !deadline.IsZero() {
 | 
						|
		fd.pfd.SetWriteDeadline(deadline)
 | 
						|
		defer fd.pfd.SetWriteDeadline(noDeadline)
 | 
						|
	}
 | 
						|
 | 
						|
	// Start the "interrupter" goroutine, if this context might be canceled.
 | 
						|
	// (The background context cannot)
 | 
						|
	//
 | 
						|
	// 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).
 | 
						|
	if ctx != context.Background() {
 | 
						|
		// 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.
 | 
						|
				ret = mapErr(ctxErr)
 | 
						|
				fd.Close() // prevent a leak
 | 
						|
			}
 | 
						|
		}()
 | 
						|
		go func() {
 | 
						|
			select {
 | 
						|
			case <-ctx.Done():
 | 
						|
				// 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 {
 | 
						|
			select {
 | 
						|
			case <-ctx.Done():
 | 
						|
				return nil, mapErr(ctx.Err())
 | 
						|
			default:
 | 
						|
			}
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		nerr, err := getsockoptIntFunc(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
 | 
						|
		if err != nil {
 | 
						|
			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
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			return nil, os.NewSyscallError("connect", err)
 | 
						|
		}
 | 
						|
		runtime.KeepAlive(fd)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) Close() error {
 | 
						|
	runtime.SetFinalizer(fd, nil)
 | 
						|
	return fd.pfd.Close()
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) shutdown(how int) error {
 | 
						|
	err := fd.pfd.Shutdown(how)
 | 
						|
	runtime.KeepAlive(fd)
 | 
						|
	return wrapSyscallError("shutdown", err)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) closeRead() error {
 | 
						|
	return fd.shutdown(syscall.SHUT_RD)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) closeWrite() error {
 | 
						|
	return fd.shutdown(syscall.SHUT_WR)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) Read(p []byte) (n int, err error) {
 | 
						|
	n, err = fd.pfd.Read(p)
 | 
						|
	runtime.KeepAlive(fd)
 | 
						|
	return n, wrapSyscallError("read", err)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
 | 
						|
	n, sa, err = fd.pfd.ReadFrom(p)
 | 
						|
	runtime.KeepAlive(fd)
 | 
						|
	return n, sa, wrapSyscallError("recvfrom", err)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
 | 
						|
	n, oobn, flags, sa, err = fd.pfd.ReadMsg(p, oob)
 | 
						|
	runtime.KeepAlive(fd)
 | 
						|
	return n, oobn, flags, sa, wrapSyscallError("recvmsg", err)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) Write(p []byte) (nn int, err error) {
 | 
						|
	nn, err = fd.pfd.Write(p)
 | 
						|
	runtime.KeepAlive(fd)
 | 
						|
	return nn, wrapSyscallError("write", err)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
 | 
						|
	n, err = fd.pfd.WriteTo(p, sa)
 | 
						|
	runtime.KeepAlive(fd)
 | 
						|
	return n, wrapSyscallError("sendto", err)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
 | 
						|
	n, oobn, err = fd.pfd.WriteMsg(p, oob, sa)
 | 
						|
	runtime.KeepAlive(fd)
 | 
						|
	return n, oobn, wrapSyscallError("sendmsg", 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)
 | 
						|
		}
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	if netfd, err = newFD(d, fd.family, fd.sotype, fd.net); err != nil {
 | 
						|
		poll.CloseFunc(d)
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if err = netfd.init(); err != nil {
 | 
						|
		fd.Close()
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	lsa, _ := syscall.Getsockname(netfd.pfd.Sysfd)
 | 
						|
	netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
 | 
						|
	return netfd, nil
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) dup() (f *os.File, err error) {
 | 
						|
	ns, call, err := fd.pfd.Dup()
 | 
						|
	if err != nil {
 | 
						|
		if call != "" {
 | 
						|
			err = os.NewSyscallError(call, err)
 | 
						|
		}
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return os.NewFile(uintptr(ns), fd.name()), nil
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) SetDeadline(t time.Time) error {
 | 
						|
	return fd.pfd.SetDeadline(t)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) SetReadDeadline(t time.Time) error {
 | 
						|
	return fd.pfd.SetReadDeadline(t)
 | 
						|
}
 | 
						|
 | 
						|
func (fd *netFD) SetWriteDeadline(t time.Time) error {
 | 
						|
	return fd.pfd.SetWriteDeadline(t)
 | 
						|
}
 |