Browse Source

auto update script executed

Toby Chui 1 year ago
parent
commit
ace7481080
5 changed files with 267 additions and 54 deletions
  1. 2 0
      main.go
  2. 116 41
      mod/tcpprox/conn.go
  3. 100 13
      mod/tcpprox/tcpprox.go
  4. 43 0
      mod/tcpprox/tcpprox_test.go
  5. 6 0
      start.go

+ 2 - 0
main.go

@@ -23,6 +23,7 @@ import (
 	"imuslab.com/zoraxy/mod/netstat"
 	"imuslab.com/zoraxy/mod/sshprox"
 	"imuslab.com/zoraxy/mod/statistic"
+	"imuslab.com/zoraxy/mod/tcpprox"
 	"imuslab.com/zoraxy/mod/tlscert"
 	"imuslab.com/zoraxy/mod/uptime"
 	"imuslab.com/zoraxy/mod/utils"
@@ -61,6 +62,7 @@ var (
 	mdnsScanner        *mdns.MDNSHost          //mDNS discovery services
 	ganManager         *ganserv.NetworkManager //Global Area Network Manager
 	webSshManager      *sshprox.Manager        //Web SSH connection service
+	tcpProxyManager    *tcpprox.Manager        //TCP Proxy Manager
 	EmailSender        *email.Sender           //Email sender that handle email sending
 )
 

+ 116 - 41
mod/tcpprox/conn.go

@@ -1,6 +1,7 @@
 package tcpprox
 
 import (
+	"errors"
 	"io"
 	"log"
 	"net"
@@ -13,12 +14,6 @@ func isValidIP(ip string) bool {
 	return parsedIP != nil
 }
 
-/*
-Connection Copy
-
-Copy between two connections, and accumlate the copied bytes in the
-accumulator number
-*/
 func connCopy(conn1 net.Conn, conn2 net.Conn, wg *sync.WaitGroup, accumulator *int64) {
 	io.Copy(conn1, conn2)
 	conn1.Close()
@@ -28,103 +23,183 @@ func connCopy(conn1 net.Conn, conn2 net.Conn, wg *sync.WaitGroup, accumulator *i
 	wg.Done()
 }
 
-func forward(conn1 net.Conn, conn2 net.Conn) {
+func forward(conn1 net.Conn, conn2 net.Conn, accumulator *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)
-	go connCopy(conn2, conn1, &wg)
+	go connCopy(conn1, conn2, &wg, accumulator)
+	go connCopy(conn2, conn1, &wg, accumulator)
 	//blocking when the wg is locked
 	wg.Wait()
 }
 
-func accept(listener net.Listener) net.Conn {
+func accept(listener net.Listener) (net.Conn, error) {
 	conn, err := listener.Accept()
 	if err != nil {
-		log.Println("[x]", "accept connect ["+conn.RemoteAddr().String()+"] faild.", err.Error())
-		return nil
+		return nil, err
 	}
 	log.Println("[√]", "accept a new client. remote address:["+conn.RemoteAddr().String()+"], local address:["+conn.LocalAddr().String()+"]")
-	return conn
+	return conn, err
 }
 
-func start_server(address string) net.Listener {
+func startListener(address string) (net.Listener, error) {
 	log.Println("[+]", "try to start server on:["+address+"]")
 	server, err := net.Listen("tcp", address)
 	if err != nil {
-		log.Fatalln("[x]", "listen address ["+address+"] faild.")
+		return nil, errors.New("listen address [" + address + "] faild")
 	}
 	log.Println("[√]", "start listen at address:["+address+"]")
-	return server
+	return server, nil
 }
 
-func port2port(port1 string, port2 string) {
-	listen1 := start_server("0.0.0.0:" + port1)
-	listen2 := start_server("0.0.0.0:" + port2)
+/*
+portA -> server
+portB -> server
+*/
+func (c *ProxyRelayConfig) Port2port(port1 string, port2 string, stopChan chan bool) error {
+	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 := accept(listen1)
-		conn2 := accept(listen2)
+		conn1, err := accept(listen1)
+		if err != nil {
+			if !c.Running {
+				return nil
+			}
+			continue
+		}
+
+		conn2, err := accept(listen2)
+		if err != nil {
+			if !c.Running {
+				return nil
+			}
+			continue
+		}
+
 		if conn1 == nil || conn2 == nil {
-			log.Println("[x]", "accept client faild. retry in ", timeout, " seconds. ")
-			time.Sleep(timeout * time.Second)
+			log.Println("[x]", "accept client faild. retry in ", c.Timeout, " seconds. ")
+			time.Sleep(time.Duration(c.Timeout) * time.Second)
 			continue
 		}
-		forward(conn1, conn2)
+		forward(conn1, conn2, &c.accumulatedByteTransfered)
 	}
 }
 
-func port2host(allowPort string, targetAddress string) {
-	server := start_server("0.0.0.0:" + allowPort)
+/*
+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 := accept(server)
-		if conn == nil {
+		conn, err := accept(server)
+		if conn == nil || err != nil {
+			if !c.Running {
+				//Terminate by stop chan. Exit listener loop
+				return nil
+			}
+
+			//Connection error. Retry
 			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. ")
+				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(timeout * time.Second)
+				time.Sleep(time.Duration(c.Timeout) * time.Second)
 				return
 			}
 			log.Println("[→]", "connect target address ["+targetAddress+"] success.")
-			forward(target, conn)
+			forward(target, conn, &c.accumulatedByteTransfered)
 		}(targetAddress)
 	}
 }
 
-func host2host(address1, address2 string) {
-	for {
+/*
+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 {
-			host1, err = net.Dial("tcp", address1)
+			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 ", timeout, " seconds. ")
-				time.Sleep(timeout * time.Second)
+				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 {
-			host2, err = net.Dial("tcp", address2)
+			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 ", timeout, " seconds. ")
-				time.Sleep(timeout * time.Second)
+				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
 			}
 		}
-		forward(host1, host2)
+		forward(host1, host2, &c.accumulatedByteTransfered)
 	}
+
+	return nil
 }

+ 100 - 13
mod/tcpprox/tcpprox.go

@@ -1,6 +1,11 @@
 package tcpprox
 
-import "imuslab.com/zoraxy/mod/database"
+import (
+	"errors"
+
+	uuid "github.com/satori/go.uuid"
+	"imuslab.com/zoraxy/mod/database"
+)
 
 /*
 	TCP Proxy
@@ -16,15 +21,24 @@ const (
 	ProxyMode_Starter   = 2
 )
 
+type ProxyRelayOptions struct {
+	Name    string
+	PortA   string
+	PortB   string
+	Timeout int
+	Mode    int
+}
+
 type ProxyRelayConfig struct {
-	UUID     string    //A UUIDv4 representing this config
-	Name     string    //Name of the config
-	Running  bool      //If the service is running
-	PortA    int       //Ports A (config depends on mode)
-	PortB    int       //Ports B (config depends on mode)
-	Mode     int       //Operation Mode
-	Timeout  int       //Timeout for connection
-	StopChan chan bool //Stop channel to stop the listener
+	UUID                      string    //A UUIDv4 representing this config
+	Name                      string    //Name of the config
+	Running                   bool      //If the service is running
+	PortA                     string    //Ports A (config depends on mode)
+	PortB                     string    //Ports B (config depends on mode)
+	Mode                      int       //Operation Mode
+	Timeout                   int       //Timeout for connection in sec
+	StopChan                  chan bool //Stop channel to stop the listener
+	accumulatedByteTransfered int64     //The total number of bytes transfered
 }
 
 type Options struct {
@@ -37,8 +51,7 @@ type Manager struct {
 	Configs []*ProxyRelayConfig
 
 	//Realtime Statistics
-	Connections               int   //currently connected connect counts
-	AccumulatedByteTransfered int64 //The total number of bytes transfered
+	Connections int //currently connected connect counts
 }
 
 func NewTCProxy(options *Options) *Manager {
@@ -49,6 +62,8 @@ func NewTCProxy(options *Options) *Manager {
 		options.Database.Read("tcprox", "rules", &previousRules)
 	}
 
+	//TODO: Remove debug
+
 	return &Manager{
 		Options:     options,
 		Configs:     previousRules,
@@ -56,12 +71,84 @@ func NewTCProxy(options *Options) *Manager {
 	}
 }
 
-func (m *Manager) NewConfig(config *ProxyRelayConfig) error {
+func (m *Manager) NewConfig(config *ProxyRelayOptions) string {
+	//Generate a new config from options
+	configUUID := uuid.NewV4().String()
+	thisConfig := ProxyRelayConfig{
+		UUID:                      configUUID,
+		Name:                      config.Name,
+		Running:                   false,
+		PortA:                     config.PortA,
+		PortB:                     config.PortB,
+		Mode:                      config.Mode,
+		Timeout:                   config.Timeout,
+		StopChan:                  nil,
+		accumulatedByteTransfered: 0,
+	}
+	m.Configs = append(m.Configs, &thisConfig)
+	m.SaveConfigToDatabase()
+	return configUUID
+}
+
+func (m *Manager) GetConfigByUUID(configUUID string) (*ProxyRelayConfig, error) {
+	// Find and return the config with the specified UUID
+	for _, config := range m.Configs {
+		if config.UUID == configUUID {
+			return config, nil
+		}
+	}
+	return nil, errors.New("config not found")
+}
+
+// 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 {
+	// Find the config with the specified UUID
+	foundConfig, err := m.GetConfigByUUID(configUUID)
+	if err != nil {
+		return err
+	}
+
+	// Validate and update the fields
+	if newName != "" {
+		foundConfig.Name = newName
+	}
+	if newPortA != "" {
+		if !isValidIP(newPortA) {
+			return errors.New("PortA is not a valid IP")
+		}
+		foundConfig.PortA = newPortA
+	}
+	if newPortB != "" {
+		if !isValidIP(newPortB) {
+			return errors.New("PortB is not a valid IP")
+		}
+		foundConfig.PortB = newPortB
+	}
+	if newMode != 0 {
+		if newMode > 2 || newMode < 0 {
+			return errors.New("invalid mode given")
+		}
+		foundConfig.Mode = newMode
+	}
+	if newTimeout != 0 {
+		foundConfig.Timeout = newTimeout
+	}
+
+	m.SaveConfigToDatabase()
+
 	return nil
 }
 
 func (m *Manager) RemoveConfig(configUUID string) error {
-	return nil
+	// Find and remove the config with the specified UUID
+	for i, config := range m.Configs {
+		if config.UUID == configUUID {
+			m.Configs = append(m.Configs[:i], m.Configs[i+1:]...)
+			m.SaveConfigToDatabase()
+			return nil
+		}
+	}
+	return errors.New("config not found")
 }
 
 func (m *Manager) SaveConfigToDatabase() {

+ 43 - 0
mod/tcpprox/tcpprox_test.go

@@ -0,0 +1,43 @@
+package tcpprox_test
+
+import (
+	"testing"
+	"time"
+
+	"imuslab.com/zoraxy/mod/tcpprox"
+)
+
+func TestPort2Port(t *testing.T) {
+	// Create a stopChan to control the loop
+	stopChan := make(chan bool)
+
+	// Create a ProxyRelayConfig with dummy values
+	config := &tcpprox.ProxyRelayConfig{
+		Timeout: 1,
+	}
+
+	// Run port2port in a separate goroutine
+	t.Log("Starting go routine for proxy service")
+	go func() {
+		err := config.Port2host("8080", ":80", stopChan)
+		if err != nil {
+			t.Errorf("port2port returned an error: %v", err)
+		}
+	}()
+
+	// Let the goroutine run for a while
+	time.Sleep(10 * time.Second)
+
+	// Send a stop signal to stopChan
+	t.Log("Sending over stop signal")
+	stopChan <- true
+
+	// Allow some time for the goroutine to exit
+	time.Sleep(1 * time.Second)
+
+	// If the goroutine is still running, it means it did not stop as expected
+	if config.Running {
+		t.Errorf("port2port did not stop as expected")
+	}
+
+}

+ 6 - 0
start.go

@@ -17,6 +17,7 @@ import (
 	"imuslab.com/zoraxy/mod/netstat"
 	"imuslab.com/zoraxy/mod/sshprox"
 	"imuslab.com/zoraxy/mod/statistic"
+	"imuslab.com/zoraxy/mod/tcpprox"
 	"imuslab.com/zoraxy/mod/tlscert"
 )
 
@@ -163,6 +164,11 @@ func startupSequence() {
 	//Create WebSSH Manager
 	webSshManager = sshprox.NewSSHProxyManager()
 
+	//Create TCP Proxy Manager
+	tcpProxyManager = tcpprox.NewTCProxy(&tcpprox.Options{
+		Database: sysdb,
+	})
+
 	//Create WoL MAC storage table
 	sysdb.NewTable("wolmac")