Переглянути джерело

frpc: support connectiong frps by socks5 proxy

fatedier 6 роки тому
батько
коміт
30af32728a
5 змінених файлів з 57 додано та 20 видалено
  1. 2 2
      client/control.go
  2. 2 1
      conf/frpc_full.ini
  3. 1 1
      tests/func_test.go
  4. 2 2
      utils/net/conn.go
  5. 50 14
      utils/net/tcp.go

+ 2 - 2
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)

+ 2 - 1
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

+ 1 - 1
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)

+ 2 - 2
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)

+ 50 - 14
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,29 +116,42 @@ 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
 	}
-
-	var proxyAuth string
 	if proxyUrl.User != nil {
-		username := proxyUrl.User.Username()
-		passwd, _ := proxyUrl.User.Password()
-		proxyAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+passwd))
+		username = proxyUrl.User.Username()
+		passwd, _ = proxyUrl.User.Password()
 	}
 
-	if proxyUrl.Scheme != "http" {
-		err = fmt.Errorf("Proxy URL scheme must be http, not [%s]", proxyUrl.Scheme)
+	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 {
+		proxyAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(user+":"+passwd))
+	}
 
 	if c, err = ConnectTcpServer(proxyUrl.Host); err != nil {
 		return
@@ -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
 }