| // 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 unix | |
| 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 { | |
| return &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, | |
| } | |
| } | |
| func (fd *netFD) init() error { | |
| return fd.pfd.Init(fd.net, true) | |
| } | |
| 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 | |
| } | |
| ctxDone := ctx.Done() | |
| if ctxDone != nil { | |
| if deadline, hasDeadline := ctx.Deadline(); hasDeadline { | |
| fd.pfd.SetWriteDeadline(deadline) | |
| defer fd.pfd.SetWriteDeadline(noDeadline) | |
| } | |
| // Load the hook function synchronously to prevent a race | |
| // with test code that restores the old value. | |
| testHookCanceledDial := testHookCanceledDial | |
| stop := context.AfterFunc(ctx, func() { | |
| // Force the runtime's poller to immediately give up | |
| // waiting for writability, unblocking waitWrite | |
| // below. | |
| _ = fd.pfd.SetWriteDeadline(aLongTimeAgo) | |
| testHookCanceledDial() | |
| }) | |
| defer func() { | |
| if !stop() && ret == nil { | |
| // The context.AfterFunc has called or is about to call | |
| // 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(ctx.Err()) | |
| // The caller closes fd on error, so there's no need to | |
| // wait for the SetWriteDeadline call to return. | |
| } | |
| }() | |
| } | |
| 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 | |
| } | |
| netfd = newFD(d, fd.family, fd.sotype, fd.net) | |
| 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 | |
| } | |
| // Defined in os package. | |
| func newUnixFile(fd int, name string) *os.File | |
| 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 newUnixFile(ns, fd.name()), nil | |
| } |