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