mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-07-03 21:01:44 +02:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
273cae2a98 | |||
6b3b89f7bf | |||
2d611a559a | |||
6c5eba01c2 | |||
f641797d10 | |||
f92ff068f3 | |||
b59ac47c8c | |||
8030f3d62a | |||
f8f623e3e4 | |||
061839756c | |||
1dcaa0c257 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -56,4 +56,5 @@ log
|
||||
tmp
|
||||
sys.*
|
||||
www/html/index.html
|
||||
*.exe
|
||||
*.exe
|
||||
/src/dist
|
||||
|
33
CHANGELOG.md
33
CHANGELOG.md
@ -1,3 +1,36 @@
|
||||
# v3.2.4 28 Jun 2025
|
||||
|
||||
A big release since v3.1.9. Versions from 3.2.0 to 3.2.3 were prereleases.
|
||||
|
||||
|
||||
+ Added Authentik support by [JokerQyou](https://github.com/tobychui/zoraxy/commits?author=JokerQyou)
|
||||
+ Added pluginsystem and moved GAN and Zerotier to plugins
|
||||
+ Add loopback detection [#573](https://github.com/tobychui/zoraxy/issues/573)
|
||||
+ Fixed Dark theme not working with Advanced Option accordion [#591](https://github.com/tobychui/zoraxy/issues/591)
|
||||
+ Update logger to include UserAgent by [Raithmir](https://github.com/Raithmir)
|
||||
+ Fixed memory usage in UI [#600](https://github.com/tobychui/zoraxy/issues/600)
|
||||
+ Added docker-compose.yml by [SamuelPalubaCZ](https://github.com/tobychui/zoraxy/commits?author=SamuelPalubaCZ)
|
||||
+ Added more statistics for proxy hosts [#201](https://github.com/tobychui/zoraxy/issues/201) and [#608](https://github.com/tobychui/zoraxy/issues/608)
|
||||
+ Fixed origin field in logs [#618](https://github.com/tobychui/zoraxy/issues/618)
|
||||
+ Added FreeBSD support by Andreas Burri
|
||||
+ Fixed HTTP proxy redirect [#626](https://github.com/tobychui/zoraxy/issues/626)
|
||||
+ Fixed proxy handling #629](https://github.com/tobychui/zoraxy/issues/629)
|
||||
+ Move Scope ID handling into CIDR check by [Nirostar](https://github.com/tobychui/zoraxy/commits?author=Nirostar)
|
||||
+ Prevent the browser from filling the saved Zoraxy login account by [WHFo](https://github.com/tobychui/zoraxy/commits?author=WHFo)
|
||||
+ Added port number and http proto to http proxy list link
|
||||
+ Fixed headers for authelia by [james-d-elliott](https://github.com/tobychui/zoraxy/commits?author=james-d-elliott)
|
||||
+ Refactored docker container list and UI improvements by [eyerrock](https://github.com/tobychui/zoraxy/commits?author=eyerrock)
|
||||
+ Refactored Dockerfile by [PassiveLemon](https://github.com/tobychui/zoraxy/commits?author=PassiveLemon)
|
||||
+ Added new HTTP proxy UI
|
||||
+ Added inbound host name edit function
|
||||
+ Added static web server option to disable listen to all interface
|
||||
+ Merged SSO implementations (Oauth2) [#649](https://github.com/tobychui/zoraxy/pull/649)
|
||||
+ Merged forward-auth optimization [#692(https://github.com/tobychui/zoraxy/pull/692)
|
||||
+ Optimized SSO UI
|
||||
+ Refactored docker image workflows by [PassiveLemon](https://github.com/tobychui/zoraxy/commits?author=PassiveLemon)
|
||||
+ Added disable chunked transfer encoding checkbox (for upstreams that uses legacy HTTP implementations)
|
||||
+ Bug fixes [#694](https://github.com/tobychui/zoraxy/issues/694), [#659](https://github.com/tobychui/zoraxy/issues/659) by [jemmy1794](https://github.com/tobychui/zoraxy/commits?author=jemmy1794), [#695](https://github.com/tobychui/zoraxy/issues/695)
|
||||
|
||||
# v3.1.9 1 Mar 2025
|
||||
|
||||
+ Fixed netstat underflow bug
|
||||
|
@ -200,6 +200,10 @@ Some section of Zoraxy are contributed by our amazing community and if you have
|
||||
|
||||
- Docker Container List by [@eyerrock](https://github.com/eyerrock)
|
||||
|
||||
- Stream Proxy [@jemmy1794](https://github.com/jemmy1794)
|
||||
|
||||
- Change Log [@Morethanevil](https://github.com/Morethanevil)
|
||||
|
||||
### Looking for Maintainer
|
||||
|
||||
- ACME DNS Challenge Module
|
||||
|
@ -47,15 +47,19 @@ 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")
|
||||
enableLogging, _ := utils.PostBool(r, "enableLogging")
|
||||
|
||||
//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,
|
||||
EnableLogging: enableLogging,
|
||||
})
|
||||
|
||||
js, _ := json.Marshal(newConfigUUID)
|
||||
@ -75,6 +79,8 @@ 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")
|
||||
enableLogging, _ := utils.PostBool(r, "enableLogging")
|
||||
|
||||
newTimeoutStr, _ := utils.PostPara(r, "timeout")
|
||||
newTimeout := -1
|
||||
@ -86,8 +92,21 @@ func (m *Manager) HandleEditProxyConfigs(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new ProxyRuleUpdateConfig with the extracted parameters
|
||||
newConfig := &ProxyRuleUpdateConfig{
|
||||
InstanceUUID: configUUID,
|
||||
NewName: newName,
|
||||
NewListeningAddr: listenAddr,
|
||||
NewProxyAddr: proxyAddr,
|
||||
UseTCP: useTCP,
|
||||
UseUDP: useUDP,
|
||||
UseProxyProtocol: useProxyProtocol,
|
||||
EnableLogging: enableLogging,
|
||||
NewTimeout: newTimeout,
|
||||
}
|
||||
|
||||
// Call the EditConfig method to modify the configuration
|
||||
err = m.EditConfig(configUUID, newName, listenAddr, proxyAddr, useTCP, useUDP, newTimeout)
|
||||
err = m.EditConfig(newConfig)
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
return
|
||||
|
110
src/mod/streamproxy/instances.go
Normal file
110
src/mod/streamproxy/instances.go
Normal file
@ -0,0 +1,110 @@
|
||||
package streamproxy
|
||||
|
||||
/*
|
||||
Instances.go
|
||||
|
||||
This file contains the methods to start, stop, and manage the proxy relay instances.
|
||||
|
||||
*/
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (c *ProxyRelayInstance) LogMsg(message string, originalError error) {
|
||||
if !c.EnableLogging {
|
||||
return
|
||||
}
|
||||
|
||||
if originalError != nil {
|
||||
log.Println(message, "error:", originalError)
|
||||
} else {
|
||||
log.Println(message)
|
||||
}
|
||||
}
|
||||
|
||||
// Start a proxy if stopped
|
||||
func (c *ProxyRelayInstance) 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)
|
||||
udpStopChan := make(chan bool)
|
||||
|
||||
//Start the proxy service
|
||||
if c.UseUDP {
|
||||
c.udpStopChan = udpStopChan
|
||||
go func() {
|
||||
err := c.ForwardUDP(c.ListeningAddress, c.ProxyTargetAddr, udpStopChan)
|
||||
if err != nil {
|
||||
if !c.UseTCP {
|
||||
c.Running = false
|
||||
c.udpStopChan = nil
|
||||
c.parent.SaveConfigToDatabase()
|
||||
}
|
||||
c.parent.logf("[proto:udp] Error starting stream proxy "+c.Name+"("+c.UUID+")", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if c.UseTCP {
|
||||
c.tcpStopChan = tcpStopChan
|
||||
go func() {
|
||||
//Default to transport mode
|
||||
err := c.Port2host(c.ListeningAddress, c.ProxyTargetAddr, tcpStopChan)
|
||||
if err != nil {
|
||||
c.Running = false
|
||||
c.tcpStopChan = nil
|
||||
c.parent.SaveConfigToDatabase()
|
||||
c.parent.logf("[proto:tcp] Error starting stream proxy "+c.Name+"("+c.UUID+")", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
//Successfully spawned off the proxy routine
|
||||
c.Running = true
|
||||
c.parent.SaveConfigToDatabase()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return if a proxy config is running
|
||||
func (c *ProxyRelayInstance) IsRunning() bool {
|
||||
return c.tcpStopChan != nil || c.udpStopChan != nil
|
||||
}
|
||||
|
||||
// Restart a proxy config
|
||||
func (c *ProxyRelayInstance) Restart() {
|
||||
if c.IsRunning() {
|
||||
c.Stop()
|
||||
}
|
||||
time.Sleep(3000 * time.Millisecond)
|
||||
c.Start()
|
||||
}
|
||||
|
||||
// Stop a running proxy if running
|
||||
func (c *ProxyRelayInstance) Stop() {
|
||||
c.parent.logf("Stopping Stream Proxy "+c.Name, nil)
|
||||
|
||||
if c.udpStopChan != nil {
|
||||
c.parent.logf("Stopping UDP for "+c.Name, nil)
|
||||
c.udpStopChan <- true
|
||||
c.udpStopChan = nil
|
||||
}
|
||||
|
||||
if c.tcpStopChan != nil {
|
||||
c.parent.logf("Stopping TCP for "+c.Name, nil)
|
||||
c.tcpStopChan <- true
|
||||
c.tcpStopChan = nil
|
||||
}
|
||||
|
||||
c.parent.logf("Stopped Stream Proxy "+c.Name, nil)
|
||||
c.Running = false
|
||||
|
||||
//Update the running status
|
||||
c.parent.SaveConfigToDatabase()
|
||||
}
|
@ -8,7 +8,6 @@ import (
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"imuslab.com/zoraxy/mod/info/logger"
|
||||
@ -24,24 +23,44 @@ 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
|
||||
EnableLogging bool
|
||||
}
|
||||
|
||||
type ProxyRelayConfig struct {
|
||||
UUID string //A UUIDv4 representing this config
|
||||
Name string //Name of the config
|
||||
Running bool //Status, read only
|
||||
AutoStart bool //If the service suppose to started automatically
|
||||
ListeningAddress string //Listening Address, usually 127.0.0.1:port
|
||||
ProxyTargetAddr string //Proxy target address
|
||||
UseTCP bool //Enable TCP proxy
|
||||
UseUDP bool //Enable UDP proxy
|
||||
Timeout int //Timeout for connection in sec
|
||||
// ProxyRuleUpdateConfig is used to update the proxy rule config
|
||||
type ProxyRuleUpdateConfig struct {
|
||||
InstanceUUID string //The target instance UUID to update
|
||||
NewName string //New name for the instance, leave empty for no change
|
||||
NewListeningAddr string //New listening address, leave empty for no change
|
||||
NewProxyAddr string //New proxy target address, leave empty for no change
|
||||
UseTCP bool //Enable TCP proxy, default to false
|
||||
UseUDP bool //Enable UDP proxy, default to false
|
||||
UseProxyProtocol bool //Enable Proxy Protocol, default to false
|
||||
EnableLogging bool //Enable Logging TCP/UDP Message, default to true
|
||||
NewTimeout int //New timeout for the connection, leave -1 for no change
|
||||
}
|
||||
|
||||
type ProxyRelayInstance struct {
|
||||
/* Runtime Config */
|
||||
UUID string //A UUIDv4 representing this config
|
||||
Name string //Name of the config
|
||||
Running bool //Status, read only
|
||||
AutoStart bool //If the service suppose to started automatically
|
||||
ListeningAddress string //Listening Address, usually 127.0.0.1:port
|
||||
ProxyTargetAddr string //Proxy target address
|
||||
UseTCP bool //Enable TCP proxy
|
||||
UseUDP bool //Enable UDP proxy
|
||||
UseProxyProtocol bool //Enable Proxy Protocol
|
||||
EnableLogging bool //Enable logging for ProxyInstance
|
||||
Timeout int //Timeout for connection in sec
|
||||
|
||||
/* Internal */
|
||||
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
|
||||
@ -60,13 +79,14 @@ type Options struct {
|
||||
type Manager struct {
|
||||
//Config and stores
|
||||
Options *Options
|
||||
Configs []*ProxyRelayConfig
|
||||
Configs []*ProxyRelayInstance
|
||||
|
||||
//Realtime Statistics
|
||||
Connections int //currently connected connect counts
|
||||
|
||||
}
|
||||
|
||||
// NewStreamProxy creates a new stream proxy manager with the given options
|
||||
func NewStreamProxy(options *Options) (*Manager, error) {
|
||||
if !utils.FileExists(options.ConfigStore) {
|
||||
err := os.MkdirAll(options.ConfigStore, 0775)
|
||||
@ -76,7 +96,7 @@ func NewStreamProxy(options *Options) (*Manager, error) {
|
||||
}
|
||||
|
||||
//Load relay configs from db
|
||||
previousRules := []*ProxyRelayConfig{}
|
||||
previousRules := []*ProxyRelayInstance{}
|
||||
streamProxyConfigFiles, err := filepath.Glob(options.ConfigStore + "/*.config")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -89,7 +109,7 @@ func NewStreamProxy(options *Options) (*Manager, error) {
|
||||
options.Logger.PrintAndLog("stream-prox", "Read stream proxy config failed", err)
|
||||
continue
|
||||
}
|
||||
thisRelayConfig := &ProxyRelayConfig{}
|
||||
thisRelayConfig := &ProxyRelayInstance{}
|
||||
err = json.Unmarshal(configBytes, thisRelayConfig)
|
||||
if err != nil {
|
||||
options.Logger.PrintAndLog("stream-prox", "Unmarshal stream proxy config failed", err)
|
||||
@ -142,6 +162,7 @@ func (m *Manager) logf(message string, originalError error) {
|
||||
m.Options.Logger.PrintAndLog("stream-prox", message, originalError)
|
||||
}
|
||||
|
||||
// NewConfig creates a new proxy relay config with the given options
|
||||
func (m *Manager) NewConfig(config *ProxyRelayOptions) string {
|
||||
//Generate two zero value for atomic int64
|
||||
aAcc := atomic.Int64{}
|
||||
@ -150,13 +171,15 @@ func (m *Manager) NewConfig(config *ProxyRelayOptions) string {
|
||||
bAcc.Store(0)
|
||||
//Generate a new config from options
|
||||
configUUID := uuid.New().String()
|
||||
thisConfig := ProxyRelayConfig{
|
||||
thisConfig := ProxyRelayInstance{
|
||||
UUID: configUUID,
|
||||
Name: config.Name,
|
||||
ListeningAddress: config.ListeningAddr,
|
||||
ProxyTargetAddr: config.ProxyAddr,
|
||||
UseTCP: config.UseTCP,
|
||||
UseUDP: config.UseUDP,
|
||||
UseProxyProtocol: config.UseProxyProtocol,
|
||||
EnableLogging: config.EnableLogging,
|
||||
Timeout: config.Timeout,
|
||||
tcpStopChan: nil,
|
||||
udpStopChan: nil,
|
||||
@ -170,7 +193,7 @@ func (m *Manager) NewConfig(config *ProxyRelayOptions) string {
|
||||
return configUUID
|
||||
}
|
||||
|
||||
func (m *Manager) GetConfigByUUID(configUUID string) (*ProxyRelayConfig, error) {
|
||||
func (m *Manager) GetConfigByUUID(configUUID string) (*ProxyRelayInstance, error) {
|
||||
// Find and return the config with the specified UUID
|
||||
for _, config := range m.Configs {
|
||||
if config.UUID == configUUID {
|
||||
@ -181,32 +204,34 @@ 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(newConfig *ProxyRuleUpdateConfig) error {
|
||||
// Find the config with the specified UUID
|
||||
foundConfig, err := m.GetConfigByUUID(configUUID)
|
||||
foundConfig, err := m.GetConfigByUUID(newConfig.InstanceUUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate and update the fields
|
||||
if newName != "" {
|
||||
foundConfig.Name = newName
|
||||
if newConfig.NewName != "" {
|
||||
foundConfig.Name = newConfig.NewName
|
||||
}
|
||||
if newListeningAddr != "" {
|
||||
foundConfig.ListeningAddress = newListeningAddr
|
||||
if newConfig.NewListeningAddr != "" {
|
||||
foundConfig.ListeningAddress = newConfig.NewListeningAddr
|
||||
}
|
||||
if newProxyAddr != "" {
|
||||
foundConfig.ProxyTargetAddr = newProxyAddr
|
||||
if newConfig.NewProxyAddr != "" {
|
||||
foundConfig.ProxyTargetAddr = newConfig.NewProxyAddr
|
||||
}
|
||||
|
||||
foundConfig.UseTCP = useTCP
|
||||
foundConfig.UseUDP = useUDP
|
||||
foundConfig.UseTCP = newConfig.UseTCP
|
||||
foundConfig.UseUDP = newConfig.UseUDP
|
||||
foundConfig.UseProxyProtocol = newConfig.UseProxyProtocol
|
||||
foundConfig.EnableLogging = newConfig.EnableLogging
|
||||
|
||||
if newTimeout != -1 {
|
||||
if newTimeout < 0 {
|
||||
if newConfig.NewTimeout != -1 {
|
||||
if newConfig.NewTimeout < 0 {
|
||||
return errors.New("invalid timeout value given")
|
||||
}
|
||||
foundConfig.Timeout = newTimeout
|
||||
foundConfig.Timeout = newConfig.NewTimeout
|
||||
}
|
||||
|
||||
m.SaveConfigToDatabase()
|
||||
@ -215,12 +240,11 @@ func (m *Manager) EditConfig(configUUID string, newName string, newListeningAddr
|
||||
if foundConfig.IsRunning() {
|
||||
foundConfig.Restart()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove the config from file by UUID
|
||||
func (m *Manager) RemoveConfig(configUUID string) error {
|
||||
//Remove the config from file
|
||||
err := os.Remove(filepath.Join(m.Options.ConfigStore, configUUID+".config"))
|
||||
if err != nil {
|
||||
return err
|
||||
@ -250,91 +274,3 @@ func (m *Manager) SaveConfigToDatabase() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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)
|
||||
udpStopChan := make(chan bool)
|
||||
|
||||
//Start the proxy service
|
||||
if c.UseUDP {
|
||||
c.udpStopChan = udpStopChan
|
||||
go func() {
|
||||
err := c.ForwardUDP(c.ListeningAddress, c.ProxyTargetAddr, udpStopChan)
|
||||
if err != nil {
|
||||
if !c.UseTCP {
|
||||
c.Running = false
|
||||
c.udpStopChan = nil
|
||||
c.parent.SaveConfigToDatabase()
|
||||
}
|
||||
c.parent.logf("[proto:udp] Error starting stream proxy "+c.Name+"("+c.UUID+")", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if c.UseTCP {
|
||||
c.tcpStopChan = tcpStopChan
|
||||
go func() {
|
||||
//Default to transport mode
|
||||
err := c.Port2host(c.ListeningAddress, c.ProxyTargetAddr, tcpStopChan)
|
||||
if err != nil {
|
||||
c.Running = false
|
||||
c.tcpStopChan = nil
|
||||
c.parent.SaveConfigToDatabase()
|
||||
c.parent.logf("[proto:tcp] Error starting stream proxy "+c.Name+"("+c.UUID+")", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
//Successfully spawned off the proxy routine
|
||||
c.Running = true
|
||||
c.parent.SaveConfigToDatabase()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return if a proxy config is running
|
||||
func (c *ProxyRelayConfig) IsRunning() bool {
|
||||
return c.tcpStopChan != nil || c.udpStopChan != nil
|
||||
}
|
||||
|
||||
// Restart a proxy config
|
||||
func (c *ProxyRelayConfig) Restart() {
|
||||
if c.IsRunning() {
|
||||
c.Stop()
|
||||
}
|
||||
time.Sleep(3000 * time.Millisecond)
|
||||
c.Start()
|
||||
}
|
||||
|
||||
// Stop a running proxy if running
|
||||
func (c *ProxyRelayConfig) Stop() {
|
||||
c.parent.logf("Stopping Stream Proxy "+c.Name, nil)
|
||||
|
||||
if c.udpStopChan != nil {
|
||||
c.parent.logf("Stopping UDP for "+c.Name, nil)
|
||||
c.udpStopChan <- true
|
||||
c.udpStopChan = nil
|
||||
}
|
||||
|
||||
if c.tcpStopChan != nil {
|
||||
c.parent.logf("Stopping TCP for "+c.Name, nil)
|
||||
c.tcpStopChan <- true
|
||||
c.tcpStopChan = nil
|
||||
}
|
||||
|
||||
c.parent.logf("Stopped Stream Proxy "+c.Name, nil)
|
||||
c.Running = false
|
||||
|
||||
//Update the running status
|
||||
c.parent.SaveConfigToDatabase()
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ func TestPort2Port(t *testing.T) {
|
||||
stopChan := make(chan bool)
|
||||
|
||||
// Create a ProxyRelayConfig with dummy values
|
||||
config := &streamproxy.ProxyRelayConfig{
|
||||
config := &streamproxy.ProxyRelayInstance{
|
||||
Timeout: 1,
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package streamproxy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
@ -30,48 +31,67 @@ func isValidPort(port string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func connCopy(conn1 net.Conn, conn2 net.Conn, wg *sync.WaitGroup, accumulator *atomic.Int64) {
|
||||
func (c *ProxyRelayInstance) 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()+"]")
|
||||
c.LogMsg("[←] close the connect at local:["+conn1.LocalAddr().String()+"] and remote:["+conn1.RemoteAddr().String()+"]", nil)
|
||||
//conn2.Close()
|
||||
//log.Println("[←]", "close the connect at local:["+conn2.LocalAddr().String()+"] and remote:["+conn2.RemoteAddr().String()+"]")
|
||||
//c.LogMsg("[←] close the connect at local:["+conn2.LocalAddr().String()+"] and remote:["+conn2.RemoteAddr().String()+"]", nil)
|
||||
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())
|
||||
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 (c *ProxyRelayInstance) forward(conn1 net.Conn, conn2 net.Conn, aTob *atomic.Int64, bToa *atomic.Int64) {
|
||||
msg := fmt.Sprintf("[+] start transmit. [%s],[%s] <-> [%s],[%s]",
|
||||
conn1.LocalAddr().String(), conn1.RemoteAddr().String(),
|
||||
conn2.LocalAddr().String(), conn2.RemoteAddr().String())
|
||||
c.LogMsg(msg, nil)
|
||||
|
||||
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
|
||||
go c.connCopy(conn1, conn2, &wg, aTob)
|
||||
go c.connCopy(conn2, conn1, &wg, bToa)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (c *ProxyRelayConfig) accept(listener net.Listener) (net.Conn, error) {
|
||||
func (c *ProxyRelayInstance) accept(listener net.Listener) (net.Conn, error) {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//Check if connection in blacklist or whitelist
|
||||
// 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")
|
||||
c.LogMsg("[x] Connection from "+addr.IP.String()+" rejected by access control policy", nil)
|
||||
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
|
||||
c.LogMsg("[√] accept a new client. remote address:["+conn.RemoteAddr().String()+"], local address:["+conn.LocalAddr().String()+"]", nil)
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func startListener(address string) (net.Listener, error) {
|
||||
@ -92,7 +112,7 @@ func startListener(address string) (net.Listener, error) {
|
||||
portA -> server
|
||||
server -> portB
|
||||
*/
|
||||
func (c *ProxyRelayConfig) Port2host(allowPort string, targetAddress string, stopChan chan bool) error {
|
||||
func (c *ProxyRelayInstance) Port2host(allowPort string, targetAddress string, stopChan chan bool) error {
|
||||
listenerStartingAddr := allowPort
|
||||
if isValidPort(allowPort) {
|
||||
//number only, e.g. 8080
|
||||
@ -112,7 +132,7 @@ func (c *ProxyRelayConfig) Port2host(allowPort string, targetAddress string, sto
|
||||
//Start stop handler
|
||||
go func() {
|
||||
<-stopChan
|
||||
log.Println("[x]", "Received stop signal. Exiting Port to Host forwarder")
|
||||
c.LogMsg("[x] Received stop signal. Exiting Port to Host forwarder", nil)
|
||||
server.Close()
|
||||
}()
|
||||
|
||||
@ -129,18 +149,32 @@ func (c *ProxyRelayConfig) Port2host(allowPort string, targetAddress string, sto
|
||||
}
|
||||
|
||||
go func(targetAddress string) {
|
||||
log.Println("[+]", "start connect host:["+targetAddress+"]")
|
||||
c.LogMsg("[+] start connect host:["+targetAddress+"]", nil)
|
||||
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. ")
|
||||
c.LogMsg("[x] connect target address ["+targetAddress+"] failed. retry in "+strconv.Itoa(c.Timeout)+" seconds.", nil)
|
||||
conn.Close()
|
||||
log.Println("[←]", "close the connect at local:["+conn.LocalAddr().String()+"] and remote:["+conn.RemoteAddr().String()+"]")
|
||||
c.LogMsg("[←] close the connect at local:["+conn.LocalAddr().String()+"] and remote:["+conn.RemoteAddr().String()+"]", nil)
|
||||
time.Sleep(time.Duration(c.Timeout) * time.Second)
|
||||
return
|
||||
}
|
||||
log.Println("[→]", "connect target address ["+targetAddress+"] success.")
|
||||
forward(target, conn, &c.aTobAccumulatedByteTransfer, &c.bToaAccumulatedByteTransfer)
|
||||
c.LogMsg("[→] connect target address ["+targetAddress+"] success.", nil)
|
||||
|
||||
if c.UseProxyProtocol {
|
||||
c.LogMsg("[+] write proxy protocol header to target address ["+targetAddress+"]", nil)
|
||||
err = writeProxyProtocolHeaderV1(target, conn)
|
||||
if err != nil {
|
||||
c.LogMsg("[x] Write proxy protocol header failed: "+err.Error(), nil)
|
||||
target.Close()
|
||||
conn.Close()
|
||||
c.LogMsg("[←] close the connect at local:["+conn.LocalAddr().String()+"] and remote:["+conn.RemoteAddr().String()+"]", nil)
|
||||
time.Sleep(time.Duration(c.Timeout) * time.Second)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.forward(target, conn, &c.aTobAccumulatedByteTransfer, &c.bToaAccumulatedByteTransfer)
|
||||
}(targetAddress)
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ func initUDPConnections(listenAddr string, targetAddress string) (*net.UDPConn,
|
||||
}
|
||||
|
||||
// Go routine which manages connection from server to single client
|
||||
func (c *ProxyRelayConfig) RunUDPConnectionRelay(conn *udpClientServerConn, lisenter *net.UDPConn) {
|
||||
func (c *ProxyRelayInstance) RunUDPConnectionRelay(conn *udpClientServerConn, lisenter *net.UDPConn) {
|
||||
var buffer [1500]byte
|
||||
for {
|
||||
// Read from server
|
||||
@ -74,7 +74,7 @@ func (c *ProxyRelayConfig) RunUDPConnectionRelay(conn *udpClientServerConn, lise
|
||||
}
|
||||
|
||||
// Close all connections that waiting for read from server
|
||||
func (c *ProxyRelayConfig) CloseAllUDPConnections() {
|
||||
func (c *ProxyRelayInstance) CloseAllUDPConnections() {
|
||||
c.udpClientMap.Range(func(clientAddr, clientServerConn interface{}) bool {
|
||||
conn := clientServerConn.(*udpClientServerConn)
|
||||
conn.ServerConn.Close()
|
||||
@ -82,7 +82,7 @@ func (c *ProxyRelayConfig) CloseAllUDPConnections() {
|
||||
})
|
||||
}
|
||||
|
||||
func (c *ProxyRelayConfig) ForwardUDP(address1, address2 string, stopChan chan bool) error {
|
||||
func (c *ProxyRelayInstance) 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) {
|
||||
@ -138,12 +138,12 @@ func (c *ProxyRelayConfig) ForwardUDP(address1, address2 string, stopChan chan b
|
||||
continue
|
||||
}
|
||||
c.udpClientMap.Store(saddr, conn)
|
||||
log.Println("[UDP] Created new connection for client " + saddr)
|
||||
c.LogMsg("[UDP] Created new connection for client "+saddr, nil)
|
||||
// Fire up routine to manage new connection
|
||||
go c.RunUDPConnectionRelay(conn, lisener)
|
||||
|
||||
} else {
|
||||
log.Println("[UDP] Found connection for client " + saddr)
|
||||
c.LogMsg("[UDP] Found connection for client "+saddr, nil)
|
||||
conn = rawConn.(*udpClientServerConn)
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,12 @@ func (ws *WebServer) HandlePortChange(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if newPort is a valid TCP port number (1-65535)
|
||||
if newPort < 1 || newPort > 65535 {
|
||||
utils.SendErrorResponse(w, "invalid port number given")
|
||||
return
|
||||
}
|
||||
|
||||
err = ws.ChangePort(strconv.Itoa(newPort))
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, err.Error())
|
||||
@ -106,6 +112,17 @@ func (ws *WebServer) SetDisableListenToAllInterface(w http.ResponseWriter, r *ht
|
||||
utils.SendErrorResponse(w, "unable to save setting")
|
||||
return
|
||||
}
|
||||
|
||||
// Update the option in the web server instance
|
||||
ws.option.DisableListenToAllInterface = disableListen
|
||||
|
||||
// If the server is running and the setting is changed, we need to restart the server
|
||||
if ws.IsRunning() {
|
||||
err = ws.Restart()
|
||||
if err != nil {
|
||||
utils.SendErrorResponse(w, "unable to restart web server: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
utils.SendOK(w)
|
||||
}
|
||||
|
@ -210,6 +210,27 @@ func (ws *WebServer) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ws *WebServer) Restart() error {
|
||||
if ws.isRunning {
|
||||
if err := ws.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := ws.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ws.option.Logger.PrintAndLog("static-webserv", "Static Web Server restarted. Listening on :"+ws.option.Port, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ws *WebServer) IsRunning() bool {
|
||||
ws.mu.Lock()
|
||||
defer ws.mu.Unlock()
|
||||
return ws.isRunning
|
||||
}
|
||||
|
||||
// UpdateDirectoryListing enables or disables directory listing.
|
||||
func (ws *WebServer) UpdateDirectoryListing(enable bool) {
|
||||
ws.option.EnableDirectoryListing = enable
|
||||
|
@ -16,6 +16,7 @@
|
||||
<th>Target Address</th>
|
||||
<th>Mode</th>
|
||||
<th>Timeout (s)</th>
|
||||
<th>Enable Logging</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -73,6 +74,22 @@
|
||||
<small>Forward UDP request on this listening socket</small></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui toggle checkbox">
|
||||
<input type="checkbox" tabindex="0" name="useProxyProtocol" class="hidden">
|
||||
<label>Enable Proxy Protocol V1<br>
|
||||
<small>Enable TCP Proxy Protocol header V1</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui toggle checkbox">
|
||||
<input type="checkbox" tabindex="0" name="enableLogging" class="hidden">
|
||||
<label>Enable Logging<br>
|
||||
<small>Enable logging of connection status and errors for this rule</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button id="addStreamProxyButton" class="ui basic button" type="submit"><i class="ui green add icon"></i> Create</button>
|
||||
<button id="editStreamProxyButton" class="ui basic button" onclick="confirmEditTCPProxyConfig(event, this);" style="display:none;"><i class="ui green check icon"></i> Update</button>
|
||||
<button class="ui basic red button" onclick="event.preventDefault(); cancelStreamProxyEdit(event);"><i class="ui red remove icon"></i> Cancel</button>
|
||||
@ -195,6 +212,10 @@
|
||||
modeText.push("UDP")
|
||||
}
|
||||
|
||||
if (config.UseProxyProtocol){
|
||||
modeText.push("ProxyProtocol V1")
|
||||
}
|
||||
|
||||
modeText = modeText.join(" & ")
|
||||
|
||||
var thisConfig = encodeURIComponent(JSON.stringify(config));
|
||||
@ -207,6 +228,10 @@
|
||||
row.append($('<td>').text(config.ProxyTargetAddr));
|
||||
row.append($('<td>').text(modeText));
|
||||
row.append($('<td>').text(config.Timeout));
|
||||
row.append($('<td>').html(config.EnableLogging ?
|
||||
'<i class="green check icon" title="Logging Enabled"></i>' :
|
||||
'<i class="red times icon" title="Logging Disabled"></i>'
|
||||
));
|
||||
row.append($('<td>').html(`
|
||||
${startButton}
|
||||
<button onclick="editTCPProxyConfig('${config.UUID}');" class="ui circular basic mini icon button" title="Edit Config"><i class="edit icon"></i></button>
|
||||
@ -252,6 +277,22 @@
|
||||
$(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 == "EnableLogging"){
|
||||
let checkboxEle = $("#streamProxyForm input[name=enableLogging]").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 +342,8 @@
|
||||
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 ,
|
||||
enableLogging: $("#streamProxyForm input[name=enableLogging]")[0].checked ,
|
||||
timeout: parseInt($("#streamProxyForm input[name=timeout]").val().trim()),
|
||||
},
|
||||
success: function(response) {
|
||||
|
Reference in New Issue
Block a user