123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- // 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.
- //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
- package net
- import (
- "context"
- "internal/poll"
- "os"
- "runtime"
- "syscall"
- )
- 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
- }
- func (fd *netFD) init() error {
- return fd.pfd.Init(fd.net, true)
- }
- 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 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, nil
- }
- fallthrough
- default:
- return nil, os.NewSyscallError("connect", err)
- }
- if err := fd.pfd.Init(fd.net, true); err != nil {
- return nil, err
- }
- 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.
- ret = mapErr(ctxErr)
- fd.Close() // prevent a leak
- }
- }()
- go func() {
- select {
- case <-ctxDone:
- // 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 <-ctxDone:
- 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) 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 {
- netfd.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
- }
|