From 30af32728ab75b1f47c455aaa8e0e49fe078791a Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 4 May 2018 18:36:38 +0800 Subject: [PATCH] frpc: support connectiong frps by socks5 proxy --- client/control.go | 4 +-- conf/frpc_full.ini | 3 +- tests/func_test.go | 2 +- utils/net/conn.go | 4 +-- utils/net/tcp.go | 68 +++++++++++++++++++++++++++++++++++----------- 5 files changed, 59 insertions(+), 22 deletions(-) diff --git a/client/control.go b/client/control.go index ba37a2db..745a5ff6 100644 --- a/client/control.go +++ b/client/control.go @@ -185,7 +185,7 @@ func (ctl *Control) login() (err error) { ctl.session.Close() } - conn, err := frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol, + conn, err := frpNet.ConnectServerByProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol, fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort)) if err != nil { return err @@ -253,7 +253,7 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) { } conn = frpNet.WrapConn(stream) } else { - conn, err = frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol, + conn, err = frpNet.ConnectServerByProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol, fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort)) if err != nil { ctl.Warn("start new connection to server error: %v", err) diff --git a/conf/frpc_full.ini b/conf/frpc_full.ini index eeb3c835..98a5b410 100644 --- a/conf/frpc_full.ini +++ b/conf/frpc_full.ini @@ -5,9 +5,10 @@ server_addr = 0.0.0.0 server_port = 7000 -# if you want to connect frps by http proxy, you can set http_proxy here or in global environment variables +# if you want to connect frps by http proxy or socks5 proxy, you can set http_proxy here or in global environment variables # it only works when protocol is tcp # http_proxy = http://user:passwd@192.168.1.128:8080 +# http_proxy = socks5://user:passwd@192.168.1.128:1080 # console or real logFile path like ./frpc.log log_file = ./frpc.log diff --git a/tests/func_test.go b/tests/func_test.go index 94b1d040..a2f4388d 100644 --- a/tests/func_test.go +++ b/tests/func_test.go @@ -279,7 +279,7 @@ func TestPluginHttpProxy(t *testing.T) { } // connect method - conn, err := net.ConnectTcpServerByHttpProxy("http://"+addr, fmt.Sprintf("127.0.0.1:%d", TEST_TCP_FRP_PORT)) + conn, err := net.ConnectTcpServerByProxy("http://"+addr, fmt.Sprintf("127.0.0.1:%d", TEST_TCP_FRP_PORT)) if assert.NoError(err) { res, err := sendTcpMsgByConn(conn, TEST_TCP_ECHO_STR) assert.NoError(err) diff --git a/utils/net/conn.go b/utils/net/conn.go index 2c60ce2f..e3473a84 100644 --- a/utils/net/conn.go +++ b/utils/net/conn.go @@ -122,10 +122,10 @@ func ConnectServer(protocol string, addr string) (c Conn, err error) { } } -func ConnectServerByHttpProxy(httpProxy string, protocol string, addr string) (c Conn, err error) { +func ConnectServerByProxy(proxyUrl string, protocol string, addr string) (c Conn, err error) { switch protocol { case "tcp": - return ConnectTcpServerByHttpProxy(httpProxy, addr) + return ConnectTcpServerByProxy(proxyUrl, addr) case "kcp": // http proxy is not supported for kcp return ConnectServer(protocol, addr) diff --git a/utils/net/tcp.go b/utils/net/tcp.go index b2c5a2b6..c9fbbbd3 100644 --- a/utils/net/tcp.go +++ b/utils/net/tcp.go @@ -23,6 +23,8 @@ import ( "net/url" "github.com/fatedier/frp/utils/log" + + "golang.org/x/net/proxy" ) type TcpListener struct { @@ -93,7 +95,7 @@ type TcpConn struct { log.Logger } -func NewTcpConn(conn *net.TCPConn) (c *TcpConn) { +func NewTcpConn(conn net.Conn) (c *TcpConn) { c = &TcpConn{ Conn: conn, Logger: log.NewPrefixLogger(""), @@ -114,28 +116,41 @@ func ConnectTcpServer(addr string) (c Conn, err error) { return } -// ConnectTcpServerByHttpProxy try to connect remote server by http proxy. -// If httpProxy is empty, it will connect server directly. -func ConnectTcpServerByHttpProxy(httpProxy string, serverAddr string) (c Conn, err error) { - if httpProxy == "" { +// ConnectTcpServerByProxy try to connect remote server by proxy. +func ConnectTcpServerByProxy(proxyStr string, serverAddr string) (c Conn, err error) { + if proxyStr == "" { return ConnectTcpServer(serverAddr) } - var proxyUrl *url.URL - if proxyUrl, err = url.Parse(httpProxy); err != nil { + var ( + proxyUrl *url.URL + username string + passwd string + ) + if proxyUrl, err = url.Parse(proxyStr); err != nil { return } + if proxyUrl.User != nil { + username = proxyUrl.User.Username() + passwd, _ = proxyUrl.User.Password() + } + switch proxyUrl.Scheme { + case "http": + return ConnectTcpServerByHttpProxy(proxyUrl, username, passwd, serverAddr) + case "socks5": + return ConnectTcpServerBySocks5Proxy(proxyUrl, username, passwd, serverAddr) + default: + err = fmt.Errorf("Proxy URL scheme must be http or socks5, not [%s]", proxyUrl.Scheme) + return + } +} + +// ConnectTcpServerByHttpProxy try to connect remote server by http proxy. +func ConnectTcpServerByHttpProxy(proxyUrl *url.URL, user string, passwd string, serverAddr string) (c Conn, err error) { var proxyAuth string if proxyUrl.User != nil { - username := proxyUrl.User.Username() - passwd, _ := proxyUrl.User.Password() - proxyAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+passwd)) - } - - if proxyUrl.Scheme != "http" { - err = fmt.Errorf("Proxy URL scheme must be http, not [%s]", proxyUrl.Scheme) - return + proxyAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(user+":"+passwd)) } if c, err = ConnectTcpServer(proxyUrl.Host); err != nil { @@ -161,6 +176,27 @@ func ConnectTcpServerByHttpProxy(httpProxy string, serverAddr string) (c Conn, e err = fmt.Errorf("ConnectTcpServer using proxy error, StatusCode [%d]", resp.StatusCode) return } - + return +} + +func ConnectTcpServerBySocks5Proxy(proxyUrl *url.URL, user string, passwd string, serverAddr string) (c Conn, err error) { + var auth *proxy.Auth + if proxyUrl.User != nil { + auth = &proxy.Auth{ + User: user, + Password: passwd, + } + } + + dialer, err := proxy.SOCKS5("tcp", proxyUrl.Host, auth, nil) + if err != nil { + return nil, err + } + + var conn net.Conn + if conn, err = dialer.Dial("tcp", serverAddr); err != nil { + return + } + c = NewTcpConn(conn) return }