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

View as plain text