]> Cypherpunks.ru repositories - gostls13.git/blob - src/net/fd_unix.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / src / net / fd_unix.go
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
7 package net
8
9 import (
10         "context"
11         "internal/poll"
12         "os"
13         "runtime"
14         "syscall"
15 )
16
17 const (
18         readSyscallName     = "read"
19         readFromSyscallName = "recvfrom"
20         readMsgSyscallName  = "recvmsg"
21         writeSyscallName    = "write"
22         writeToSyscallName  = "sendto"
23         writeMsgSyscallName = "sendmsg"
24 )
25
26 func 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
40 func (fd *netFD) init() error {
41         return fd.pfd.Init(fd.net, true)
42 }
43
44 func (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
55 func (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
171 func (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.
194 func newUnixFile(fd int, name string) *os.File
195
196 func (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 }