diff --git a/go.mod b/go.mod index 449c837..06aed90 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/nknorg/nconnect go 1.20 require ( + github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 github.com/eycorsican/go-tun2socks v1.16.11 github.com/gin-contrib/gzip v0.0.3 github.com/gin-gonic/gin v1.9.0 @@ -16,6 +17,7 @@ require ( github.com/nknorg/nkngomobile v0.0.0-20220615081414-671ad1afdfa9 github.com/nknorg/tuna v0.0.0-20230405223335-eb60c60c5953 github.com/shadowsocks/go-shadowsocks2 v0.1.2 + github.com/stretchr/testify v1.8.1 github.com/txthinking/brook v0.0.0-20230418095906-76ced63f1803 github.com/txthinking/socks5 v0.0.0-20230307062227-0e1677eca4ba golang.org/x/net v0.8.0 @@ -27,6 +29,7 @@ require ( github.com/andybalholm/brotli v1.0.4 // indirect github.com/bytedance/sonic v1.8.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gaukas/godicttls v0.0.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect @@ -62,6 +65,7 @@ require ( github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/phuslu/iploc v1.0.20230201 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qtls-go1-18 v0.2.0 // indirect github.com/quic-go/qtls-go1-19 v0.2.0 // indirect github.com/quic-go/qtls-go1-20 v0.1.0 // indirect diff --git a/go.sum b/go.sum index 867bf88..28bf846 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA= github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 h1:BjkPE3785EwPhhyuFkbINB+2a1xATwk8SNDWnJiD41g= +github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5/go.mod h1:jtAfVaU/2cu1+wdSRPWE2c1N2qeAA3K4RH9pYgqwets= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= diff --git a/tests/client.json b/tests/client.json index 9880006..766d164 100644 --- a/tests/client.json +++ b/tests/client.json @@ -22,6 +22,7 @@ "tunaMinFee": "0.00001", "tunaFeeRatio": 0.1, "tunaGeoDBPath": ".", + "tunaDisableMeasureBandwidth": false, "tunaMeasureStoragePath": ".", "adminIdentifier": "nConnect", "webRootPath": "web/dist", diff --git a/tests/config.go b/tests/config.go index 8b96db4..8b4f00b 100644 --- a/tests/config.go +++ b/tests/config.go @@ -1,7 +1,6 @@ package tests var port int = 1080 -var proxyAddr string = "127.0.0.1:1080" const ( numMsgs = 10 diff --git a/tests/dns.go b/tests/dns.go index 017343b..39648ba 100644 --- a/tests/dns.go +++ b/tests/dns.go @@ -2,18 +2,18 @@ package tests import ( "fmt" - "time" "github.com/txthinking/brook" ) -func dnsQuery() { +func dnsQuery() error { + proxyAddr := fmt.Sprintf("127.0.0.1:%v", port) for i := 1; i <= numMsgs; i++ { err := brook.Socks5Test(proxyAddr, "", "", "http3.ooo", "137.184.237.95", "8.8.8.8:53") if err != nil { fmt.Printf("TestDNSProxy try %v err: %v\n", i, err) - time.Sleep(time.Duration(i) * time.Second) - break + return err } } + return nil } diff --git a/tests/main_test.go b/tests/main_test.go index 4f20c94..8da358a 100644 --- a/tests/main_test.go +++ b/tests/main_test.go @@ -1,17 +1,67 @@ package tests import ( + "flag" + "fmt" + "log" "os" "testing" "time" + + "github.com/nknorg/tuna/types" ) +var remoteTuna = flag.Bool("remoteTuna", false, "use remote tuna node") +var tun = flag.Bool("tun", false, "use tun device") + func TestMain(m *testing.M) { - go StartTcpServer() - go StartWebServer() - go StartUdpServer() + flag.Parse() + if *remoteTuna { + fmt.Println("We are using remote tuna node") + } else { + fmt.Println("Using local tuna node. If want to use remote tuna node, please run: go test -v -remoteTuna .") + } + + go func() { + err := StartTcpServer() + if err != nil { + log.Fatalf("StartTcpServer err %v", err) + return + } + }() + go func() { + err := StartWebServer() + if err != nil { + log.Fatalf("StartWebServer err %v", err) + return + } + }() + go func() { + err := StartUdpServer() + if err != nil { + log.Fatalf("StartUdpServer err %v", err) + return + } + }() + + var tunaNode *types.Node + var err error + if !(*remoteTuna) { + tunaNode, err = getTunaNode() + if err != nil { + log.Fatalf("getTunaNode err %v", err) + return + } + } + + go func() { + err := startNconnect("server.json", true, true, false, tunaNode) + if err != nil { + log.Fatalf("start nconnect server err: %v", err) + return + } + }() - go StartNconnectServerWithTunaNode(true, true, false) time.Sleep(15 * time.Second) exitVal := m.Run() diff --git a/tests/proxy_test.go b/tests/proxy_test.go index ce3b81c..76a1c54 100644 --- a/tests/proxy_test.go +++ b/tests/proxy_test.go @@ -1,9 +1,10 @@ package tests import ( - "fmt" "testing" "time" + + "github.com/stretchr/testify/require" ) // go test -v -run=TestProxy @@ -11,17 +12,22 @@ func TestProxy(t *testing.T) { tuna, udp, tun := true, true, false go func() { err := startNconnect("client.json", tuna, udp, tun, nil) - if err != nil { - fmt.Printf("start nconnect client err: %v\n", err) - return - } + require.NoError(t, err) }() - time.Sleep(15 * time.Second) - dnsQuery() + time.Sleep(5 * time.Second) + + err := waitSSAndTunaReady() + require.NoError(t, err) + + err = dnsQuery() + require.NoError(t, err) for _, server := range servers { - StartWebClient("http://" + server + httpPort + "/httpEcho") - StartTCPClient(server + tcpPort) - StartUDPClient(server + udpPort) + err := StartWebClient("http://" + server + httpPort + "/httpEcho") + require.NoError(t, err) + err = StartTCPClient(server + tcpPort) + require.NoError(t, err) + err = StartUDPClient(server + udpPort) + require.NoError(t, err) } } diff --git a/tests/pub.go b/tests/pub.go index dee889c..b305b56 100644 --- a/tests/pub.go +++ b/tests/pub.go @@ -5,8 +5,11 @@ import ( "encoding/json" "fmt" "log" + "net" "os" + "time" + "github.com/cakturk/go-netstat/netstat" "github.com/nknorg/nconnect" "github.com/nknorg/nconnect/config" nkn "github.com/nknorg/nkn-sdk-go" @@ -40,9 +43,11 @@ func startNconnect(configFile string, tuna, udp, tun bool, n *types.Node) error } if opts.Client { - proxyAddr = fmt.Sprintf("127.0.0.1:%v", port) - opts.LocalSocksAddr = proxyAddr - port++ + port, err := getFreePort(port) + if err != nil { + return err + } + opts.LocalSocksAddr = fmt.Sprintf("127.0.0.1:%v", port) } fmt.Printf("opts.RemoteAdminAddr: %+v\n", opts.RemoteAdminAddr) @@ -56,22 +61,6 @@ func startNconnect(configFile string, tuna, udp, tun bool, n *types.Node) error return err } -func StartNconnectServerWithTunaNode(tuna, udp, tun bool) { - tunaNode, err := getTunaNode() - if err != nil { - fmt.Printf("getTunaNode err %v\n", err) - return - } - - go func() { - err := startNconnect("server.json", tuna, udp, tun, tunaNode) - if err != nil { - fmt.Printf("start nconnect server err: %v\n", err) - return - } - }() -} - func getTunaNode() (*types.Node, error) { tunaSeed, _ := hex.DecodeString(seedHex) acc, err := nkn.NewAccount(tunaSeed) @@ -135,3 +124,59 @@ type Person struct { Name string Age int } + +func getFreePort(port int) (int, error) { + for i := 0; i < 100; i++ { + addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%v", port)) + if err != nil { + return 0, err + } + + l, err := net.ListenTCP("tcp", addr) + if err != nil { + port++ + continue + } + + defer l.Close() + + return l.Addr().(*net.TCPAddr).Port, nil + } + return 0, fmt.Errorf("can't find free port") +} + +func waitSSAndTunaReady() error { + ssIsReady := false + for i := 0; i < 100; i++ { + tabs, err := netstat.TCPSocks(func(s *netstat.SockTabEntry) bool { + return s.State == netstat.Listen && s.LocalAddr.Port == uint16(port) + }) + if err != nil { + fmt.Printf("waitSSAndTunaReady err: %v\n", err) + } + if len(tabs) >= 1 { + ssIsReady = true + break + } + time.Sleep(2 * time.Second) + } + + if !ssIsReady { + return fmt.Errorf("ss is not ready after 200 seconds, give up") + } + + for i := 0; i < 100; i++ { + tabs, err := netstat.TCPSocks(func(s *netstat.SockTabEntry) bool { + return s.State == netstat.Established && s.RemoteAddr.Port == 30020 + }) + if err != nil { + fmt.Printf("waitSSAndTunaReady err: %v\n", err) + } + time.Sleep(2 * time.Second) + if len(tabs) >= 1 { + return nil + } + } + + return fmt.Errorf("tuna is not connected after 200 seconds, give up") +} diff --git a/tests/server.json b/tests/server.json index abc7aff..90c436f 100644 --- a/tests/server.json +++ b/tests/server.json @@ -23,6 +23,7 @@ "tunaFeeRatio": 0.1, "tunaServiceName": "reverse", "tunaGeoDBPath": ".", + "tunaDisableMeasureBandwidth": false, "tunaMeasureStoragePath": ".", "adminIdentifier": "nConnect", "webRootPath": "web/dist", diff --git a/tests/tcp.go b/tests/tcp.go index fc357bc..5f39160 100644 --- a/tests/tcp.go +++ b/tests/tcp.go @@ -17,29 +17,32 @@ func StartTcpServer() error { fmt.Println("TCP Server is listening at ", tcpPort) for { - conn, err := tcpServer.Accept() + c, err := tcpServer.Accept() if err != nil { return err } - b := make([]byte, 1024) - for { - n, err := conn.Read(b) - if err != nil { - fmt.Printf("StartTcpServer, Read err %v\n", err) - break + go func(conn net.Conn) { + b := make([]byte, 1024) + for { + n, err := conn.Read(b) + if err != nil { + fmt.Printf("StartTcpServer, Read err %v\n", err) + break + } + fmt.Printf("TCP Server got: %v\n", string(b[:n])) + _, err = conn.Write(b[:n]) + if err != nil { + fmt.Printf("StartTcpServer, write err %v\n", err) + break + } } - fmt.Printf("TCP Server got: %v\n", string(b[:n])) - _, err = conn.Write(b[:n]) - if err != nil { - fmt.Printf("StartTcpServer, write err %v\n", err) - break - } - } + }(c) } } func StartTCPClient(serverAddr string) error { auth := proxy.Auth{User: "", Password: ""} + proxyAddr := fmt.Sprintf("127.0.0.1:%v", port) dailer, err := proxy.SOCKS5("tcp", proxyAddr, &auth, &net.Dialer{ Timeout: 60 * time.Second, KeepAlive: 30 * time.Second, @@ -54,7 +57,7 @@ func StartTCPClient(serverAddr string) error { fmt.Printf("StartTCPClient, dailer.Dial err: %v\n", err) return err } - fmt.Printf("StartTCPClient, dail to %v success\n", serverAddr) + fmt.Printf("StartTCPClient, dail to %v success\n", serverAddr) user := &Person{Name: "tcp_boy", Age: 0} for i := 0; i < numMsgs; i++ { @@ -63,25 +66,24 @@ func StartTCPClient(serverAddr string) error { _, err = conn.Write(b1) if err != nil { fmt.Printf("StartTCPClient, conn.Write err: %v\n", err) - break + return err } b2 := make([]byte, 1024) n, err := conn.Read(b2) if err != nil { fmt.Printf("StartTCPClient, conn.Read err: %v\n", err) - break + return err } respUser := &Person{} err = json.Unmarshal(b2[:n], respUser) if err != nil { fmt.Printf("StartTCPClient, json.Unmarshal err: %v\n", err) - break + return err } if respUser.Age != user.Age { - fmt.Printf("StartTCPClient, got wrong response, sent %+v, recv %+v\n", user, respUser) - break + return fmt.Errorf("StartTCPClient, got wrong response, sent %+v, recv %+v", user, respUser) } fmt.Printf("Got echo %+v\n", respUser) } @@ -103,25 +105,24 @@ func StartTCPTunClient(serverAddr string) error { _, err = conn.Write(b1) if err != nil { fmt.Printf("StartTCPClient, conn.Write err: %v\n", err) - break + return err } b2 := make([]byte, 1024) n, err := conn.Read(b2) if err != nil { fmt.Printf("StartTCPClient, conn.Read err: %v\n", err) - break + return err } respUser := &Person{} err = json.Unmarshal(b2[:n], respUser) if err != nil { fmt.Printf("StartTCPClient, json.Unmarshal err: %v\n", err) - break + return err } if respUser.Age != user.Age { - fmt.Printf("StartTCPClient, got wrong response, sent %+v, recv %+v\n", user, respUser) - break + return fmt.Errorf("StartTCPClient, got wrong response, sent %+v, recv %+v", user, respUser) } } diff --git a/tests/tun_test.go b/tests/tun_test.go index aab104d..c60b99a 100644 --- a/tests/tun_test.go +++ b/tests/tun_test.go @@ -4,24 +4,37 @@ import ( "fmt" "testing" "time" + + "github.com/stretchr/testify/require" ) -// go test -v -run=TestTun +// go test -v -run=TestTun -tun func TestTun(t *testing.T) { + fmt.Println("Make sure run this case at root or administrator shell") + + if !(*tun) { + t.Skip("Skip TestTun, if you want to run this test, please use: go test -v -tun .") + } + tuna, udp, tun := true, true, true go func() { err := startNconnect("client.json", tuna, udp, tun, nil) - if err != nil { - fmt.Printf("start nconnect client err: %v\n", err) - return - } + require.NoError(t, err) }() - time.Sleep(20 * time.Second) - dnsQuery() + time.Sleep(10 * time.Second) + + err := waitSSAndTunaReady() + require.NoError(t, err) + + err = dnsQuery() + require.NoError(t, err) for _, server := range servers { - StartTunWebClient("http://" + server + httpPort + "/httpEcho") - StartTCPClient(server + tcpPort) - StartUDPClient(server + udpPort) + err := StartTunWebClient("http://" + server + httpPort + "/httpEcho") + require.NoError(t, err) + err = StartTCPClient(server + tcpPort) + require.NoError(t, err) + err = StartUDPClient(server + udpPort) + require.NoError(t, err) } } diff --git a/tests/udp.go b/tests/udp.go index dadc449..842a455 100644 --- a/tests/udp.go +++ b/tests/udp.go @@ -27,21 +27,20 @@ func StartUdpServer() error { n, addr, err := udpServer.ReadFromUDP(b) if err != nil { fmt.Printf("StartUdpServer.ReadFromUDP err: %v\n", err) - break + return err } time.Sleep(100 * time.Millisecond) _, _, err = udpServer.WriteMsgUDP(b[:n], nil, addr) if err != nil { fmt.Printf("StartUdpServer.WriteMsgUDP err: %v\n", err) - break + return err } } - - return nil } func StartUDPClient(serverAddr string) error { + proxyAddr := fmt.Sprintf("127.0.0.1:%v", port) s5c, err := socks5.NewClient(proxyAddr, "", "", 0, 60) if err != nil { return err @@ -59,18 +58,17 @@ func StartUDPClient(serverAddr string) error { send, _ := json.Marshal(user) if _, err := uc.Write(send); err != nil { fmt.Println("StartUDPClient.Write err ", err) - break + return err } recv := make([]byte, 512) n, err := uc.Read(recv) if err != nil { fmt.Println("StartUDPClient.Read err ", err) - break + return err } if !bytes.Equal(recv[:n], send) { - fmt.Printf("StartUDPClient.recv %v is not as same as sent %v\n", string(recv[:n]), string(send)) - break + return fmt.Errorf("StartUDPClient.recv %v is not as same as sent %v", string(recv[:n]), string(send)) } else { fmt.Printf("StartUDPClient got echo: %v\n", string(recv[:n])) } diff --git a/tests/web.go b/tests/web.go index 11651f0..1a59faa 100644 --- a/tests/web.go +++ b/tests/web.go @@ -41,6 +41,7 @@ func httpEcho(w http.ResponseWriter, r *http.Request) { func StartWebClient(httpServUrl string) error { fmt.Printf("http request to: %v\n", httpServUrl) + proxyAddr := fmt.Sprintf("127.0.0.1:%v", port) socksProxy := "socks5://" + proxyAddr proxy := func(_ *http.Request) (*url.URL, error) { return url.Parse(socksProxy) @@ -63,39 +64,38 @@ func StartWebClient(httpServUrl string) error { err := json.NewEncoder(b).Encode(user) if err != nil { fmt.Printf("StartWebClient.Encode err: %v\n", err) - break + return err } req, err := http.NewRequest(http.MethodPost, httpServUrl, b) req.Header.Set("Content-type", "application/json") if err != nil { fmt.Printf("StartWebClient.http.NewRequest err: %v\n", err) - break + return err } resp, err := httpClient.Do(req) if err != nil { fmt.Printf("StartWebClient.http.Do err: %v\n", err) - break + return err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { fmt.Printf("StartWebClient.io.ReadAll err: %v\n", err) - break + return err } respUser := &Person{} err = json.Unmarshal(body, respUser) if err != nil { fmt.Printf("StartWebClient.json.Unmarshal %v err: %v\n", string(body), err) - break + return err } if respUser.Age != user.Age { - fmt.Printf("StartWebClient got wrong response, sent %+v, recv %+v\n", user, respUser) - break + return fmt.Errorf("StartWebClient got wrong response, sent %+v, recv %+v", user, respUser) } else { fmt.Printf("StartWebClient got echo: %+v\n", respUser) } @@ -119,39 +119,38 @@ func StartTunWebClient(httpServUrl string) error { err := json.NewEncoder(b).Encode(user) if err != nil { fmt.Printf("StartWebClient.Encode err: %v\n", err) - break + return err } req, err := http.NewRequest(http.MethodPost, httpServUrl, b) req.Header.Set("Content-type", "application/json") if err != nil { fmt.Printf("StartWebClient.http.NewRequest err: %v\n", err) - break + return err } resp, err := httpClient.Do(req) if err != nil { fmt.Printf("StartWebClient.http.Do err: %v\n", err) - break + return err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { fmt.Printf("StartWebClient.io.ReadAll err: %v\n", err) - break + return err } respUser := &Person{} err = json.Unmarshal(body, respUser) if err != nil { fmt.Printf("StartWebClient.json.Unmarshal err: %v\n", err) - break + return err } if respUser.Age != user.Age { - fmt.Printf("StartWebClient got wrong response, sent %+v, recv %+v\n", user, respUser) - break + return fmt.Errorf("StartWebClient got wrong response, sent %+v, recv %+v", user, respUser) } }