Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/ipsock_plan9.go

Documentation: net

     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  	"syscall"
    14  )
    15  
    16  // probe probes IPv4, IPv6 and IPv4-mapped IPv6 communication
    17  // capabilities.
    18  //
    19  // Plan 9 uses IPv6 natively, see ip(3).
    20  func (p *ipStackCapabilities) probe() {
    21  	p.ipv4Enabled = probe(netdir+"/iproute", "4i")
    22  	p.ipv6Enabled = probe(netdir+"/iproute", "6i")
    23  	if p.ipv4Enabled && p.ipv6Enabled {
    24  		p.ipv4MappedIPv6Enabled = true
    25  	}
    26  }
    27  
    28  func probe(filename, query string) bool {
    29  	var file *file
    30  	var err error
    31  	if file, err = open(filename); err != nil {
    32  		return false
    33  	}
    34  	defer file.close()
    35  
    36  	r := false
    37  	for line, ok := file.readLine(); ok && !r; line, ok = file.readLine() {
    38  		f := getFields(line)
    39  		if len(f) < 3 {
    40  			continue
    41  		}
    42  		for i := 0; i < len(f); i++ {
    43  			if query == f[i] {
    44  				r = true
    45  				break
    46  			}
    47  		}
    48  	}
    49  	return r
    50  }
    51  
    52  // parsePlan9Addr parses address of the form [ip!]port (e.g. 127.0.0.1!80).
    53  func parsePlan9Addr(s string) (ip IP, iport int, err error) {
    54  	addr := IPv4zero // address contains port only
    55  	i := bytealg.IndexByteString(s, '!')
    56  	if i >= 0 {
    57  		addr = ParseIP(s[:i])
    58  		if addr == nil {
    59  			return nil, 0, &ParseError{Type: "IP address", Text: s}
    60  		}
    61  	}
    62  	p, plen, ok := dtoi(s[i+1:])
    63  	if !ok {
    64  		return nil, 0, &ParseError{Type: "port", Text: s}
    65  	}
    66  	if p < 0 || p > 0xFFFF {
    67  		return nil, 0, &AddrError{Err: "invalid port", Addr: s[i+1 : i+1+plen]}
    68  	}
    69  	return addr, p, nil
    70  }
    71  
    72  func readPlan9Addr(net, filename string) (addr Addr, err error) {
    73  	var buf [128]byte
    74  
    75  	f, err := os.Open(filename)
    76  	if err != nil {
    77  		return
    78  	}
    79  	defer f.Close()
    80  	n, err := f.Read(buf[:])
    81  	if err != nil {
    82  		return
    83  	}
    84  	ip, port, err := parsePlan9Addr(string(buf[:n]))
    85  	if err != nil {
    86  		return
    87  	}
    88  	switch net {
    89  	case "tcp4", "udp4":
    90  		if ip.Equal(IPv6zero) {
    91  			ip = ip[:IPv4len]
    92  		}
    93  	}
    94  	switch net {
    95  	case "tcp", "tcp4", "tcp6":
    96  		addr = &TCPAddr{IP: ip, Port: port}
    97  	case "udp", "udp4", "udp6":
    98  		addr = &UDPAddr{IP: ip, Port: port}
    99  	default:
   100  		return nil, UnknownNetworkError(net)
   101  	}
   102  	return addr, nil
   103  }
   104  
   105  func startPlan9(ctx context.Context, net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) {
   106  	var (
   107  		ip   IP
   108  		port int
   109  	)
   110  	switch a := addr.(type) {
   111  	case *TCPAddr:
   112  		proto = "tcp"
   113  		ip = a.IP
   114  		port = a.Port
   115  	case *UDPAddr:
   116  		proto = "udp"
   117  		ip = a.IP
   118  		port = a.Port
   119  	default:
   120  		err = UnknownNetworkError(net)
   121  		return
   122  	}
   123  
   124  	if port > 65535 {
   125  		err = InvalidAddrError("port should be < 65536")
   126  		return
   127  	}
   128  
   129  	clone, dest, err := queryCS1(ctx, proto, ip, port)
   130  	if err != nil {
   131  		return
   132  	}
   133  	f, err := os.OpenFile(clone, os.O_RDWR, 0)
   134  	if err != nil {
   135  		return
   136  	}
   137  	var buf [16]byte
   138  	n, err := f.Read(buf[:])
   139  	if err != nil {
   140  		f.Close()
   141  		return
   142  	}
   143  	return f, dest, proto, string(buf[:n]), nil
   144  }
   145  
   146  func fixErr(err error) {
   147  	oe, ok := err.(*OpError)
   148  	if !ok {
   149  		return
   150  	}
   151  	nonNilInterface := func(a Addr) bool {
   152  		switch a := a.(type) {
   153  		case *TCPAddr:
   154  			return a == nil
   155  		case *UDPAddr:
   156  			return a == nil
   157  		case *IPAddr:
   158  			return a == nil
   159  		default:
   160  			return false
   161  		}
   162  	}
   163  	if nonNilInterface(oe.Source) {
   164  		oe.Source = nil
   165  	}
   166  	if nonNilInterface(oe.Addr) {
   167  		oe.Addr = nil
   168  	}
   169  	if pe, ok := oe.Err.(*fs.PathError); ok {
   170  		if _, ok = pe.Err.(syscall.ErrorString); ok {
   171  			oe.Err = pe.Err
   172  		}
   173  	}
   174  }
   175  
   176  func dialPlan9(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) {
   177  	defer func() { fixErr(err) }()
   178  	type res struct {
   179  		fd  *netFD
   180  		err error
   181  	}
   182  	resc := make(chan res)
   183  	go func() {
   184  		testHookDialChannel()
   185  		fd, err := dialPlan9Blocking(ctx, net, laddr, raddr)
   186  		select {
   187  		case resc <- res{fd, err}:
   188  		case <-ctx.Done():
   189  			if fd != nil {
   190  				fd.Close()
   191  			}
   192  		}
   193  	}()
   194  	select {
   195  	case res := <-resc:
   196  		return res.fd, res.err
   197  	case <-ctx.Done():
   198  		return nil, mapErr(ctx.Err())
   199  	}
   200  }
   201  
   202  func dialPlan9Blocking(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) {
   203  	if isWildcard(raddr) {
   204  		raddr = toLocal(raddr, net)
   205  	}
   206  	f, dest, proto, name, err := startPlan9(ctx, net, raddr)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	if la := plan9LocalAddr(laddr); la == "" {
   211  		err = hangupCtlWrite(ctx, proto, f, "connect "+dest)
   212  	} else {
   213  		err = hangupCtlWrite(ctx, proto, f, "connect "+dest+" "+la)
   214  	}
   215  	if err != nil {
   216  		f.Close()
   217  		return nil, err
   218  	}
   219  	data, err := os.OpenFile(netdir+"/"+proto+"/"+name+"/data", os.O_RDWR, 0)
   220  	if err != nil {
   221  		f.Close()
   222  		return nil, err
   223  	}
   224  	laddr, err = readPlan9Addr(net, netdir+"/"+proto+"/"+name+"/local")
   225  	if err != nil {
   226  		data.Close()
   227  		f.Close()
   228  		return nil, err
   229  	}
   230  	return newFD(proto, name, nil, f, data, laddr, raddr)
   231  }
   232  
   233  func listenPlan9(ctx context.Context, net string, laddr Addr) (fd *netFD, err error) {
   234  	defer func() { fixErr(err) }()
   235  	f, dest, proto, name, err := startPlan9(ctx, net, laddr)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  	_, err = f.WriteString("announce " + dest)
   240  	if err != nil {
   241  		f.Close()
   242  		return nil, &OpError{Op: "announce", Net: net, Source: laddr, Addr: nil, Err: err}
   243  	}
   244  	laddr, err = readPlan9Addr(net, netdir+"/"+proto+"/"+name+"/local")
   245  	if err != nil {
   246  		f.Close()
   247  		return nil, err
   248  	}
   249  	return newFD(proto, name, nil, f, nil, laddr, nil)
   250  }
   251  
   252  func (fd *netFD) netFD() (*netFD, error) {
   253  	return newFD(fd.net, fd.n, fd.listen, fd.ctl, fd.data, fd.laddr, fd.raddr)
   254  }
   255  
   256  func (fd *netFD) acceptPlan9() (nfd *netFD, err error) {
   257  	defer func() { fixErr(err) }()
   258  	if err := fd.pfd.ReadLock(); err != nil {
   259  		return nil, err
   260  	}
   261  	defer fd.pfd.ReadUnlock()
   262  	listen, err := os.Open(fd.dir + "/listen")
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	var buf [16]byte
   267  	n, err := listen.Read(buf[:])
   268  	if err != nil {
   269  		listen.Close()
   270  		return nil, err
   271  	}
   272  	name := string(buf[:n])
   273  	ctl, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/ctl", os.O_RDWR, 0)
   274  	if err != nil {
   275  		listen.Close()
   276  		return nil, err
   277  	}
   278  	data, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/data", os.O_RDWR, 0)
   279  	if err != nil {
   280  		listen.Close()
   281  		ctl.Close()
   282  		return nil, err
   283  	}
   284  	raddr, err := readPlan9Addr(fd.net, netdir+"/"+fd.net+"/"+name+"/remote")
   285  	if err != nil {
   286  		listen.Close()
   287  		ctl.Close()
   288  		data.Close()
   289  		return nil, err
   290  	}
   291  	return newFD(fd.net, name, listen, ctl, data, fd.laddr, raddr)
   292  }
   293  
   294  func isWildcard(a Addr) bool {
   295  	var wildcard bool
   296  	switch a := a.(type) {
   297  	case *TCPAddr:
   298  		wildcard = a.isWildcard()
   299  	case *UDPAddr:
   300  		wildcard = a.isWildcard()
   301  	case *IPAddr:
   302  		wildcard = a.isWildcard()
   303  	}
   304  	return wildcard
   305  }
   306  
   307  func toLocal(a Addr, net string) Addr {
   308  	switch a := a.(type) {
   309  	case *TCPAddr:
   310  		a.IP = loopbackIP(net)
   311  	case *UDPAddr:
   312  		a.IP = loopbackIP(net)
   313  	case *IPAddr:
   314  		a.IP = loopbackIP(net)
   315  	}
   316  	return a
   317  }
   318  
   319  // plan9LocalAddr returns a Plan 9 local address string.
   320  // See setladdrport at https://9p.io/sources/plan9/sys/src/9/ip/devip.c.
   321  func plan9LocalAddr(addr Addr) string {
   322  	var ip IP
   323  	port := 0
   324  	switch a := addr.(type) {
   325  	case *TCPAddr:
   326  		if a != nil {
   327  			ip = a.IP
   328  			port = a.Port
   329  		}
   330  	case *UDPAddr:
   331  		if a != nil {
   332  			ip = a.IP
   333  			port = a.Port
   334  		}
   335  	}
   336  	if len(ip) == 0 || ip.IsUnspecified() {
   337  		if port == 0 {
   338  			return ""
   339  		}
   340  		return itoa.Itoa(port)
   341  	}
   342  	return ip.String() + "!" + itoa.Itoa(port)
   343  }
   344  
   345  func hangupCtlWrite(ctx context.Context, proto string, ctl *os.File, msg string) error {
   346  	if proto != "tcp" {
   347  		_, err := ctl.WriteString(msg)
   348  		return err
   349  	}
   350  	written := make(chan struct{})
   351  	errc := make(chan error)
   352  	go func() {
   353  		select {
   354  		case <-ctx.Done():
   355  			ctl.WriteString("hangup")
   356  			errc <- mapErr(ctx.Err())
   357  		case <-written:
   358  			errc <- nil
   359  		}
   360  	}()
   361  	_, err := ctl.WriteString(msg)
   362  	close(written)
   363  	if e := <-errc; err == nil && e != nil { // we hung up
   364  		return e
   365  	}
   366  	return err
   367  }
   368  

View as plain text