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.
14 // Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
17 // Plan 9 uses IPv6 natively, see ip(3).
18 func (p *ipStackCapabilities) probe() {
19 p.ipv4Enabled = probe(netdir+"/iproute", "4i")
20 p.ipv6Enabled = probe(netdir+"/iproute", "6i")
21 if p.ipv4Enabled && p.ipv6Enabled {
22 p.ipv4MappedIPv6Enabled = true
26 func probe(filename, query string) bool {
29 if file, err = open(filename); err != nil {
35 for line, ok := file.readLine(); ok && !r; line, ok = file.readLine() {
40 for i := 0; i < len(f); i++ {
50 // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
51 func parsePlan9Addr(s string) (ip IP, iport int, err error) {
52 addr := IPv4zero // address contains port only
53 i := bytealg.IndexByteString(s, '!')
57 return nil, 0, &ParseError{Type: "IP address", Text: s}
60 p, _, ok := dtoi(s[i+1:])
62 return nil, 0, &ParseError{Type: "port", Text: s}
64 if p < 0 || p > 0xFFFF {
65 return nil, 0, &AddrError{Err: "invalid port", Addr: string(p)}
70 func readPlan9Addr(proto, filename string) (addr Addr, err error) {
73 f, err := os.Open(filename)
78 n, err := f.Read(buf[:])
82 ip, port, err := parsePlan9Addr(string(buf[:n]))
88 addr = &TCPAddr{IP: ip, Port: port}
90 addr = &UDPAddr{IP: ip, Port: port}
92 return nil, UnknownNetworkError(proto)
97 func startPlan9(ctx context.Context, net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) {
102 switch a := addr.(type) {
112 err = UnknownNetworkError(net)
117 err = InvalidAddrError("port should be < 65536")
121 clone, dest, err := queryCS1(ctx, proto, ip, port)
125 f, err := os.OpenFile(clone, os.O_RDWR, 0)
130 n, err := f.Read(buf[:])
135 return f, dest, proto, string(buf[:n]), nil
138 func fixErr(err error) {
139 oe, ok := err.(*OpError)
143 nonNilInterface := func(a Addr) bool {
144 switch a := a.(type) {
155 if nonNilInterface(oe.Source) {
158 if nonNilInterface(oe.Addr) {
161 if pe, ok := oe.Err.(*os.PathError); ok {
162 if _, ok = pe.Err.(syscall.ErrorString); ok {
168 func dialPlan9(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) {
169 defer func() { fixErr(err) }()
174 resc := make(chan res)
176 testHookDialChannel()
177 fd, err := dialPlan9Blocking(ctx, net, laddr, raddr)
179 case resc <- res{fd, err}:
188 return res.fd, res.err
190 return nil, mapErr(ctx.Err())
194 func dialPlan9Blocking(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) {
195 if isWildcard(raddr) {
196 raddr = toLocal(raddr, net)
198 f, dest, proto, name, err := startPlan9(ctx, net, raddr)
202 _, err = f.WriteString("connect " + dest)
207 data, err := os.OpenFile(netdir+"/"+proto+"/"+name+"/data", os.O_RDWR, 0)
212 laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local")
218 return newFD(proto, name, nil, f, data, laddr, raddr)
221 func listenPlan9(ctx context.Context, net string, laddr Addr) (fd *netFD, err error) {
222 defer func() { fixErr(err) }()
223 f, dest, proto, name, err := startPlan9(ctx, net, laddr)
227 _, err = f.WriteString("announce " + dest)
232 laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local")
237 return newFD(proto, name, nil, f, nil, laddr, nil)
240 func (fd *netFD) netFD() (*netFD, error) {
241 return newFD(fd.net, fd.n, fd.listen, fd.ctl, fd.data, fd.laddr, fd.raddr)
244 func (fd *netFD) acceptPlan9() (nfd *netFD, err error) {
245 defer func() { fixErr(err) }()
246 if err := fd.pfd.ReadLock(); err != nil {
249 defer fd.pfd.ReadUnlock()
250 listen, err := os.Open(fd.dir + "/listen")
255 n, err := listen.Read(buf[:])
260 name := string(buf[:n])
261 ctl, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/ctl", os.O_RDWR, 0)
266 data, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/data", os.O_RDWR, 0)
272 raddr, err := readPlan9Addr(fd.net, netdir+"/"+fd.net+"/"+name+"/remote")
279 return newFD(fd.net, name, listen, ctl, data, fd.laddr, raddr)
282 func isWildcard(a Addr) bool {
284 switch a := a.(type) {
286 wildcard = a.isWildcard()
288 wildcard = a.isWildcard()
290 wildcard = a.isWildcard()
295 func toLocal(a Addr, net string) Addr {
296 switch a := a.(type) {
298 a.IP = loopbackIP(net)
300 a.IP = loopbackIP(net)
302 a.IP = loopbackIP(net)