xjasonlyu / tun2socks

tun2socks - powered by gVisor TCP/IP stack
https://github.com/xjasonlyu/tun2socks/wiki
GNU General Public License v3.0
2.85k stars 405 forks source link

[Bug] TCP relay is incorrect #218

Closed nange closed 1 year ago

nange commented 1 year ago

Verify steps

Version

latest

What OS are you seeing the problem on?

Linux

Description

We should exec TCPConn.CloseWrite() instead of SetReadDeadline when io.CopyBuffer() completed.

CLI or Config

tun2socks --device tun://tun0 --proxy direct:// -interface wlp0s20f3

Logs

No response

How to Reproduce

The source of client:

package main

import (
    "fmt"
    "net"
    "os"
)

func main() {
    if len(os.Args) <= 1 {
        fmt.Println("please enter some greeting message")
        return
    }

    conn, err := net.Dial("tcp", "your-ip:port")
    if err != nil {
        fmt.Println("dial:", err.Error())
        return
    }
    defer conn.Close()

    if _, err := conn.Write([]byte(os.Args[1])); err != nil {
        fmt.Println("write greeting message to remote:", err.Error())
        return
    }

    if err := conn.(*net.TCPConn).CloseWrite(); err != nil {
        fmt.Println("close write:", err.Error())
        return
    }

    ret := make([]byte, 1024)
    nr, err := conn.Read(ret)
    if err != nil {
        fmt.Println("read from remote:", err.Error())
        if nr > 0 {
            fmt.Println("read from remote:", string(ret[:nr]))
        }
        return
    }
    fmt.Println("read from remote:", string(ret[:nr]))
}

The source of remote:

package main

import (
    "fmt"
    "net"
    "os"
    "time"
)

func main() {
    if len(os.Args) <= 1 {
        fmt.Println("please set server port using os.Args[1]")
        return
    }
    lis, err := net.Listen("tcp", ":"+os.Args[1])
    if err != nil {
        fmt.Println("listen:", err.Error())
        return
    }
    fmt.Println("server listen on :" + os.Args[1])
    for {
        conn, err := lis.Accept()
        if err != nil {
            fmt.Println("accept:", err.Error())
            continue
        }
        go handleConn(conn)
    }
}

func handleConn(conn net.Conn) {
    defer conn.Close()

    for {
        b := make([]byte, 1024)
        nr, err := conn.Read(b)
        if err != nil {
            fmt.Println("read:", err.Error())
            if nr > 0 {
                fmt.Println("read err, bug got:", b[:nr])
            }
            return
        }
        fmt.Println("read:", string(b[:nr]))
        time.Sleep(10 * time.Second)
        if _, err := conn.Write(b[:nr]); err != nil {
            fmt.Println("write to remote:", err.Error())
        }
    }
}

Steps

  1. Compile client and remote source code
  2. Run remote bin on remote server
  3. Run tun2socks on local server
  4. Set proper ip route on local server
  5. Run client bin on local server. (eg: ./test hello)

What did you expect to see?

On local server:

read from remote: hello

What did you see instead?

read from remote: EOF
nange commented 1 year ago

I'll create a PR soon.

heiher commented 1 year ago

Nice tests for shutting down part of full-duplex connection. It also helps me to check if another socks5 tunnel is working well.