diff --git a/go.mod b/go.mod index 4d6fd8f..fda2862 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,8 @@ require ( github.com/ginuerzh/tls-dissector v0.0.1 github.com/go-log/log v0.1.0 github.com/gobwas/glob v0.2.3 + github.com/golang/mock v1.2.0 // indirect + github.com/gorilla/websocket v1.4.0 // indirect github.com/hashicorp/golang-lru v0.5.0 // indirect github.com/klauspost/compress v1.4.1 github.com/klauspost/cpuid v1.2.0 // indirect @@ -20,6 +22,8 @@ require ( github.com/lucas-clemente/quic-go v0.10.0 github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced // indirect github.com/miekg/dns v1.1.1 + github.com/onsi/ginkgo v1.7.0 // indirect + github.com/onsi/gomega v1.4.3 // indirect github.com/pkg/errors v0.8.0 // indirect github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 github.com/shadowsocks/shadowsocks-go v0.0.0-20170121203516-97a5c71f80ba @@ -28,8 +32,8 @@ require ( github.com/tjfoc/gmsm v1.0.1 // indirect golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 golang.org/x/net v0.0.0-20181201002055-351d144fa1fc + golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e // indirect - golang.org/x/text v0.3.0 // indirect gopkg.in/gorilla/websocket.v1 v1.4.0 gopkg.in/xtaci/kcp-go.v4 v4.3.2 gopkg.in/xtaci/smux.v1 v1.0.7 diff --git a/go.sum b/go.sum index 660acee..369737c 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/dchest/siphash v1.2.0 h1:YWOShuhvg0GqbQpMa60QlCGtEyf7O7HC1Jf0VjdQ60M= github.com/dchest/siphash v1.2.0/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ginuerzh/gosocks4 v0.0.1 h1:ojDKUyz+uaEeRm2usY1cyQiXTqJqrKxfeE6SVBXq4m0= github.com/ginuerzh/gosocks4 v0.0.1/go.mod h1:8SdwBMKjfJ9+BfP2vDJM1jcrgWUbWV6qxBPHHVrwptY= github.com/ginuerzh/gosocks5 v0.2.0 h1:K0Ua23U9LU3BZrf3XpGDcs0mP8DiEpa6PJE4TA/MU3s= @@ -24,8 +26,16 @@ github.com/go-log/log v0.1.0 h1:wudGTNsiGzrD5ZjgIkVZ517ugi2XRe9Q/xRCzwEO4/U= github.com/go-log/log v0.1.0/go.mod h1:4mBwpdRMFLiuXZDCwU2lKQFsoSCo72j3HqBK9d81N2M= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= @@ -40,6 +50,11 @@ github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cce github.com/lucas-clemente/quic-go-certificates v0.0.0-20160823095156-d2f86524cced/go.mod h1:NCcRLrOTZbzhZvixZLlERbJtDtYsmMw8Jc4vS8Z0g58= github.com/miekg/dns v1.1.1 h1:DVkblRdiScEnEr0LR9nTnEQqHYycjkXW9bOjd+2EL2o= github.com/miekg/dns v1.1.1/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE= @@ -55,16 +70,29 @@ github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/ golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e h1:njOxP/wVblhCLIUhjHXf6X+dzTt5OQ3vMQo9mkOIKIo= golang.org/x/sys v0.0.0-20181206074257-70b957f3b65e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gorilla/websocket.v1 v1.4.0 h1:lREme3ezAGPCpxSHwjGkHhAJX+ed2B6vzAJ+kaqBEIM= gopkg.in/gorilla/websocket.v1 v1.4.0/go.mod h1:Ons1i8d00TjvJPdla7bJyeXFsdOacUyrTYbg9IetsIE= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/xtaci/kcp-go.v4 v4.3.2 h1:S9IF+L55Ugzl/hVA6wvuL3SuAtTUzH2cBBC88MXQxnE= gopkg.in/xtaci/kcp-go.v4 v4.3.2/go.mod h1:fFYTlSOHNOHDNTKfoqarZMQsu7g7oXKwJ9wq0i9lODc= gopkg.in/xtaci/smux.v1 v1.0.7 h1:qootIZs4ZPSx5blhvgaFpx2epdFSWkyw99xT+q0mRXI= gopkg.in/xtaci/smux.v1 v1.0.7/go.mod h1:NbrPjLp8lNAYN8KqTplnFr2JjIBbr7CdHBkHtHsXtWA= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/http2_test.go b/http2_test.go new file mode 100644 index 0000000..4157b99 --- /dev/null +++ b/http2_test.go @@ -0,0 +1,766 @@ +package gost + +import ( + "crypto/rand" + "crypto/tls" + "fmt" + "net/http/httptest" + "net/url" + "testing" +) + +func http2ProxyRoundtrip(targetURL string, data []byte, clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + ln, err := HTTP2Listener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: HTTP2Connector(clientInfo), + Transporter: HTTP2Transporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTP2Handler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestHTTP2Proxy(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range httpProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := http2ProxyRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers) + if err == nil { + if tc.errStr != "" { + t.Errorf("#%d should failed with error %s", i, tc.errStr) + } + } else { + if tc.errStr == "" { + t.Errorf("#%d got error %v", i, err) + } + if err.Error() != tc.errStr { + t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) + } + } + }) + } +} + +func BenchmarkHTTP2Proxy(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := HTTP2Listener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTP2Connector(url.UserPassword("admin", "123456")), + Transporter: HTTP2Transporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTP2Handler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + for i := 0; i < b.N; i++ { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } +} + +func BenchmarkHTTP2ProxyParallel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := HTTP2Listener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTP2Connector(url.UserPassword("admin", "123456")), + Transporter: HTTP2Transporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTP2Handler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } + }) +} + +func httpOverH2Roundtrip(targetURL string, data []byte, tlsConfig *tls.Config, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := H2Listener("", tlsConfig) + if err != nil { + return err + } + + client := &Client{ + Connector: HTTPConnector(clientInfo), + Transporter: H2Transporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestHTTPOverH2(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range httpProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := httpOverH2Roundtrip(httpSrv.URL, sendData, nil, tc.cliUser, tc.srvUsers) + if err == nil { + if tc.errStr != "" { + t.Errorf("#%d should failed with error %s", i, tc.errStr) + } + } else { + if tc.errStr == "" { + t.Errorf("#%d got error %v", i, err) + } + if err.Error() != tc.errStr { + t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) + } + } + }) + } +} + +func BenchmarkHTTPOverH2(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := H2Listener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: H2Transporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + for i := 0; i < b.N; i++ { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } +} + +func BenchmarkHTTPOverH2Parallel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := H2Listener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: H2Transporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } + }) +} + +func socks5OverH2Roundtrip(targetURL string, data []byte, tlsConfig *tls.Config, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := H2Listener("", tlsConfig) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS5Connector(clientInfo), + Transporter: H2Transporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS5Handler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS5OverH2(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range socks5ProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := socks5OverH2Roundtrip(httpSrv.URL, sendData, + nil, + tc.cliUser, + tc.srvUsers, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func socks4OverH2Roundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error { + ln, err := H2Listener("", tlsConfig) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4Connector(), + Transporter: H2Transporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4OverH2(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4OverH2Roundtrip(httpSrv.URL, sendData, nil) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func socks4aOverH2Roundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error { + ln, err := H2Listener("", tlsConfig) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4AConnector(), + Transporter: H2Transporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4AOverH2(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4aOverH2Roundtrip(httpSrv.URL, sendData, nil) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func ssOverH2Roundtrip(targetURL string, data []byte, tlsConfig *tls.Config, + clientInfo, serverInfo *url.Userinfo) error { + + ln, err := H2Listener("", tlsConfig) + if err != nil { + return err + } + + client := &Client{ + Connector: ShadowConnector(clientInfo), + Transporter: H2Transporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: ShadowHandler( + UsersHandlerOption(serverInfo), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSSOverH2(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + var ssProxyTests = []struct { + clientCipher *url.Userinfo + serverCipher *url.Userinfo + pass bool + }{ + {nil, nil, false}, + {&url.Userinfo{}, &url.Userinfo{}, false}, + {url.User("abc"), url.User("abc"), false}, + {url.UserPassword("abc", "def"), url.UserPassword("abc", "def"), false}, + + {url.User("aes-128-cfb"), url.User("aes-128-cfb"), false}, + {url.User("aes-128-cfb"), url.UserPassword("aes-128-cfb", "123456"), false}, + {url.UserPassword("aes-128-cfb", "123456"), url.User("aes-128-cfb"), false}, + {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-128-cfb", "abc"), false}, + {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-128-cfb", "123456"), true}, + } + for i, tc := range ssProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := ssOverH2Roundtrip(httpSrv.URL, sendData, + nil, + tc.clientCipher, + tc.serverCipher, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func httpOverH2CRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := H2CListener("") + if err != nil { + return err + } + + client := &Client{ + Connector: HTTPConnector(clientInfo), + Transporter: H2CTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestHTTPOverH2C(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range httpProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := httpOverH2CRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers) + if err == nil { + if tc.errStr != "" { + t.Errorf("#%d should failed with error %s", i, tc.errStr) + } + } else { + if tc.errStr == "" { + t.Errorf("#%d got error %v", i, err) + } + if err.Error() != tc.errStr { + t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) + } + } + }) + } +} + +func BenchmarkHTTPOverH2C(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := H2CListener("") + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: H2CTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + for i := 0; i < b.N; i++ { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } +} + +func BenchmarkHTTPOverH2CParallel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := H2CListener("") + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: H2CTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } + }) +} + +func socks5OverH2CRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := H2CListener("") + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS5Connector(clientInfo), + Transporter: H2CTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS5Handler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS5OverH2C(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range socks5ProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := socks5OverH2CRoundtrip(httpSrv.URL, sendData, + tc.cliUser, + tc.srvUsers, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func socks4OverH2CRoundtrip(targetURL string, data []byte) error { + ln, err := H2CListener("") + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4Connector(), + Transporter: H2CTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4OverH2C(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4OverH2CRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func socks4aOverH2CRoundtrip(targetURL string, data []byte) error { + ln, err := H2CListener("") + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4AConnector(), + Transporter: H2CTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4AOverH2C(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4aOverH2CRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func ssOverH2CRoundtrip(targetURL string, data []byte, + clientInfo, serverInfo *url.Userinfo) error { + + ln, err := H2CListener("") + if err != nil { + return err + } + + client := &Client{ + Connector: ShadowConnector(clientInfo), + Transporter: H2CTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: ShadowHandler( + UsersHandlerOption(serverInfo), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSSOverH2C(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + var ssProxyTests = []struct { + clientCipher *url.Userinfo + serverCipher *url.Userinfo + pass bool + }{ + {nil, nil, false}, + {&url.Userinfo{}, &url.Userinfo{}, false}, + {url.User("abc"), url.User("abc"), false}, + {url.UserPassword("abc", "def"), url.UserPassword("abc", "def"), false}, + + {url.User("aes-128-cfb"), url.User("aes-128-cfb"), false}, + {url.User("aes-128-cfb"), url.UserPassword("aes-128-cfb", "123456"), false}, + {url.UserPassword("aes-128-cfb", "123456"), url.User("aes-128-cfb"), false}, + {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-128-cfb", "abc"), false}, + {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-128-cfb", "123456"), true}, + } + + for i, tc := range ssProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := ssOverH2CRoundtrip(httpSrv.URL, sendData, + tc.clientCipher, + tc.serverCipher, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} diff --git a/http_test.go b/http_test.go index eb91ad1..251aa52 100644 --- a/http_test.go +++ b/http_test.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "io/ioutil" + "net" "net/http" "net/http/httptest" "net/url" @@ -16,42 +17,43 @@ import ( "time" ) +func init() { + // SetLogger(&LogLogger{}) + // Debug = true + cert, err := GenCertificate() + if err != nil { + panic(err) + } + DefaultTLSConfig = &tls.Config{ + Certificates: []tls.Certificate{cert}, + } +} + var httpTestHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { io.Copy(w, r.Body) }) -func proxyRoundtrip(client *Client, server *Server, targetURL string, data []byte) (err error) { +// proxyConn obtains a connection to the proxy server. +func proxyConn(client *Client, server *Server) (net.Conn, error) { conn, err := client.Dial(server.Addr().String()) if err != nil { - return - } - defer conn.Close() - - conn.SetDeadline(time.Now().Add(1 * time.Second)) - - conn, err = client.Handshake(conn) - if err != nil { - return - } - u, err := url.Parse(targetURL) - if err != nil { - return - } - conn, err = client.Connect(conn, u.Host) - if err != nil { - return + return nil, err } - if u.Scheme == "https" { - conn = tls.Client(conn, - &tls.Config{ - InsecureSkipVerify: true, - }) - u.Scheme = "http" + cc, err := client.Handshake(conn, AddrHandshakeOption(server.Addr().String())) + if err != nil { + conn.Close() + return nil, err } + + return cc, nil +} + +// httpRoundtrip does a HTTP request-response roundtrip, and checks the data received. +func httpRoundtrip(conn net.Conn, targetURL string, data []byte) (err error) { req, err := http.NewRequest( http.MethodGet, - u.String(), + targetURL, bytes.NewReader(data), ) if err != nil { @@ -78,10 +80,32 @@ func proxyRoundtrip(client *Client, server *Server, targetURL string, data []byt if !bytes.Equal(data, recv) { return fmt.Errorf("data not equal") } - return } +func proxyRoundtrip(client *Client, server *Server, targetURL string, data []byte) (err error) { + conn, err := proxyConn(client, server) + if err != nil { + return err + } + defer conn.Close() + + u, err := url.Parse(targetURL) + if err != nil { + return + } + + conn.SetDeadline(time.Now().Add(1 * time.Second)) + defer conn.SetDeadline(time.Time{}) + + conn, err = client.Connect(conn, u.Host) + if err != nil { + return + } + + return httpRoundtrip(conn, targetURL, data) +} + var httpProxyTests = []struct { cliUser *url.Userinfo srvUsers []*url.Userinfo diff --git a/kcp_test.go b/kcp_test.go new file mode 100644 index 0000000..73fae6e --- /dev/null +++ b/kcp_test.go @@ -0,0 +1,311 @@ +package gost + +import ( + "crypto/rand" + "fmt" + "net/http/httptest" + "net/url" + "testing" +) + +func httpOverKCPRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := KCPListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: HTTPConnector(clientInfo), + Transporter: KCPTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestHTTPOverKCP(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range httpProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := httpOverKCPRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers) + if err == nil { + if tc.errStr != "" { + t.Errorf("#%d should failed with error %s", i, tc.errStr) + } + } else { + if tc.errStr == "" { + t.Errorf("#%d got error %v", i, err) + } + if err.Error() != tc.errStr { + t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) + } + } + }) + } +} + +func BenchmarkHTTPOverKCP(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := KCPListener("", nil) + if err != nil { + b.Error(err) + } + b.Log(ln.Addr()) + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: KCPTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + for i := 0; i < b.N; i++ { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } +} + +func BenchmarkHTTPOverKCPParallel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := KCPListener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: KCPTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } + }) +} + +func socks5OverKCPRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := KCPListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS5Connector(clientInfo), + Transporter: KCPTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS5Handler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS5OverKCP(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range socks5ProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := socks5OverKCPRoundtrip(httpSrv.URL, sendData, + tc.cliUser, + tc.srvUsers, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func socks4OverKCPRoundtrip(targetURL string, data []byte) error { + ln, err := KCPListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4Connector(), + Transporter: KCPTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4OverKCP(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4OverKCPRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func socks4aOverKCPRoundtrip(targetURL string, data []byte) error { + ln, err := KCPListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4AConnector(), + Transporter: KCPTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4AOverKCP(t *testing.T) { + + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4aOverKCPRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func ssOverKCPRoundtrip(targetURL string, data []byte, + clientInfo, serverInfo *url.Userinfo) error { + + ln, err := KCPListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: ShadowConnector(clientInfo), + Transporter: KCPTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: ShadowHandler( + UsersHandlerOption(serverInfo), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSSOverKCP(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range ssProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := ssOverKCPRoundtrip(httpSrv.URL, sendData, + tc.clientCipher, + tc.serverCipher, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} diff --git a/quic.go b/quic.go index f008558..541bda3 100644 --- a/quic.go +++ b/quic.go @@ -21,7 +21,7 @@ type quicSession struct { } func (session *quicSession) GetConn() (*quicConn, error) { - stream, err := session.session.OpenStream() + stream, err := session.session.OpenStreamSync() if err != nil { return nil, err } @@ -130,6 +130,10 @@ func (tr *quicTransporter) initSession(addr string, conn net.Conn, config *QUICC HandshakeTimeout: config.Timeout, KeepAlive: config.KeepAlive, IdleTimeout: config.IdleTimeout, + Versions: []quic.VersionNumber{ + quic.VersionGQUIC43, + quic.VersionGQUIC39, + }, } session, err := quic.Dial(udpConn, udpAddr, addr, config.TLSConfig, quicConfig) if err != nil { diff --git a/quic_test.go b/quic_test.go new file mode 100644 index 0000000..bce853f --- /dev/null +++ b/quic_test.go @@ -0,0 +1,311 @@ +package gost + +import ( + "crypto/rand" + "fmt" + "net/http/httptest" + "net/url" + "testing" +) + +func httpOverQUICRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := QUICListener("localhost:0", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: HTTPConnector(clientInfo), + Transporter: QUICTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestHTTPOverQUIC(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range httpProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := httpOverQUICRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers) + if err == nil { + if tc.errStr != "" { + t.Errorf("#%d should failed with error %s", i, tc.errStr) + } + } else { + if tc.errStr == "" { + t.Errorf("#%d got error %v", i, err) + } + if err.Error() != tc.errStr { + t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) + } + } + }) + } +} + +func BenchmarkHTTPOverQUIC(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := QUICListener("localhost:0", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: QUICTransporter(&QUICConfig{KeepAlive: true}), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + for i := 0; i < b.N; i++ { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } +} + +func BenchmarkHTTPOverQUICParallel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := QUICListener("localhost:0", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: QUICTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } + }) +} + +func socks5OverQUICRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := QUICListener("localhost:0", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS5Connector(clientInfo), + Transporter: QUICTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS5Handler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS5OverQUIC(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range socks5ProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := socks5OverQUICRoundtrip(httpSrv.URL, sendData, + tc.cliUser, + tc.srvUsers, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func socks4OverQUICRoundtrip(targetURL string, data []byte) error { + ln, err := QUICListener("localhost:0", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4Connector(), + Transporter: QUICTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4OverQUIC(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4OverQUICRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func socks4aOverQUICRoundtrip(targetURL string, data []byte) error { + ln, err := QUICListener("localhost:0", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4AConnector(), + Transporter: QUICTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4AOverQUIC(t *testing.T) { + + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4aOverQUICRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func ssOverQUICRoundtrip(targetURL string, data []byte, + clientInfo, serverInfo *url.Userinfo) error { + + ln, err := QUICListener("localhost:0", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: ShadowConnector(clientInfo), + Transporter: QUICTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: ShadowHandler( + UsersHandlerOption(serverInfo), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSSOverQUIC(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range ssProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := ssOverQUICRoundtrip(httpSrv.URL, sendData, + tc.clientCipher, + tc.serverCipher, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} diff --git a/sni_test.go b/sni_test.go index fe75201..a705ce2 100644 --- a/sni_test.go +++ b/sni_test.go @@ -90,15 +90,18 @@ func sniProxyRoundtrip(targetURL string, data []byte, host string) error { } func TestSNIProxy(t *testing.T) { - // SetLogger(&LogLogger{}) - // Debug = true httpSrv := httptest.NewTLSServer(httpTestHandler) defer httpSrv.Close() sendData := make([]byte, 128) rand.Read(sendData) - err := sniProxyRoundtrip("https://github.com", sendData, "google.com") + err := sniProxyRoundtrip("https://github.com", sendData, "") + if err != nil { + t.Errorf("got error: %v", err) + } + + err = sniProxyRoundtrip("https://github.com", sendData, "google.com") if err != nil { t.Errorf("got error: %v", err) } diff --git a/ss_test.go b/ss_test.go index bb7a4dd..c35ae36 100644 --- a/ss_test.go +++ b/ss_test.go @@ -23,54 +23,54 @@ var ssProxyTests = []struct { {url.UserPassword("aes-128-cfb", "123456"), url.User("aes-128-cfb"), false}, {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-128-cfb", "abc"), false}, {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-128-cfb", "123456"), true}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-192-cfb", "123456"), false}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-256-cfb", "123456"), false}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-128-ctr", "123456"), false}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-192-ctr", "123456"), false}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-256-ctr", "123456"), false}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("des-cfb", "123456"), false}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("bf-cfb", "123456"), false}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("cast5-cfb", "123456"), false}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("rc4-md5", "123456"), false}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("chacha20", "123456"), false}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("chacha20-ietf", "123456"), false}, - {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("salsa20", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-192-cfb", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-256-cfb", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-128-ctr", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-192-ctr", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("aes-256-ctr", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("des-cfb", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("bf-cfb", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("cast5-cfb", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("rc4-md5", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("chacha20", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("chacha20-ietf", "123456"), false}, + // {url.UserPassword("aes-128-cfb", "123456"), url.UserPassword("salsa20", "123456"), false}, {url.User("aes-192-cfb"), url.User("aes-192-cfb"), false}, {url.User("aes-192-cfb"), url.UserPassword("aes-192-cfb", "123456"), false}, {url.UserPassword("aes-192-cfb", "123456"), url.User("aes-192-cfb"), false}, {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-192-cfb", "abc"), false}, {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-192-cfb", "123456"), true}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-128-cfb", "123456"), false}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-256-cfb", "123456"), false}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-128-ctr", "123456"), false}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-192-ctr", "123456"), false}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-256-ctr", "123456"), false}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("des-cfb", "123456"), false}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("bf-cfb", "123456"), false}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("cast5-cfb", "123456"), false}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("rc4-md5", "123456"), false}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("chacha20", "123456"), false}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("chacha20-ietf", "123456"), false}, - {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("salsa20", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-128-cfb", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-256-cfb", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-128-ctr", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-192-ctr", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("aes-256-ctr", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("des-cfb", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("bf-cfb", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("cast5-cfb", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("rc4-md5", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("chacha20", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("chacha20-ietf", "123456"), false}, + // {url.UserPassword("aes-192-cfb", "123456"), url.UserPassword("salsa20", "123456"), false}, {url.User("aes-256-cfb"), url.User("aes-256-cfb"), false}, {url.User("aes-256-cfb"), url.UserPassword("aes-256-cfb", "123456"), false}, {url.UserPassword("aes-256-cfb", "123456"), url.User("aes-256-cfb"), false}, {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-256-cfb", "abc"), false}, {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-256-cfb", "123456"), true}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-128-cfb", "123456"), false}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-192-cfb", "123456"), false}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-128-ctr", "123456"), false}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-192-ctr", "123456"), false}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-256-ctr", "123456"), false}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("des-cfb", "123456"), false}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("bf-cfb", "123456"), false}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("cast5-cfb", "123456"), false}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("rc4-md5", "123456"), false}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("chacha20", "123456"), false}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("chacha20-ietf", "123456"), false}, - {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("salsa20", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-128-cfb", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-192-cfb", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-128-ctr", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-192-ctr", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("aes-256-ctr", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("des-cfb", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("bf-cfb", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("cast5-cfb", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("rc4-md5", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("chacha20", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("chacha20-ietf", "123456"), false}, + // {url.UserPassword("aes-256-cfb", "123456"), url.UserPassword("salsa20", "123456"), false}, {url.User("aes-128-ctr"), url.User("aes-128-ctr"), false}, {url.User("aes-128-ctr"), url.UserPassword("aes-128-ctr", "123456"), false}, diff --git a/ssh.go b/ssh.go index 0c72848..113d30d 100644 --- a/ssh.go +++ b/ssh.go @@ -252,6 +252,7 @@ func (tr *sshTunnelTransporter) Handshake(conn net.Conn, options ...HandshakeOpt Timeout: opts.Timeout, HostKeyCallback: ssh.InsecureIgnoreHostKey(), } + // TODO: support pubkey auth. if opts.User != nil { config.User = opts.User.Username() password, _ := opts.User.Password() @@ -847,13 +848,13 @@ func (c *sshConn) RemoteAddr() net.Addr { } func (c *sshConn) SetDeadline(t time.Time) error { - return nil + return c.conn.SetDeadline(t) } func (c *sshConn) SetReadDeadline(t time.Time) error { - return nil + return c.conn.SetReadDeadline(t) } func (c *sshConn) SetWriteDeadline(t time.Time) error { - return nil + return c.conn.SetWriteDeadline(t) } diff --git a/ssh_test.go b/ssh_test.go new file mode 100644 index 0000000..5efb008 --- /dev/null +++ b/ssh_test.go @@ -0,0 +1,313 @@ +package gost + +import ( + "crypto/rand" + "crypto/tls" + "fmt" + "net/http/httptest" + "net/url" + "testing" +) + +func httpOverSSHTunnelRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := SSHTunnelListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: HTTPConnector(clientInfo), + Transporter: SSHTunnelTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestHTTPOverSSHTunnel(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range httpProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := httpOverSSHTunnelRoundtrip(httpSrv.URL, sendData, nil, tc.cliUser, tc.srvUsers) + if err == nil { + if tc.errStr != "" { + t.Errorf("#%d should failed with error %s", i, tc.errStr) + } + } else { + if tc.errStr == "" { + t.Errorf("#%d got error %v", i, err) + } + if err.Error() != tc.errStr { + t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) + } + } + }) + } +} + +func BenchmarkHTTPOverSSHTunnel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := SSHTunnelListener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: SSHTunnelTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + for i := 0; i < b.N; i++ { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } +} + +func BenchmarkHTTPOverSSHTunnelParallel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := SSHTunnelListener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: SSHTunnelTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } + }) +} + +func socks5OverSSHTunnelRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := SSHTunnelListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS5Connector(clientInfo), + Transporter: SSHTunnelTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS5Handler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS5OverSSHTunnel(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range socks5ProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := socks5OverSSHTunnelRoundtrip(httpSrv.URL, sendData, + nil, + tc.cliUser, + tc.srvUsers, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func socks4OverSSHTunnelRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error { + ln, err := SSHTunnelListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4Connector(), + Transporter: SSHTunnelTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4OverSSHTunnel(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4OverSSHTunnelRoundtrip(httpSrv.URL, sendData, nil) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func socks4aOverSSHTunnelRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error { + ln, err := SSHTunnelListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4AConnector(), + Transporter: SSHTunnelTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4AOverSSHTunnel(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4aOverSSHTunnelRoundtrip(httpSrv.URL, sendData, nil) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func ssOverSSHTunnelRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config, + clientInfo, serverInfo *url.Userinfo) error { + + ln, err := SSHTunnelListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: ShadowConnector(clientInfo), + Transporter: SSHTunnelTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: ShadowHandler( + UsersHandlerOption(serverInfo), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSSOverSSHTunnel(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range ssProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := ssOverSSHTunnelRoundtrip(httpSrv.URL, sendData, + nil, + tc.clientCipher, + tc.serverCipher, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} diff --git a/tls_test.go b/tls_test.go index bfa8ca7..fc396ff 100644 --- a/tls_test.go +++ b/tls_test.go @@ -36,14 +36,6 @@ func httpOverTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config, } func TestHTTPOverTLS(t *testing.T) { - cert, err := GenCertificate() - if err != nil { - t.Error(err) - } - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - httpSrv := httptest.NewServer(httpTestHandler) defer httpSrv.Close() @@ -53,7 +45,7 @@ func TestHTTPOverTLS(t *testing.T) { for i, tc := range httpProxyTests { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { - err := httpOverTLSRoundtrip(httpSrv.URL, sendData, tlsConfig, tc.cliUser, tc.srvUsers) + err := httpOverTLSRoundtrip(httpSrv.URL, sendData, nil, tc.cliUser, tc.srvUsers) if err == nil { if tc.errStr != "" { t.Errorf("#%d should failed with error %s", i, tc.errStr) @@ -77,15 +69,7 @@ func BenchmarkHTTPOverTLS(b *testing.B) { sendData := make([]byte, 128) rand.Read(sendData) - cert, err := GenCertificate() - if err != nil { - b.Error(err) - } - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - - ln, err := TLSListener("", tlsConfig) + ln, err := TLSListener("", nil) if err != nil { b.Error(err) } @@ -118,15 +102,7 @@ func BenchmarkHTTPOverTLSParallel(b *testing.B) { sendData := make([]byte, 128) rand.Read(sendData) - cert, err := GenCertificate() - if err != nil { - b.Error(err) - } - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - - ln, err := TLSListener("", tlsConfig) + ln, err := TLSListener("", nil) if err != nil { b.Error(err) } @@ -181,15 +157,6 @@ func socks5OverTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config } func TestSOCKS5OverTLS(t *testing.T) { - cert, err := GenCertificate() - if err != nil { - t.Error(err) - } - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - DefaultTLSConfig = tlsConfig - httpSrv := httptest.NewServer(httpTestHandler) defer httpSrv.Close() @@ -200,7 +167,7 @@ func TestSOCKS5OverTLS(t *testing.T) { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { err := socks5OverTLSRoundtrip(httpSrv.URL, sendData, - tlsConfig, + nil, tc.cliUser, tc.srvUsers, ) @@ -241,21 +208,13 @@ func socks4OverTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config } func TestSOCKS4OverTLS(t *testing.T) { - cert, err := GenCertificate() - if err != nil { - t.Error(err) - } - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - httpSrv := httptest.NewServer(httpTestHandler) defer httpSrv.Close() sendData := make([]byte, 128) rand.Read(sendData) - err = socks4OverTLSRoundtrip(httpSrv.URL, sendData, tlsConfig) + err := socks4OverTLSRoundtrip(httpSrv.URL, sendData, nil) // t.Logf("#%d %v", i, err) if err != nil { t.Errorf("got error: %v", err) @@ -285,21 +244,13 @@ func socks4aOverTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Confi } func TestSOCKS4AOverTLS(t *testing.T) { - cert, err := GenCertificate() - if err != nil { - t.Error(err) - } - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - httpSrv := httptest.NewServer(httpTestHandler) defer httpSrv.Close() sendData := make([]byte, 128) rand.Read(sendData) - err = socks4aOverTLSRoundtrip(httpSrv.URL, sendData, tlsConfig) + err := socks4aOverTLSRoundtrip(httpSrv.URL, sendData, nil) // t.Logf("#%d %v", i, err) if err != nil { t.Errorf("got error: %v", err) @@ -333,15 +284,6 @@ func ssOverTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config, } func TestSSOverTLS(t *testing.T) { - cert, err := GenCertificate() - if err != nil { - t.Error(err) - } - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - } - DefaultTLSConfig = tlsConfig - httpSrv := httptest.NewServer(httpTestHandler) defer httpSrv.Close() @@ -352,7 +294,310 @@ func TestSSOverTLS(t *testing.T) { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { err := ssOverTLSRoundtrip(httpSrv.URL, sendData, - tlsConfig, + nil, + tc.clientCipher, + tc.serverCipher, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func httpOverMTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := MTLSListener("", tlsConfig) + if err != nil { + return err + } + + client := &Client{ + Connector: HTTPConnector(clientInfo), + Transporter: MTLSTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestHTTPOverMTLS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range httpProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := httpOverMTLSRoundtrip(httpSrv.URL, sendData, nil, tc.cliUser, tc.srvUsers) + if err == nil { + if tc.errStr != "" { + t.Errorf("#%d should failed with error %s", i, tc.errStr) + } + } else { + if tc.errStr == "" { + t.Errorf("#%d got error %v", i, err) + } + if err.Error() != tc.errStr { + t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) + } + } + }) + } +} + +func BenchmarkHTTPOverMTLS(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := MTLSListener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: MTLSTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + for i := 0; i < b.N; i++ { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } +} + +func BenchmarkHTTPOverMTLSParallel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := MTLSListener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: MTLSTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } + }) +} + +func socks5OverMTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := MTLSListener("", tlsConfig) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS5Connector(clientInfo), + Transporter: MTLSTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS5Handler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS5OverMTLS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range socks5ProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := socks5OverMTLSRoundtrip(httpSrv.URL, sendData, + nil, + tc.cliUser, + tc.srvUsers, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func socks4OverMTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error { + ln, err := MTLSListener("", tlsConfig) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4Connector(), + Transporter: MTLSTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4OverMTLS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4OverMTLSRoundtrip(httpSrv.URL, sendData, nil) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func socks4aOverMTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config) error { + ln, err := MTLSListener("", tlsConfig) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4AConnector(), + Transporter: MTLSTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4AOverMTLS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4aOverMTLSRoundtrip(httpSrv.URL, sendData, nil) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func ssOverMTLSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config, + clientInfo, serverInfo *url.Userinfo) error { + + ln, err := MTLSListener("", tlsConfig) + if err != nil { + return err + } + + client := &Client{ + Connector: ShadowConnector(clientInfo), + Transporter: MTLSTransporter(), + } + + server := &Server{ + Listener: ln, + Handler: ShadowHandler( + UsersHandlerOption(serverInfo), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSSOverMTLS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range ssProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := ssOverMTLSRoundtrip(httpSrv.URL, sendData, + nil, tc.clientCipher, tc.serverCipher, ) diff --git a/ws_test.go b/ws_test.go new file mode 100644 index 0000000..0617e5e --- /dev/null +++ b/ws_test.go @@ -0,0 +1,614 @@ +package gost + +import ( + "crypto/rand" + "fmt" + "net/http/httptest" + "net/url" + "testing" +) + +func httpOverWSRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := WSListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: HTTPConnector(clientInfo), + Transporter: WSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestHTTPOverWS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range httpProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := httpOverWSRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers) + if err == nil { + if tc.errStr != "" { + t.Errorf("#%d should failed with error %s", i, tc.errStr) + } + } else { + if tc.errStr == "" { + t.Errorf("#%d got error %v", i, err) + } + if err.Error() != tc.errStr { + t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) + } + } + }) + } +} + +func BenchmarkHTTPOverWS(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := WSListener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: WSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + for i := 0; i < b.N; i++ { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } +} + +func BenchmarkHTTPOverWSParallel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := WSListener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: WSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } + }) +} + +func socks5OverWSRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := WSListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS5Connector(clientInfo), + Transporter: WSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS5Handler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS5OverWS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range socks5ProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := socks5OverWSRoundtrip(httpSrv.URL, sendData, + tc.cliUser, + tc.srvUsers, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func socks4OverWSRoundtrip(targetURL string, data []byte) error { + ln, err := WSListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4Connector(), + Transporter: WSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4OverWS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4OverWSRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func socks4aOverWSRoundtrip(targetURL string, data []byte) error { + ln, err := WSListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4AConnector(), + Transporter: WSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4AOverWS(t *testing.T) { + + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4aOverWSRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func ssOverWSRoundtrip(targetURL string, data []byte, + clientInfo, serverInfo *url.Userinfo) error { + + ln, err := WSListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: ShadowConnector(clientInfo), + Transporter: WSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: ShadowHandler( + UsersHandlerOption(serverInfo), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSSOverWS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range ssProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := ssOverWSRoundtrip(httpSrv.URL, sendData, + tc.clientCipher, + tc.serverCipher, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func httpOverMWSRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := MWSListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: HTTPConnector(clientInfo), + Transporter: MWSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestHTTPOverMWS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range httpProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := httpOverMWSRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers) + if err == nil { + if tc.errStr != "" { + t.Errorf("#%d should failed with error %s", i, tc.errStr) + } + } else { + if tc.errStr == "" { + t.Errorf("#%d got error %v", i, err) + } + if err.Error() != tc.errStr { + t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) + } + } + }) + } +} + +func BenchmarkHTTPOverMWS(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := MWSListener("", nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: MWSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + for i := 0; i < b.N; i++ { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } +} + +func BenchmarkHTTPOverMWSParallel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := MWSListener("", nil) + if err != nil { + b.Error(err) + } + + b.Log(ln.Addr()) + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: MWSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } + }) +} + +func socks5OverMWSRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := MWSListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS5Connector(clientInfo), + Transporter: MWSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS5Handler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS5OverMWS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range socks5ProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := socks5OverMWSRoundtrip(httpSrv.URL, sendData, + tc.cliUser, + tc.srvUsers, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func socks4OverMWSRoundtrip(targetURL string, data []byte) error { + ln, err := MWSListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4Connector(), + Transporter: MWSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4OverMWS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4OverMWSRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func socks4aOverMWSRoundtrip(targetURL string, data []byte) error { + ln, err := MWSListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4AConnector(), + Transporter: MWSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4AOverMWS(t *testing.T) { + + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4aOverMWSRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func ssOverMWSRoundtrip(targetURL string, data []byte, + clientInfo, serverInfo *url.Userinfo) error { + + ln, err := MWSListener("", nil) + if err != nil { + return err + } + + client := &Client{ + Connector: ShadowConnector(clientInfo), + Transporter: MWSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: ShadowHandler( + UsersHandlerOption(serverInfo), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSSOverMWS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range ssProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := ssOverMWSRoundtrip(httpSrv.URL, sendData, + tc.clientCipher, + tc.serverCipher, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} diff --git a/wss_test.go b/wss_test.go new file mode 100644 index 0000000..f8b2ada --- /dev/null +++ b/wss_test.go @@ -0,0 +1,615 @@ +package gost + +import ( + "crypto/rand" + "crypto/tls" + "fmt" + "net/http/httptest" + "net/url" + "testing" +) + +func httpOverWSSRoundtrip(targetURL string, data []byte, tlsConfig *tls.Config, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := WSSListener("", tlsConfig, nil) + if err != nil { + return err + } + + client := &Client{ + Connector: HTTPConnector(clientInfo), + Transporter: WSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestHTTPOverWSS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range httpProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := httpOverWSSRoundtrip(httpSrv.URL, sendData, nil, tc.cliUser, tc.srvUsers) + if err == nil { + if tc.errStr != "" { + t.Errorf("#%d should failed with error %s", i, tc.errStr) + } + } else { + if tc.errStr == "" { + t.Errorf("#%d got error %v", i, err) + } + if err.Error() != tc.errStr { + t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) + } + } + }) + } +} + +func BenchmarkHTTPOverWSS(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := WSSListener("", nil, nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: WSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + for i := 0; i < b.N; i++ { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } +} + +func BenchmarkHTTPOverWSSParallel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := WSSListener("", nil, nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: WSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } + }) +} + +func socks5OverWSSRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := WSSListener("", nil, nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS5Connector(clientInfo), + Transporter: WSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS5Handler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS5OverWSS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range socks5ProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := socks5OverWSSRoundtrip(httpSrv.URL, sendData, + tc.cliUser, + tc.srvUsers, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func socks4OverWSSRoundtrip(targetURL string, data []byte) error { + ln, err := WSSListener("", nil, nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4Connector(), + Transporter: WSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4OverWSS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4OverWSSRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func socks4aOverWSSRoundtrip(targetURL string, data []byte) error { + ln, err := WSSListener("", nil, nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4AConnector(), + Transporter: WSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4AOverWSS(t *testing.T) { + + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4aOverWSSRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func ssOverWSSRoundtrip(targetURL string, data []byte, + clientInfo, serverInfo *url.Userinfo) error { + + ln, err := WSSListener("", nil, nil) + if err != nil { + return err + } + + client := &Client{ + Connector: ShadowConnector(clientInfo), + Transporter: WSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: ShadowHandler( + UsersHandlerOption(serverInfo), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSSOverWSS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range ssProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := ssOverWSSRoundtrip(httpSrv.URL, sendData, + tc.clientCipher, + tc.serverCipher, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func httpOverMWSSRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := MWSSListener("", nil, nil) + if err != nil { + return err + } + + client := &Client{ + Connector: HTTPConnector(clientInfo), + Transporter: MWSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestHTTPOverMWSS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range httpProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := httpOverMWSSRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers) + if err == nil { + if tc.errStr != "" { + t.Errorf("#%d should failed with error %s", i, tc.errStr) + } + } else { + if tc.errStr == "" { + t.Errorf("#%d got error %v", i, err) + } + if err.Error() != tc.errStr { + t.Errorf("#%d got error %v, want %v", i, err, tc.errStr) + } + } + }) + } +} + +func BenchmarkHTTPOverMWSS(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := MWSSListener("", nil, nil) + if err != nil { + b.Error(err) + } + + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: MWSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + for i := 0; i < b.N; i++ { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } +} + +func BenchmarkHTTPOverMWSSParallel(b *testing.B) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + ln, err := MWSSListener("", nil, nil) + if err != nil { + b.Error(err) + } + + b.Log(ln.Addr()) + client := &Client{ + Connector: HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: MWSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: HTTPHandler( + UsersHandlerOption(url.UserPassword("admin", "123456")), + ), + } + go server.Run() + defer server.Close() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := proxyRoundtrip(client, server, httpSrv.URL, sendData); err != nil { + b.Error(err) + } + } + }) +} + +func socks5OverMWSSRoundtrip(targetURL string, data []byte, + clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error { + + ln, err := MWSSListener("", nil, nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS5Connector(clientInfo), + Transporter: MWSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS5Handler( + UsersHandlerOption(serverInfo...), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS5OverMWSS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range socks5ProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := socks5OverMWSSRoundtrip(httpSrv.URL, sendData, + tc.cliUser, + tc.srvUsers, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +} + +func socks4OverMWSSRoundtrip(targetURL string, data []byte) error { + ln, err := MWSSListener("", nil, nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4Connector(), + Transporter: MWSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4OverMWSS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4OverMWSSRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func socks4aOverMWSSRoundtrip(targetURL string, data []byte) error { + ln, err := MWSSListener("", nil, nil) + if err != nil { + return err + } + + client := &Client{ + Connector: SOCKS4AConnector(), + Transporter: MWSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: SOCKS4Handler(), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSOCKS4AOverMWSS(t *testing.T) { + + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + err := socks4aOverMWSSRoundtrip(httpSrv.URL, sendData) + // t.Logf("#%d %v", i, err) + if err != nil { + t.Errorf("got error: %v", err) + } +} + +func ssOverMWSSRoundtrip(targetURL string, data []byte, + clientInfo, serverInfo *url.Userinfo) error { + + ln, err := MWSSListener("", nil, nil) + if err != nil { + return err + } + + client := &Client{ + Connector: ShadowConnector(clientInfo), + Transporter: MWSSTransporter(nil), + } + + server := &Server{ + Listener: ln, + Handler: ShadowHandler( + UsersHandlerOption(serverInfo), + ), + } + + go server.Run() + defer server.Close() + + return proxyRoundtrip(client, server, targetURL, data) +} + +func TestSSOverMWSS(t *testing.T) { + httpSrv := httptest.NewServer(httpTestHandler) + defer httpSrv.Close() + + sendData := make([]byte, 128) + rand.Read(sendData) + + for i, tc := range ssProxyTests { + tc := tc + t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { + err := ssOverMWSSRoundtrip(httpSrv.URL, sendData, + tc.clientCipher, + tc.serverCipher, + ) + if err == nil { + if !tc.pass { + t.Errorf("#%d should failed", i) + } + } else { + // t.Logf("#%d %v", i, err) + if tc.pass { + t.Errorf("#%d got error: %v", i, err) + } + } + }) + } +}