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  	"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  		fd, err := dialPlan9Blocking(ctx, net, laddr, raddr)
   185  		select {
   186  		case resc <- res{fd, err}:
   187  		case <-ctx.Done():
   188  			if fd != nil {
   189  				fd.Close()
   190  			}
   191  		}
   192  	}()
   193  	select {
   194  	case res := <-resc:
   195  		return res.fd, res.err
   196  	case <-ctx.Done():
   197  		return nil, mapErr(ctx.Err())
   198  	}
   199  }
   200  
   201  func dialPlan9Blocking(ctx context.Context, net string, laddr, raddr Addr) (fd *netFD, err error) {
   202  	if isWildcard(raddr) {
   203  		raddr = toLocal(raddr, net)
   204  	}
   205  	f, dest, proto, name, err := startPlan9(ctx, net, raddr)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	if la := plan9LocalAddr(laddr); la == "" {
   210  		err = hangupCtlWrite(ctx, proto, f, "connect "+dest)
   211  	} else {
   212  		err = hangupCtlWrite(ctx, proto, f, "connect "+dest+" "+la)
   213  	}
   214  	if err != nil {
   215  		f.Close()
   216  		return nil, err
   217  	}
   218  	data, err := os.OpenFile(netdir+"/"+proto+"/"+name+"/data", os.O_RDWR, 0)
   219  	if err != nil {
   220  		f.Close()
   221  		return nil, err
   222  	}
   223  	laddr, err = readPlan9Addr(net, netdir+"/"+proto+"/"+name+"/local")
   224  	if err != nil {
   225  		data.Close()
   226  		f.Close()
   227  		return nil, err
   228  	}
   229  	return newFD(proto, name, nil, f, data, laddr, raddr)
   230  }
   231  
   232  func listenPlan9(ctx context.Context, net string, laddr Addr) (fd *netFD, err error) {
   233  	defer func() { fixErr(err) }()
   234  	f, dest, proto, name, err := startPlan9(ctx, net, laddr)
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  	_, err = f.WriteString("announce " + dest)
   239  	if err != nil {
   240  		f.Close()
   241  		return nil, &OpError{Op: "announce", Net: net, Source: laddr, Addr: nil, Err: err}
   242  	}
   243  	laddr, err = readPlan9Addr(net, netdir+"/"+proto+"/"+name+"/local")
   244  	if err != nil {
   245  		f.Close()
   246  		return nil, err
   247  	}
   248  	return newFD(proto, name, nil, f, nil, laddr, nil)
   249  }
   250  
   251  func (fd *netFD) netFD() (*netFD, error) {
   252  	return newFD(fd.net, fd.n, fd.listen, fd.ctl, fd.data, fd.laddr, fd.raddr)
   253  }
   254  
   255  func (fd *netFD) acceptPlan9() (nfd *netFD, err error) {
   256  	defer func() { fixErr(err) }()
   257  	if err := fd.pfd.ReadLock(); err != nil {
   258  		return nil, err
   259  	}
   260  	defer fd.pfd.ReadUnlock()
   261  	listen, err := os.Open(fd.dir + "/listen")
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  	var buf [16]byte
   266  	n, err := listen.Read(buf[:])
   267  	if err != nil {
   268  		listen.Close()
   269  		return nil, err
   270  	}
   271  	name := string(buf[:n])
   272  	ctl, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/ctl", os.O_RDWR, 0)
   273  	if err != nil {
   274  		listen.Close()
   275  		return nil, err
   276  	}
   277  	data, err := os.OpenFile(netdir+"/"+fd.net+"/"+name+"/data", os.O_RDWR, 0)
   278  	if err != nil {
   279  		listen.Close()
   280  		ctl.Close()
   281  		return nil, err
   282  	}
   283  	raddr, err := readPlan9Addr(fd.net, netdir+"/"+fd.net+"/"+name+"/remote")
   284  	if err != nil {
   285  		listen.Close()
   286  		ctl.Close()
   287  		data.Close()
   288  		return nil, err
   289  	}
   290  	return newFD(fd.net, name, listen, ctl, data, fd.laddr, raddr)
   291  }
   292  
   293  func isWildcard(a Addr) bool {
   294  	var wildcard bool
   295  	switch a := a.(type) {
   296  	case *TCPAddr:
   297  		wildcard = a.isWildcard()
   298  	case *UDPAddr:
   299  		wildcard = a.isWildcard()
   300  	case *IPAddr:
   301  		wildcard = a.isWildcard()
   302  	}
   303  	return wildcard
   304  }
   305  
   306  func toLocal(a Addr, net string) Addr {
   307  	switch a := a.(type) {
   308  	case *TCPAddr:
   309  		a.IP = loopbackIP(net)
   310  	case *UDPAddr:
   311  		a.IP = loopbackIP(net)
   312  	case *IPAddr:
   313  		a.IP = loopbackIP(net)
   314  	}
   315  	return a
   316  }
   317  
   318  // plan9LocalAddr returns a Plan 9 local address string.
   319  // See setladdrport at https://9p.io/sources/plan9/sys/src/9/ip/devip.c.
   320  func plan9LocalAddr(addr Addr) string {
   321  	var ip IP
   322  	port := 0
   323  	switch a := addr.(type) {
   324  	case *TCPAddr:
   325  		if a != nil {
   326  			ip = a.IP
   327  			port = a.Port
   328  		}
   329  	case *UDPAddr:
   330  		if a != nil {
   331  			ip = a.IP
   332  			port = a.Port
   333  		}
   334  	}
   335  	if len(ip) == 0 || ip.IsUnspecified() {
   336  		if port == 0 {
   337  			return ""
   338  		}
   339  		return itoa.Itoa(port)
   340  	}
   341  	return ip.String() + "!" + itoa.Itoa(port)
   342  }
   343  
   344  func hangupCtlWrite(ctx context.Context, proto string, ctl *os.File, msg string) error {
   345  	if proto != "tcp" {
   346  		_, err := ctl.WriteString(msg)
   347  		return err
   348  	}
   349  	written := make(chan struct{})
   350  	errc := make(chan error)
   351  	go func() {
   352  		select {
   353  		case <-ctx.Done():
   354  			ctl.WriteString("hangup")
   355  			errc <- mapErr(ctx.Err())
   356  		case <-written:
   357  			errc <- nil
   358  		}
   359  	}()
   360  	_, err := ctl.WriteString(msg)
   361  	close(written)
   362  	if e := <-errc; err == nil && e != nil { // we hung up
   363  		return e
   364  	}
   365  	return err
   366  }
   367  

View as plain text