Files
2026-04-05 20:33:30 +06:00

302 lines
8.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package wireguard
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
"vpn-client/internal/config"
"vpn-client/internal/logger"
)
// Connect подключается к WireGuard серверу
func Connect(configName string, logsDir string) error {
// Загружаем конфигурации
configs, err := config.LoadConfigs()
if err != nil {
return fmt.Errorf("ошибка загрузки конфигураций: %w", err)
}
// Ищем конфиг
var wgConfig *config.WireGuardConfig
for i := range configs.WireGuard {
if configs.WireGuard[i].Name == configName {
wgConfig = &configs.WireGuard[i]
break
}
}
if wgConfig == nil {
return fmt.Errorf("конфиг '%s' не найден", configName)
}
// Создаем файл конфига
interfaceName := strings.ReplaceAll(configName, " ", "_")
configPath := filepath.Join(config.ConfigDir, interfaceName+".conf")
if err := os.WriteFile(configPath, []byte(wgConfig.Config), 0600); err != nil {
return fmt.Errorf("ошибка создания файла конфига: %w", err)
}
fmt.Printf("Подключение к WireGuard '%s'...\n", configName)
logFile := logger.GetLogPath(logsDir, "wireguard")
logger.LogMessage(logFile, fmt.Sprintf("Начало подключения к '%s'", configName))
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
// Для Windows используем wireguard.exe
wgPaths := []string{
`C:\Program Files\WireGuard\wireguard.exe`,
`C:\Program Files (x86)\WireGuard\wireguard.exe`,
}
var wgExe string
for _, path := range wgPaths {
if _, err := os.Stat(path); err == nil {
wgExe = path
break
}
}
if wgExe == "" {
return fmt.Errorf("WireGuard не найден. Установите WireGuard для Windows\nСкачать: https://www.wireguard.com/install/")
}
// Импортируем туннель
cmd = exec.Command(wgExe, "/installtunnelservice", configPath)
output, err := cmd.CombinedOutput()
if err != nil {
logger.LogMessage(logFile, fmt.Sprintf("Ошибка подключения к '%s': %s", configName, string(output)))
return fmt.Errorf("ошибка подключения: %s", string(output))
}
// Сохраняем состояние
state := &config.ConnectionState{
Connected: true,
ConfigName: configName,
ConfigType: "wireguard",
StartTime: time.Now().Format(time.RFC3339),
Interface: interfaceName,
ProcessPID: 0, // WireGuard на Windows работает как служба
LogFile: "",
}
if err := config.SaveState(state); err != nil {
return fmt.Errorf("ошибка сохранения состояния: %w", err)
}
logger.LogMessage(logFile, fmt.Sprintf("Успешно подключено к '%s'", configName))
fmt.Printf("✓ Подключено к '%s'\n", configName)
} else {
// Для Linux/Mac используем wg-quick
cmd = exec.Command("sudo", "wg-quick", "up", configPath)
output, err := cmd.CombinedOutput()
if err != nil {
logger.LogMessage(logFile, fmt.Sprintf("Ошибка подключения к '%s': %s", configName, string(output)))
return fmt.Errorf("ошибка подключения: %s", string(output))
}
// Сохраняем состояние
state := &config.ConnectionState{
Connected: true,
ConfigName: configName,
ConfigType: "wireguard",
StartTime: time.Now().Format(time.RFC3339),
Interface: interfaceName,
ProcessPID: 0,
LogFile: "",
}
if err := config.SaveState(state); err != nil {
return fmt.Errorf("ошибка сохранения состояния: %w", err)
}
logger.LogMessage(logFile, fmt.Sprintf("Успешно подключено к '%s'", configName))
fmt.Printf("✓ Подключено к '%s'\n", configName)
}
return nil
}
// Disconnect отключается от WireGuard
func Disconnect(interfaceName, logsDir string) error {
logFile := logger.GetLogPath(logsDir, "wireguard")
logger.LogMessage(logFile, fmt.Sprintf("Начало отключения от '%s'", interfaceName))
var cmd *exec.Cmd
if runtime.GOOS == "windows" {
wgPaths := []string{
`C:\Program Files\WireGuard\wireguard.exe`,
`C:\Program Files (x86)\WireGuard\wireguard.exe`,
}
var wgExe string
for _, path := range wgPaths {
if _, err := os.Stat(path); err == nil {
wgExe = path
break
}
}
if wgExe != "" {
cmd = exec.Command(wgExe, "/uninstalltunnelservice", interfaceName)
cmd.Run()
}
} else {
configPath := filepath.Join(config.ConfigDir, interfaceName+".conf")
cmd = exec.Command("sudo", "wg-quick", "down", configPath)
cmd.Run()
}
logger.LogMessage(logFile, fmt.Sprintf("Отключено от '%s'", interfaceName))
return nil
}
// GetStats получает статистику WireGuard
func GetStats(interfaceName string) (map[string]string, error) {
stats := map[string]string{
"rx": "N/A",
"tx": "N/A",
}
if runtime.GOOS == "windows" {
// Для Windows пытаемся получить статистику через wg.exe
wgPaths := []string{
`C:\Program Files\WireGuard\wg.exe`,
`C:\Program Files (x86)\WireGuard\wg.exe`,
}
var wgExe string
for _, path := range wgPaths {
if _, err := os.Stat(path); err == nil {
wgExe = path
break
}
}
if wgExe != "" {
cmd := exec.Command(wgExe, "show", interfaceName, "transfer")
output, err := cmd.Output()
if err == nil && len(output) > 0 {
lines := strings.Split(strings.TrimSpace(string(output)), "\n")
for _, line := range lines {
parts := strings.Fields(line)
if len(parts) >= 3 {
rx := parseBytes(parts[1])
tx := parseBytes(parts[2])
stats["rx"] = formatBytes(rx)
stats["tx"] = formatBytes(tx)
break
}
}
}
}
} else {
// Для Linux/Mac
cmd := exec.Command("wg", "show", interfaceName, "transfer")
output, err := cmd.Output()
if err == nil && len(output) > 0 {
lines := strings.Split(strings.TrimSpace(string(output)), "\n")
if len(lines) > 0 {
parts := strings.Fields(lines[0])
if len(parts) >= 2 {
rx := parseBytes(parts[0])
tx := parseBytes(parts[1])
stats["rx"] = formatBytes(rx)
stats["tx"] = formatBytes(tx)
}
}
}
}
return stats, nil
}
// AddConfig добавляет WireGuard конфигурацию
func AddConfig(name, configText string) error {
configs, err := config.LoadConfigs()
if err != nil {
return fmt.Errorf("ошибка загрузки конфигураций: %w", err)
}
configs.WireGuard = append(configs.WireGuard, config.WireGuardConfig{
Name: name,
Config: configText,
})
if err := config.SaveConfigs(configs); err != nil {
return fmt.Errorf("ошибка сохранения конфигураций: %w", err)
}
return nil
}
// AddConfigFromFile добавляет WireGuard конфигурацию из файла
func AddConfigFromFile(name, filePath string) error {
data, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("ошибка чтения файла: %w", err)
}
return AddConfig(name, string(data))
}
// DeleteConfig удаляет WireGuard конфигурацию
func DeleteConfig(name string) error {
configs, err := config.LoadConfigs()
if err != nil {
return fmt.Errorf("ошибка загрузки конфигураций: %w", err)
}
var filtered []config.WireGuardConfig
found := false
for _, cfg := range configs.WireGuard {
if cfg.Name != name {
filtered = append(filtered, cfg)
} else {
found = true
}
}
if !found {
return fmt.Errorf("конфиг '%s' не найден", name)
}
configs.WireGuard = filtered
if err := config.SaveConfigs(configs); err != nil {
return fmt.Errorf("ошибка сохранения конфигураций: %w", err)
}
return nil
}
func parseBytes(s string) int64 {
var val int64
fmt.Sscanf(s, "%d", &val)
return val
}
func formatBytes(bytes int64) string {
const unit = 1024
if bytes < unit {
return fmt.Sprintf("%d B", bytes)
}
div, exp := int64(unit), 0
for n := bytes / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.2f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}