diff --git a/config.json b/config.json new file mode 100644 index 0000000..5caea07 --- /dev/null +++ b/config.json @@ -0,0 +1,22 @@ +{ + "log": { + "level": "info" + }, + "inbounds": [ + { + "type": "mixed", + "listen": "127.0.0.1", + "listen_port": 1080, + "set_system_proxy": true + } + ], + "outbounds": [ + { + "type": "vless", + "server": "193.124.93.179", + "server_port": 39590, + "uuid": "3da694cb-cca3-4849-81c3-9ff60c4dc399", + "network": "tcp" + } + ] +} \ No newline at end of file diff --git a/hamy-vpn-client-fixed.exe b/hamy-vpn-client-fixed.exe index 072bfac..96c162a 100644 Binary files a/hamy-vpn-client-fixed.exe and b/hamy-vpn-client-fixed.exe differ diff --git a/main.go b/main.go index bf74297..10328b4 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( "regexp" "strconv" "strings" + "sync" "syscall" "unsafe" @@ -36,6 +37,7 @@ type Config struct { var ( currentProcess *exec.Cmd + processMutex sync.Mutex // Mutex to protect process operations configs []Config // Store all configurations activeConfig int // Index of the active config (-1 if none) configFilePath string = "configs.json" // Path to save/load configs @@ -164,6 +166,18 @@ func runSingBox(configPath string) error { return nil } + // Acquire lock to prevent concurrent process operations + processMutex.Lock() + defer processMutex.Unlock() + + // Kill any existing process before starting a new one + if currentProcess != nil && currentProcess.Process != nil { + // Attempt to terminate gracefully first + currentProcess.Process.Kill() + currentProcess.Wait() // Wait for graceful termination + currentProcess = nil + } + cmd := exec.Command(singBoxPath, "run", "-c", configPath) // Make the process run as hidden @@ -177,13 +191,45 @@ func runSingBox(configPath string) error { } currentProcess = cmd + + // Start a goroutine to wait for the process to finish + go func() { + cmd.Wait() + // Clear the process reference when it finishes + processMutex.Lock() + if currentProcess == cmd { + currentProcess = nil + } + processMutex.Unlock() + }() + return nil } +// isProcessRunning checks if the process is still running +func isProcessRunning(cmd *exec.Cmd) bool { + if cmd == nil || cmd.Process == nil { + return false + } + + // On Windows, check if the process is still alive by getting its state + err := cmd.Process.Signal(syscall.Signal(0)) + return err == nil +} + // killCurrentProcess terminates the current sing-box process if running func killCurrentProcess() { + processMutex.Lock() + defer processMutex.Unlock() + if currentProcess != nil && currentProcess.Process != nil { - currentProcess.Process.Kill() + // Check if the process is still running before attempting to kill it + if isProcessRunning(currentProcess) { + // Use TerminateProcess syscall for more reliable termination on Windows + currentProcess.Process.Kill() + // Wait for the process to actually terminate + currentProcess.Wait() + } currentProcess = nil } } @@ -452,8 +498,13 @@ func generateConfigFromVLESSURL(vlessURL, outputPath string) error { return nil } +var proxyMutex sync.Mutex // Mutex to protect proxy operations + // setSystemProxy enables the system proxy with the specified server and port func setSystemProxy(proxyServer string) error { + proxyMutex.Lock() + defer proxyMutex.Unlock() + key, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.SET_VALUE) if err != nil { return fmt.Errorf("failed to open registry key: %v", err) @@ -486,6 +537,9 @@ func setSystemProxy(proxyServer string) error { // disableSystemProxy disables the system proxy func disableSystemProxy() error { + proxyMutex.Lock() + defer proxyMutex.Unlock() + key, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.SET_VALUE) if err != nil { return fmt.Errorf("failed to open registry key: %v", err) @@ -594,12 +648,18 @@ func main() { return } + // Prevent multiple simultaneous connection attempts + connectButton.Disable() + defer connectButton.Enable() // Re-enable the button after the operation completes + // Toggle connection state if !isConnected { // Connect - generate config and run sing-box err := generateAndRunSingBox(configs[activeConfig].URL) if err != nil { + log.Printf("Failed to start connection: %v", err) dialog.ShowError(fmt.Errorf("failed to start connection: %v", err), myWindow) + // Revert connection state on failure isConnected = false updateConnectionButtonText(connectButton) @@ -614,6 +674,15 @@ func main() { if err != nil { log.Printf("Failed to set system proxy: %v", err) dialog.ShowError(fmt.Errorf("failed to set system proxy: %v", err), myWindow) + + // If proxy failed to set, kill the sing-box process and revert connection + killCurrentProcess() + isConnected = false + updateConnectionButtonText(connectButton) + statusLabel.Text = "Отключено" + statusLabel.Color = color.RGBA{R: 128, G: 128, B: 128, A: 255} + statusLabel.Refresh() + return } // Update connection state after successful connection @@ -808,9 +877,7 @@ func main() { // Register an app lifecycle listener to handle cleanup when the app exits myApp.Lifecycle().SetOnStopped(func() { // Kill the current process if running - if currentProcess != nil && currentProcess.Process != nil { - currentProcess.Process.Kill() - } + killCurrentProcess() // Disable proxy when the application exits err := disableSystemProxy()