Initial commit
This commit is contained in:
301
internal/wireguard/wireguard.go
Normal file
301
internal/wireguard/wireguard.go
Normal 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])
|
||||
}
|
||||
Reference in New Issue
Block a user