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