Skip to content

Commit 2a37940

Browse files
committed
refactoring and minor improvements
1 parent 581789a commit 2a37940

6 files changed

Lines changed: 266 additions & 149 deletions

File tree

.github/workflows/release.yml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,27 @@ jobs:
3131
GOOS=windows GOARCH=amd64 go build -o dist/reverse-soxy-windows-amd64.exe ./cmd/reverse-soxy
3232
GOOS=windows GOARCH=arm64 go build -o dist/reverse-soxy-windows-arm64.exe ./cmd/reverse-soxy
3333
34+
- name: Zip binaries
35+
run: |
36+
cd dist
37+
zip reverse-soxy-linux-amd64.zip reverse-soxy-linux-amd64
38+
zip reverse-soxy-linux-arm64.zip reverse-soxy-linux-arm64
39+
zip reverse-soxy-darwin-amd64.zip reverse-soxy-darwin-amd64
40+
zip reverse-soxy-darwin-arm64.zip reverse-soxy-darwin-arm64
41+
zip reverse-soxy-windows-amd64.zip reverse-soxy-windows-amd64.exe
42+
zip reverse-soxy-windows-arm64.zip reverse-soxy-windows-arm64.exe
43+
44+
- name: Generate checksum file
45+
run: |
46+
cd dist
47+
sha256sum *.zip > checksums
48+
3449
- name: Create Release
3550
id: create_release
3651
uses: softprops/action-gh-release@v1
3752
with:
38-
files: dist/*
53+
files: |
54+
dist/*.zip
55+
dist/checksums
3956
env:
4057
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22
/build/
33

44
# ignore backup files
5-
*.bak
6-
go.sum
5+
*.bak

Makefile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Makefile for reverse-soxy
2+
3+
APP_NAME := reverse-soxy
4+
BUILD_DIR := build
5+
6+
.PHONY: all clean linux_amd64 linux_arm64 darwin_amd64 darwin_arm64 windows_amd64 windows_arm64
7+
8+
all: linux_amd64 linux_arm64 darwin_amd64 darwin_arm64 windows_amd64 windows_arm64
9+
10+
$(BUILD_DIR):
11+
mkdir -p $(BUILD_DIR)
12+
13+
linux_amd64: | $(BUILD_DIR)
14+
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o $(BUILD_DIR)/$(APP_NAME)-linux-amd64 ./cmd/reverse-soxy
15+
16+
linux_arm64: | $(BUILD_DIR)
17+
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o $(BUILD_DIR)/$(APP_NAME)-linux-arm64 ./cmd/reverse-soxy
18+
19+
darwin_amd64: | $(BUILD_DIR)
20+
GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o $(BUILD_DIR)/$(APP_NAME)-darwin-amd64 ./cmd/reverse-soxy
21+
22+
darwin_arm64: | $(BUILD_DIR)
23+
GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o $(BUILD_DIR)/$(APP_NAME)-darwin-arm64 ./cmd/reverse-soxy
24+
25+
windows_amd64: | $(BUILD_DIR)
26+
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o $(BUILD_DIR)/$(APP_NAME)-windows-amd64.exe ./cmd/reverse-soxy
27+
28+
windows_arm64: | $(BUILD_DIR)
29+
GOOS=windows GOARCH=arm64 go build -ldflags="-s -w" -o $(BUILD_DIR)/$(APP_NAME)-windows-arm64.exe ./cmd/reverse-soxy
30+
31+
clean:
32+
rm -rf $(BUILD_DIR)

cmd/reverse-soxy/main.go

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func main() {
2929
cfgPath := flag.String("config", "", "YAML config file path")
3030
modeFlag := flag.String("mode", "", "Component mode: proxy (default), agent, relay")
3131
relayListenPort := flag.Int("relay-listen-port", 9000, "Port for both Proxy registrations and Agent tunnels (relay mode)")
32+
retryFlag := flag.Int("retry", 10, "Maximum number of retries")
3233
registerFlag := flag.Bool("register", false, "Proxy registers its availability to Relay server")
3334
relayAddr := flag.String("relay-addr", "", "Relay server address (IP:port) for registration or agent dialing")
3435
flag.Parse()
@@ -42,11 +43,6 @@ func main() {
4243
os.Exit(0)
4344
}()
4445

45-
// ensure shared secret is provided
46-
if *secretFlag == "" {
47-
logger.Fatal("Shared secret required: use -secret flag or config")
48-
}
49-
5046
// Optional YAML config override
5147
if *cfgPath != "" {
5248
data, err := os.ReadFile(*cfgPath)
@@ -57,20 +53,52 @@ func main() {
5753
SocksListenAddr string `yaml:"socks_listen_addr"`
5854
TunnelListenPort int `yaml:"tunnel_listen_port"`
5955
TunnelAddr string `yaml:"tunnel_addr"`
56+
MaxRetries int `yaml:"max_retries"`
57+
Secret string `yaml:"secret"`
58+
RelayListenPort int `yaml:"relay_listen_port"`
59+
RelayAddr string `yaml:"relay_addr"`
6060
}
6161
if err := yaml.Unmarshal(data, &cfg); err != nil {
6262
logger.Fatalf("Failed to parse config: %v", err)
6363
}
64-
if cfg.SocksListenAddr != "" {
64+
65+
// Apply config values only if CLI flags are at their defaults
66+
if *socksAddr == "127.0.0.1:1080" && cfg.SocksListenAddr != "" {
6567
*socksAddr = cfg.SocksListenAddr
6668
}
67-
if cfg.TunnelListenPort != 0 {
69+
if *tunnelPort == 9000 && cfg.TunnelListenPort != 0 {
6870
*tunnelPort = cfg.TunnelListenPort
6971
}
70-
if cfg.TunnelAddr != "" {
72+
if *tunnelAddr == "" && cfg.TunnelAddr != "" {
7173
*tunnelAddr = cfg.TunnelAddr
7274
}
73-
logger.Debug("Loaded config from %s: socks_listen_addr=%s, tunnel_listen_port=%d, tunnel_addr=%s", *cfgPath, cfg.SocksListenAddr, cfg.TunnelListenPort, cfg.TunnelAddr)
75+
if *secretFlag == "" && cfg.Secret != "" {
76+
*secretFlag = cfg.Secret
77+
}
78+
if *relayListenPort == 9000 && cfg.RelayListenPort != 0 {
79+
*relayListenPort = cfg.RelayListenPort
80+
}
81+
if *relayAddr == "" && cfg.RelayAddr != "" {
82+
*relayAddr = cfg.RelayAddr
83+
}
84+
if *retryFlag == 10 && cfg.MaxRetries != 0 {
85+
*retryFlag = cfg.MaxRetries
86+
}
87+
88+
logger.Debug("Loaded config from %s: socks_listen_addr=%s, tunnel_listen_port=%d, tunnel_addr=%s, secret=%s, relay_listen_port=%d, relay_addr=%s, max_retries=%d",
89+
*cfgPath,
90+
cfg.SocksListenAddr,
91+
cfg.TunnelListenPort,
92+
cfg.TunnelAddr,
93+
cfg.Secret,
94+
cfg.RelayListenPort,
95+
cfg.RelayAddr,
96+
cfg.MaxRetries)
97+
}
98+
99+
// ensure shared secret is provided
100+
if *secretFlag == "" {
101+
logger.Fatal("Shared secret required: use -secret flag or config")
74102
}
75103

76104
// validate tunnelAddr if provided
@@ -103,10 +131,10 @@ func main() {
103131
proxy.RunProxyRelay(*relayAddr, *socksAddr, *secretFlag)
104132
} else if *relayAddr != "" {
105133
// agent via relay
106-
proxy.RunRelayAgent(*relayAddr, *secretFlag)
134+
proxy.RunAgentRelay(*relayAddr, *secretFlag, *retryFlag)
107135
} else if *tunnelAddr != "" {
108136
// direct agent
109-
proxy.RunAgent(*tunnelAddr, *secretFlag)
137+
proxy.RunAgent(*tunnelAddr, *secretFlag, *retryFlag)
110138
} else {
111139
proxy.RunProxy(*socksAddr, *tunnelPort, *secretFlag)
112140
}

internal/proxy/agent.go

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,82 @@ var (
2121
sessionsMap = make(map[uint32]*session)
2222
)
2323

24-
// RunAgent initiates a secure connection to the proxy with authentication/encryption
25-
func RunAgent(proxyAddr, secret string) {
26-
// continuously dial and maintain tunnel
24+
// DefaultMaxRetries is the default number of times to retry connecting before giving up
25+
const DefaultMaxRetries = 10
26+
27+
// RunAgentRelay connects to the proxy via a relay server
28+
func RunAgentRelay(relayAddr, secret string, maxRetries int) {
29+
// Use default value if maxRetries is not positive
30+
if maxRetries <= 0 {
31+
maxRetries = DefaultMaxRetries
32+
}
33+
34+
retryCount := 0
35+
2736
for {
37+
rawConn, err := net.Dial("tcp", relayAddr)
38+
if err != nil {
39+
retryCount++
40+
logger.Error("AgentRelay dial failed: %v (attempt %d/%d)", err, retryCount, maxRetries)
41+
if retryCount >= maxRetries {
42+
logger.Info("Maximum retry attempts (%d) reached, exiting", maxRetries)
43+
return
44+
}
45+
time.Sleep(5 * time.Second)
46+
continue
47+
}
48+
// send fixed 8-byte header
49+
hdr := []byte("AGENT ")
50+
if _, err := rawConn.Write(hdr); err != nil {
51+
rawConn.Close()
52+
logger.Error("AgentRelay header send error: %v", err)
53+
time.Sleep(5 * time.Second)
54+
continue
55+
}
56+
// secure handshake
57+
secureConn, err := NewSecureClientConn(rawConn, secret)
58+
if err != nil {
59+
logger.Error("AgentRelay handshake failed: %v", err)
60+
rawConn.Close()
61+
time.Sleep(5 * time.Second)
62+
continue
63+
}
64+
logger.Info("Agent connected via relay %s", relayAddr)
65+
// handle tunnel until error
66+
handleTunnelReadsServer(secureConn)
67+
secureConn.Close()
68+
time.Sleep(5 * time.Second)
69+
}
70+
}
71+
72+
// RunAgent initiates a direct secure connection to the proxy with authentication/encryption
73+
// If maxRetries <= 0, it will use DefaultMaxRetries
74+
func RunAgent(proxyAddr, secret string, maxRetries int) {
75+
// Use default value if maxRetries is not positive
76+
if maxRetries <= 0 {
77+
maxRetries = DefaultMaxRetries
78+
}
79+
80+
retryCount := 0
81+
82+
// continuously dial and maintain tunnel
83+
for retryCount < maxRetries {
2884
rawConn, err := net.Dial("tcp", proxyAddr)
2985
if err != nil {
30-
logger.Error("Agent connection failed: %v", err)
86+
retryCount++
87+
logger.Error("Agent connection failed: %v (attempt %d/%d)", err, retryCount, maxRetries)
88+
89+
if retryCount >= maxRetries {
90+
logger.Info("Maximum retry attempts (%d) reached, exiting", maxRetries)
91+
return
92+
}
93+
3194
time.Sleep(5 * time.Second)
3295
continue
3396
}
97+
98+
// Reset retry counter on successful connection
99+
retryCount = 0
34100
// TCP optimizations
35101
if tcpConn, ok := rawConn.(*net.TCPConn); ok {
36102
tcpConn.SetNoDelay(true)
@@ -51,6 +117,15 @@ func RunAgent(proxyAddr, secret string) {
51117
logger.Info("Agent disconnected, retrying in 5s")
52118
secureConn.Close()
53119
time.Sleep(5 * time.Second)
120+
121+
// Increment retry counter for disconnection
122+
retryCount++
123+
logger.Info("Reconnection attempt %d/%d", retryCount, maxRetries)
124+
125+
if retryCount >= maxRetries {
126+
logger.Info("Maximum retry attempts (%d) reached, exiting", maxRetries)
127+
return
128+
}
54129
}
55130
}
56131

0 commit comments

Comments
 (0)