Source file src/net/ipsock_plan9.go

     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 := make(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 := make(chan struct{})
   352  	errc := make(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  

View as plain text