From f9f16d74a970484103b631892177b7c0c7bc8218 Mon Sep 17 00:00:00 2001 From: "rui.zheng" Date: Sun, 30 Jul 2017 00:55:41 +0800 Subject: [PATCH] add SSH remote forward support --- gost/client.go | 16 ++- gost/examples/bench/cli.go | 8 +- gost/examples/bench/srv.go | 133 +++++++++++-------------- gost/examples/forward/direct/client.go | 34 +++++++ gost/examples/forward/direct/server.go | 82 +++++++++++++++ gost/examples/forward/remote/client.go | 35 +++++++ gost/examples/forward/remote/server.go | 82 +++++++++++++++ gost/examples/quic/quicc.go | 21 ++-- gost/examples/quic/quics.go | 7 +- gost/examples/ssh/sshc.go | 18 ++-- gost/examples/ssh/sshd.go | 7 +- gost/forward.go | 74 +++++++++----- gost/node.go | 3 +- gost/server.go | 22 ++-- gost/ssh.go | 121 +++++++++++++++++----- gost/ws.go | 7 +- 16 files changed, 489 insertions(+), 181 deletions(-) create mode 100644 gost/examples/forward/direct/client.go create mode 100644 gost/examples/forward/direct/server.go create mode 100644 gost/examples/forward/remote/client.go create mode 100644 gost/examples/forward/remote/server.go diff --git a/gost/client.go b/gost/client.go index ab85a25..d26ffa6 100644 --- a/gost/client.go +++ b/gost/client.go @@ -16,14 +16,6 @@ type Client struct { Transporter Transporter } -// NewClient creates a proxy client. -func NewClient(c Connector, tr Transporter) *Client { - return &Client{ - Connector: c, - Transporter: tr, - } -} - // Dial connects to the target address. func (c *Client) Dial(addr string, options ...DialOption) (net.Conn, error) { return c.Transporter.Dial(addr, options...) @@ -40,7 +32,7 @@ func (c *Client) Connect(conn net.Conn, addr string) (net.Conn, error) { } // DefaultClient is a standard HTTP proxy client. -var DefaultClient = NewClient(HTTPConnector(nil), TCPTransporter()) +var DefaultClient = &Client{Connector: HTTPConnector(nil), Transporter: TCPTransporter()} // Dial connects to the address addr via the DefaultClient. func Dial(addr string, options ...DialOption) (net.Conn, error) { @@ -174,3 +166,9 @@ func KCPConfigHandshakeOption(config *KCPConfig) HandshakeOption { opts.KCPConfig = config } } + +func QUICConfigHandshakeOption(config *QUICConfig) HandshakeOption { + return func(opts *HandshakeOptions) { + opts.QUICConfig = config + } +} diff --git a/gost/examples/bench/cli.go b/gost/examples/bench/cli.go index 638b7fb..ec488c4 100644 --- a/gost/examples/bench/cli.go +++ b/gost/examples/bench/cli.go @@ -142,10 +142,10 @@ func main() { // http+quic gost.Node{ Addr: "localhost:6121", - Client: gost.NewClient( - gost.HTTPConnector(url.UserPassword("admin", "123456")), - gost.QUICTransporter(nil), - ), + Client: &gost.Client{ + Connector: gost.HTTPConnector(url.UserPassword("admin", "123456")), + Transporter: gost.QUICTransporter(nil), + }, }, ) diff --git a/gost/examples/bench/srv.go b/gost/examples/bench/srv.go index c041363..7d5d42b 100644 --- a/gost/examples/bench/srv.go +++ b/gost/examples/bench/srv.go @@ -37,7 +37,6 @@ func main() { // go rtcpForwardServer() // go rudpForwardServer() // go tcpRedirectServer() - // go sshForwardServer() go sshTunnelServer() // go http2Server() go quicServer() @@ -46,100 +45,99 @@ func main() { func httpServer() { s := &gost.Server{} - s.Handle(gost.HTTPHandler( - gost.UsersHandlerOption(url.UserPassword("admin", "123456")), - )) ln, err := gost.TCPListener(":18080") if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.HTTPHandler( + gost.UsersHandlerOption(url.UserPassword("admin", "123456")), + ) + log.Fatal(s.Serve(ln, h)) } func socks5Server() { s := &gost.Server{} - s.Handle(gost.SOCKS5Handler( - gost.UsersHandlerOption(url.UserPassword("admin", "123456")), - gost.TLSConfigHandlerOption(tlsConfig()), - )) ln, err := gost.TCPListener(":11080") if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.SOCKS5Handler( + gost.UsersHandlerOption(url.UserPassword("admin", "123456")), + gost.TLSConfigHandlerOption(tlsConfig()), + ) + log.Fatal(s.Serve(ln, h)) } func shadowServer() { s := &gost.Server{} - s.Handle(gost.ShadowHandler( - gost.UsersHandlerOption(url.UserPassword("chacha20", "123456")), - )) ln, err := gost.TCPListener(":18338") if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.ShadowHandler( + gost.UsersHandlerOption(url.UserPassword("chacha20", "123456")), + ) + log.Fatal(s.Serve(ln, h)) } func tlsServer() { s := &gost.Server{} - s.Handle(gost.HTTPHandler( - gost.UsersHandlerOption(url.UserPassword("admin", "123456")), - )) ln, err := gost.TLSListener(":11443", tlsConfig()) if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.HTTPHandler( + gost.UsersHandlerOption(url.UserPassword("admin", "123456")), + ) + log.Fatal(s.Serve(ln, h)) } func wsServer() { s := &gost.Server{} - s.Handle(gost.HTTPHandler( - gost.UsersHandlerOption(url.UserPassword("admin", "123456")), - )) ln, err := gost.WSListener(":18000", nil) if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.HTTPHandler( + gost.UsersHandlerOption(url.UserPassword("admin", "123456")), + ) + log.Fatal(s.Serve(ln, h)) } func wssServer() { s := &gost.Server{} - s.Handle(gost.HTTPHandler( - gost.UsersHandlerOption(url.UserPassword("admin", "123456")), - )) - ln, err := gost.WSSListener(":18443", &gost.WSOptions{TLSConfig: tlsConfig()}) + ln, err := gost.WSSListener(":18443", tlsConfig(), nil) if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.HTTPHandler( + gost.UsersHandlerOption(url.UserPassword("admin", "123456")), + ) + log.Fatal(s.Serve(ln, h)) } func kcpServer() { s := &gost.Server{} - s.Handle(gost.HTTPHandler()) ln, err := gost.KCPListener(":18388", nil) if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.HTTPHandler() + log.Fatal(s.Serve(ln, h)) } func tcpForwardServer() { s := &gost.Server{} - s.Handle(gost.TCPForwardHandler("ginuerzh.xyz:22")) ln, err := gost.TCPListener(":2222") if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.TCPForwardHandler("localhost:22") + log.Fatal(s.Serve(ln, h)) } func rtcpForwardServer() { s := &gost.Server{} - s.Handle(gost.RTCPForwardHandler(":1222", "ginuerzh.xyz:22")) ln, err := gost.RTCPForwardListener( ":1222", gost.NewChain( @@ -148,22 +146,22 @@ func rtcpForwardServer() { Transport: "tcp", Addr: "localhost:12345", User: url.UserPassword("admin", "123456"), - Client: gost.NewClient( - gost.SOCKS5Connector(url.UserPassword("admin", "123456")), - gost.TCPTransporter(), - ), + Client: &gost.Client{ + Connector: gost.SOCKS5Connector(url.UserPassword("admin", "123456")), + Transporter: gost.TCPTransporter(), + }, }, ), ) if err != nil { log.Fatal() } - log.Fatal(s.Serve(ln)) + h := gost.RTCPForwardHandler(":1222", "localhost:22") + log.Fatal(s.Serve(ln, h)) } func rudpForwardServer() { s := &gost.Server{} - s.Handle(gost.RUDPForwardHandler(":10053", "localhost:53")) ln, err := gost.RUDPForwardListener( ":10053", gost.NewChain( @@ -172,86 +170,67 @@ func rudpForwardServer() { Transport: "tcp", Addr: "localhost:12345", User: url.UserPassword("admin", "123456"), - Client: gost.NewClient( - gost.SOCKS5Connector(url.UserPassword("admin", "123456")), - gost.TCPTransporter(), - ), + Client: &gost.Client{ + Connector: gost.SOCKS5Connector(url.UserPassword("admin", "123456")), + Transporter: gost.TCPTransporter(), + }, }, ), ) if err != nil { log.Fatal() } - log.Fatal(s.Serve(ln)) + h := gost.RUDPForwardHandler(":10053", "localhost:53") + log.Fatal(s.Serve(ln, h)) } func tcpRedirectServer() { s := &gost.Server{} - s.Handle(gost.TCPRedirectHandler()) ln, err := gost.TCPListener(":8008") if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) -} - -func sshForwardServer() { - s := &gost.Server{} - s.Handle( - gost.SSHForwardHandler( - gost.AddrHandlerOption(":1222"), - gost.UsersHandlerOption(url.UserPassword("admin", "123456")), - gost.TLSConfigHandlerOption(tlsConfig()), - ), - ) - - ln, err := gost.TCPListener(":1222") - if err != nil { - log.Fatal(err) - } - log.Fatal(s.Serve(ln)) + h := gost.TCPRedirectHandler() + log.Fatal(s.Serve(ln, h)) } func sshTunnelServer() { s := &gost.Server{} - s.Handle( - gost.HTTPHandler( - gost.UsersHandlerOption(url.UserPassword("admin", "123456")), - ), - ) - ln, err := gost.SSHTunnelListener(":12222", &gost.SSHConfig{TLSConfig: tlsConfig()}) if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.HTTPHandler( + gost.UsersHandlerOption(url.UserPassword("admin", "123456")), + ) + log.Fatal(s.Serve(ln, h)) } func http2Server() { // http2.VerboseLogs = true s := &gost.Server{} - s.Handle(gost.HTTP2Handler( - gost.UsersHandlerOption(url.UserPassword("admin", "123456")), - )) ln, err := gost.TLSListener(":1443", tlsConfig()) // HTTP2 h2 mode // ln, err := gost.TCPListener(":1443") // HTTP2 h2c mode if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.HTTP2Handler( + gost.UsersHandlerOption(url.UserPassword("admin", "123456")), + ) + log.Fatal(s.Serve(ln, h)) } func quicServer() { s := &gost.Server{} - s.Handle(gost.HTTPHandler( - gost.UsersHandlerOption(url.UserPassword("admin", "123456")), - )) ln, err := gost.QUICListener("localhost:6121", &gost.QUICConfig{TLSConfig: tlsConfig()}) if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.HTTPHandler( + gost.UsersHandlerOption(url.UserPassword("admin", "123456")), + ) + log.Fatal(s.Serve(ln, h)) } var ( diff --git a/gost/examples/forward/direct/client.go b/gost/examples/forward/direct/client.go new file mode 100644 index 0000000..9b78b35 --- /dev/null +++ b/gost/examples/forward/direct/client.go @@ -0,0 +1,34 @@ +package main + +import ( + "log" + + "github.com/ginuerzh/gost/gost" +) + +func main() { + tcpForward() +} + +func tcpForward() { + chain := gost.NewChain( + gost.Node{ + Addr: "localhost:11222", + Client: &gost.Client{ + Connector: gost.SSHDirectForwardConnector(), + Transporter: gost.SSHForwardTransporter(), + }, + }, + ) + + s := &gost.Server{} + ln, err := gost.TCPListener(":11800") + if err != nil { + log.Fatal(err) + } + h := gost.TCPForwardHandler( + "localhost:22", + gost.ChainHandlerOption(chain), + ) + log.Fatal(s.Serve(ln, h)) +} diff --git a/gost/examples/forward/direct/server.go b/gost/examples/forward/direct/server.go new file mode 100644 index 0000000..2e68828 --- /dev/null +++ b/gost/examples/forward/direct/server.go @@ -0,0 +1,82 @@ +package main + +import ( + "crypto/tls" + "log" + + "github.com/ginuerzh/gost/gost" +) + +func main() { + sshForwardServer() +} + +func sshForwardServer() { + s := &gost.Server{} + ln, err := gost.TCPListener(":11222") + if err != nil { + log.Fatal(err) + } + h := gost.SSHForwardHandler( + gost.AddrHandlerOption(":11222"), + // gost.UsersHandlerOption(url.UserPassword("admin", "123456")), + gost.TLSConfigHandlerOption(tlsConfig()), + ) + log.Fatal(s.Serve(ln, h)) +} + +var ( + rawCert = []byte(`-----BEGIN CERTIFICATE----- +MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw +EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5 +MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N +0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw +hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4 +8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv +482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR +LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC +CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN +l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS +cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w +emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj +b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57 +lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg== +-----END CERTIFICATE-----`) + rawKey = []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N +ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo +N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv +GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh +Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg +IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n +IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H +r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe +yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru +kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk +TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU +k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o +/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK +HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg +HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY +CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d +JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr +pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt +/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD +xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL +vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX +1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt +7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4 +fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw +cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI= +-----END RSA PRIVATE KEY-----`) +) + +func tlsConfig() *tls.Config { + cert, err := tls.X509KeyPair(rawCert, rawKey) + if err != nil { + panic(err) + } + return &tls.Config{Certificates: []tls.Certificate{cert}} +} diff --git a/gost/examples/forward/remote/client.go b/gost/examples/forward/remote/client.go new file mode 100644 index 0000000..5091ada --- /dev/null +++ b/gost/examples/forward/remote/client.go @@ -0,0 +1,35 @@ +package main + +import ( + "log" + + "github.com/ginuerzh/gost/gost" +) + +func main() { + sshRemoteForward() +} + +func sshRemoteForward() { + chain := gost.NewChain( + gost.Node{ + Protocol: "forward", + Transport: "ssh", + Addr: "localhost:11222", + Client: &gost.Client{ + Connector: gost.SSHRemoteForwardConnector(), + Transporter: gost.SSHForwardTransporter(), + }, + }, + ) + + s := &gost.Server{} + ln, err := gost.RTCPForwardListener(":11800", chain) + if err != nil { + log.Fatal(err) + } + h := gost.RTCPForwardHandler( + "localhost:10000", + ) + log.Fatal(s.Serve(ln, h)) +} diff --git a/gost/examples/forward/remote/server.go b/gost/examples/forward/remote/server.go new file mode 100644 index 0000000..971bf98 --- /dev/null +++ b/gost/examples/forward/remote/server.go @@ -0,0 +1,82 @@ +package main + +import ( + "crypto/tls" + "log" + + "github.com/ginuerzh/gost/gost" +) + +func main() { + sshRemoteForwardServer() +} + +func sshRemoteForwardServer() { + s := &gost.Server{} + ln, err := gost.TCPListener(":11222") + if err != nil { + log.Fatal(err) + } + h := gost.SSHForwardHandler( + gost.AddrHandlerOption(":11222"), + // gost.UsersHandlerOption(url.UserPassword("admin", "123456")), + gost.TLSConfigHandlerOption(tlsConfig()), + ) + log.Fatal(s.Serve(ln, h)) +} + +var ( + rawCert = []byte(`-----BEGIN CERTIFICATE----- +MIIC+jCCAeKgAwIBAgIRAMlREhz8Miu1FQozsxbeqyMwDQYJKoZIhvcNAQELBQAw +EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNzA1MTkwNTM5MDJaFw0xODA1MTkwNTM5 +MDJaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCyfqvv0kDriciEAVIW6JaWYFCL9a19jj1wmAGmVGxV3kNsr01kpa6N +0EBqnrcy7WknhCt1d43CqhKtTcXgJ/J9phZVxlizb8sUB85hm+MvP0N3HCg3f0Jw +hLuMrPijS6xjyw0fKCK/p6OUYMIfo5cdqeZid2WV4Ozts5uRd6Dmy2kyBe8Zg1F4 +8YJGuTWZmL2L7uZUiPY4T3q9+1iucq3vUpxymVRi1BTXnTpx+C0GS8NNgeEmevHv +482vHM5DNflAQ+mvGZvBVduq/AfirCDnt2DIZm1DcZXLrY9F3EPrlRZexmAhCDGR +LIKnMmoGicBM11Aw1fDIfJAHynk43tjPAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC +CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAx8Lna8DcQv0bRB3L9i2+KRN +l/UhPCoFagxk1cZore4p0w+1m7OgigOoTpg5jh78DzVDhScZlgJ0bBVYp5rojeJS +cBDC9lCDcaXQfFmT5LykCAwIgw/gs+rw5Aq0y3D0m8CcqKosyZa9wnZ2cVy/+45w +emcSdboc65ueZScv38/W7aTUoVRcjyRUv0jv0zW0EPnnDlluVkeZo9spBhiTTwoj +b3zGODs6alTNIJwZIHNxxyOmfJPpVVp8BzGbMk7YBixSlZ/vbrrYV34TcSiy7J57 +lNNoVWM+OwiVk1+AEZfQDwaQfef5tsIkAZBUyITkkDKRhygtwM2110dejbEsgg== +-----END CERTIFICATE-----`) + rawKey = []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAsn6r79JA64nIhAFSFuiWlmBQi/WtfY49cJgBplRsVd5DbK9N +ZKWujdBAap63Mu1pJ4QrdXeNwqoSrU3F4CfyfaYWVcZYs2/LFAfOYZvjLz9Ddxwo +N39CcIS7jKz4o0usY8sNHygiv6ejlGDCH6OXHanmYndlleDs7bObkXeg5stpMgXv +GYNRePGCRrk1mZi9i+7mVIj2OE96vftYrnKt71KccplUYtQU1506cfgtBkvDTYHh +Jnrx7+PNrxzOQzX5QEPprxmbwVXbqvwH4qwg57dgyGZtQ3GVy62PRdxD65UWXsZg +IQgxkSyCpzJqBonATNdQMNXwyHyQB8p5ON7YzwIDAQABAoIBAQCG4doj3Apa8z+n +IShbT1+cOyQi34A+xOIA151Hh7xmFxN0afRd/iWt3JUQ/OcLgQRZbDM7DSD+3W5H +r+G7xfQkpwFxx/T3g58+f7ehYx+GcJQWyhxJ88zNIkBnyb4KCAE5WBOOW9IGajPe +yE9pgUGMlPsXpYoKfHIOHg+NGY1pWUGBfBNR2kGrbkpZMmyy5bGa8dyrwAFBFRru +kcmmKvate8UlbRspFtd4nR/GQLTBrcDJ1k1i1Su/4BpDuDeK6LPI8ZRePGqbdcxk +TS30lsdYozuGfjZ5Zu8lSIJ//+7RjfDg8r684dpWjpalq8Quen60ZrIs01CSbfyU +k8gOzTHhAoGBAOKhp41wXveegq+WylSXFyngm4bzF4dVdTRsSbJVk7NaOx1vCU6o +/xIHoGEQyLI6wF+EaHmY89/Qu6tSV97XyBbiKeskopv5iXS/BsWTHJ1VbCA1ZLmK +HgGllEkS0xfc9AdB7b6/K7LxAAQVKP3DtN6+6pSDZh9Sv2M1j0DbhkNbAoGBAMmg +HcMfExaaeskjHqyLudtKX+znwaIoumleOGuavohR4R+Fpk8Yv8Xhb5U7Yr4gk0vY +CFmhp1WAi6QMZ/8jePlKKXl3Ney827luoKiMczp2DoYE0t0u2Kw3LfkNKfjADZ7d +JI6xPJV9/X1erwjq+4UdKqrpOf05SY4nkBMcvr6dAoGAXzisvbDJNiFTp5Mj0Abr +pJzKvBjHegVeCXi2PkfWlzUCQYu1zWcURO8PY7k5mik1SuzHONAbJ578Oy+N3AOt +/m9oTXRHHmHqbzMUFU+KZlDN7XqBp7NwiCCZ/Vn7d7tOjP4Wdl68baL07sI1RupD +xJNS3LOY5PBPmc+XMRkLgKECgYEAgBNDlJSCrZMHeAjlDTncn53I/VXiPD2e3BvL +vx6W9UT9ueZN1GSmPO6M0MDeYmOS7VSXSUhUYQ28pkJzNTC1QbWITu4YxP7anBnX +1/kPoQ0pAJzDzVharlqGy3M/PBHTFRzogfO3xkY35ZFlokaR6uayGcr42Q+w16nt +7RYPXEkCgYEA3GQYirHnGZuQ952jMvduqnpgkJiSnr0fa+94Rwa1pAhxHLFMo5s4 +fqZOtqKPj2s5X1JR0VCey1ilCcaAhWeb3tXCpbYLZSbMtjtqwA6LUeGY+Xdupsjw +cfWIcOfHsIm2kP+RCxEnZf1XwiN9wyJeiUKlE0dqmx9j7F0Bm+7YDhI= +-----END RSA PRIVATE KEY-----`) +) + +func tlsConfig() *tls.Config { + cert, err := tls.X509KeyPair(rawCert, rawKey) + if err != nil { + panic(err) + } + return &tls.Config{Certificates: []tls.Certificate{cert}} +} diff --git a/gost/examples/quic/quicc.go b/gost/examples/quic/quicc.go index 145b97b..d8c03d3 100644 --- a/gost/examples/quic/quicc.go +++ b/gost/examples/quic/quicc.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "flag" "log" + "time" "github.com/ginuerzh/gost/gost" ) @@ -17,7 +18,7 @@ func init() { log.SetFlags(log.LstdFlags | log.Lshortfile) flag.StringVar(&laddr, "L", ":18080", "listen address") - flag.StringVar(&faddr, "F", ":6121", "forward address") + flag.StringVar(&faddr, "F", "localhost:6121", "forward address") flag.BoolVar(&quiet, "q", false, "quiet mode") flag.BoolVar(&gost.Debug, "d", false, "debug mode") flag.Parse() @@ -33,23 +34,23 @@ func main() { Protocol: "socks5", Transport: "quic", Addr: faddr, - Client: gost.NewClient( - gost.SOCKS5Connector(nil), - gost.QUICTransporter(nil), - ), + Client: &gost.Client{ + Connector: gost.SOCKS5Connector(nil), + Transporter: gost.QUICTransporter(&gost.QUICConfig{Timeout: 30 * time.Second, KeepAlive: true}), + }, }, ) s := &gost.Server{} - s.Handle(gost.SOCKS5Handler( - gost.ChainHandlerOption(chain), - gost.TLSConfigHandlerOption(tlsConfig()), - )) ln, err := gost.TCPListener(laddr) if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.SOCKS5Handler( + gost.ChainHandlerOption(chain), + gost.TLSConfigHandlerOption(tlsConfig()), + ) + log.Fatal(s.Serve(ln, h)) } var ( diff --git a/gost/examples/quic/quics.go b/gost/examples/quic/quics.go index d03f6f0..1fa5bb1 100644 --- a/gost/examples/quic/quics.go +++ b/gost/examples/quic/quics.go @@ -33,16 +33,13 @@ func main() { func quicServer() { s := &gost.Server{} - s.Handle( - gost.SOCKS5Handler(gost.TLSConfigHandlerOption(tlsConfig())), - ) - ln, err := gost.QUICListener(laddr, &gost.QUICConfig{TLSConfig: tlsConfig()}) if err != nil { log.Fatal(err) } + h := gost.SOCKS5Handler(gost.TLSConfigHandlerOption(tlsConfig())) log.Println("server listen on", laddr) - log.Fatal(s.Serve(ln)) + log.Fatal(s.Serve(ln, h)) } var ( diff --git a/gost/examples/ssh/sshc.go b/gost/examples/ssh/sshc.go index a5fc674..098c8f2 100644 --- a/gost/examples/ssh/sshc.go +++ b/gost/examples/ssh/sshc.go @@ -33,23 +33,23 @@ func main() { Protocol: "socks5", Transport: "ssh", Addr: faddr, - Client: gost.NewClient( - gost.SOCKS5Connector(nil), - gost.SSHTunnelTransporter(), - ), + Client: &gost.Client{ + Connector: gost.SOCKS5Connector(nil), + Transporter: gost.SSHTunnelTransporter(), + }, }, ) s := &gost.Server{} - s.Handle(gost.SOCKS5Handler( - gost.ChainHandlerOption(chain), - gost.TLSConfigHandlerOption(tlsConfig()), - )) ln, err := gost.TCPListener(laddr) if err != nil { log.Fatal(err) } - log.Fatal(s.Serve(ln)) + h := gost.SOCKS5Handler( + gost.ChainHandlerOption(chain), + gost.TLSConfigHandlerOption(tlsConfig()), + ) + log.Fatal(s.Serve(ln, h)) } var ( diff --git a/gost/examples/ssh/sshd.go b/gost/examples/ssh/sshd.go index 0cfc2d2..5fa3eb8 100644 --- a/gost/examples/ssh/sshd.go +++ b/gost/examples/ssh/sshd.go @@ -33,16 +33,13 @@ func main() { func sshTunnelServer() { s := &gost.Server{} - s.Handle( - gost.SOCKS5Handler(gost.TLSConfigHandlerOption(tlsConfig())), - ) - ln, err := gost.SSHTunnelListener(laddr, &gost.SSHConfig{TLSConfig: tlsConfig()}) if err != nil { log.Fatal(err) } + h := gost.SOCKS5Handler(gost.TLSConfigHandlerOption(tlsConfig())) log.Println("server listen on", laddr) - log.Fatal(s.Serve(ln)) + log.Fatal(s.Serve(ln, h)) } var ( diff --git a/gost/forward.go b/gost/forward.go index 31b6c68..77d41da 100644 --- a/gost/forward.go +++ b/gost/forward.go @@ -74,16 +74,14 @@ func (h *udpForwardHandler) Handle(conn net.Conn) { } type rtcpForwardHandler struct { - laddr string raddr string options *HandlerOptions } // RTCPForwardHandler creates a server Handler for TCP remote port forwarding server. // The raddr is the remote address that the server will forward to. -func RTCPForwardHandler(laddr, raddr string, opts ...HandlerOption) Handler { +func RTCPForwardHandler(raddr string, opts ...HandlerOption) Handler { h := &rtcpForwardHandler{ - laddr: laddr, raddr: raddr, options: &HandlerOptions{}, } @@ -98,14 +96,14 @@ func (h *rtcpForwardHandler) Handle(conn net.Conn) { cc, err := net.DialTimeout("tcp", h.raddr, DialTimeout) if err != nil { - log.Logf("[rtcp] %s -> %s : %s", h.laddr, h.raddr, err) + log.Logf("[rtcp] %s -> %s : %s", conn.LocalAddr(), h.raddr, err) return } defer cc.Close() - log.Logf("[rtcp] %s <-> %s", h.laddr, h.raddr) + log.Logf("[rtcp] %s <-> %s", conn.LocalAddr(), h.raddr) transport(cc, conn) - log.Logf("[rtcp] %s >-< %s", h.laddr, h.raddr) + log.Logf("[rtcp] %s >-< %s", conn.LocalAddr(), h.raddr) } type rudpForwardHandler struct { @@ -217,9 +215,9 @@ func (l *udpForwardListener) Close() error { } type rtcpForwardListener struct { - addr net.Addr - chain *Chain - close chan struct{} + addr net.Addr + chain *Chain + closed chan struct{} } // RTCPForwardListener creates a Listener for TCP remote port forwarding server. @@ -230,33 +228,59 @@ func RTCPForwardListener(addr string, chain *Chain) (Listener, error) { } return &rtcpForwardListener{ - addr: laddr, - chain: chain, - close: make(chan struct{}), + addr: laddr, + chain: chain, + closed: make(chan struct{}), }, nil } func (l *rtcpForwardListener) Accept() (net.Conn, error) { select { - case <-l.close: + case <-l.closed: return nil, errors.New("closed") default: } - conn, err := l.chain.Conn() - if err != nil { - return nil, err + var tempDelay time.Duration + for { + conn, err := l.accept() + if err != nil { + if tempDelay == 0 { + tempDelay = 1000 * time.Millisecond + } else { + tempDelay *= 2 + } + if max := 6 * time.Second; tempDelay > max { + tempDelay = max + } + log.Logf("[ssh-rtcp] Accept error: %v; retrying in %v", err, tempDelay) + time.Sleep(tempDelay) + continue + } + return conn, nil } - cc, err := l.handshake(conn) - if err != nil { - conn.Close() - return nil, err - } - - return cc, nil } -func (l *rtcpForwardListener) handshake(conn net.Conn) (net.Conn, error) { +func (l *rtcpForwardListener) accept() (conn net.Conn, err error) { + lastNode := l.chain.LastNode() + if lastNode.Protocol == "forward" && lastNode.Transport == "ssh" { + conn, err = l.chain.Dial(l.addr.String()) + } else if lastNode.Protocol == "socks5" { + cc, er := l.chain.Conn() + if er != nil { + return nil, er + } + conn, err = l.waitConnectSOCKS5(cc) + if err != nil { + cc.Close() + } + } else { + err = errors.New("invalid chain") + } + return +} + +func (l *rtcpForwardListener) waitConnectSOCKS5(conn net.Conn) (net.Conn, error) { conn, err := socks5Handshake(conn, l.chain.LastNode().User) if err != nil { return nil, err @@ -301,7 +325,7 @@ func (l *rtcpForwardListener) Addr() net.Addr { } func (l *rtcpForwardListener) Close() error { - close(l.close) + close(l.closed) return nil } diff --git a/gost/node.go b/gost/node.go index 23e963e..ce72707 100644 --- a/gost/node.go +++ b/gost/node.go @@ -9,6 +9,7 @@ type Node struct { Addr string Protocol string Transport string - User *url.Userinfo + User *url.Userinfo Client *Client + Server *Server } diff --git a/gost/server.go b/gost/server.go index f7aa799..ed65b35 100644 --- a/gost/server.go +++ b/gost/server.go @@ -10,19 +10,23 @@ import ( // Server is a proxy server. type Server struct { - l net.Listener - handler Handler -} - -// Handle sets a handler for the server. -func (s *Server) Handle(h Handler) { - s.handler = h } // Serve serves as a proxy server. -func (s *Server) Serve(l net.Listener) error { +func (s *Server) Serve(l net.Listener, h Handler) error { defer l.Close() + if l == nil { + ln, err := TCPListener(":8080") + if err != nil { + return err + } + l = ln + } + if h == nil { + h = HTTPHandler() + } + var tempDelay time.Duration for { conn, e := l.Accept() @@ -43,7 +47,7 @@ func (s *Server) Serve(l net.Listener) error { return e } tempDelay = 0 - go s.handler.Handle(conn) + go h.Handle(conn) } } diff --git a/gost/ssh.go b/gost/ssh.go index 68528d9..98305bb 100644 --- a/gost/ssh.go +++ b/gost/ssh.go @@ -9,6 +9,7 @@ import ( "net" "net/url" "strconv" + "strings" "sync" "time" @@ -26,31 +27,88 @@ const ( GostSSHTunnelRequest = "gost-tunnel" // extended request type for ssh tunnel ) -type sshForwardConnector struct { +type sshDirectForwardConnector struct { } -func SSHForwardConnector() Connector { - return &sshForwardConnector{} +func SSHDirectForwardConnector() Connector { + return &sshDirectForwardConnector{} } -func (c *sshForwardConnector) Connect(conn net.Conn, addr string) (net.Conn, error) { - cc, ok := conn.(*sshNopConn) +func (c *sshDirectForwardConnector) Connect(conn net.Conn, raddr string) (net.Conn, error) { + cc, ok := conn.(*sshNopConn) // TODO: this is an ugly type assertion, need to find a better solution. if !ok { return nil, errors.New("ssh: wrong connection type") } - conn, err := cc.session.client.Dial("tcp", addr) + conn, err := cc.session.client.Dial("tcp", raddr) if err != nil { - log.Logf("[ssh-tcp] %s -> %s : %s", cc.session.addr, addr, err) + log.Logf("[ssh-tcp] %s -> %s : %s", cc.session.addr, raddr, err) return nil, err } return conn, nil } +type sshRemoteForwardConnector struct { +} + +func SSHRemoteForwardConnector() Connector { + return &sshRemoteForwardConnector{} +} + +func (c *sshRemoteForwardConnector) Connect(conn net.Conn, addr string) (net.Conn, error) { + cc, ok := conn.(*sshNopConn) // TODO: this is an ugly type assertion, need to find a better solution. + if !ok { + return nil, errors.New("ssh: wrong connection type") + } + + cc.session.once.Do(func() { + go func() { + defer log.Log("ssh-rtcp: session is closed") + defer close(cc.session.connChan) + + if cc.session == nil || cc.session.client == nil { + return + } + if strings.HasPrefix(addr, ":") { + addr = "0.0.0.0" + addr + } + ln, err := cc.session.client.Listen("tcp", addr) + if err != nil { + return + } + for { + rc, err := ln.Accept() + if err != nil { + log.Logf("[ssh-rtcp] %s <-> %s accpet : %s", ln.Addr(), addr, err) + return + } + + select { + case cc.session.connChan <- rc: + default: + log.Logf("[ssh-rtcp] %s - %s: connection queue is full", ln.Addr(), addr) + } + } + }() + }) + + sc, ok := <-cc.session.connChan + if !ok { + return nil, errors.New("ssh-rtcp: connection is closed") + } + return sc, nil +} + type sshForwardTransporter struct { sessions map[string]*sshSession sessionMutex sync.Mutex } +func SSHForwardTransporter() Transporter { + return &sshForwardTransporter{ + sessions: make(map[string]*sshSession), + } +} + func (tr *sshForwardTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) { opts := &DialOptions{} for _, option := range options { @@ -61,7 +119,7 @@ func (tr *sshForwardTransporter) Dial(addr string, options ...DialOption) (conn defer tr.sessionMutex.Unlock() session, ok := tr.sessions[addr] - if !ok { + if !ok || session.Closed() { if opts.Chain == nil { conn, err = net.DialTimeout("tcp", addr, opts.Timeout) } else { @@ -87,7 +145,8 @@ func (tr *sshForwardTransporter) Handshake(conn net.Conn, options ...HandshakeOp } config := ssh.ClientConfig{ - Timeout: opts.Timeout, + Timeout: opts.Timeout, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), } if opts.User != nil { config.User = opts.User.Username() @@ -101,6 +160,10 @@ func (tr *sshForwardTransporter) Handshake(conn net.Conn, options ...HandshakeOp defer tr.sessionMutex.Unlock() session, ok := tr.sessions[opts.Addr] + if session != nil && session.conn != conn { + conn.Close() + return nil, errors.New("ssh: unrecognized connection") + } if !ok || session.client == nil { sshConn, chans, reqs, err := ssh.NewClientConn(conn, opts.Addr, &config) if err != nil { @@ -110,13 +173,23 @@ func (tr *sshForwardTransporter) Handshake(conn net.Conn, options ...HandshakeOp } session = &sshSession{ - addr: opts.Addr, - conn: conn, - client: ssh.NewClient(sshConn, chans, reqs), - closed: make(chan struct{}), + addr: opts.Addr, + conn: conn, + client: ssh.NewClient(sshConn, chans, reqs), + closed: make(chan struct{}), + deaded: make(chan struct{}), + connChan: make(chan net.Conn, 1024), } tr.sessions[opts.Addr] = session + go session.Ping(opts.Interval, 1) + go session.waitServer() + go session.waitClose() } + if session.Closed() { + delete(tr.sessions, opts.Addr) + return nil, ErrSessionDead + } + return &sshNopConn{session: session}, nil } @@ -230,11 +303,13 @@ func (tr *sshTunnelTransporter) Multiplex() bool { } type sshSession struct { - addr string - conn net.Conn - client *ssh.Client - closed chan struct{} - deaded chan struct{} + addr string + conn net.Conn + client *ssh.Client + closed chan struct{} + deaded chan struct{} + once sync.Once + connChan chan net.Conn } func (s *sshSession) Ping(interval time.Duration, retries int) { @@ -334,7 +409,7 @@ func SSHForwardHandler(opts ...HandlerOption) Handler { if h.options.TLSConfig != nil && len(h.options.TLSConfig.Certificates) > 0 { signer, err := ssh.NewSignerFromKey(h.options.TLSConfig.Certificates[0].PrivateKey) if err != nil { - log.Log("[sshf]", err) + log.Log("[ssh-forward]", err) } h.config.AddHostKey(signer) } @@ -345,15 +420,15 @@ func SSHForwardHandler(opts ...HandlerOption) Handler { func (h *sshForwardHandler) Handle(conn net.Conn) { sshConn, chans, reqs, err := ssh.NewServerConn(conn, h.config) if err != nil { - log.Logf("[sshf] %s -> %s : %s", conn.RemoteAddr(), h.options.Addr, err) + log.Logf("[ssh-forward] %s -> %s : %s", conn.RemoteAddr(), h.options.Addr, err) conn.Close() return } defer sshConn.Close() - log.Logf("[sshf] %s <-> %s", conn.RemoteAddr(), h.options.Addr) + log.Logf("[ssh-forward] %s <-> %s", conn.RemoteAddr(), h.options.Addr) h.handleForward(sshConn, chans, reqs) - log.Logf("[sshf] %s >-< %s", conn.RemoteAddr(), h.options.Addr) + log.Logf("[ssh-forward] %s >-< %s", conn.RemoteAddr(), h.options.Addr) } func (h *sshForwardHandler) handleForward(conn ssh.Conn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) { @@ -366,7 +441,7 @@ func (h *sshForwardHandler) handleForward(conn ssh.Conn, chans <-chan ssh.NewCha case RemoteForwardRequest: go h.tcpipForwardRequest(conn, req, quit) default: - log.Log("[ssh] unknown channel type:", req.Type) + // log.Log("[ssh] unknown channel type:", req.Type) if req.WantReply { req.Reply(false, nil) } diff --git a/gost/ws.go b/gost/ws.go index 204be1b..fb56254 100644 --- a/gost/ws.go +++ b/gost/ws.go @@ -19,7 +19,6 @@ type WSOptions struct { WriteBufferSize int HandshakeTimeout time.Duration EnableCompression bool - TLSConfig *tls.Config } type websocketConn struct { @@ -243,7 +242,7 @@ type wssListener struct { } // WSSListener creates a Listener for websocket secure proxy server. -func WSSListener(addr string, options *WSOptions) (Listener, error) { +func WSSListener(addr string, tlsConfig *tls.Config, options *WSOptions) (Listener, error) { tcpAddr, err := net.ResolveTCPAddr("tcp", addr) if err != nil { return nil, err @@ -269,7 +268,7 @@ func WSSListener(addr string, options *WSOptions) (Listener, error) { mux.Handle("/ws", http.HandlerFunc(l.upgrade)) l.srv = &http.Server{ Addr: addr, - TLSConfig: options.TLSConfig, + TLSConfig: tlsConfig, Handler: mux, } @@ -279,7 +278,7 @@ func WSSListener(addr string, options *WSOptions) (Listener, error) { } go func() { - err := l.srv.Serve(tls.NewListener(tcpKeepAliveListener{ln}, options.TLSConfig)) + err := l.srv.Serve(tls.NewListener(tcpKeepAliveListener{ln}, tlsConfig)) if err != nil { l.errChan <- err }