Initial commit

This commit is contained in:
2026-04-05 20:33:30 +06:00
commit 83fbe7afdd
18 changed files with 3038 additions and 0 deletions

View File

@@ -0,0 +1,301 @@
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])
}