Source file
src/net/ipsock_plan9.go
1
2
3
4
5 package net
6
7 import (
8 "context"
9 "internal/bytealg"
10 "internal/itoa"
11 "io/fs"
12 "os"
13 "strconv"
14 "syscall"
15 )
16
17
18
19
20
21 func (p *ipStackCapabilities) probe() {
22 p.ipv4Enabled = probe(netdir+"/iproute", "4i")
23 p.ipv6Enabled = probe(netdir+"/iproute", "6i")
24 if p.ipv4Enabled && p.ipv6Enabled {
25 p.ipv4MappedIPv6Enabled = true
26 }
27 }
28
29 func probe(filename, query string) bool {
30 var file *file
31 var err error
32 if file, err = open(filename); err != nil {
33 return false
34 }
35 defer file.close()
36
37 r := false
38 for line, ok := file.readLine(); ok && !r; line, ok = file.readLine() {
39 f := getFields(line)
40 if len(f) < 3 {
41 continue
42 }
43 for i := 0; i < len(f); i++ {
44 if query == f[i] {
45 r = true
46 break
47 }
48 }
49 }
50 return r
51 }
52
53
54 func parsePlan9Addr(s string) (ip IP, iport int, err error) {
55 addr := IPv4zero
56 i := bytealg.IndexByteString(s, '!')
57 if i >= 0 {
58 addr = ParseIP(s[:i])
59 if addr == nil {
60 return nil, 0, &ParseError{Type: "IP address", Text: s}
61 }
62 }
63 p, plen, ok := dtoi(s[i+1:])
64 if !ok {
65 return nil, 0, &ParseError{Type: "port", Text: s}
66 }
67 if p < 0 || p > 0xFFFF {
68 return nil, 0, &AddrError{Err: "invalid port", Addr: s[i+1 : i+1+plen]}
69 }
70 return addr, p, nil
71 }
72
73 func readPlan9Addr(net, filename string) (addr Addr, err error) {
74 var buf [128]byte
75
76 f, err := os.Open(filename)
77 if err != nil {
78 return
79 }
80 defer f.Close()
81 n, err := f.Read(buf[:])
82 if err != nil {
83 return
84 }
85 ip, port, err := parsePlan9Addr(string(buf[:n]))
86 if err != nil {
87 return
88 }
89 switch net {
90 case "tcp4", "udp4":
91 if ip.Equal(IPv6zero) {
92 ip = ip[:IPv4len]
93 }
94 }
95 switch net {
96 case "tcp", "tcp4", "tcp6":
97 addr = &TCPAddr{IP: ip, Port: port}
98 case "udp", "udp4", "udp6":
99 addr = &UDPAddr{IP: ip, Port: port}
100 default:
101 return nil, UnknownNetworkError(net)
102 }
103 return addr, nil
104 }
105
106 func startPlan9(ctx context.Context, net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) {
107 var (
108 ip IP
109 port int
110 )
111 switch a := addr.(type) {
112 case *TCPAddr:
113 proto = "tcp"
114 ip = a.IP
115 port = a.Port
116 case *UDPAddr:
117 proto = "udp"
118 ip = a.IP
119 port = a.Port
120 default:
121 err = UnknownNetworkError(net)
122 return
123 }
124
125 if port > 65535 {
126 err = InvalidAddrError("port should be < 65536")
127 return
128 }
129
130 clone, dest, err := queryCS1(ctx, proto, ip, port)
131 if err != nil {
132 err = handlePlan9DNSError(err, net+":"+ip.String()+":"+strconv.Itoa(port))
133 return
134 }
135 f, err := os.OpenFile(clone, os.O_RDWR, 0)
136 if err != nil {
137 return
138 }
139 var buf [16]byte
140 n, err := f.Read(buf[:])
141 if err != nil {
142 f.Close()
143 return
144 }
145 return f, dest, proto, string(buf[:n]), nil
146 }
147
148 func fixErr(err error) {
149 oe, ok := err.(*OpError)
150 if !ok {
151 return
152 }
153 nonNilInterface := func(a Addr) bool {
154 switch a := a.(type) {
155 case *TCPAddr:
156 return a == nil
157 case *UDPAddr:
158 return a == nil
159 case *IPAddr:
160 return a == nil
161 default:
162 return false
163 }
164 }
165 if nonNilInterface(oe.Source) {
166 oe.Source = nil
167 }
168 if nonNilInterface(oe.Addr) {
169 oe.Addr = nil
170 }
171 if pe, ok := oe.Err.(*fs.PathError); ok {
172 if _, ok = pe.Err.(syscall.ErrorString); ok {
173 oe.Err = pe.Err
174 }
175 }
176 }
177
178 func dialPlan9(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) {
179 defer func() { fixErr(err) }()
180 type res struct {
181 fd *netFD
182 err error
183 }
184 resc := make(chan res)
185 go func() {
186 fd, err := dialPlan9Blocking(ctx, net, laddr, raddr)
187 select {
188 case resc <- res{fd, err}:
189 case <-ctx.Done():
190 if fd != nil {
191 fd.Close()
192 }
193 }
194 }()
195 select {
196 case res := <-resc:
197 return res.fd, res.err
198 case <-ctx.Done():
199 return nil, mapErr(ctx.Err())
200 }
201 }
202
203 func dialPlan9Blocking(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) {
204 if isWildcard(raddr) {
205 raddr = toLocal(raddr, net)
206 }
207 f, dest, proto, name, err := startPlan9(ctx, net, raddr)
208 if err != nil {
209 return nil, err
210 }
211 if la := plan9LocalAddr(laddr); la == "" {
212 err = hangupCtlWrite(ctx, proto, f, "connect "+dest)
213 } else {
214 err = hangupCtlWrite(ctx, proto, f, "connect "+dest+" "+la)
215 }
216 if err != nil {
217 f.Close()
218 return nil, err
219 }
220 data, err := os.OpenFile(netdir+"/"+proto+"/"+name+"/data", os.O_RDWR, 0)
221 if err != nil {
222 f.Close()
223 return nil, err
224 }
225 laddr, err = readPlan9Addr(net, netdir+"/"+proto+"/"+name+"/local")
226 if err != nil {
227 data.Close()
228 f.Close()
229 return nil, err
230 }
231 return newFD(proto, name, nil, f, data, laddr, raddr)
232 }
233
234 func listenPlan9(ctx context.Context, net string, laddr Addr) (fd *netFD, err error) {
235 defer func() { fixErr(err) }()
236 f, dest, proto, name, err := startPlan9(ctx, net, laddr)
237 if err != nil {
238 return nil, err
239 }
240 _, err = f.WriteString("announce " + dest)
241 if err != nil {
242 f.Close()
243 return nil, &OpError{Op: "announce", Net: net, Source: laddr, Addr: nil, Err: err}
244 }
245 laddr, err = readPlan9Addr(net, netdir+"/"+proto+"/"+name+"/local")
246 if err != nil {
247 f.Close()
248 return nil, err
249 }
250 return newFD(proto, name, nil, f, nil, laddr, nil)
251 }
252
253 func (fd *netFD) netFD() (*netFD, error) {
254 return newFD(fd.net, fd.n, fd.listen, fd.ctl, fd.data, fd.laddr, fd.raddr)
255 }
256
257 func (fd *netFD) acceptPlan9() (nfd *netFD, err error) {
258 defer func() { fixErr(err) }()
259 if err := fd.pfd.ReadLock(); err != nil {
260 return nil, err
261 }
262 defer fd.pfd.ReadUnlock()
263 listen, err := os.Open(fd.dir + "/listen")
264 if err != nil {
265 return nil, err
266 }
267 var buf [16]byte
268 n, err := listen.Read(buf[:])
269 if err != nil {
270 listen.Close()
271 return nil, err
272 }
273 name := string(buf[:n])
274 ctl, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/ctl", os.O_RDWR, 0)
275 if err != nil {
276 listen.Close()
277 return nil, err
278 }
279 data, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/data", os.O_RDWR, 0)
280 if err != nil {
281 listen.Close()
282 ctl.Close()
283 return nil, err
284 }
285 raddr, err := readPlan9Addr(fd.net, netdir+"/"+fd.net+"/"+name+"/remote")
286 if err != nil {
287 listen.Close()
288 ctl.Close()
289 data.Close()
290 return nil, err
291 }
292 return newFD(fd.net, name, listen, ctl, data, fd.laddr, raddr)
293 }
294
295 func isWildcard(a Addr) bool {
296 var wildcard bool
297 switch a := a.(type) {
298 case *TCPAddr:
299 wildcard = a.isWildcard()
300 case *UDPAddr:
301 wildcard = a.isWildcard()
302 case *IPAddr:
303 wildcard = a.isWildcard()
304 }
305 return wildcard
306 }
307
308 func toLocal(a Addr, net string) Addr {
309 switch a := a.(type) {
310 case *TCPAddr:
311 a.IP = loopbackIP(net)
312 case *UDPAddr:
313 a.IP = loopbackIP(net)
314 case *IPAddr:
315 a.IP = loopbackIP(net)
316 }
317 return a
318 }
319
320
321
322 func plan9LocalAddr(addr Addr) string {
323 var ip IP
324 port := 0
325 switch a := addr.(type) {
326 case *TCPAddr:
327 if a != nil {
328 ip = a.IP
329 port = a.Port
330 }
331 case *UDPAddr:
332 if a != nil {
333 ip = a.IP
334 port = a.Port
335 }
336 }
337 if len(ip) == 0 || ip.IsUnspecified() {
338 if port == 0 {
339 return ""
340 }
341 return itoa.Itoa(port)
342 }
343 return ip.String() + "!" + itoa.Itoa(port)
344 }
345
346 func hangupCtlWrite(ctx context.Context, proto string, ctl *os.File, msg string) error {
347 if proto != "tcp" {
348 _, err := ctl.WriteString(msg)
349 return err
350 }
351 written := make(chan struct{})
352 errc := make(chan error)
353 go func() {
354 select {
355 case <-ctx.Done():
356 ctl.WriteString("hangup")
357 errc <- mapErr(ctx.Err())
358 case <-written:
359 errc <- nil
360 }
361 }()
362 _, err := ctl.WriteString(msg)
363 close(written)
364 if e := <-errc; err == nil && e != nil {
365 return e
366 }
367 return err
368 }
369
View as plain text