v2 init commit

This commit is contained in:
Toby Chui
2023-05-22 23:05:59 +08:00
parent 5ac0fdde1d
commit c07d5f85df
87 changed files with 273125 additions and 0 deletions

324
src/mod/tcpprox/conn.go Normal file
View File

@@ -0,0 +1,324 @@
package tcpprox
import (
"errors"
"io"
"log"
"net"
"strconv"
"sync"
"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 *int64) {
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 forward(conn1 net.Conn, conn2 net.Conn, aTob *int64, bToa *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 accept(listener net.Listener) (net.Conn, error) {
conn, err := listener.Accept()
if err != nil {
return nil, err
}
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)
}
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) 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 := 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 ", c.Timeout, " seconds. ")
time.Sleep(time.Duration(c.Timeout) * time.Second)
continue
}
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 := 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
}
}
forward(host1, host2, &c.aTobAccumulatedByteTransfer, &c.bToaAccumulatedByteTransfer)
}
return nil
}

162
src/mod/tcpprox/handler.go Normal file
View File

@@ -0,0 +1,162 @@
package tcpprox
import (
"encoding/json"
"net/http"
"strconv"
"imuslab.com/zoraxy/mod/utils"
)
/*
Handler.go
Handlers for the tcprox. Remove this file
if your application do not need any http
handler.
*/
func (m *Manager) HandleAddProxyConfig(w http.ResponseWriter, r *http.Request) {
name, err := utils.PostPara(r, "name")
if err != nil {
utils.SendErrorResponse(w, "name cannot be empty")
return
}
portA, err := utils.PostPara(r, "porta")
if err != nil {
utils.SendErrorResponse(w, "first address cannot be empty")
return
}
portB, err := utils.PostPara(r, "portb")
if err != nil {
utils.SendErrorResponse(w, "second address cannot be empty")
return
}
timeoutStr, _ := utils.PostPara(r, "timeout")
timeout := m.Options.DefaultTimeout
if timeoutStr != "" {
timeout, err = strconv.Atoi(timeoutStr)
if err != nil {
utils.SendErrorResponse(w, "invalid timeout value: "+timeoutStr)
return
}
}
modeValue := ProxyMode_Transport
mode, err := utils.PostPara(r, "mode")
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
newConfigUUID := m.NewConfig(&ProxyRelayOptions{
Name: name,
PortA: portA,
PortB: portB,
Timeout: timeout,
Mode: modeValue,
})
js, _ := json.Marshal(newConfigUUID)
utils.SendJSONResponse(w, string(js))
}
func (m *Manager) HandleEditProxyConfigs(w http.ResponseWriter, r *http.Request) {
// Extract POST parameters using utils.PostPara
configUUID, err := utils.PostPara(r, "uuid")
if err != nil {
utils.SendErrorResponse(w, "config UUID cannot be empty")
return
}
newName, _ := utils.PostPara(r, "name")
newPortA, _ := utils.PostPara(r, "porta")
newPortB, _ := utils.PostPara(r, "portb")
newModeStr, _ := utils.PostPara(r, "mode")
newMode := -1
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")
newTimeout := -1
if newTimeoutStr != "" {
newTimeout, err = strconv.Atoi(newTimeoutStr)
if err != nil {
utils.SendErrorResponse(w, "invalid newTimeout value: "+newTimeoutStr)
return
}
}
// Call the EditConfig method to modify the configuration
err = m.EditConfig(configUUID, newName, newPortA, newPortB, newMode, newTimeout)
if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}
utils.SendOK(w)
}
func (m *Manager) HandleListConfigs(w http.ResponseWriter, r *http.Request) {
js, _ := json.Marshal(m.Configs)
utils.SendJSONResponse(w, string(js))
}
func (m *Manager) HandleGetProxyStatus(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
}
js, _ := json.Marshal(targetConfig)
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)
}

289
src/mod/tcpprox/nb.go.ref Normal file
View File

@@ -0,0 +1,289 @@
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
}

159
src/mod/tcpprox/tcpprox.go Normal file
View File

@@ -0,0 +1,159 @@
package tcpprox
import (
"errors"
uuid "github.com/satori/go.uuid"
"imuslab.com/zoraxy/mod/database"
)
/*
TCP Proxy
Forward port from one port to another
Also accept active connection and passive
connection
*/
const (
ProxyMode_Listen = 0
ProxyMode_Transport = 1
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 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
aTobAccumulatedByteTransfer int64 //Accumulated byte transfer from A to B
bToaAccumulatedByteTransfer int64 //Accumulated byte transfer from B to A
}
type Options struct {
Database *database.Database
DefaultTimeout int
}
type Manager struct {
//Config and stores
Options *Options
Configs []*ProxyRelayConfig
//Realtime Statistics
Connections int //currently connected connect counts
}
func NewTCProxy(options *Options) *Manager {
options.Database.NewTable("tcprox")
previousRules := []*ProxyRelayConfig{}
if options.Database.KeyExists("tcprox", "rules") {
options.Database.Read("tcprox", "rules", &previousRules)
}
return &Manager{
Options: options,
Configs: previousRules,
Connections: 0,
}
}
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,
aTobAccumulatedByteTransfer: 0,
bToaAccumulatedByteTransfer: 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 != "" {
foundConfig.PortA = newPortA
}
if newPortB != "" {
foundConfig.PortB = newPortB
}
if newMode != -1 {
if newMode > 2 || newMode < 0 {
return errors.New("invalid mode given")
}
foundConfig.Mode = newMode
}
if newTimeout != -1 {
if newTimeout < 0 {
return errors.New("invalid timeout value given")
}
foundConfig.Timeout = newTimeout
}
err = foundConfig.ValidateConfigs()
if err != nil {
return err
}
m.SaveConfigToDatabase()
return nil
}
func (m *Manager) RemoveConfig(configUUID string) error {
// 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() {
m.Options.Database.Write("tcprox", "rules", m.Configs)
}

View File

@@ -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", "124.244.86.40:8080", stopChan)
if err != nil {
t.Errorf("port2port returned an error: %v", err)
}
}()
// Let the goroutine run for a while
time.Sleep(20 * 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")
}
}