package socket import ( "errors" "fmt" "golang.org/x/sys/unix" "log" "os" "strings" "sync" "tunnel/pkg/pack" "tunnel/pkg/server/env" "tunnel/pkg/server/queue" "unsafe" ) const maxTunBufSize = 65535 var errPartialWrite = errors.New("partial write") type ifReq struct { name [unix.IFNAMSIZ]uint8 flags uint16 } type tunSocket struct { name string } type tunConn struct { name string s *tunSocket fp *os.File once sync.Once } func newTunSocket(name string) (S, error) { return &tunSocket{name: name}, nil } func (s *tunSocket) String() string { return fmt.Sprintf("tun/%s", s.name) } func (s *tunSocket) Single() {} func (s *tunSocket) Open(env.Env) (Conn, error) { fd, err := unix.Open("/dev/net/tun", unix.O_RDWR, 0) if err != nil { return nil, err } ifr := &ifReq{} copy(ifr.name[:], s.name) ifr.flags = unix.IFF_TUN | unix.IFF_NO_PI if err := ioctl(fd, unix.TUNSETIFF, unsafe.Pointer(ifr)); err != nil { unix.Close(fd) return nil, fmt.Errorf("ioctl TUNSETIFF %s: %w", s.name, err) } if err := unix.SetNonblock(fd, true); err != nil { unix.Close(fd) return nil, fmt.Errorf("set nonblock %s: %w", s.name, err) } c := &tunConn{ name: strings.Trim(string(ifr.name[:]), "\x00"), fp: os.NewFile(uintptr(fd), "tun"), } return c, nil } func (s *tunSocket) Close() { } func (c *tunConn) Send(wq queue.Q) error { buf := make([]byte, maxTunBufSize) enc := pack.NewEncoder(wq.Writer()) for { n, err := c.fp.Read(buf) if err != nil { return err } enc.Lps(buf[0:n]) } } func (c *tunConn) Recv(rq queue.Q) error { dec := pack.NewDecoder(rq.Reader()) for { b, err := dec.Lps() if err != nil { return err } n, err := c.fp.Write(b) if err != nil { return err } if n != len(b) { return errPartialWrite } } } func (c *tunConn) String() string { return "tun/" + c.name } func (c *tunConn) Close() error { err := ErrAlreadyClosed c.once.Do(func() { log.Println("close", c) err = c.fp.Close() }) return err }