Przeglądaj źródła

frpc: add support for connecting server through http proxies, see #67

fatedier 8 lat temu
rodzic
commit
e262ac6abd

+ 2 - 0
conf/frpc.ini

@@ -4,6 +4,8 @@
 # in square brackets, as in "[::1]:80", "[ipv6-host]:http" or "[ipv6-host%zone]:80"
 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
+# http_proxy = http://user:pwd@192.168.1.128:8080
 # console or real logFile path like ./frpc.log
 log_file = ./frpc.log
 # debug, info, warn, error

+ 5 - 1
src/cmd/frpc/control.go

@@ -130,7 +130,11 @@ func msgSender(cli *client.ProxyClient, c *conn.Conn, msgSendChan chan interface
 }
 
 func loginToServer(cli *client.ProxyClient) (c *conn.Conn, err error) {
-	c, err = conn.ConnectServer(client.ServerAddr, client.ServerPort)
+	if client.HttpProxy == "" {
+		c, err = conn.ConnectServer(fmt.Sprintf("%s:%d", client.ServerAddr, client.ServerPort))
+	} else {
+		c, err = conn.ConnectServerByHttpProxy(client.HttpProxy, fmt.Sprintf("%s:%d", client.ServerAddr, client.ServerPort))
+	}
 	if err != nil {
 		log.Error("ProxyName [%s], connect to server [%s:%d] error, %v", cli.Name, client.ServerAddr, client.ServerPort, err)
 		return

+ 6 - 2
src/models/client/client.go

@@ -37,7 +37,7 @@ type ProxyClient struct {
 }
 
 func (p *ProxyClient) GetLocalConn() (c *conn.Conn, err error) {
-	c, err = conn.ConnectServer(p.LocalIp, p.LocalPort)
+	c, err = conn.ConnectServer(fmt.Sprintf("%s:%d", p.LocalIp, p.LocalPort))
 	if err != nil {
 		log.Error("ProxyName [%s], connect to local port error, %v", p.Name, err)
 	}
@@ -51,7 +51,11 @@ func (p *ProxyClient) GetRemoteConn(addr string, port int64) (c *conn.Conn, err
 		}
 	}()
 
-	c, err = conn.ConnectServer(addr, port)
+	if HttpProxy == "" {
+		c, err = conn.ConnectServer(fmt.Sprintf("%s:%d", addr, port))
+	} else {
+		c, err = conn.ConnectServerByHttpProxy(HttpProxy, fmt.Sprintf("%s:%d", addr, port))
+	}
 	if err != nil {
 		log.Error("ProxyName [%s], connect to server [%s:%d] error, %v", p.Name, addr, port, err)
 		return

+ 10 - 0
src/models/client/config.go

@@ -16,6 +16,7 @@ package client
 
 import (
 	"fmt"
+	"os"
 	"strconv"
 	"strings"
 
@@ -26,6 +27,7 @@ import (
 var (
 	ServerAddr        string = "0.0.0.0"
 	ServerPort        int64  = 7000
+	HttpProxy         string = ""
 	LogFile           string = "console"
 	LogWay            string = "console"
 	LogLevel          string = "info"
@@ -57,6 +59,14 @@ func LoadConf(confFile string) (err error) {
 		ServerPort, _ = strconv.ParseInt(tmpStr, 10, 64)
 	}
 
+	tmpStr, ok = conf.Get("common", "http_proxy")
+	if ok {
+		HttpProxy = tmpStr
+	} else {
+		// get http_proxy from env
+		HttpProxy = os.Getenv("http_proxy")
+	}
+
 	tmpStr, ok = conf.Get("common", "log_file")
 	if ok {
 		LogFile = tmpStr

+ 48 - 2
src/utils/conn/conn.go

@@ -16,9 +16,12 @@ package conn
 
 import (
 	"bufio"
+	"encoding/base64"
 	"fmt"
 	"io"
 	"net"
+	"net/http"
+	"net/url"
 	"strings"
 	"sync"
 	"time"
@@ -104,9 +107,9 @@ func NewConn(conn net.Conn) (c *Conn) {
 	return c
 }
 
-func ConnectServer(host string, port int64) (c *Conn, err error) {
+func ConnectServer(addr string) (c *Conn, err error) {
 	c = &Conn{}
-	servertAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", host, port))
+	servertAddr, err := net.ResolveTCPAddr("tcp", addr)
 	if err != nil {
 		return
 	}
@@ -120,6 +123,49 @@ func ConnectServer(host string, port int64) (c *Conn, err error) {
 	return c, nil
 }
 
+func ConnectServerByHttpProxy(httpProxy string, serverAddr string) (c *Conn, err error) {
+	var proxyUrl *url.URL
+	if proxyUrl, err = url.Parse(httpProxy); err != nil {
+		return
+	}
+
+	var proxyAuth string
+	if proxyUrl.User != nil {
+		proxyAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(proxyUrl.User.String()))
+	}
+
+	if proxyUrl.Scheme != "http" {
+		err = fmt.Errorf("Proxy URL scheme must be http, not [%s]", proxyUrl.Scheme)
+		return
+	}
+
+	if c, err = ConnectServer(proxyUrl.Host); err != nil {
+		return
+	}
+
+	req, err := http.NewRequest("CONNECT", "https://"+serverAddr, nil)
+	if err != nil {
+		return
+	}
+	if proxyAuth != "" {
+		req.Header.Set("Proxy-Authorization", proxyAuth)
+	}
+	req.Header.Set("User-Agent", "Mozilla/5.0")
+	req.Write(c.TcpConn)
+
+	resp, err := http.ReadResponse(bufio.NewReader(c), req)
+	if err != nil {
+		return
+	}
+	resp.Body.Close()
+	if resp.StatusCode != 200 {
+		err = fmt.Errorf("ConnectServer using proxy error, StatusCode [%d]", resp.StatusCode)
+		return
+	}
+
+	return
+}
+
 // if the tcpConn is different with c.TcpConn
 // you should call c.Close() first
 func (c *Conn) SetTcpConn(tcpConn net.Conn) {

+ 1 - 1
test/func_test.go

@@ -19,7 +19,7 @@ var (
 )
 
 func TestEchoServer(t *testing.T) {
-	c, err := conn.ConnectServer("0.0.0.0", ECHO_PORT)
+	c, err := conn.ConnectServer(fmt.Sprintf("0.0.0.0:%d", ECHO_PORT))
 	if err != nil {
 		t.Fatalf("connect to echo server error: %v", err)
 	}