...
Run Format

Source file src/net/ipsock_plan9.go

Documentation: net

  // 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
  }
  

View as plain text