diff --git a/src/mod/streamproxy/handler.go b/src/mod/streamproxy/handler.go index bc78148..22d523a 100644 --- a/src/mod/streamproxy/handler.go +++ b/src/mod/streamproxy/handler.go @@ -47,15 +47,17 @@ func (m *Manager) HandleAddProxyConfig(w http.ResponseWriter, r *http.Request) { useTCP, _ := utils.PostBool(r, "useTCP") useUDP, _ := utils.PostBool(r, "useUDP") + useProxyProtocol, _ := utils.PostBool(r, "useProxyProtocol") //Create the target config newConfigUUID := m.NewConfig(&ProxyRelayOptions{ - Name: name, - ListeningAddr: strings.TrimSpace(listenAddr), - ProxyAddr: strings.TrimSpace(proxyAddr), - Timeout: timeout, - UseTCP: useTCP, - UseUDP: useUDP, + Name: name, + ListeningAddr: strings.TrimSpace(listenAddr), + ProxyAddr: strings.TrimSpace(proxyAddr), + Timeout: timeout, + UseTCP: useTCP, + UseUDP: useUDP, + UseProxyProtocol: useProxyProtocol, }) js, _ := json.Marshal(newConfigUUID) @@ -75,6 +77,7 @@ func (m *Manager) HandleEditProxyConfigs(w http.ResponseWriter, r *http.Request) proxyAddr, _ := utils.PostPara(r, "proxyAddr") useTCP, _ := utils.PostBool(r, "useTCP") useUDP, _ := utils.PostBool(r, "useUDP") + useProxyProtocol, _ := utils.PostBool(r, "useProxyProtocol") newTimeoutStr, _ := utils.PostPara(r, "timeout") newTimeout := -1 @@ -87,7 +90,7 @@ func (m *Manager) HandleEditProxyConfigs(w http.ResponseWriter, r *http.Request) } // Call the EditConfig method to modify the configuration - err = m.EditConfig(configUUID, newName, listenAddr, proxyAddr, useTCP, useUDP, newTimeout) + err = m.EditConfig(configUUID, newName, listenAddr, proxyAddr, useTCP, useUDP, useProxyProtocol, newTimeout) if err != nil { utils.SendErrorResponse(w, err.Error()) return diff --git a/src/mod/streamproxy/streamproxy.go b/src/mod/streamproxy/streamproxy.go index 36155a3..4e3cc90 100644 --- a/src/mod/streamproxy/streamproxy.go +++ b/src/mod/streamproxy/streamproxy.go @@ -24,12 +24,13 @@ import ( */ type ProxyRelayOptions struct { - Name string - ListeningAddr string - ProxyAddr string - Timeout int - UseTCP bool - UseUDP bool + Name string + ListeningAddr string + ProxyAddr string + Timeout int + UseTCP bool + UseUDP bool + UseProxyProtocol bool } type ProxyRelayConfig struct { @@ -41,6 +42,7 @@ type ProxyRelayConfig struct { ProxyTargetAddr string //Proxy target address UseTCP bool //Enable TCP proxy UseUDP bool //Enable UDP proxy + UseProxyProtocol bool //Enable Proxy Protocol Timeout int //Timeout for connection in sec tcpStopChan chan bool //Stop channel for TCP listener udpStopChan chan bool //Stop channel for UDP listener @@ -157,6 +159,7 @@ func (m *Manager) NewConfig(config *ProxyRelayOptions) string { ProxyTargetAddr: config.ProxyAddr, UseTCP: config.UseTCP, UseUDP: config.UseUDP, + UseProxyProtocol: config.UseProxyProtocol, Timeout: config.Timeout, tcpStopChan: nil, udpStopChan: nil, @@ -181,7 +184,7 @@ func (m *Manager) GetConfigByUUID(configUUID string) (*ProxyRelayConfig, error) } // Edit the config based on config UUID, leave empty for unchange fields -func (m *Manager) EditConfig(configUUID string, newName string, newListeningAddr string, newProxyAddr string, useTCP bool, useUDP bool, newTimeout int) error { +func (m *Manager) EditConfig(configUUID string, newName string, newListeningAddr string, newProxyAddr string, useTCP bool, useUDP bool, useProxyProtocol bool, newTimeout int) error { // Find the config with the specified UUID foundConfig, err := m.GetConfigByUUID(configUUID) if err != nil { @@ -201,6 +204,7 @@ func (m *Manager) EditConfig(configUUID string, newName string, newListeningAddr foundConfig.UseTCP = useTCP foundConfig.UseUDP = useUDP + foundConfig.UseProxyProtocol = useProxyProtocol if newTimeout != -1 { if newTimeout < 0 { diff --git a/src/mod/streamproxy/tcpprox.go b/src/mod/streamproxy/tcpprox.go index 6fcaed0..f817edf 100644 --- a/src/mod/streamproxy/tcpprox.go +++ b/src/mod/streamproxy/tcpprox.go @@ -2,6 +2,7 @@ package streamproxy import ( "errors" + "fmt" "io" "log" "net" @@ -43,6 +44,23 @@ func connCopy(conn1 net.Conn, conn2 net.Conn, wg *sync.WaitGroup, accumulator *a wg.Done() } +func writeProxyProtocolHeaderV1(dst net.Conn, src net.Conn) error { + clientAddr, ok1 := src.RemoteAddr().(*net.TCPAddr) + proxyAddr, ok2 := src.LocalAddr().(*net.TCPAddr) + if !ok1 || !ok2 { + return errors.New("invalid TCP address for proxy protocol") + } + + header := fmt.Sprintf("PROXY TCP4 %s %s %d %d\r\n", + clientAddr.IP.String(), + proxyAddr.IP.String(), + clientAddr.Port, + proxyAddr.Port) + + _, err := dst.Write([]byte(header)) + return err +} + func forward(conn1 net.Conn, conn2 net.Conn, aTob *atomic.Int64, bToa *atomic.Int64) { log.Printf("[+] start transmit. [%s],[%s] <-> [%s],[%s] \n", conn1.LocalAddr().String(), conn1.RemoteAddr().String(), conn2.LocalAddr().String(), conn2.RemoteAddr().String()) var wg sync.WaitGroup @@ -140,6 +158,20 @@ func (c *ProxyRelayConfig) Port2host(allowPort string, targetAddress string, sto return } log.Println("[→]", "connect target address ["+targetAddress+"] success.") + + if c.UseProxyProtocol { + log.Println("[+]", "write proxy protocol header to target address ["+targetAddress+"]") + err = writeProxyProtocolHeaderV1(target, conn) + if err != nil { + log.Println("[x]", "Write proxy protocol header faild: ", err) + target.Close() + conn.Close() + log.Println("[←]", "close the connect at local:["+conn.LocalAddr().String()+"] and remote:["+conn.RemoteAddr().String()+"]") + time.Sleep(time.Duration(c.Timeout) * time.Second) + return + } + } + forward(target, conn, &c.aTobAccumulatedByteTransfer, &c.bToaAccumulatedByteTransfer) }(targetAddress) } diff --git a/src/web/components/streamprox.html b/src/web/components/streamprox.html index d30ddbd..f1593d5 100644 --- a/src/web/components/streamprox.html +++ b/src/web/components/streamprox.html @@ -73,6 +73,14 @@ Forward UDP request on this listening socket +
+
+ + +
+
@@ -195,6 +203,10 @@ modeText.push("UDP") } + if (config.UseProxyProtocol){ + modeText.push("ProxyProtocol V1") + } + modeText = modeText.join(" & ") var thisConfig = encodeURIComponent(JSON.stringify(config)); @@ -252,6 +264,14 @@ $(checkboxEle).checkbox("set unchecked"); } return; + }else if (key == "UseProxyProtocol"){ + let checkboxEle = $("#streamProxyForm input[name=useProxyProtocol]").parent(); + if (value === true){ + $(checkboxEle).checkbox("set checked"); + }else{ + $(checkboxEle).checkbox("set unchecked"); + } + return; }else if (key == "ListeningAddress"){ field = $("#streamProxyForm input[name=listenAddr]"); }else if (key == "ProxyTargetAddr"){ @@ -301,6 +321,7 @@ proxyAddr: $("#streamProxyForm input[name=proxyAddr]").val().trim(), useTCP: $("#streamProxyForm input[name=useTCP]")[0].checked , useUDP: $("#streamProxyForm input[name=useUDP]")[0].checked , + useProxyProtocol: $("#streamProxyForm input[name=useProxyProtocol]")[0].checked , timeout: parseInt($("#streamProxyForm input[name=timeout]").val().trim()), }, success: function(response) {