ipsock_plan9.mx raw

   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  
   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  // probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
  18  // capabilities.
  19  //
  20  // Plan 9 uses IPv6 natively, see ip(3).
  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  // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
  54  func parsePlan9Addr(s string) (ip IP, iport int, err error) {
  55  	addr := IPv4zero // address contains port only
  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 := 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  // plan9LocalAddr returns a Plan 9 local address string.
 321  // See setladdrport at https://9p.io/sources/plan9/sys/src/9/ip/devip.c.
 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 := chan struct{}{}
 352  	errc := 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 { // we hung up
 365  		return e
 366  	}
 367  	return err
 368  }
 369