|  | // 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. | 
|  |  | 
|  | // Internet protocol family sockets for Plan 9 | 
|  |  | 
|  | package net | 
|  |  | 
|  | import ( | 
|  | "errors" | 
|  | "os" | 
|  | "syscall" | 
|  | ) | 
|  |  | 
|  | func probe(filename, query string) bool { | 
|  | var file *file | 
|  | var err error | 
|  | if file, err = open(filename); err != nil { | 
|  | return false | 
|  | } | 
|  |  | 
|  | 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 | 
|  | } | 
|  | } | 
|  | } | 
|  | file.close() | 
|  | return r | 
|  | } | 
|  |  | 
|  | func probeIPv4Stack() bool { | 
|  | return probe(netdir+"/iproute", "4i") | 
|  | } | 
|  |  | 
|  | // probeIPv6Stack returns two boolean values.  If the first boolean | 
|  | // value is true, kernel supports basic IPv6 functionality.  If the | 
|  | // second boolean value is true, kernel supports IPv6 IPv4-mapping. | 
|  | func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { | 
|  | // Plan 9 uses IPv6 natively, see ip(3). | 
|  | r := probe(netdir+"/iproute", "6i") | 
|  | v := false | 
|  | if r { | 
|  | v = probe(netdir+"/iproute", "4i") | 
|  | } | 
|  | return r, v | 
|  | } | 
|  |  | 
|  | // 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, errors.New("parsing IP failed") | 
|  | } | 
|  | } | 
|  | p, _, ok := dtoi(s[i+1:], 0) | 
|  | if !ok { | 
|  | return nil, 0, errors.New("parsing port failed") | 
|  | } | 
|  | if p < 0 || p > 0xFFFF { | 
|  | return nil, 0, &AddrError{"invalid port", 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, errors.New("unknown protocol " + proto) | 
|  | } | 
|  | return addr, nil | 
|  | } | 
|  |  | 
|  | func startPlan9(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 | 
|  | } | 
|  |  | 
|  | clone, dest, err := queryCS1(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 netErr(e error) { | 
|  | oe, ok := e.(*OpError) | 
|  | if !ok { | 
|  | return | 
|  | } | 
|  | if pe, ok := oe.Err.(*os.PathError); ok { | 
|  | if _, ok = pe.Err.(syscall.ErrorString); ok { | 
|  | oe.Err = pe.Err | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func dialPlan9(net string, laddr, raddr Addr) (fd *netFD, err error) { | 
|  | defer func() { netErr(err) }() | 
|  | f, dest, proto, name, err := startPlan9(net, raddr) | 
|  | if err != nil { | 
|  | return nil, &OpError{"dial", net, raddr, err} | 
|  | } | 
|  | _, err = f.WriteString("connect " + dest) | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return nil, &OpError{"dial", f.Name(), raddr, err} | 
|  | } | 
|  | data, err := os.OpenFile(netdir+"/"+proto+"/"+name+"/data", os.O_RDWR, 0) | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return nil, &OpError{"dial", net, raddr, err} | 
|  | } | 
|  | laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local") | 
|  | if err != nil { | 
|  | data.Close() | 
|  | f.Close() | 
|  | return nil, &OpError{"dial", proto, raddr, err} | 
|  | } | 
|  | return newFD(proto, name, f, data, laddr, raddr) | 
|  | } | 
|  |  | 
|  | func listenPlan9(net string, laddr Addr) (fd *netFD, err error) { | 
|  | defer func() { netErr(err) }() | 
|  | f, dest, proto, name, err := startPlan9(net, laddr) | 
|  | if err != nil { | 
|  | return nil, &OpError{"listen", net, laddr, err} | 
|  | } | 
|  | _, err = f.WriteString("announce " + dest) | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return nil, &OpError{"announce", proto, laddr, err} | 
|  | } | 
|  | laddr, err = readPlan9Addr(proto, netdir+"/"+proto+"/"+name+"/local") | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return nil, &OpError{Op: "listen", Net: net, Err: err} | 
|  | } | 
|  | return newFD(proto, name, f, nil, laddr, nil) | 
|  | } | 
|  |  | 
|  | func (l *netFD) netFD() (*netFD, error) { | 
|  | return newFD(l.proto, l.n, l.ctl, l.data, l.laddr, l.raddr) | 
|  | } | 
|  |  | 
|  | func (l *netFD) acceptPlan9() (fd *netFD, err error) { | 
|  | defer func() { netErr(err) }() | 
|  | if err := l.readLock(); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | defer l.readUnlock() | 
|  | f, err := os.Open(l.dir + "/listen") | 
|  | if err != nil { | 
|  | return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err} | 
|  | } | 
|  | var buf [16]byte | 
|  | n, err := f.Read(buf[:]) | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return nil, &OpError{"accept", l.dir + "/listen", l.laddr, err} | 
|  | } | 
|  | name := string(buf[:n]) | 
|  | data, err := os.OpenFile(netdir+"/"+l.proto+"/"+name+"/data", os.O_RDWR, 0) | 
|  | if err != nil { | 
|  | f.Close() | 
|  | return nil, &OpError{"accept", l.proto, l.laddr, err} | 
|  | } | 
|  | raddr, err := readPlan9Addr(l.proto, netdir+"/"+l.proto+"/"+name+"/remote") | 
|  | if err != nil { | 
|  | data.Close() | 
|  | f.Close() | 
|  | return nil, &OpError{"accept", l.proto, l.laddr, err} | 
|  | } | 
|  | return newFD(l.proto, name, f, data, l.laddr, raddr) | 
|  | } |