// 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 || 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 }