mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-04 22:57:20 +02:00
Added stream proxy UDP support
+ Added UDP support #147 (wip) + Updated structure for proxy storage + Renamed TCPprox module to streamproxy + Added multi selection for white / blacklist #176
This commit is contained in:
parent
136d1ecafb
commit
c6f7f37aaf
15
src/api.go
15
src/api.go
@ -141,14 +141,13 @@ func initAPIs() {
|
|||||||
authRouter.HandleFunc("/api/gan/members/delete", ganManager.HandleMemberDelete)
|
authRouter.HandleFunc("/api/gan/members/delete", ganManager.HandleMemberDelete)
|
||||||
|
|
||||||
//TCP Proxy
|
//TCP Proxy
|
||||||
authRouter.HandleFunc("/api/tcpprox/config/add", tcpProxyManager.HandleAddProxyConfig)
|
authRouter.HandleFunc("/api/streamprox/config/add", streamProxyManager.HandleAddProxyConfig)
|
||||||
authRouter.HandleFunc("/api/tcpprox/config/edit", tcpProxyManager.HandleEditProxyConfigs)
|
authRouter.HandleFunc("/api/streamprox/config/edit", streamProxyManager.HandleEditProxyConfigs)
|
||||||
authRouter.HandleFunc("/api/tcpprox/config/list", tcpProxyManager.HandleListConfigs)
|
authRouter.HandleFunc("/api/streamprox/config/list", streamProxyManager.HandleListConfigs)
|
||||||
authRouter.HandleFunc("/api/tcpprox/config/start", tcpProxyManager.HandleStartProxy)
|
authRouter.HandleFunc("/api/streamprox/config/start", streamProxyManager.HandleStartProxy)
|
||||||
authRouter.HandleFunc("/api/tcpprox/config/stop", tcpProxyManager.HandleStopProxy)
|
authRouter.HandleFunc("/api/streamprox/config/stop", streamProxyManager.HandleStopProxy)
|
||||||
authRouter.HandleFunc("/api/tcpprox/config/delete", tcpProxyManager.HandleRemoveProxy)
|
authRouter.HandleFunc("/api/streamprox/config/delete", streamProxyManager.HandleRemoveProxy)
|
||||||
authRouter.HandleFunc("/api/tcpprox/config/status", tcpProxyManager.HandleGetProxyStatus)
|
authRouter.HandleFunc("/api/streamprox/config/status", streamProxyManager.HandleGetProxyStatus)
|
||||||
authRouter.HandleFunc("/api/tcpprox/config/validate", tcpProxyManager.HandleConfigValidate)
|
|
||||||
|
|
||||||
//mDNS APIs
|
//mDNS APIs
|
||||||
authRouter.HandleFunc("/api/mdns/list", HandleMdnsListing)
|
authRouter.HandleFunc("/api/mdns/list", HandleMdnsListing)
|
||||||
|
@ -28,7 +28,7 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/sshprox"
|
"imuslab.com/zoraxy/mod/sshprox"
|
||||||
"imuslab.com/zoraxy/mod/statistic"
|
"imuslab.com/zoraxy/mod/statistic"
|
||||||
"imuslab.com/zoraxy/mod/statistic/analytic"
|
"imuslab.com/zoraxy/mod/statistic/analytic"
|
||||||
"imuslab.com/zoraxy/mod/tcpprox"
|
"imuslab.com/zoraxy/mod/streamproxy"
|
||||||
"imuslab.com/zoraxy/mod/tlscert"
|
"imuslab.com/zoraxy/mod/tlscert"
|
||||||
"imuslab.com/zoraxy/mod/uptime"
|
"imuslab.com/zoraxy/mod/uptime"
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
@ -54,7 +54,7 @@ var (
|
|||||||
name = "Zoraxy"
|
name = "Zoraxy"
|
||||||
version = "3.0.6"
|
version = "3.0.6"
|
||||||
nodeUUID = "generic"
|
nodeUUID = "generic"
|
||||||
development = false //Set this to false to use embedded web fs
|
development = true //Set this to false to use embedded web fs
|
||||||
bootTime = time.Now().Unix()
|
bootTime = time.Now().Unix()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -79,7 +79,7 @@ var (
|
|||||||
mdnsScanner *mdns.MDNSHost //mDNS discovery services
|
mdnsScanner *mdns.MDNSHost //mDNS discovery services
|
||||||
ganManager *ganserv.NetworkManager //Global Area Network Manager
|
ganManager *ganserv.NetworkManager //Global Area Network Manager
|
||||||
webSshManager *sshprox.Manager //Web SSH connection service
|
webSshManager *sshprox.Manager //Web SSH connection service
|
||||||
tcpProxyManager *tcpprox.Manager //TCP Proxy Manager
|
streamProxyManager *streamproxy.Manager //Stream Proxy Manager for TCP / UDP forwarding
|
||||||
acmeHandler *acme.ACMEHandler //Handler for ACME Certificate renew
|
acmeHandler *acme.ACMEHandler //Handler for ACME Certificate renew
|
||||||
acmeAutoRenewer *acme.AutoRenewer //Handler for ACME auto renew ticking
|
acmeAutoRenewer *acme.AutoRenewer //Handler for ACME auto renew ticking
|
||||||
staticWebServer *webserv.WebServer //Static web server for hosting simple stuffs
|
staticWebServer *webserv.WebServer //Static web server for hosting simple stuffs
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package tcpprox
|
package streamproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/utils"
|
"imuslab.com/zoraxy/mod/utils"
|
||||||
)
|
)
|
||||||
@ -22,13 +23,13 @@ func (m *Manager) HandleAddProxyConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
portA, err := utils.PostPara(r, "porta")
|
listenAddr, err := utils.PostPara(r, "listenAddr")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "first address cannot be empty")
|
utils.SendErrorResponse(w, "first address cannot be empty")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
portB, err := utils.PostPara(r, "portb")
|
proxyAddr, err := utils.PostPara(r, "proxyAddr")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, "second address cannot be empty")
|
utils.SendErrorResponse(w, "second address cannot be empty")
|
||||||
return
|
return
|
||||||
@ -44,27 +45,17 @@ func (m *Manager) HandleAddProxyConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
modeValue := ProxyMode_Transport
|
useTCP, _ := utils.PostBool(r, "useTCP")
|
||||||
mode, err := utils.PostPara(r, "mode")
|
useUDP, _ := utils.PostBool(r, "useUDP")
|
||||||
if err != nil || mode == "" {
|
|
||||||
utils.SendErrorResponse(w, "no mode given")
|
|
||||||
} else if mode == "listen" {
|
|
||||||
modeValue = ProxyMode_Listen
|
|
||||||
} else if mode == "transport" {
|
|
||||||
modeValue = ProxyMode_Transport
|
|
||||||
} else if mode == "starter" {
|
|
||||||
modeValue = ProxyMode_Starter
|
|
||||||
} else {
|
|
||||||
utils.SendErrorResponse(w, "invalid mode given. Only support listen / transport / starter")
|
|
||||||
}
|
|
||||||
|
|
||||||
//Create the target config
|
//Create the target config
|
||||||
newConfigUUID := m.NewConfig(&ProxyRelayOptions{
|
newConfigUUID := m.NewConfig(&ProxyRelayOptions{
|
||||||
Name: name,
|
Name: name,
|
||||||
PortA: portA,
|
ListeningAddr: strings.TrimSpace(listenAddr),
|
||||||
PortB: portB,
|
ProxyAddr: strings.TrimSpace(proxyAddr),
|
||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
Mode: modeValue,
|
UseTCP: useTCP,
|
||||||
|
UseUDP: useUDP,
|
||||||
})
|
})
|
||||||
|
|
||||||
js, _ := json.Marshal(newConfigUUID)
|
js, _ := json.Marshal(newConfigUUID)
|
||||||
@ -80,22 +71,10 @@ func (m *Manager) HandleEditProxyConfigs(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
newName, _ := utils.PostPara(r, "name")
|
newName, _ := utils.PostPara(r, "name")
|
||||||
newPortA, _ := utils.PostPara(r, "porta")
|
listenAddr, _ := utils.PostPara(r, "listenAddr")
|
||||||
newPortB, _ := utils.PostPara(r, "portb")
|
proxyAddr, _ := utils.PostPara(r, "proxyAddr")
|
||||||
newModeStr, _ := utils.PostPara(r, "mode")
|
useTCP, _ := utils.PostBool(r, "useTCP")
|
||||||
newMode := -1
|
useUDP, _ := utils.PostBool(r, "useUDP")
|
||||||
if newModeStr != "" {
|
|
||||||
if newModeStr == "listen" {
|
|
||||||
newMode = 0
|
|
||||||
} else if newModeStr == "transport" {
|
|
||||||
newMode = 1
|
|
||||||
} else if newModeStr == "starter" {
|
|
||||||
newMode = 2
|
|
||||||
} else {
|
|
||||||
utils.SendErrorResponse(w, "invalid new mode value")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newTimeoutStr, _ := utils.PostPara(r, "timeout")
|
newTimeoutStr, _ := utils.PostPara(r, "timeout")
|
||||||
newTimeout := -1
|
newTimeout := -1
|
||||||
@ -108,7 +87,7 @@ func (m *Manager) HandleEditProxyConfigs(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call the EditConfig method to modify the configuration
|
// Call the EditConfig method to modify the configuration
|
||||||
err = m.EditConfig(configUUID, newName, newPortA, newPortB, newMode, newTimeout)
|
err = m.EditConfig(configUUID, newName, listenAddr, proxyAddr, useTCP, useUDP, newTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.SendErrorResponse(w, err.Error())
|
utils.SendErrorResponse(w, err.Error())
|
||||||
return
|
return
|
||||||
@ -158,6 +137,7 @@ func (m *Manager) HandleStopProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !targetProxyConfig.IsRunning() {
|
if !targetProxyConfig.IsRunning() {
|
||||||
|
targetProxyConfig.Running = false
|
||||||
utils.SendErrorResponse(w, "target proxy service is not running")
|
utils.SendErrorResponse(w, "target proxy service is not running")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -180,6 +160,7 @@ func (m *Manager) HandleRemoveProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if targetProxyConfig.IsRunning() {
|
if targetProxyConfig.IsRunning() {
|
||||||
|
targetProxyConfig.Running = false
|
||||||
utils.SendErrorResponse(w, "Service is running")
|
utils.SendErrorResponse(w, "Service is running")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -209,25 +190,3 @@ func (m *Manager) HandleGetProxyStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
js, _ := json.Marshal(targetConfig)
|
js, _ := json.Marshal(targetConfig)
|
||||||
utils.SendJSONResponse(w, string(js))
|
utils.SendJSONResponse(w, string(js))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) HandleConfigValidate(w http.ResponseWriter, r *http.Request) {
|
|
||||||
uuid, err := utils.GetPara(r, "uuid")
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, "invalid uuid given")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
targetConfig, err := m.GetConfigByUUID(uuid)
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = targetConfig.ValidateConfigs()
|
|
||||||
if err != nil {
|
|
||||||
utils.SendErrorResponse(w, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.SendOK(w)
|
|
||||||
}
|
|
@ -1,7 +1,8 @@
|
|||||||
package tcpprox
|
package streamproxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -18,34 +19,31 @@ import (
|
|||||||
connection
|
connection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const (
|
|
||||||
ProxyMode_Listen = 0
|
|
||||||
ProxyMode_Transport = 1
|
|
||||||
ProxyMode_Starter = 2
|
|
||||||
ProxyMode_UDP = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
type ProxyRelayOptions struct {
|
type ProxyRelayOptions struct {
|
||||||
Name string
|
Name string
|
||||||
PortA string
|
ListeningAddr string
|
||||||
PortB string
|
ProxyAddr string
|
||||||
Timeout int
|
Timeout int
|
||||||
Mode int
|
UseTCP bool
|
||||||
|
UseUDP bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProxyRelayConfig struct {
|
type ProxyRelayConfig struct {
|
||||||
UUID string //A UUIDv4 representing this config
|
UUID string //A UUIDv4 representing this config
|
||||||
Name string //Name of the config
|
Name string //Name of the config
|
||||||
Running bool //If the service is running
|
Running bool //Status, read only
|
||||||
PortA string //Ports A (config depends on mode)
|
AutoStart bool //If the service suppose to started automatically
|
||||||
PortB string //Ports B (config depends on mode)
|
ListeningAddress string //Listening Address, usually 127.0.0.1:port
|
||||||
Mode int //Operation Mode
|
ProxyTargetAddr string //Proxy target address
|
||||||
|
UseTCP bool //Enable TCP proxy
|
||||||
|
UseUDP bool //Enable UDP proxy
|
||||||
Timeout int //Timeout for connection in sec
|
Timeout int //Timeout for connection in sec
|
||||||
stopChan chan bool //Stop channel to stop the listener
|
tcpStopChan chan bool //Stop channel for TCP listener
|
||||||
|
udpStopChan chan bool //Stop channel for UDP listener
|
||||||
aTobAccumulatedByteTransfer atomic.Int64 //Accumulated byte transfer from A to B
|
aTobAccumulatedByteTransfer atomic.Int64 //Accumulated byte transfer from A to B
|
||||||
bToaAccumulatedByteTransfer atomic.Int64 //Accumulated byte transfer from B to A
|
bToaAccumulatedByteTransfer atomic.Int64 //Accumulated byte transfer from B to A
|
||||||
|
udpClientMap sync.Map //map storing the UDP client-server connections
|
||||||
parent *Manager `json:"-"`
|
parent *Manager `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
@ -60,11 +58,11 @@ type Manager struct {
|
|||||||
Configs []*ProxyRelayConfig
|
Configs []*ProxyRelayConfig
|
||||||
|
|
||||||
//Realtime Statistics
|
//Realtime Statistics
|
||||||
Connections int //currently connected connect counts
|
Connections int //currently connected connect counts
|
||||||
UDPClientMap sync.Map //map storing the UDP client-server connections
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTCProxy(options *Options) *Manager {
|
func NewStreamProxy(options *Options) *Manager {
|
||||||
options.Database.NewTable("tcprox")
|
options.Database.NewTable("tcprox")
|
||||||
|
|
||||||
//Load relay configs from db
|
//Load relay configs from db
|
||||||
@ -108,16 +106,17 @@ func (m *Manager) NewConfig(config *ProxyRelayOptions) string {
|
|||||||
thisConfig := ProxyRelayConfig{
|
thisConfig := ProxyRelayConfig{
|
||||||
UUID: configUUID,
|
UUID: configUUID,
|
||||||
Name: config.Name,
|
Name: config.Name,
|
||||||
Running: false,
|
ListeningAddress: config.ListeningAddr,
|
||||||
PortA: config.PortA,
|
ProxyTargetAddr: config.ProxyAddr,
|
||||||
PortB: config.PortB,
|
UseTCP: config.UseTCP,
|
||||||
Mode: config.Mode,
|
UseUDP: config.UseUDP,
|
||||||
Timeout: config.Timeout,
|
Timeout: config.Timeout,
|
||||||
stopChan: nil,
|
tcpStopChan: nil,
|
||||||
|
udpStopChan: nil,
|
||||||
aTobAccumulatedByteTransfer: aAcc,
|
aTobAccumulatedByteTransfer: aAcc,
|
||||||
bToaAccumulatedByteTransfer: bAcc,
|
bToaAccumulatedByteTransfer: bAcc,
|
||||||
|
udpClientMap: sync.Map{},
|
||||||
parent: m,
|
parent: m,
|
||||||
}
|
}
|
||||||
m.Configs = append(m.Configs, &thisConfig)
|
m.Configs = append(m.Configs, &thisConfig)
|
||||||
m.SaveConfigToDatabase()
|
m.SaveConfigToDatabase()
|
||||||
@ -135,7 +134,7 @@ func (m *Manager) GetConfigByUUID(configUUID string) (*ProxyRelayConfig, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Edit the config based on config UUID, leave empty for unchange fields
|
// Edit the config based on config UUID, leave empty for unchange fields
|
||||||
func (m *Manager) EditConfig(configUUID string, newName string, newPortA string, newPortB string, newMode int, newTimeout int) error {
|
func (m *Manager) EditConfig(configUUID string, newName string, newListeningAddr string, newProxyAddr string, useTCP bool, useUDP bool, newTimeout int) error {
|
||||||
// Find the config with the specified UUID
|
// Find the config with the specified UUID
|
||||||
foundConfig, err := m.GetConfigByUUID(configUUID)
|
foundConfig, err := m.GetConfigByUUID(configUUID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -146,18 +145,16 @@ func (m *Manager) EditConfig(configUUID string, newName string, newPortA string,
|
|||||||
if newName != "" {
|
if newName != "" {
|
||||||
foundConfig.Name = newName
|
foundConfig.Name = newName
|
||||||
}
|
}
|
||||||
if newPortA != "" {
|
if newListeningAddr != "" {
|
||||||
foundConfig.PortA = newPortA
|
foundConfig.ListeningAddress = newListeningAddr
|
||||||
}
|
}
|
||||||
if newPortB != "" {
|
if newProxyAddr != "" {
|
||||||
foundConfig.PortB = newPortB
|
foundConfig.ProxyTargetAddr = newProxyAddr
|
||||||
}
|
|
||||||
if newMode != -1 {
|
|
||||||
if newMode > 2 || newMode < 0 {
|
|
||||||
return errors.New("invalid mode given")
|
|
||||||
}
|
|
||||||
foundConfig.Mode = newMode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foundConfig.UseTCP = useTCP
|
||||||
|
foundConfig.UseUDP = useUDP
|
||||||
|
|
||||||
if newTimeout != -1 {
|
if newTimeout != -1 {
|
||||||
if newTimeout < 0 {
|
if newTimeout < 0 {
|
||||||
return errors.New("invalid timeout value given")
|
return errors.New("invalid timeout value given")
|
||||||
@ -165,13 +162,6 @@ func (m *Manager) EditConfig(configUUID string, newName string, newPortA string,
|
|||||||
foundConfig.Timeout = newTimeout
|
foundConfig.Timeout = newTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
err = foundConfig.ValidateConfigs()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
m.SaveConfigToDatabase()
|
m.SaveConfigToDatabase()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -192,3 +182,78 @@ func (m *Manager) RemoveConfig(configUUID string) error {
|
|||||||
func (m *Manager) SaveConfigToDatabase() {
|
func (m *Manager) SaveConfigToDatabase() {
|
||||||
m.Options.Database.Write("tcprox", "rules", m.Configs)
|
m.Options.Database.Write("tcprox", "rules", m.Configs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Config Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Start a proxy if stopped
|
||||||
|
func (c *ProxyRelayConfig) Start() error {
|
||||||
|
if c.IsRunning() {
|
||||||
|
c.Running = true
|
||||||
|
return errors.New("proxy already running")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a stopChan to control the loop
|
||||||
|
tcpStopChan := make(chan bool)
|
||||||
|
c.tcpStopChan = tcpStopChan
|
||||||
|
|
||||||
|
udpStopChan := make(chan bool)
|
||||||
|
c.udpStopChan = udpStopChan
|
||||||
|
|
||||||
|
//Start the proxy service
|
||||||
|
if c.UseUDP {
|
||||||
|
go func() {
|
||||||
|
if !c.UseTCP {
|
||||||
|
//By default running state shows TCP proxy. If TCP is not in use, UDP is shown instead
|
||||||
|
c.Running = true
|
||||||
|
}
|
||||||
|
err := c.ForwardUDP(c.ListeningAddress, c.ProxyTargetAddr, udpStopChan)
|
||||||
|
if err != nil {
|
||||||
|
if !c.UseTCP {
|
||||||
|
c.Running = false
|
||||||
|
}
|
||||||
|
log.Println("[TCP] Error starting stream proxy " + c.Name + "(" + c.UUID + "): " + err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.UseTCP {
|
||||||
|
go func() {
|
||||||
|
//Default to transport mode
|
||||||
|
c.Running = true
|
||||||
|
err := c.Port2host(c.ListeningAddress, c.ProxyTargetAddr, tcpStopChan)
|
||||||
|
if err != nil {
|
||||||
|
c.Running = false
|
||||||
|
log.Println("[TCP] Error starting stream proxy " + c.Name + "(" + c.UUID + "): " + err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
//Successfully spawned off the proxy routine
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop a running proxy if running
|
||||||
|
func (c *ProxyRelayConfig) IsRunning() bool {
|
||||||
|
return c.tcpStopChan != nil || c.udpStopChan != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop a running proxy if running
|
||||||
|
func (c *ProxyRelayConfig) Stop() {
|
||||||
|
log.Println("[PROXY] Stopping Stream Proxy " + c.Name)
|
||||||
|
|
||||||
|
if c.udpStopChan != nil {
|
||||||
|
c.udpStopChan <- true
|
||||||
|
c.udpStopChan = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.tcpStopChan != nil {
|
||||||
|
c.tcpStopChan <- true
|
||||||
|
c.tcpStopChan = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("[PROXY] Stopped Stream Proxy " + c.Name)
|
||||||
|
c.Running = false
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
package tcpprox_test
|
package streamproxy_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"imuslab.com/zoraxy/mod/tcpprox"
|
"imuslab.com/zoraxy/mod/streamproxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPort2Port(t *testing.T) {
|
func TestPort2Port(t *testing.T) {
|
||||||
@ -12,7 +12,7 @@ func TestPort2Port(t *testing.T) {
|
|||||||
stopChan := make(chan bool)
|
stopChan := make(chan bool)
|
||||||
|
|
||||||
// Create a ProxyRelayConfig with dummy values
|
// Create a ProxyRelayConfig with dummy values
|
||||||
config := &tcpprox.ProxyRelayConfig{
|
config := &streamproxy.ProxyRelayConfig{
|
||||||
Timeout: 1,
|
Timeout: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ func TestPort2Port(t *testing.T) {
|
|||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
// If the goroutine is still running, it means it did not stop as expected
|
// If the goroutine is still running, it means it did not stop as expected
|
||||||
if config.Running {
|
if config.IsRunning() {
|
||||||
t.Errorf("port2port did not stop as expected")
|
t.Errorf("port2port did not stop as expected")
|
||||||
}
|
}
|
||||||
|
|
146
src/mod/streamproxy/tcpprox.go
Normal file
146
src/mod/streamproxy/tcpprox.go
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
package streamproxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isValidIP(ip string) bool {
|
||||||
|
parsedIP := net.ParseIP(ip)
|
||||||
|
return parsedIP != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidPort(port string) bool {
|
||||||
|
portInt, err := strconv.Atoi(port)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if portInt < 1 || portInt > 65535 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func connCopy(conn1 net.Conn, conn2 net.Conn, wg *sync.WaitGroup, accumulator *atomic.Int64) {
|
||||||
|
n, err := io.Copy(conn1, conn2)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
accumulator.Add(n) //Add to accumulator
|
||||||
|
conn1.Close()
|
||||||
|
log.Println("[←]", "close the connect at local:["+conn1.LocalAddr().String()+"] and remote:["+conn1.RemoteAddr().String()+"]")
|
||||||
|
//conn2.Close()
|
||||||
|
//log.Println("[←]", "close the connect at local:["+conn2.LocalAddr().String()+"] and remote:["+conn2.RemoteAddr().String()+"]")
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// wait tow goroutines
|
||||||
|
wg.Add(2)
|
||||||
|
go connCopy(conn1, conn2, &wg, aTob)
|
||||||
|
go connCopy(conn2, conn1, &wg, bToa)
|
||||||
|
//blocking when the wg is locked
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyRelayConfig) accept(listener net.Listener) (net.Conn, error) {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if connection in blacklist or whitelist
|
||||||
|
if addr, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
|
||||||
|
if !c.parent.Options.AccessControlHandler(conn) {
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
conn.Close()
|
||||||
|
log.Println("[x]", "Connection from "+addr.IP.String()+" rejected by access control policy")
|
||||||
|
return nil, errors.New("Connection from " + addr.IP.String() + " rejected by access control policy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("[√]", "accept a new client. remote address:["+conn.RemoteAddr().String()+"], local address:["+conn.LocalAddr().String()+"]")
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func startListener(address string) (net.Listener, error) {
|
||||||
|
log.Println("[+]", "try to start server on:["+address+"]")
|
||||||
|
server, err := net.Listen("tcp", address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("listen address [" + address + "] faild")
|
||||||
|
}
|
||||||
|
log.Println("[√]", "start listen at address:["+address+"]")
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Forwarder Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
portA -> server
|
||||||
|
server -> portB
|
||||||
|
*/
|
||||||
|
func (c *ProxyRelayConfig) Port2host(allowPort string, targetAddress string, stopChan chan bool) error {
|
||||||
|
listenerStartingAddr := allowPort
|
||||||
|
if isValidPort(allowPort) {
|
||||||
|
//number only, e.g. 8080
|
||||||
|
listenerStartingAddr = "0.0.0.0:" + allowPort
|
||||||
|
} else if strings.HasPrefix(allowPort, ":") && isValidPort(allowPort[1:]) {
|
||||||
|
//port number starting with :, e.g. :8080
|
||||||
|
listenerStartingAddr = "0.0.0.0" + allowPort
|
||||||
|
}
|
||||||
|
|
||||||
|
server, err := startListener(listenerStartingAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
targetAddress = strings.TrimSpace(targetAddress)
|
||||||
|
|
||||||
|
//Start stop handler
|
||||||
|
go func() {
|
||||||
|
<-stopChan
|
||||||
|
log.Println("[x]", "Received stop signal. Exiting Port to Host forwarder")
|
||||||
|
server.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
//Start blocking loop for accepting connections
|
||||||
|
for {
|
||||||
|
conn, err := c.accept(server)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, net.ErrClosed) {
|
||||||
|
//Terminate by stop chan. Exit listener loop
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
//Connection error. Retry
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go func(targetAddress string) {
|
||||||
|
log.Println("[+]", "start connect host:["+targetAddress+"]")
|
||||||
|
target, err := net.Dial("tcp", targetAddress)
|
||||||
|
if err != nil {
|
||||||
|
// temporarily unavailable, don't use fatal.
|
||||||
|
log.Println("[x]", "connect target address ["+targetAddress+"] faild. retry in ", c.Timeout, "seconds. ")
|
||||||
|
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
|
||||||
|
}
|
||||||
|
log.Println("[→]", "connect target address ["+targetAddress+"] success.")
|
||||||
|
forward(target, conn, &c.aTobAccumulatedByteTransfer, &c.bToaAccumulatedByteTransfer)
|
||||||
|
}(targetAddress)
|
||||||
|
}
|
||||||
|
}
|
157
src/mod/streamproxy/udpprox.go
Normal file
157
src/mod/streamproxy/udpprox.go
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package streamproxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
UDP Proxy Module
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Information maintained for each client/server connection
|
||||||
|
type udpClientServerConn struct {
|
||||||
|
ClientAddr *net.UDPAddr // Address of the client
|
||||||
|
ServerConn *net.UDPConn // UDP connection to server
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a new connection by opening a UDP connection to the server
|
||||||
|
func createNewUDPConn(srvAddr, cliAddr *net.UDPAddr) *udpClientServerConn {
|
||||||
|
conn := new(udpClientServerConn)
|
||||||
|
conn.ClientAddr = cliAddr
|
||||||
|
srvudp, err := net.DialUDP("udp", nil, srvAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
conn.ServerConn = srvudp
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start listener, return inbound lisener and proxy target UDP address
|
||||||
|
func initUDPConnections(listenAddr string, targetAddress string) (*net.UDPConn, *net.UDPAddr, error) {
|
||||||
|
// Set up Proxy
|
||||||
|
saddr, err := net.ResolveUDPAddr("udp", listenAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
inboundConn, err := net.ListenUDP("udp", saddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("[UDP] Proxy listening on " + listenAddr)
|
||||||
|
|
||||||
|
outboundConn, err := net.ResolveUDPAddr("udp", targetAddress)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return inboundConn, outboundConn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go routine which manages connection from server to single client
|
||||||
|
func (c *ProxyRelayConfig) RunUDPConnectionRelay(conn *udpClientServerConn, lisenter *net.UDPConn) {
|
||||||
|
var buffer [1500]byte
|
||||||
|
for {
|
||||||
|
// Read from server
|
||||||
|
n, err := conn.ServerConn.Read(buffer[0:])
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, net.ErrClosed) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Relay it to client
|
||||||
|
_, err = lisenter.WriteToUDP(buffer[0:n], conn.ClientAddr)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close all connections that waiting for read from server
|
||||||
|
func (c *ProxyRelayConfig) CloseAllUDPConnections() {
|
||||||
|
c.udpClientMap.Range(func(clientAddr, clientServerConn interface{}) bool {
|
||||||
|
conn := clientServerConn.(*udpClientServerConn)
|
||||||
|
conn.ServerConn.Close()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyRelayConfig) ForwardUDP(address1, address2 string, stopChan chan bool) error {
|
||||||
|
//By default the incoming listen Address is int
|
||||||
|
//We need to add the loopback address into it
|
||||||
|
if isValidPort(address1) {
|
||||||
|
//Port number only. Missing the : in front
|
||||||
|
address1 = ":" + address1
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(address1, ":") {
|
||||||
|
//Prepend 127.0.0.1 to the address
|
||||||
|
address1 = "127.0.0.1" + address1
|
||||||
|
}
|
||||||
|
|
||||||
|
lisener, targetAddr, err := initUDPConnections(address1, address2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
//Stop channel receiver
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stopChan:
|
||||||
|
//Stop signal received
|
||||||
|
//Stop server -> client forwarder
|
||||||
|
c.CloseAllUDPConnections()
|
||||||
|
//Stop client -> server forwarder
|
||||||
|
//Force close, will terminate ReadFromUDP for inbound listener
|
||||||
|
lisener.Close()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
var buffer [1500]byte
|
||||||
|
for {
|
||||||
|
n, cliaddr, err := lisener.ReadFromUDP(buffer[0:])
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, net.ErrClosed) {
|
||||||
|
//Proxy stopped
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.aTobAccumulatedByteTransfer.Add(int64(n))
|
||||||
|
saddr := cliaddr.String()
|
||||||
|
rawConn, found := c.udpClientMap.Load(saddr)
|
||||||
|
var conn *udpClientServerConn
|
||||||
|
if !found {
|
||||||
|
conn = createNewUDPConn(targetAddr, cliaddr)
|
||||||
|
if conn == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.udpClientMap.Store(saddr, conn)
|
||||||
|
log.Println("[UDP] Created new connection for client " + saddr)
|
||||||
|
// Fire up routine to manage new connection
|
||||||
|
go c.RunUDPConnectionRelay(conn, lisener)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log.Println("[UDP] Found connection for client " + saddr)
|
||||||
|
conn = rawConn.(*udpClientServerConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relay to server
|
||||||
|
_, err = conn.ServerConn.Write(buffer[0:n])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,348 +0,0 @@
|
|||||||
package tcpprox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func isValidIP(ip string) bool {
|
|
||||||
parsedIP := net.ParseIP(ip)
|
|
||||||
return parsedIP != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidPort(port string) bool {
|
|
||||||
portInt, err := strconv.Atoi(port)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if portInt < 1 || portInt > 65535 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func isReachable(target string) bool {
|
|
||||||
timeout := time.Duration(2 * time.Second) // Set the timeout value as per your requirement
|
|
||||||
conn, err := net.DialTimeout("tcp", target, timeout)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func connCopy(conn1 net.Conn, conn2 net.Conn, wg *sync.WaitGroup, accumulator *atomic.Int64) {
|
|
||||||
n, err := io.Copy(conn1, conn2)
|
|
||||||
if err != nil {
|
|
||||||
//Add to accumulator
|
|
||||||
accumulator.Add(n)
|
|
||||||
}
|
|
||||||
conn1.Close()
|
|
||||||
log.Println("[←]", "close the connect at local:["+conn1.LocalAddr().String()+"] and remote:["+conn1.RemoteAddr().String()+"]")
|
|
||||||
//conn2.Close()
|
|
||||||
//log.Println("[←]", "close the connect at local:["+conn2.LocalAddr().String()+"] and remote:["+conn2.RemoteAddr().String()+"]")
|
|
||||||
wg.Done()
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
// wait tow goroutines
|
|
||||||
wg.Add(2)
|
|
||||||
go connCopy(conn1, conn2, &wg, aTob)
|
|
||||||
go connCopy(conn2, conn1, &wg, bToa)
|
|
||||||
//blocking when the wg is locked
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ProxyRelayConfig) accept(listener net.Listener) (net.Conn, error) {
|
|
||||||
|
|
||||||
conn, err := listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check if connection in blacklist or whitelist
|
|
||||||
if addr, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
|
|
||||||
if !c.parent.Options.AccessControlHandler(conn) {
|
|
||||||
time.Sleep(300 * time.Millisecond)
|
|
||||||
conn.Close()
|
|
||||||
log.Println("[x]", "Connection from "+addr.IP.String()+" rejected by access control policy")
|
|
||||||
return nil, errors.New("Connection from " + addr.IP.String() + " rejected by access control policy")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("[√]", "accept a new client. remote address:["+conn.RemoteAddr().String()+"], local address:["+conn.LocalAddr().String()+"]")
|
|
||||||
return conn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func startListener(address string) (net.Listener, error) {
|
|
||||||
log.Println("[+]", "try to start server on:["+address+"]")
|
|
||||||
server, err := net.Listen("tcp", address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("listen address [" + address + "] faild")
|
|
||||||
}
|
|
||||||
log.Println("[√]", "start listen at address:["+address+"]")
|
|
||||||
return server, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Config Functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Config validator
|
|
||||||
func (c *ProxyRelayConfig) ValidateConfigs() error {
|
|
||||||
if c.Mode == ProxyMode_Transport {
|
|
||||||
//Port2Host: PortA int, PortB string
|
|
||||||
if !isValidPort(c.PortA) {
|
|
||||||
return errors.New("first address must be a valid port number")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isReachable(c.PortB) {
|
|
||||||
return errors.New("second address is unreachable")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
} else if c.Mode == ProxyMode_Listen {
|
|
||||||
//Port2Port: Both port are port number
|
|
||||||
if !isValidPort(c.PortA) {
|
|
||||||
return errors.New("first address is not a valid port number")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isValidPort(c.PortB) {
|
|
||||||
return errors.New("second address is not a valid port number")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
} else if c.Mode == ProxyMode_Starter {
|
|
||||||
//Host2Host: Both have to be hosts
|
|
||||||
if !isReachable(c.PortA) {
|
|
||||||
return errors.New("first address is unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isReachable(c.PortB) {
|
|
||||||
return errors.New("second address is unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return errors.New("invalid mode given")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a proxy if stopped
|
|
||||||
func (c *ProxyRelayConfig) Start() error {
|
|
||||||
if c.Running {
|
|
||||||
return errors.New("proxy already running")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a stopChan to control the loop
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
c.stopChan = stopChan
|
|
||||||
|
|
||||||
//Validate configs
|
|
||||||
err := c.ValidateConfigs()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
//Start the proxy service
|
|
||||||
go func() {
|
|
||||||
c.Running = true
|
|
||||||
if c.Mode == ProxyMode_Transport {
|
|
||||||
err = c.Port2host(c.PortA, c.PortB, stopChan)
|
|
||||||
} else if c.Mode == ProxyMode_Listen {
|
|
||||||
err = c.Port2port(c.PortA, c.PortB, stopChan)
|
|
||||||
} else if c.Mode == ProxyMode_Starter {
|
|
||||||
err = c.Host2host(c.PortA, c.PortB, stopChan)
|
|
||||||
} else if c.Mode == ProxyMode_UDP {
|
|
||||||
err = c.ForwardUDP(c.PortA, c.PortB, stopChan)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
c.Running = false
|
|
||||||
log.Println("Error starting proxy service " + c.Name + "(" + c.UUID + "): " + err.Error())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
//Successfully spawned off the proxy routine
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop a running proxy if running
|
|
||||||
func (c *ProxyRelayConfig) IsRunning() bool {
|
|
||||||
return c.Running || c.stopChan != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop a running proxy if running
|
|
||||||
func (c *ProxyRelayConfig) Stop() {
|
|
||||||
if c.Running || c.stopChan != nil {
|
|
||||||
c.stopChan <- true
|
|
||||||
time.Sleep(300 * time.Millisecond)
|
|
||||||
c.stopChan = nil
|
|
||||||
c.Running = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Forwarder Functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
portA -> server
|
|
||||||
portB -> server
|
|
||||||
*/
|
|
||||||
func (c *ProxyRelayConfig) Port2port(port1 string, port2 string, stopChan chan bool) error {
|
|
||||||
//Trim the Prefix of : if exists
|
|
||||||
listen1, err := startListener("0.0.0.0:" + port1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
listen2, err := startListener("0.0.0.0:" + port2)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("[√]", "listen port:", port1, "and", port2, "success. waiting for client...")
|
|
||||||
c.Running = true
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-stopChan
|
|
||||||
log.Println("[x]", "Received stop signal. Exiting Port to Port forwarder")
|
|
||||||
c.Running = false
|
|
||||||
listen1.Close()
|
|
||||||
listen2.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
|
||||||
conn1, err := c.accept(listen1)
|
|
||||||
if err != nil {
|
|
||||||
if !c.Running {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
conn2, err := c.accept(listen2)
|
|
||||||
if err != nil {
|
|
||||||
if !c.Running {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if conn1 == nil || conn2 == nil {
|
|
||||||
log.Println("[x]", "accept client faild. retry in ", c.Timeout, " seconds. ")
|
|
||||||
time.Sleep(time.Duration(c.Timeout) * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
go forward(conn1, conn2, &c.aTobAccumulatedByteTransfer, &c.bToaAccumulatedByteTransfer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
portA -> server
|
|
||||||
server -> portB
|
|
||||||
*/
|
|
||||||
func (c *ProxyRelayConfig) Port2host(allowPort string, targetAddress string, stopChan chan bool) error {
|
|
||||||
server, err := startListener("0.0.0.0:" + allowPort)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
//Start stop handler
|
|
||||||
go func() {
|
|
||||||
<-stopChan
|
|
||||||
log.Println("[x]", "Received stop signal. Exiting Port to Host forwarder")
|
|
||||||
c.Running = false
|
|
||||||
server.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
//Start blocking loop for accepting connections
|
|
||||||
for {
|
|
||||||
conn, err := c.accept(server)
|
|
||||||
if conn == nil || err != nil {
|
|
||||||
if !c.Running {
|
|
||||||
//Terminate by stop chan. Exit listener loop
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//Connection error. Retry
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
go func(targetAddress string) {
|
|
||||||
log.Println("[+]", "start connect host:["+targetAddress+"]")
|
|
||||||
target, err := net.Dial("tcp", targetAddress)
|
|
||||||
if err != nil {
|
|
||||||
// temporarily unavailable, don't use fatal.
|
|
||||||
log.Println("[x]", "connect target address ["+targetAddress+"] faild. retry in ", c.Timeout, "seconds. ")
|
|
||||||
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
|
|
||||||
}
|
|
||||||
log.Println("[→]", "connect target address ["+targetAddress+"] success.")
|
|
||||||
forward(target, conn, &c.aTobAccumulatedByteTransfer, &c.bToaAccumulatedByteTransfer)
|
|
||||||
}(targetAddress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
server -> portA
|
|
||||||
server -> portB
|
|
||||||
*/
|
|
||||||
func (c *ProxyRelayConfig) Host2host(address1, address2 string, stopChan chan bool) error {
|
|
||||||
c.Running = true
|
|
||||||
go func() {
|
|
||||||
<-stopChan
|
|
||||||
log.Println("[x]", "Received stop signal. Exiting Host to Host forwarder")
|
|
||||||
c.Running = false
|
|
||||||
}()
|
|
||||||
|
|
||||||
for c.Running {
|
|
||||||
log.Println("[+]", "try to connect host:["+address1+"] and ["+address2+"]")
|
|
||||||
var host1, host2 net.Conn
|
|
||||||
var err error
|
|
||||||
for {
|
|
||||||
d := net.Dialer{Timeout: time.Duration(c.Timeout)}
|
|
||||||
host1, err = d.Dial("tcp", address1)
|
|
||||||
if err == nil {
|
|
||||||
log.Println("[→]", "connect ["+address1+"] success.")
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
log.Println("[x]", "connect target address ["+address1+"] faild. retry in ", c.Timeout, " seconds. ")
|
|
||||||
time.Sleep(time.Duration(c.Timeout) * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.Running {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
d := net.Dialer{Timeout: time.Duration(c.Timeout)}
|
|
||||||
host2, err = d.Dial("tcp", address2)
|
|
||||||
if err == nil {
|
|
||||||
log.Println("[→]", "connect ["+address2+"] success.")
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
log.Println("[x]", "connect target address ["+address2+"] faild. retry in ", c.Timeout, " seconds. ")
|
|
||||||
time.Sleep(time.Duration(c.Timeout) * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.Running {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
go forward(host1, host2, &c.aTobAccumulatedByteTransfer, &c.bToaAccumulatedByteTransfer)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,289 +0,0 @@
|
|||||||
package tcpprox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const timeout = 5
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
//log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)
|
|
||||||
log.SetFlags(log.Ldate | log.Lmicroseconds)
|
|
||||||
|
|
||||||
printWelcome()
|
|
||||||
|
|
||||||
args := os.Args
|
|
||||||
argc := len(os.Args)
|
|
||||||
if argc <= 2 {
|
|
||||||
printHelp()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO:support UDP protocol
|
|
||||||
|
|
||||||
/*var logFileError error
|
|
||||||
if argc > 5 && args[4] == "-log" {
|
|
||||||
logPath := args[5] + "/" + time.Now().Format("2006_01_02_15_04_05") // "2006-01-02 15:04:05"
|
|
||||||
logPath += args[1] + "-" + strings.Replace(args[2], ":", "_", -1) + "-" + args[3] + ".log"
|
|
||||||
logPath = strings.Replace(logPath, `\`, "/", -1)
|
|
||||||
logPath = strings.Replace(logPath, "//", "/", -1)
|
|
||||||
logFile, logFileError = os.OpenFile(logPath, os.O_APPEND|os.O_CREATE, 0666)
|
|
||||||
if logFileError != nil {
|
|
||||||
log.Fatalln("[x]", "log file path error.", logFileError.Error())
|
|
||||||
}
|
|
||||||
log.Println("[√]", "open test log file success. path:", logPath)
|
|
||||||
}*/
|
|
||||||
|
|
||||||
switch args[1] {
|
|
||||||
case "-listen":
|
|
||||||
if argc < 3 {
|
|
||||||
log.Fatalln(`-listen need two arguments, like "nb -listen 1997 2017".`)
|
|
||||||
}
|
|
||||||
port1 := checkPort(args[2])
|
|
||||||
port2 := checkPort(args[3])
|
|
||||||
log.Println("[√]", "start to listen port:", port1, "and port:", port2)
|
|
||||||
port2port(port1, port2)
|
|
||||||
break
|
|
||||||
case "-tran":
|
|
||||||
if argc < 3 {
|
|
||||||
log.Fatalln(`-tran need two arguments, like "nb -tran 1997 192.168.1.2:3389".`)
|
|
||||||
}
|
|
||||||
port := checkPort(args[2])
|
|
||||||
var remoteAddress string
|
|
||||||
if checkIp(args[3]) {
|
|
||||||
remoteAddress = args[3]
|
|
||||||
}
|
|
||||||
split := strings.SplitN(remoteAddress, ":", 2)
|
|
||||||
log.Println("[√]", "start to transmit address:", remoteAddress, "to address:", split[0]+":"+port)
|
|
||||||
port2host(port, remoteAddress)
|
|
||||||
break
|
|
||||||
case "-slave":
|
|
||||||
if argc < 3 {
|
|
||||||
log.Fatalln(`-slave need two arguments, like "nb -slave 127.0.0.1:3389 8.8.8.8:1997".`)
|
|
||||||
}
|
|
||||||
var address1, address2 string
|
|
||||||
checkIp(args[2])
|
|
||||||
if checkIp(args[2]) {
|
|
||||||
address1 = args[2]
|
|
||||||
}
|
|
||||||
checkIp(args[3])
|
|
||||||
if checkIp(args[3]) {
|
|
||||||
address2 = args[3]
|
|
||||||
}
|
|
||||||
log.Println("[√]", "start to connect address:", address1, "and address:", address2)
|
|
||||||
host2host(address1, address2)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
printHelp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func printWelcome() {
|
|
||||||
fmt.Println("+----------------------------------------------------------------+")
|
|
||||||
fmt.Println("| Welcome to use NATBypass Ver1.0.0 . |")
|
|
||||||
fmt.Println("| Code by cw1997 at 2017-10-19 03:59:51 |")
|
|
||||||
fmt.Println("| If you have some problem when you use the tool, |")
|
|
||||||
fmt.Println("| please submit issue at : https://github.com/cw1997/NATBypass . |")
|
|
||||||
fmt.Println("+----------------------------------------------------------------+")
|
|
||||||
fmt.Println()
|
|
||||||
// sleep one second because the fmt is not thread-safety.
|
|
||||||
// if not to do this, fmt.Print will print after the log.Print.
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
func printHelp() {
|
|
||||||
fmt.Println(`usage: "-listen port1 port2" example: "nb -listen 1997 2017" `)
|
|
||||||
fmt.Println(` "-tran port1 ip:port2" example: "nb -tran 1997 192.168.1.2:3389" `)
|
|
||||||
fmt.Println(` "-slave ip1:port1 ip2:port2" example: "nb -slave 127.0.0.1:3389 8.8.8.8:1997" `)
|
|
||||||
fmt.Println(`============================================================`)
|
|
||||||
fmt.Println(`optional argument: "-log logpath" . example: "nb -listen 1997 2017 -log d:/nb" `)
|
|
||||||
fmt.Println(`log filename format: Y_m_d_H_i_s-agrs1-args2-args3.log`)
|
|
||||||
fmt.Println(`============================================================`)
|
|
||||||
fmt.Println(`if you want more help, please read "README.md". `)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkPort(port string) string {
|
|
||||||
PortNum, err := strconv.Atoi(port)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("[x]", "port should be a number")
|
|
||||||
}
|
|
||||||
if PortNum < 1 || PortNum > 65535 {
|
|
||||||
log.Fatalln("[x]", "port should be a number and the range is [1,65536)")
|
|
||||||
}
|
|
||||||
return port
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkIp(address string) bool {
|
|
||||||
ipAndPort := strings.Split(address, ":")
|
|
||||||
if len(ipAndPort) != 2 {
|
|
||||||
log.Fatalln("[x]", "address error. should be a string like [ip:port]. ")
|
|
||||||
}
|
|
||||||
ip := ipAndPort[0]
|
|
||||||
port := ipAndPort[1]
|
|
||||||
checkPort(port)
|
|
||||||
pattern := `^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$`
|
|
||||||
ok, err := regexp.MatchString(pattern, ip)
|
|
||||||
if err != nil || !ok {
|
|
||||||
log.Fatalln("[x]", "ip error. ")
|
|
||||||
}
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func port2port(port1 string, port2 string) {
|
|
||||||
listen1 := start_server("0.0.0.0:" + port1)
|
|
||||||
listen2 := start_server("0.0.0.0:" + port2)
|
|
||||||
log.Println("[√]", "listen port:", port1, "and", port2, "success. waiting for client...")
|
|
||||||
for {
|
|
||||||
conn1 := accept(listen1)
|
|
||||||
conn2 := accept(listen2)
|
|
||||||
if conn1 == nil || conn2 == nil {
|
|
||||||
log.Println("[x]", "accept client faild. retry in ", timeout, " seconds. ")
|
|
||||||
time.Sleep(timeout * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
forward(conn1, conn2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func port2host(allowPort string, targetAddress string) {
|
|
||||||
server := start_server("0.0.0.0:" + allowPort)
|
|
||||||
for {
|
|
||||||
conn := accept(server)
|
|
||||||
if conn == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
//println(targetAddress)
|
|
||||||
go func(targetAddress string) {
|
|
||||||
log.Println("[+]", "start connect host:["+targetAddress+"]")
|
|
||||||
target, err := net.Dial("tcp", targetAddress)
|
|
||||||
if err != nil {
|
|
||||||
// temporarily unavailable, don't use fatal.
|
|
||||||
log.Println("[x]", "connect target address ["+targetAddress+"] faild. retry in ", timeout, "seconds. ")
|
|
||||||
conn.Close()
|
|
||||||
log.Println("[←]", "close the connect at local:["+conn.LocalAddr().String()+"] and remote:["+conn.RemoteAddr().String()+"]")
|
|
||||||
time.Sleep(timeout * time.Second)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Println("[→]", "connect target address ["+targetAddress+"] success.")
|
|
||||||
forward(target, conn)
|
|
||||||
}(targetAddress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func host2host(address1, address2 string) {
|
|
||||||
for {
|
|
||||||
log.Println("[+]", "try to connect host:["+address1+"] and ["+address2+"]")
|
|
||||||
var host1, host2 net.Conn
|
|
||||||
var err error
|
|
||||||
for {
|
|
||||||
host1, err = net.Dial("tcp", address1)
|
|
||||||
if err == nil {
|
|
||||||
log.Println("[→]", "connect ["+address1+"] success.")
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
log.Println("[x]", "connect target address ["+address1+"] faild. retry in ", timeout, " seconds. ")
|
|
||||||
time.Sleep(timeout * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
host2, err = net.Dial("tcp", address2)
|
|
||||||
if err == nil {
|
|
||||||
log.Println("[→]", "connect ["+address2+"] success.")
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
log.Println("[x]", "connect target address ["+address2+"] faild. retry in ", timeout, " seconds. ")
|
|
||||||
time.Sleep(timeout * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
forward(host1, host2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func start_server(address string) net.Listener {
|
|
||||||
log.Println("[+]", "try to start server on:["+address+"]")
|
|
||||||
server, err := net.Listen("tcp", address)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("[x]", "listen address ["+address+"] faild.")
|
|
||||||
}
|
|
||||||
log.Println("[√]", "start listen at address:["+address+"]")
|
|
||||||
return server
|
|
||||||
/*defer server.Close()
|
|
||||||
|
|
||||||
for {
|
|
||||||
conn, err := server.Accept()
|
|
||||||
log.Println("accept a new client. remote address:[" + conn.RemoteAddr().String() +
|
|
||||||
"], local address:[" + conn.LocalAddr().String() + "]")
|
|
||||||
if err != nil {
|
|
||||||
log.Println("accept a new client faild.", err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
//go recvConnMsg(conn)
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func accept(listener net.Listener) net.Conn {
|
|
||||||
conn, err := listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
log.Println("[x]", "accept connect ["+conn.RemoteAddr().String()+"] faild.", err.Error())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
log.Println("[√]", "accept a new client. remote address:["+conn.RemoteAddr().String()+"], local address:["+conn.LocalAddr().String()+"]")
|
|
||||||
return conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func forward(conn1 net.Conn, conn2 net.Conn) {
|
|
||||||
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
|
|
||||||
// wait tow goroutines
|
|
||||||
wg.Add(2)
|
|
||||||
go connCopy(conn1, conn2, &wg)
|
|
||||||
go connCopy(conn2, conn1, &wg)
|
|
||||||
//blocking when the wg is locked
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func connCopy(conn1 net.Conn, conn2 net.Conn, wg *sync.WaitGroup) {
|
|
||||||
//TODO:log, record the data from conn1 and conn2.
|
|
||||||
logFile := openLog(conn1.LocalAddr().String(), conn1.RemoteAddr().String(), conn2.LocalAddr().String(), conn2.RemoteAddr().String())
|
|
||||||
if logFile != nil {
|
|
||||||
w := io.MultiWriter(conn1, logFile)
|
|
||||||
io.Copy(w, conn2)
|
|
||||||
} else {
|
|
||||||
io.Copy(conn1, conn2)
|
|
||||||
}
|
|
||||||
conn1.Close()
|
|
||||||
log.Println("[←]", "close the connect at local:["+conn1.LocalAddr().String()+"] and remote:["+conn1.RemoteAddr().String()+"]")
|
|
||||||
//conn2.Close()
|
|
||||||
//log.Println("[←]", "close the connect at local:["+conn2.LocalAddr().String()+"] and remote:["+conn2.RemoteAddr().String()+"]")
|
|
||||||
wg.Done()
|
|
||||||
}
|
|
||||||
func openLog(address1, address2, address3, address4 string) *os.File {
|
|
||||||
args := os.Args
|
|
||||||
argc := len(os.Args)
|
|
||||||
var logFileError error
|
|
||||||
var logFile *os.File
|
|
||||||
if argc > 5 && args[4] == "-log" {
|
|
||||||
address1 = strings.Replace(address1, ":", "_", -1)
|
|
||||||
address2 = strings.Replace(address2, ":", "_", -1)
|
|
||||||
address3 = strings.Replace(address3, ":", "_", -1)
|
|
||||||
address4 = strings.Replace(address4, ":", "_", -1)
|
|
||||||
timeStr := time.Now().Format("2006_01_02_15_04_05") // "2006-01-02 15:04:05"
|
|
||||||
logPath := args[5] + "/" + timeStr + args[1] + "-" + address1 + "_" + address2 + "-" + address3 + "_" + address4 + ".log"
|
|
||||||
logPath = strings.Replace(logPath, `\`, "/", -1)
|
|
||||||
logPath = strings.Replace(logPath, "//", "/", -1)
|
|
||||||
logFile, logFileError = os.OpenFile(logPath, os.O_APPEND|os.O_CREATE, 0666)
|
|
||||||
if logFileError != nil {
|
|
||||||
log.Fatalln("[x]", "log file path error.", logFileError.Error())
|
|
||||||
}
|
|
||||||
log.Println("[√]", "open test log file success. path:", logPath)
|
|
||||||
}
|
|
||||||
return logFile
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
package tcpprox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
UDP Proxy Module
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Information maintained for each client/server connection
|
|
||||||
type udpClientServerConn struct {
|
|
||||||
ClientAddr *net.UDPAddr // Address of the client
|
|
||||||
ServerConn *net.UDPConn // UDP connection to server
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a new connection by opening a UDP connection to the server
|
|
||||||
func createNewUDPConn(srvAddr, cliAddr *net.UDPAddr) *udpClientServerConn {
|
|
||||||
conn := new(udpClientServerConn)
|
|
||||||
conn.ClientAddr = cliAddr
|
|
||||||
srvudp, err := net.DialUDP("udp", nil, srvAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
conn.ServerConn = srvudp
|
|
||||||
return conn
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start listener, return inbound lisener and proxy target UDP address
|
|
||||||
func initUDPConnections(listenAddr string, targetAddress string) (*net.UDPConn, *net.UDPAddr, error) {
|
|
||||||
// Set up Proxy
|
|
||||||
saddr, err := net.ResolveUDPAddr("udp", listenAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
inboundConn, err := net.ListenUDP("udp", saddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Proxy serving on port %s\n", listenAddr)
|
|
||||||
|
|
||||||
outboundConn, err := net.ResolveUDPAddr("udp", targetAddress)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return inboundConn, outboundConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ProxyRelayConfig) ForwardUDP(address1, address2 string, stopChan chan bool) error {
|
|
||||||
lisener, targetAddr, err := initUDPConnections(address1, address2)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer [1500]byte
|
|
||||||
for {
|
|
||||||
n, cliaddr, err := lisener.ReadFromUDP(buffer[0:])
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c.aTobAccumulatedByteTransfer.Add(int64(n))
|
|
||||||
saddr := cliaddr.String()
|
|
||||||
dlock()
|
|
||||||
conn, found := ClientDict[saddr]
|
|
||||||
if !found {
|
|
||||||
conn = createNewUDPConn(targetAddr, cliaddr)
|
|
||||||
if conn == nil {
|
|
||||||
dunlock()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ClientDict[saddr] = conn
|
|
||||||
dunlock()
|
|
||||||
Vlogf(2, "Created new connection for client %s\n", saddr)
|
|
||||||
// Fire up routine to manage new connection
|
|
||||||
go RunConnection(conn)
|
|
||||||
} else {
|
|
||||||
Vlogf(5, "Found connection for client %s\n", saddr)
|
|
||||||
dunlock()
|
|
||||||
}
|
|
||||||
// Relay to server
|
|
||||||
_, err = conn.ServerConn.Write(buffer[0:n])
|
|
||||||
if checkreport(1, err) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -68,9 +68,9 @@ func PostBool(r *http.Request, key string) (bool, error) {
|
|||||||
|
|
||||||
x = strings.TrimSpace(x)
|
x = strings.TrimSpace(x)
|
||||||
|
|
||||||
if x == "1" || strings.ToLower(x) == "true" {
|
if x == "1" || strings.ToLower(x) == "true" || strings.ToLower(x) == "on" {
|
||||||
return true, nil
|
return true, nil
|
||||||
} else if x == "0" || strings.ToLower(x) == "false" {
|
} else if x == "0" || strings.ToLower(x) == "false" || strings.ToLower(x) == "off" {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
"imuslab.com/zoraxy/mod/sshprox"
|
"imuslab.com/zoraxy/mod/sshprox"
|
||||||
"imuslab.com/zoraxy/mod/statistic"
|
"imuslab.com/zoraxy/mod/statistic"
|
||||||
"imuslab.com/zoraxy/mod/statistic/analytic"
|
"imuslab.com/zoraxy/mod/statistic/analytic"
|
||||||
"imuslab.com/zoraxy/mod/tcpprox"
|
"imuslab.com/zoraxy/mod/streamproxy"
|
||||||
"imuslab.com/zoraxy/mod/tlscert"
|
"imuslab.com/zoraxy/mod/tlscert"
|
||||||
"imuslab.com/zoraxy/mod/webserv"
|
"imuslab.com/zoraxy/mod/webserv"
|
||||||
)
|
)
|
||||||
@ -229,7 +229,7 @@ func startupSequence() {
|
|||||||
webSshManager = sshprox.NewSSHProxyManager()
|
webSshManager = sshprox.NewSSHProxyManager()
|
||||||
|
|
||||||
//Create TCP Proxy Manager
|
//Create TCP Proxy Manager
|
||||||
tcpProxyManager = tcpprox.NewTCProxy(&tcpprox.Options{
|
streamProxyManager = streamproxy.NewStreamProxy(&streamproxy.Options{
|
||||||
Database: sysdb,
|
Database: sysdb,
|
||||||
AccessControlHandler: accessController.DefaultAccessRule.AllowConnectionAccess,
|
AccessControlHandler: accessController.DefaultAccessRule.AllowConnectionAccess,
|
||||||
})
|
})
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Select Country</label>
|
<label>Select Country</label>
|
||||||
<div id="countrySelector" class="ui fluid search selection dropdown">
|
<div id="countrySelector" class="ui fluid search multiple selection dropdown">
|
||||||
<input type="hidden" name="country">
|
<input type="hidden" name="country">
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
<div class="default text">Select Country</div>
|
<div class="default text">Select Country</div>
|
||||||
@ -382,7 +382,7 @@
|
|||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Select Country</label>
|
<label>Select Country</label>
|
||||||
<div id="countrySelectorWhitelist" class="ui fluid search selection dropdown">
|
<div id="countrySelectorWhitelist" class="ui fluid search multiple selection dropdown">
|
||||||
<input type="hidden" name="country">
|
<input type="hidden" name="country">
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
<div class="default text">Select Country</div>
|
<div class="default text">Select Country</div>
|
||||||
@ -1018,42 +1018,71 @@
|
|||||||
|
|
||||||
function addCountryToBlacklist() {
|
function addCountryToBlacklist() {
|
||||||
var countryCode = $("#countrySelector").dropdown("get value").toLowerCase();
|
var countryCode = $("#countrySelector").dropdown("get value").toLowerCase();
|
||||||
$('#countrySelector').dropdown('clear');
|
let ccs = [countryCode];
|
||||||
$.ajax({
|
if (countryCode.includes(",")){
|
||||||
type: "POST",
|
//Multiple country codes selected
|
||||||
url: "/api/blacklist/country/add",
|
//Usually just a few countries a for loop will get the job done
|
||||||
data: { cc: countryCode, id: currentEditingAccessRule},
|
ccs = countryCode.split(",");
|
||||||
success: function(response) {
|
}
|
||||||
if (response.error != undefined){
|
|
||||||
msgbox(response.error, false);
|
|
||||||
}
|
|
||||||
initBannedCountryList();
|
|
||||||
},
|
|
||||||
error: function(xhr, status, error) {
|
|
||||||
// handle error response
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeFromBannedList(countryCode){
|
let counter = 0;
|
||||||
if (confirm("Confirm removing " + getCountryName(countryCode) + " from blacklist?")){
|
for(var i = 0; i < ccs.length; i++){
|
||||||
countryCode = countryCode.toLowerCase();
|
let thisCountryCode = ccs[i];
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/blacklist/country/remove",
|
type: "POST",
|
||||||
method: "POST",
|
url: "/api/blacklist/country/add",
|
||||||
data: { cc: countryCode, id: currentEditingAccessRule},
|
data: { cc: thisCountryCode, id: currentEditingAccessRule},
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.error != undefined){
|
if (response.error != undefined){
|
||||||
msgbox(response.error, false);
|
msgbox(response.error, false);
|
||||||
}
|
}
|
||||||
initBannedCountryList();
|
|
||||||
|
if (counter == (ccs.length - 1)){
|
||||||
|
//Last item
|
||||||
|
setTimeout(function(){
|
||||||
|
initBannedCountryList();
|
||||||
|
if (ccs.length == 1){
|
||||||
|
//Single country
|
||||||
|
msgbox(`Added ${getCountryName(ccs[0])} to blacklist`);
|
||||||
|
}else{
|
||||||
|
msgbox(ccs.length + " countries added to blacklist");
|
||||||
|
}
|
||||||
|
|
||||||
|
}, (ccs.length==1)?0:100);
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
console.error("Error removing country from blacklist: " + error);
|
// handle error response
|
||||||
// Handle error response
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$('#countrySelector').dropdown('clear');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeFromBannedList(countryCode){
|
||||||
|
countryCode = countryCode.toLowerCase();
|
||||||
|
let countryName = getCountryName(countryCode);
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/blacklist/country/remove",
|
||||||
|
method: "POST",
|
||||||
|
data: { cc: countryCode, id: currentEditingAccessRule},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.error != undefined){
|
||||||
|
msgbox(response.error, false);
|
||||||
|
}else{
|
||||||
|
msgbox(countryName + " removed from blacklist");
|
||||||
|
}
|
||||||
|
initBannedCountryList();
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
console.error("Error removing country from blacklist: " + error);
|
||||||
|
// Handle error response
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function addIpBlacklist(){
|
function addIpBlacklist(){
|
||||||
@ -1126,21 +1155,45 @@
|
|||||||
|
|
||||||
function addCountryToWhitelist() {
|
function addCountryToWhitelist() {
|
||||||
var countryCode = $("#countrySelectorWhitelist").dropdown("get value").toLowerCase();
|
var countryCode = $("#countrySelectorWhitelist").dropdown("get value").toLowerCase();
|
||||||
$('#countrySelectorWhitelist').dropdown('clear');
|
let ccs = [countryCode];
|
||||||
$.ajax({
|
if (countryCode.includes(",")){
|
||||||
type: "POST",
|
//Multiple country codes selected
|
||||||
url: "/api/whitelist/country/add",
|
//Usually just a few countries a for loop will get the job done
|
||||||
data: { cc: countryCode , id: currentEditingAccessRule},
|
ccs = countryCode.split(",");
|
||||||
success: function(response) {
|
}
|
||||||
if (response.error != undefined){
|
|
||||||
msgbox(response.error, false);
|
let counter = 0;
|
||||||
|
for(var i = 0; i < ccs.length; i++){
|
||||||
|
let thisCountryCode = ccs[i];
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: "/api/whitelist/country/add",
|
||||||
|
data: { cc: thisCountryCode , id: currentEditingAccessRule},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.error != undefined){
|
||||||
|
msgbox(response.error, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (counter == (ccs.length - 1)){
|
||||||
|
setTimeout(function(){
|
||||||
|
initWhitelistCountryList();
|
||||||
|
if (ccs.length == 1){
|
||||||
|
//Single country
|
||||||
|
msgbox(`Added ${getCountryName(ccs[0])} to whitelist`);
|
||||||
|
}else{
|
||||||
|
msgbox(ccs.length + " countries added to whitelist");
|
||||||
|
}
|
||||||
|
}, (ccs.length==1)?0:100);
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
},
|
||||||
|
error: function(xhr, status, error) {
|
||||||
|
// handle error response
|
||||||
}
|
}
|
||||||
initWhitelistCountryList();
|
});
|
||||||
},
|
}
|
||||||
error: function(xhr, status, error) {
|
|
||||||
// handle error response
|
$('#countrySelectorWhitelist').dropdown('clear');
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeFromWhiteList(countryCode){
|
function removeFromWhiteList(countryCode){
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<div class="standardContainer">
|
<div class="standardContainer">
|
||||||
<div class="ui basic segment">
|
<div class="ui basic segment">
|
||||||
<h2>TCP Proxy</h2>
|
<h2>Stream Proxy</h2>
|
||||||
<p>Proxy traffic flow on layer 3 via TCP/IP</p>
|
<p>Proxy traffic flow on layer 3 via TCP or UDP</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui basic segment" style="margin-top: 0;">
|
<div class="ui basic segment" style="margin-top: 0;">
|
||||||
<h4>TCP Proxy Rules</h4>
|
<h4>TCP / UDP Proxy Rules</h4>
|
||||||
<p>A list of TCP proxy rules created on this host. To enable them, use the toggle button on the right.</p>
|
<p>A list of TCP proxy rules created on this host. To enable them, use the toggle button on the right.</p>
|
||||||
<div style="overflow-x: auto; min-height: 400px;">
|
<div style="overflow-x: auto; min-height: 400px;">
|
||||||
<table id="proxyTable" class="ui celled unstackable table">
|
<table id="proxyTable" class="ui celled unstackable table">
|
||||||
@ -29,9 +29,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui basic segment" id="addproxyConfig">
|
<div class="ui basic segment" id="addproxyConfig">
|
||||||
<h4>Add or Edit TCP Proxy</h4>
|
<h4>Add or Edit Stream Proxy</h4>
|
||||||
<p>Create or edit a new proxy instance</p>
|
<p>Create or edit a new stream proxy instance</p>
|
||||||
<form id="tcpProxyForm" class="ui form">
|
<form id="streamProxyForm" class="ui form">
|
||||||
<div class="field" style="display:none;">
|
<div class="field" style="display:none;">
|
||||||
<label>UUID</label>
|
<label>UUID</label>
|
||||||
<input type="text" name="uuid">
|
<input type="text" name="uuid">
|
||||||
@ -41,29 +41,41 @@
|
|||||||
<input type="text" name="name" placeholder="Config Name">
|
<input type="text" name="name" placeholder="Config Name">
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Port A</label>
|
<label>Listening Port / Address with Port</label>
|
||||||
<input type="text" name="porta" placeholder="First address or port">
|
<input type="text" name="listenAddr" placeholder="">
|
||||||
|
<small>Port to listen on this host. e.g. :25565 or 127.0.0.1:25565. <br>
|
||||||
|
If you are using Docker, you will need to expose this port to host network.</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Port B</label>
|
<label>Proxy Target Address with Port</label>
|
||||||
<input type="text" name="portb" placeholder="Second address or port">
|
<input type="text" name="proxyAddr" placeholder="">
|
||||||
|
<small>Server address to forward TCP / UDP package. e.g. 192.168.1.100:25565</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Timeout (s)</label>
|
<label>Timeout (s)</label>
|
||||||
<input type="text" name="timeout" placeholder="Timeout (s)">
|
<input type="text" name="timeout" placeholder="" value="10">
|
||||||
|
<small>Connection timeout in seconds</small>
|
||||||
|
</div>
|
||||||
|
<Br>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui toggle checkbox">
|
||||||
|
<input type="checkbox" tabindex="0" name="useTCP" class="hidden">
|
||||||
|
<label>Enable TCP<br>
|
||||||
|
<small>Forward TCP request on this listening socket</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Mode</label>
|
<div class="ui toggle checkbox">
|
||||||
<select name="mode" class="ui dropdown">
|
<input type="checkbox" tabindex="0" name="useUDP" class="hidden">
|
||||||
<option value="">Select Mode</option>
|
<label>Enable UDP<br>
|
||||||
<option value="listen">Listen</option>
|
<small>Forward UDP request on this listening socket</small></label>
|
||||||
<option value="transport">Transport</option>
|
</div>
|
||||||
<option value="starter">Starter</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<button id="addTcpProxyButton" class="ui basic button" type="submit"><i class="ui green add icon"></i> Create</button>
|
<button id="addStreamProxyButton" class="ui basic button" type="submit"><i class="ui green add icon"></i> Create</button>
|
||||||
<button id="editTcpProxyButton" class="ui basic button" onclick="confirmEditTCPProxyConfig(event);" style="display:none;"><i class="ui green check icon"></i> Update</button>
|
<button id="editStreamProxyButton" class="ui basic button" onclick="confirmEditTCPProxyConfig(event);" style="display:none;"><i class="ui green check icon"></i> Update</button>
|
||||||
<button class="ui basic red button" onclick="event.preventDefault(); cancelTCPProxyEdit(event);"><i class="ui red remove icon"></i> Cancel</button>
|
<button class="ui basic red button" onclick="event.preventDefault(); cancelStreamProxyEdit(event);"><i class="ui red remove icon"></i> Cancel</button>
|
||||||
|
<!--
|
||||||
<div class="ui basic inverted segment" style="background: var(--theme_background_inverted); border-radius: 0.6em;">
|
<div class="ui basic inverted segment" style="background: var(--theme_background_inverted); border-radius: 0.6em;">
|
||||||
<p>TCP Proxy support the following TCP sockets proxy modes</p>
|
<p>TCP Proxy support the following TCP sockets proxy modes</p>
|
||||||
<table class="ui celled padded inverted basic table">
|
<table class="ui celled padded inverted basic table">
|
||||||
@ -128,18 +140,19 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
let editingTCPProxyConfigUUID = ""; //The current editing TCP Proxy config UUID
|
let editingStreamProxyConfigUUID = ""; //The current editing TCP Proxy config UUID
|
||||||
|
|
||||||
$("#tcpProxyForm .dropdown").dropdown();
|
$("#streamProxyForm .dropdown").dropdown();
|
||||||
$('#tcpProxyForm').on('submit', function(event) {
|
$('#streamProxyForm').on('submit', function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
//Check if update mode
|
//Check if update mode
|
||||||
if ($("#editTcpProxyButton").is(":visible")){
|
if ($("#editStreamProxyButton").is(":visible")){
|
||||||
confirmEditTCPProxyConfig(event);
|
confirmEditTCPProxyConfig(event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -154,7 +167,7 @@
|
|||||||
// Send the AJAX POST request
|
// Send the AJAX POST request
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: '/api/tcpprox/config/add',
|
url: '/api/streamprox/config/add',
|
||||||
data: form.serialize(),
|
data: form.serialize(),
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
@ -162,7 +175,7 @@
|
|||||||
}else{
|
}else{
|
||||||
msgbox("Config Added");
|
msgbox("Config Added");
|
||||||
}
|
}
|
||||||
clearTCPProxyAddEditForm();
|
clearStreamProxyAddEditForm();
|
||||||
initProxyConfigList();
|
initProxyConfigList();
|
||||||
$("#addproxyConfig").slideUp("fast");
|
$("#addproxyConfig").slideUp("fast");
|
||||||
},
|
},
|
||||||
@ -172,15 +185,15 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function clearTCPProxyAddEditForm(){
|
function clearStreamProxyAddEditForm(){
|
||||||
$('#tcpProxyForm input, #tcpProxyForm select').val('');
|
$('#streamProxyForm input, #streamProxyForm select').val('');
|
||||||
$('#tcpProxyForm select').dropdown('clear');
|
$('#streamProxyForm select').dropdown('clear');
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelTCPProxyEdit(event=undefined) {
|
function cancelStreamProxyEdit(event=undefined) {
|
||||||
clearTCPProxyAddEditForm();
|
clearStreamProxyAddEditForm();
|
||||||
$("#addTcpProxyButton").show();
|
$("#addStreamProxyButton").show();
|
||||||
$("#editTcpProxyButton").hide();
|
$("#editStreamProxyButton").hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateTCPProxyConfig(form){
|
function validateTCPProxyConfig(form){
|
||||||
@ -230,35 +243,36 @@
|
|||||||
proxyConfigs.forEach(function(config) {
|
proxyConfigs.forEach(function(config) {
|
||||||
var runningLogo = 'Stopped';
|
var runningLogo = 'Stopped';
|
||||||
var runningClass = "stopped";
|
var runningClass = "stopped";
|
||||||
var startButton = `<button onclick="startTcpProx('${config.UUID}');" class="ui button" title="Start Proxy"><i class="green play icon"></i> Start Proxy</button>`;
|
var startButton = `<button onclick="startStreamProx('${config.UUID}');" class="ui button" title="Start Proxy"><i class="green play icon"></i> Start Proxy</button>`;
|
||||||
if (config.Running){
|
if (config.Running){
|
||||||
runningLogo = 'Running';
|
runningLogo = 'Running';
|
||||||
startButton = `<button onclick="stopTcpProx('${config.UUID}');" class="ui button" title="Start Proxy"><i class="red stop icon"></i> Stop Proxy</button>`;
|
startButton = `<button onclick="stopStreamProx('${config.UUID}');" class="ui button" title="Start Proxy"><i class="red stop icon"></i> Stop Proxy</button>`;
|
||||||
runningClass = "running"
|
runningClass = "running"
|
||||||
}
|
}
|
||||||
|
|
||||||
var modeText = "Unknown";
|
var modeText = [];
|
||||||
if (config.Mode == 0){
|
if (config.UseTCP){
|
||||||
modeText = "Listen";
|
modeText.push("TCP")
|
||||||
}else if (config.Mode == 1){
|
|
||||||
modeText = "Transport";
|
|
||||||
}else if (config.Mode == 2){
|
|
||||||
modeText = "Starter";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.UseUDP){
|
||||||
|
modeText.push("UDP")
|
||||||
|
}
|
||||||
|
|
||||||
|
modeText = modeText.join(" | ")
|
||||||
|
|
||||||
var thisConfig = encodeURIComponent(JSON.stringify(config));
|
var thisConfig = encodeURIComponent(JSON.stringify(config));
|
||||||
|
|
||||||
var row = $(`<tr class="tcproxConfig ${runningClass}" uuid="${config.UUID}" config="${thisConfig}">`);
|
var row = $(`<tr class="tcproxConfig ${runningClass}" uuid="${config.UUID}" config="${thisConfig}">`);
|
||||||
row.append($('<td>').html(`
|
row.append($('<td>').html(`
|
||||||
${config.Name}
|
${config.Name}
|
||||||
<div class="statusText">${runningLogo}</div>`));
|
<div class="statusText">${runningLogo}</div>`));
|
||||||
row.append($('<td>').text(config.PortA));
|
row.append($('<td>').text(config.ListeningAddress));
|
||||||
row.append($('<td>').text(config.PortB));
|
row.append($('<td>').text(config.ProxyTargetAddr));
|
||||||
row.append($('<td>').text(modeText));
|
row.append($('<td>').text(modeText));
|
||||||
row.append($('<td>').text(config.Timeout));
|
row.append($('<td>').text(config.Timeout));
|
||||||
row.append($('<td>').html(`
|
row.append($('<td>').html(`
|
||||||
<div class="ui basic vertical fluid tiny buttons">
|
<div class="ui basic vertical fluid tiny buttons">
|
||||||
<button class="ui button" onclick="validateProxyConfig('${config.UUID}', this);" title="Validate Config"><i class="teal question circle outline icon"></i> CXN Test</button>
|
|
||||||
${startButton}
|
${startButton}
|
||||||
<button onclick="editTCPProxyConfig('${config.UUID}');" class="ui button" title="Edit Config"><i class="edit icon"></i> Edit </button>
|
<button onclick="editTCPProxyConfig('${config.UUID}');" class="ui button" title="Edit Config"><i class="edit icon"></i> Edit </button>
|
||||||
<button onclick="deleteTCPProxyConfig('${config.UUID}');" class="ui red basic button" title="Delete Config"><i class="trash icon"></i> Remove</button>
|
<button onclick="deleteTCPProxyConfig('${config.UUID}');" class="ui red basic button" title="Delete Config"><i class="trash icon"></i> Remove</button>
|
||||||
@ -281,49 +295,46 @@
|
|||||||
return thisConfig;
|
return thisConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateProxyConfig(configUUID, btn){
|
|
||||||
$(btn).html(`<i class="ui loading spinner icon"></i>`);
|
|
||||||
$.ajax({
|
|
||||||
url: "/api/tcpprox/config/validate",
|
|
||||||
data: {uuid: configUUID},
|
|
||||||
success: function(data){
|
|
||||||
if (data.error != undefined){
|
|
||||||
let errormsg = data.error.charAt(0).toUpperCase() + data.error.slice(1);
|
|
||||||
$(btn).html(`<i class="red times icon"></i> ${errormsg}`);
|
|
||||||
msgbox(data.error, false, 6000);
|
|
||||||
}else{
|
|
||||||
$(btn).html(`<i class="green check icon"></i> Config Valid`);
|
|
||||||
msgbox("Config Check Passed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function editTCPProxyConfig(configUUID){
|
function editTCPProxyConfig(configUUID){
|
||||||
let targetConfig = getConfigDetailsFromDOM(configUUID);
|
let targetConfig = getConfigDetailsFromDOM(configUUID);
|
||||||
if (targetConfig != null){
|
if (targetConfig != null){
|
||||||
$("#addTcpProxyButton").hide();
|
$("#addStreamProxyButton").hide();
|
||||||
$("#editTcpProxyButton").show();
|
$("#editStreamProxyButton").show();
|
||||||
$.each(targetConfig, function(key, value) {
|
$.each(targetConfig, function(key, value) {
|
||||||
var field = $("#tcpProxyForm").find('[name="' + key.toLowerCase() + '"]');
|
var field;
|
||||||
if (field.length > 0) {
|
if (key == "UseTCP"){
|
||||||
if (field.is('input')) {
|
let checkboxEle = $("#streamProxyForm input[name=useTCP]").parent();
|
||||||
field.val(value);
|
if (value === true){
|
||||||
}else if (field.is('select')){
|
$(checkboxEle).checkbox("set checked");
|
||||||
if (key.toLowerCase() == "mode"){
|
}else{
|
||||||
if (value == 0){
|
$(checkboxEle).checkbox("set unchecked");
|
||||||
value = "listen";
|
|
||||||
}else if (value == 1){
|
|
||||||
value = "transport";
|
|
||||||
}else if (value == 2){
|
|
||||||
value = "starter";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$(field).dropdown("set selected", value);
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}else if (key == "UseUDP"){
|
||||||
|
let checkboxEle = $("#streamProxyForm input[name=useUDP]").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"){
|
||||||
|
field = $("#streamProxyForm input[name=proxyAddr]");
|
||||||
|
}else if (key == "UUID"){
|
||||||
|
field = $("#streamProxyForm input[name=uuid]");
|
||||||
|
}else if (key == "Name"){
|
||||||
|
field = $("#streamProxyForm input[name=name]");
|
||||||
|
}else if (key == "Timeout"){
|
||||||
|
field = $("#streamProxyForm input[name=timeout]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field != undefined && field.length > 0) {
|
||||||
|
field.val(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
editingTCPProxyConfigUUID = configUUID;
|
editingStreamProxyConfigUUID = configUUID;
|
||||||
$("#addproxyConfig").slideDown("fast");
|
$("#addproxyConfig").slideDown("fast");
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
@ -334,7 +345,7 @@
|
|||||||
function confirmEditTCPProxyConfig(event){
|
function confirmEditTCPProxyConfig(event){
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
var form = $("#tcpProxyForm");
|
var form = $("#streamProxyForm");
|
||||||
|
|
||||||
var formValid = validateTCPProxyConfig(form);
|
var formValid = validateTCPProxyConfig(form);
|
||||||
if (!formValid){
|
if (!formValid){
|
||||||
@ -344,8 +355,17 @@
|
|||||||
// Send the AJAX POST request
|
// Send the AJAX POST request
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: '/api/tcpprox/config/edit',
|
url: '/api/streamprox/config/edit',
|
||||||
data: form.serialize(),
|
method: "POST",
|
||||||
|
data: {
|
||||||
|
uuid: $("#streamProxyForm input[name=uuid]").val().trim(),
|
||||||
|
name: $("#streamProxyForm input[name=name]").val().trim(),
|
||||||
|
listenAddr: $("#streamProxyForm input[name=listenAddr]").val().trim(),
|
||||||
|
proxyAddr: $("#streamProxyForm input[name=proxyAddr]").val().trim(),
|
||||||
|
useTCP: $("#streamProxyForm input[name=useTCP]")[0].checked ,
|
||||||
|
useUDP: $("#streamProxyForm input[name=useUDP]")[0].checked ,
|
||||||
|
timeout: parseInt($("#streamProxyForm input[name=timeout]").val().trim()),
|
||||||
|
},
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
msgbox(response.error, false, 6000);
|
msgbox(response.error, false, 6000);
|
||||||
@ -353,7 +373,7 @@
|
|||||||
msgbox("Config Updated");
|
msgbox("Config Updated");
|
||||||
}
|
}
|
||||||
initProxyConfigList();
|
initProxyConfigList();
|
||||||
cancelTCPProxyEdit();
|
cancelStreamProxyEdit();
|
||||||
|
|
||||||
},
|
},
|
||||||
error: function() {
|
error: function() {
|
||||||
@ -364,7 +384,7 @@
|
|||||||
|
|
||||||
function deleteTCPProxyConfig(configUUID){
|
function deleteTCPProxyConfig(configUUID){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/tcpprox/config/delete",
|
url: "/api/streamprox/config/delete",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data: {uuid: configUUID},
|
data: {uuid: configUUID},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
@ -379,9 +399,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Start a TCP proxy by their config UUID
|
//Start a TCP proxy by their config UUID
|
||||||
function startTcpProx(configUUID){
|
function startStreamProx(configUUID){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/tcpprox/config/start",
|
url: "/api/streamprox/config/start",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data: {uuid: configUUID},
|
data: {uuid: configUUID},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
@ -397,9 +417,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Stop a TCP proxy by their config UUID
|
//Stop a TCP proxy by their config UUID
|
||||||
function stopTcpProx(configUUID){
|
function stopStreamProx(configUUID){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/api/tcpprox/config/stop",
|
url: "/api/streamprox/config/stop",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data: {uuid: configUUID},
|
data: {uuid: configUUID},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
@ -417,7 +437,7 @@
|
|||||||
function initProxyConfigList(){
|
function initProxyConfigList(){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: '/api/tcpprox/config/list',
|
url: '/api/streamprox/config/list',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
renderProxyConfigs(response);
|
renderProxyConfigs(response);
|
||||||
},
|
},
|
@ -52,8 +52,8 @@
|
|||||||
<a class="item" tag="rules">
|
<a class="item" tag="rules">
|
||||||
<i class="simplistic plus square icon"></i> Create Proxy Rules
|
<i class="simplistic plus square icon"></i> Create Proxy Rules
|
||||||
</a>
|
</a>
|
||||||
<a class="item" tag="tcpprox">
|
<a class="item" tag="streamproxy">
|
||||||
<i class="simplistic exchange icon"></i> TCP Proxy
|
<i class="simplistic exchange icon"></i> Stream Proxy
|
||||||
</a>
|
</a>
|
||||||
<div class="ui divider menudivider">Access & Connections</div>
|
<div class="ui divider menudivider">Access & Connections</div>
|
||||||
<a class="item" tag="cert">
|
<a class="item" tag="cert">
|
||||||
@ -125,7 +125,7 @@
|
|||||||
<div id="zgrok" class="functiontab" target="zgrok.html"></div>
|
<div id="zgrok" class="functiontab" target="zgrok.html"></div>
|
||||||
|
|
||||||
<!-- TCP Proxy -->
|
<!-- TCP Proxy -->
|
||||||
<div id="tcpprox" class="functiontab" target="tcpprox.html"></div>
|
<div id="streamproxy" class="functiontab" target="streamprox.html"></div>
|
||||||
|
|
||||||
<!-- Web Server -->
|
<!-- Web Server -->
|
||||||
<div id="webserv" class="functiontab" target="webserv.html"></div>
|
<div id="webserv" class="functiontab" target="webserv.html"></div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user