fd_unix.mx raw
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) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa syscall.Sockaddr, ret error) {
45 // Do not need to call fd.writeLock here,
46 // because fd is not yet accessible to user,
47 // so no concurrent operations are possible.
48 switch err := connectFunc(fd.pfd.Sysfd, ra); err {
49 case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
50 case nil, syscall.EISCONN:
51 select {
52 case <-ctx.Done():
53 return nil, mapErr(ctx.Err())
54 default:
55 }
56 if err := fd.pfd.Init(fd.net, true); err != nil {
57 return nil, err
58 }
59 runtime.KeepAlive(fd)
60 return nil, nil
61 case syscall.EINVAL:
62 // On Solaris and illumos we can see EINVAL if the socket has
63 // already been accepted and closed by the server. Treat this
64 // as a successful connection--writes to the socket will see
65 // EOF. For details and a test case in C see
66 // https://golang.org/issue/6828.
67 if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" {
68 return nil, nil
69 }
70 return nil, os.NewSyscallError("connect", err)
71 default:
72 return nil, os.NewSyscallError("connect", err)
73 }
74 if err := fd.pfd.Init(fd.net, true); err != nil {
75 return nil, err
76 }
77 if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
78 fd.pfd.SetWriteDeadline(deadline)
79 defer fd.pfd.SetWriteDeadline(noDeadline)
80 }
81
82 // Moxie: no goroutines. Check context before connecting.
83 if ctx.Err() != nil {
84 return nil, mapErr(ctx.Err())
85 }
86
87 for {
88 // Performing multiple connect system calls on a
89 // non-blocking socket under Unix variants does not
90 // necessarily result in earlier errors being
91 // returned. Instead, once runtime-integrated network
92 // poller tells us that the socket is ready, get the
93 // SO_ERROR socket option to see if the connection
94 // succeeded or failed. See issue 7474 for further
95 // details.
96 if err := fd.pfd.WaitWrite(); err != nil {
97 select {
98 case <-ctx.Done():
99 return nil, mapErr(ctx.Err())
100 default:
101 }
102 return nil, err
103 }
104 nerr, err := getsockoptIntFunc(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
105 if err != nil {
106 return nil, os.NewSyscallError("getsockopt", err)
107 }
108 switch err := syscall.Errno(nerr); err {
109 case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
110 case syscall.EISCONN:
111 return nil, nil
112 case syscall.Errno(0):
113 // The runtime poller can wake us up spuriously;
114 // see issues 14548 and 19289. Check that we are
115 // really connected; if not, wait again.
116 if rsa, err := syscall.Getpeername(fd.pfd.Sysfd); err == nil {
117 return rsa, nil
118 }
119 default:
120 return nil, os.NewSyscallError("connect", err)
121 }
122 runtime.KeepAlive(fd)
123 }
124 }
125
126 func (fd *netFD) accept() (netfd *netFD, err error) {
127 d, rsa, errcall, err := fd.pfd.Accept()
128 if err != nil {
129 if errcall != "" {
130 err = wrapSyscallError(errcall, err)
131 }
132 return nil, err
133 }
134
135 if netfd, err = newFD(d, fd.family, fd.sotype, fd.net); err != nil {
136 poll.CloseFunc(d)
137 return nil, err
138 }
139 if err = netfd.init(); err != nil {
140 netfd.Close()
141 return nil, err
142 }
143 lsa, _ := syscall.Getsockname(netfd.pfd.Sysfd)
144 netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
145 return netfd, nil
146 }
147
148 // Defined in os package.
149 func newUnixFile(fd int, name string) *os.File
150
151 func (fd *netFD) dup() (f *os.File, err error) {
152 ns, call, err := fd.pfd.Dup()
153 if err != nil {
154 if call != "" {
155 err = os.NewSyscallError(call, err)
156 }
157 return nil, err
158 }
159
160 return newUnixFile(ns, fd.name()), nil
161 }
162