github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/net/fd_unix.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 6 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris 7 8 package net 9 10 import ( 11 "context" 12 "internal/poll" 13 "os" 14 "runtime" 15 "syscall" 16 ) 17 18 const ( 19 readSyscallName = "read" 20 readFromSyscallName = "recvfrom" 21 readMsgSyscallName = "recvmsg" 22 writeSyscallName = "write" 23 writeToSyscallName = "sendto" 24 writeMsgSyscallName = "sendmsg" 25 ) 26 27 func newFD(sysfd, family, sotype int, net string) (*netFD, error) { 28 ret := &netFD{ 29 pfd: poll.FD{ 30 Sysfd: sysfd, 31 IsStream: sotype == syscall.SOCK_STREAM, 32 ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW, 33 }, 34 family: family, 35 sotype: sotype, 36 net: net, 37 } 38 return ret, nil 39 } 40 41 func (fd *netFD) init() error { 42 return fd.pfd.Init(fd.net, true) 43 } 44 45 func (fd *netFD) name() string { 46 var ls, rs string 47 if fd.laddr != nil { 48 ls = fd.laddr.String() 49 } 50 if fd.raddr != nil { 51 rs = fd.raddr.String() 52 } 53 return fd.net + ":" + ls + "->" + rs 54 } 55 56 func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa syscall.Sockaddr, ret error) { 57 // Do not need to call fd.writeLock here, 58 // because fd is not yet accessible to user, 59 // so no concurrent operations are possible. 60 switch err := connectFunc(fd.pfd.Sysfd, ra); err { 61 case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR: 62 case nil, syscall.EISCONN: 63 select { 64 case <-ctx.Done(): 65 return nil, mapErr(ctx.Err()) 66 default: 67 } 68 if err := fd.pfd.Init(fd.net, true); err != nil { 69 return nil, err 70 } 71 runtime.KeepAlive(fd) 72 return nil, nil 73 case syscall.EINVAL: 74 // On Solaris and illumos we can see EINVAL if the socket has 75 // already been accepted and closed by the server. Treat this 76 // as a successful connection--writes to the socket will see 77 // EOF. For details and a test case in C see 78 // https://golang.org/issue/6828. 79 if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" { 80 return nil, nil 81 } 82 fallthrough 83 default: 84 return nil, os.NewSyscallError("connect", err) 85 } 86 if err := fd.pfd.Init(fd.net, true); err != nil { 87 return nil, err 88 } 89 if deadline, hasDeadline := ctx.Deadline(); hasDeadline { 90 fd.pfd.SetWriteDeadline(deadline) 91 defer fd.pfd.SetWriteDeadline(noDeadline) 92 } 93 94 // Start the "interrupter" goroutine, if this context might be canceled. 95 // (The background context cannot) 96 // 97 // The interrupter goroutine waits for the context to be done and 98 // interrupts the dial (by altering the fd's write deadline, which 99 // wakes up waitWrite). 100 if ctx != context.Background() { 101 // Wait for the interrupter goroutine to exit before returning 102 // from connect. 103 done := make(chan struct{}) 104 interruptRes := make(chan error) 105 defer func() { 106 close(done) 107 if ctxErr := <-interruptRes; ctxErr != nil && ret == nil { 108 // The interrupter goroutine called SetWriteDeadline, 109 // but the connect code below had returned from 110 // waitWrite already and did a successful connect (ret 111 // == nil). Because we've now poisoned the connection 112 // by making it unwritable, don't return a successful 113 // dial. This was issue 16523. 114 ret = mapErr(ctxErr) 115 fd.Close() // prevent a leak 116 } 117 }() 118 go func() { 119 select { 120 case <-ctx.Done(): 121 // Force the runtime's poller to immediately give up 122 // waiting for writability, unblocking waitWrite 123 // below. 124 fd.pfd.SetWriteDeadline(aLongTimeAgo) 125 testHookCanceledDial() 126 interruptRes <- ctx.Err() 127 case <-done: 128 interruptRes <- nil 129 } 130 }() 131 } 132 133 for { 134 // Performing multiple connect system calls on a 135 // non-blocking socket under Unix variants does not 136 // necessarily result in earlier errors being 137 // returned. Instead, once runtime-integrated network 138 // poller tells us that the socket is ready, get the 139 // SO_ERROR socket option to see if the connection 140 // succeeded or failed. See issue 7474 for further 141 // details. 142 if err := fd.pfd.WaitWrite(); err != nil { 143 select { 144 case <-ctx.Done(): 145 return nil, mapErr(ctx.Err()) 146 default: 147 } 148 return nil, err 149 } 150 nerr, err := getsockoptIntFunc(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) 151 if err != nil { 152 return nil, os.NewSyscallError("getsockopt", err) 153 } 154 switch err := syscall.Errno(nerr); err { 155 case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR: 156 case syscall.EISCONN: 157 return nil, nil 158 case syscall.Errno(0): 159 // The runtime poller can wake us up spuriously; 160 // see issues 14548 and 19289. Check that we are 161 // really connected; if not, wait again. 162 if rsa, err := syscall.Getpeername(fd.pfd.Sysfd); err == nil { 163 return rsa, nil 164 } 165 default: 166 return nil, os.NewSyscallError("connect", err) 167 } 168 runtime.KeepAlive(fd) 169 } 170 } 171 172 func (fd *netFD) accept() (netfd *netFD, err error) { 173 d, rsa, errcall, err := fd.pfd.Accept() 174 if err != nil { 175 if errcall != "" { 176 err = wrapSyscallError(errcall, err) 177 } 178 return nil, err 179 } 180 181 if netfd, err = newFD(d, fd.family, fd.sotype, fd.net); err != nil { 182 poll.CloseFunc(d) 183 return nil, err 184 } 185 if err = netfd.init(); err != nil { 186 netfd.Close() 187 return nil, err 188 } 189 lsa, _ := syscall.Getsockname(netfd.pfd.Sysfd) 190 netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa)) 191 return netfd, nil 192 } 193 194 func (fd *netFD) dup() (f *os.File, err error) { 195 ns, call, err := fd.pfd.Dup() 196 if err != nil { 197 if call != "" { 198 err = os.NewSyscallError(call, err) 199 } 200 return nil, err 201 } 202 203 return os.NewFile(uintptr(ns), fd.name()), nil 204 }