Home | History | Annotate | Download | only in net
      1 // Copyright 2011 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 	"errors"
      9 	"io"
     10 	"os"
     11 	"syscall"
     12 )
     13 
     14 func (fd *netFD) status(ln int) (string, error) {
     15 	if !fd.ok() {
     16 		return "", syscall.EINVAL
     17 	}
     18 
     19 	status, err := os.Open(fd.dir + "/status")
     20 	if err != nil {
     21 		return "", err
     22 	}
     23 	defer status.Close()
     24 	buf := make([]byte, ln)
     25 	n, err := io.ReadFull(status, buf[:])
     26 	if err != nil {
     27 		return "", err
     28 	}
     29 	return string(buf[:n]), nil
     30 }
     31 
     32 func newFileFD(f *os.File) (net *netFD, err error) {
     33 	var ctl *os.File
     34 	close := func(fd int) {
     35 		if err != nil {
     36 			syscall.Close(fd)
     37 		}
     38 	}
     39 
     40 	path, err := syscall.Fd2path(int(f.Fd()))
     41 	if err != nil {
     42 		return nil, os.NewSyscallError("fd2path", err)
     43 	}
     44 	comp := splitAtBytes(path, "/")
     45 	n := len(comp)
     46 	if n < 3 || comp[0][0:3] != "net" {
     47 		return nil, syscall.EPLAN9
     48 	}
     49 
     50 	name := comp[2]
     51 	switch file := comp[n-1]; file {
     52 	case "ctl", "clone":
     53 		syscall.ForkLock.RLock()
     54 		fd, err := syscall.Dup(int(f.Fd()), -1)
     55 		syscall.ForkLock.RUnlock()
     56 		if err != nil {
     57 			return nil, os.NewSyscallError("dup", err)
     58 		}
     59 		defer close(fd)
     60 
     61 		dir := netdir + "/" + comp[n-2]
     62 		ctl = os.NewFile(uintptr(fd), dir+"/"+file)
     63 		ctl.Seek(0, 0)
     64 		var buf [16]byte
     65 		n, err := ctl.Read(buf[:])
     66 		if err != nil {
     67 			return nil, err
     68 		}
     69 		name = string(buf[:n])
     70 	default:
     71 		if len(comp) < 4 {
     72 			return nil, errors.New("could not find control file for connection")
     73 		}
     74 		dir := netdir + "/" + comp[1] + "/" + name
     75 		ctl, err = os.OpenFile(dir+"/ctl", os.O_RDWR, 0)
     76 		if err != nil {
     77 			return nil, err
     78 		}
     79 		defer close(int(ctl.Fd()))
     80 	}
     81 	dir := netdir + "/" + comp[1] + "/" + name
     82 	laddr, err := readPlan9Addr(comp[1], dir+"/local")
     83 	if err != nil {
     84 		return nil, err
     85 	}
     86 	return newFD(comp[1], name, ctl, nil, laddr, nil)
     87 }
     88 
     89 func fileConn(f *os.File) (Conn, error) {
     90 	fd, err := newFileFD(f)
     91 	if err != nil {
     92 		return nil, err
     93 	}
     94 	if !fd.ok() {
     95 		return nil, syscall.EINVAL
     96 	}
     97 
     98 	fd.data, err = os.OpenFile(fd.dir+"/data", os.O_RDWR, 0)
     99 	if err != nil {
    100 		return nil, err
    101 	}
    102 
    103 	switch fd.laddr.(type) {
    104 	case *TCPAddr:
    105 		return newTCPConn(fd), nil
    106 	case *UDPAddr:
    107 		return newUDPConn(fd), nil
    108 	}
    109 	return nil, syscall.EPLAN9
    110 }
    111 
    112 func fileListener(f *os.File) (Listener, error) {
    113 	fd, err := newFileFD(f)
    114 	if err != nil {
    115 		return nil, err
    116 	}
    117 	switch fd.laddr.(type) {
    118 	case *TCPAddr:
    119 	default:
    120 		return nil, syscall.EPLAN9
    121 	}
    122 
    123 	// check that file corresponds to a listener
    124 	s, err := fd.status(len("Listen"))
    125 	if err != nil {
    126 		return nil, err
    127 	}
    128 	if s != "Listen" {
    129 		return nil, errors.New("file does not represent a listener")
    130 	}
    131 
    132 	return &TCPListener{fd}, nil
    133 }
    134 
    135 func filePacketConn(f *os.File) (PacketConn, error) {
    136 	return nil, syscall.EPLAN9
    137 }
    138