|  | // Copyright 2009 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package net | 
|  |  | 
|  | import ( | 
|  | "context" | 
|  | "os" | 
|  | "syscall" | 
|  | ) | 
|  |  | 
|  | // Probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication | 
|  | // capabilities. | 
|  | // | 
|  | // Plan 9 uses IPv6 natively, see ip(3). | 
|  | func (p *ipStackCapabilities) probe() { | 
|  | p.ipv4Enabled = probe(netdir+"/iproute", "4i") | 
|  | p.ipv6Enabled = probe(netdir+"/iproute", "6i") | 
|  | if p.ipv4Enabled && p.ipv6Enabled { | 
|  | p.ipv4MappedIPv6Enabled = true | 
|  | } | 
|  | } | 
|  |  | 
|  | func probe(filename, query string) bool { | 
|  | var file *file | 
|  | var err error | 
|  | if file, err = open(filename); err != nil { | 
|  | return false | 
|  | } | 
|  | defer file.close() | 
|  |  | 
|  | r := false | 
|  | for line, ok := file.readLine(); ok && !r; line, ok = file.readLine() { | 
|  | f := getFields(line) | 
|  | if len(f) < 3 { | 
|  | continue | 
|  | } | 
|  | for i := 0; i < len(f); i++ { | 
|  | if query == f[i] { | 
|  | r = true | 
|  | break | 
|  | } | 
|  | } | 
|  | } | 
|  | return r | 
|  | } | 
|  |  | 
|  | // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80). | 
|  | func parsePlan9Addr(s string) (ip IP, iport int, err error) { | 
|  | addr := IPv4zero // address contains port only | 
|  | i := byteIndex(s, '!') | 
|  | if i >= 0 { | 
|  | addr = ParseIP(s[:i]) | 
|  | if addr == nil { | 
|  | return nil, 0, &ParseError{Type: "IP address", Text: s} | 
|  | } | 
|  | } | 
|  | p, _, ok := dtoi(s[i+1:]) | 
|  | if !ok { | 
|  | return nil, 0, &ParseError{Type: "port", Text: s} | 
|  | } | 
|  | if p < 0 || p > 0xFFFF { | 
|  | return nil, 0, &AddrError{Err: "invalid port", Addr: string(p)} | 
|  | } | 
|  | return addr, p, nil | 
|  | } | 
|  |  | 
|  | func readPlan9Addr(proto, filename string) (addr Addr, err error) { | 
|  | var buf [128]byte | 
|  |  | 
|  | f, err := os.Open(filename) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | defer f.Close() | 
|  | n, err := f.Read(buf[:]) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | ip, port, err := parsePlan9Addr(string(buf[:n])) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | switch proto { | 
|  | case "tcp": | 
|  | addr = &TCPAddr{IP: ip, Port: port} | 
|  | case "udp": | 
|  | addr = &UDPAddr{IP: ip, Port: port} | 
|  | default: | 
|  | return nil, UnknownNetworkError(proto) | 
|  | } | 
|  | return addr, nil | 
|  | } | 
|  |  | 
|  | func startPlan9(ctx context.Context, net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) { | 
|  | var ( | 
|  | ip   IP | 
|  | port int | 
|  | ) | 
|  | switch a := addr.(type) { | 
|  | case *TCPAddr: | 
|  | proto = "tcp" | 
|  | ip = a.IP | 
|  | port = a.Port | 
|  | case *UDPAddr: | 
|  | proto = "udp" | 
|  | ip = a.IP | 
|  | port = a.Port | 
|  | default: | 
|  | err = UnknownNetworkError(net) | 
|  | return | 
|  | } | 
|  |  | 
|  | if port > 65535 { | 
|  | err = InvalidAddrError("port should be < 65536") | 
|  | return | 
|  | } | 
|  |  | 
|  | clone, dest, err := queryCS1(ctx, proto, ip, port) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | f, err := os.OpenFile(clone, os.O_RDWR, 0) | 
|  | if err != nil { | 
|  | return | 
|  | } | 
|  | var buf [16]byte | 
|  | n, err := f.Read(buf[:]) | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return | 
|  | } | 
|  | return f, dest, proto, string(buf[:n]), nil | 
|  | } | 
|  |  | 
|  | func fixErr(err error) { | 
|  | oe, ok := err.(*OpError) | 
|  | if !ok { | 
|  | return | 
|  | } | 
|  | nonNilInterface := func(a Addr) bool { | 
|  | switch a := a.(type) { | 
|  | case *TCPAddr: | 
|  | return a == nil | 
|  | case *UDPAddr: | 
|  | return a == nil | 
|  | case *IPAddr: | 
|  | return a == nil | 
|  | default: | 
|  | return false | 
|  | } | 
|  | } | 
|  | if nonNilInterface(oe.Source) { | 
|  | oe.Source = nil | 
|  | } | 
|  | if nonNilInterface(oe.Addr) { | 
|  | oe.Addr = nil | 
|  | } | 
|  | if pe, ok := oe.Err.(*os.PathError); ok { | 
|  | if _, ok = pe.Err.(syscall.ErrorString); ok { | 
|  | oe.Err = pe.Err | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func dialPlan9(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) { | 
|  | defer func() { fixErr(err) }() | 
|  | type res struct { | 
|  | fd  *netFD | 
|  | err error | 
|  | } | 
|  | resc := make(chan res) | 
|  | go func() { | 
|  | testHookDialChannel() | 
|  | fd, err := dialPlan9Blocking(ctx, net, laddr, raddr) | 
|  | select { | 
|  | case resc <- res{fd, err}: | 
|  | case <-ctx.Done(): | 
|  | if fd != nil { | 
|  | fd.Close() | 
|  | } | 
|  | } | 
|  | }() | 
|  | select { | 
|  | case res := <-resc: | 
|  | return res.fd, res.err | 
|  | case <-ctx.Done(): | 
|  | return nil, mapErr(ctx.Err()) | 
|  | } | 
|  | } | 
|  |  | 
|  | func dialPlan9Blocking(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) { | 
|  | if isWildcard(raddr) { | 
|  | raddr = toLocal(raddr, net) | 
|  | } | 
|  | f, dest, proto, name, err := startPlan9(ctx, net, raddr) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | _, err = f.WriteString("connect " + dest) | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return nil, err | 
|  | } | 
|  | data, err := os.OpenFile(netdir+"/"+proto+"/"+name+"/data", os.O_RDWR, 0) | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return nil, err | 
|  | } | 
|  | laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local") | 
|  | if err != nil { | 
|  | data.Close() | 
|  | f.Close() | 
|  | return nil, err | 
|  | } | 
|  | return newFD(proto, name, nil, f, data, laddr, raddr) | 
|  | } | 
|  |  | 
|  | func listenPlan9(ctx context.Context, net string, laddr Addr) (fd *netFD, err error) { | 
|  | defer func() { fixErr(err) }() | 
|  | f, dest, proto, name, err := startPlan9(ctx, net, laddr) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | _, err = f.WriteString("announce " + dest) | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return nil, err | 
|  | } | 
|  | laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local") | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return nil, err | 
|  | } | 
|  | return newFD(proto, name, nil, f, nil, laddr, nil) | 
|  | } | 
|  |  | 
|  | func (fd *netFD) netFD() (*netFD, error) { | 
|  | return newFD(fd.net, fd.n, fd.listen, fd.ctl, fd.data, fd.laddr, fd.raddr) | 
|  | } | 
|  |  | 
|  | func (fd *netFD) acceptPlan9() (nfd *netFD, err error) { | 
|  | defer func() { fixErr(err) }() | 
|  | if err := fd.pfd.ReadLock(); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | defer fd.pfd.ReadUnlock() | 
|  | listen, err := os.Open(fd.dir + "/listen") | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | var buf [16]byte | 
|  | n, err := listen.Read(buf[:]) | 
|  | if err != nil { | 
|  | listen.Close() | 
|  | return nil, err | 
|  | } | 
|  | name := string(buf[:n]) | 
|  | ctl, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/ctl", os.O_RDWR, 0) | 
|  | if err != nil { | 
|  | listen.Close() | 
|  | return nil, err | 
|  | } | 
|  | data, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/data", os.O_RDWR, 0) | 
|  | if err != nil { | 
|  | listen.Close() | 
|  | ctl.Close() | 
|  | return nil, err | 
|  | } | 
|  | raddr, err := readPlan9Addr(fd.net, netdir+"/"+fd.net+"/"+name+"/remote") | 
|  | if err != nil { | 
|  | listen.Close() | 
|  | ctl.Close() | 
|  | data.Close() | 
|  | return nil, err | 
|  | } | 
|  | return newFD(fd.net, name, listen, ctl, data, fd.laddr, raddr) | 
|  | } | 
|  |  | 
|  | func isWildcard(a Addr) bool { | 
|  | var wildcard bool | 
|  | switch a := a.(type) { | 
|  | case *TCPAddr: | 
|  | wildcard = a.isWildcard() | 
|  | case *UDPAddr: | 
|  | wildcard = a.isWildcard() | 
|  | case *IPAddr: | 
|  | wildcard = a.isWildcard() | 
|  | } | 
|  | return wildcard | 
|  | } | 
|  |  | 
|  | func toLocal(a Addr, net string) Addr { | 
|  | switch a := a.(type) { | 
|  | case *TCPAddr: | 
|  | a.IP = loopbackIP(net) | 
|  | case *UDPAddr: | 
|  | a.IP = loopbackIP(net) | 
|  | case *IPAddr: | 
|  | a.IP = loopbackIP(net) | 
|  | } | 
|  | return a | 
|  | } |