- System-wide proxy: automatic Windows proxy configuration for all apps - DNS leak protection: force all DNS queries through VPN - Config encryption: AES-256-GCM encryption for all config files - File protection: strict access permissions for config directory - Leak detection: built-in security check system - Kill Switch: temporarily disabled (will be improved in next version) Security features: ✓ Automatic system proxy setup ✓ DNS leak protection (optional) ✓ AES-256-GCM config encryption ✓ File and directory protection ✓ Security leak checker ⚠ Kill Switch disabled (caused internet blocking issues) Emergency recovery scripts included: - ОТКЛЮЧИТЬ_KILLSWITCH.bat - EMERGENCY_FIX_INTERNET.bat - ЕСЛИ_СЛОМАЛСЯ_ИНТЕРНЕТ.txt Documentation: - Markdown/SECURITY_GUIDE.md - full security guide - БЕЗОПАСНОСТЬ_БЫСТРЫЙ_СТАРТ.md - quick start guide - CHANGELOG_SECURITY.md - detailed changelog
167 lines
5.1 KiB
Go
167 lines
5.1 KiB
Go
package vpn
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"runtime"
|
||
"syscall"
|
||
"time"
|
||
|
||
"vpn-client/internal/config"
|
||
"vpn-client/internal/logger"
|
||
"vpn-client/internal/proxy"
|
||
"vpn-client/internal/security"
|
||
"vpn-client/internal/wireguard"
|
||
)
|
||
|
||
// Disconnect отключает VPN
|
||
func Disconnect(logsDir string) error {
|
||
// Загружаем состояние
|
||
state, err := config.LoadState()
|
||
if err != nil {
|
||
return fmt.Errorf("ошибка загрузки состояния: %w", err)
|
||
}
|
||
|
||
if !state.Connected {
|
||
return fmt.Errorf("VPN не подключен")
|
||
}
|
||
|
||
fmt.Printf("Отключение от '%s'...\n", state.ConfigName)
|
||
|
||
// Отключаем защитные механизмы (только DNS, Kill Switch не используется)
|
||
fmt.Println("\nОтключение защитных механизмов...")
|
||
dnsProtection := security.NewDNSProtection()
|
||
if dnsProtection.IsEnabled() {
|
||
if err := dnsProtection.Disable(); err != nil {
|
||
fmt.Printf("%s Предупреждение при отключении DNS: %v\n", "⚠", err)
|
||
} else {
|
||
fmt.Println("✓ Защита DNS отключена")
|
||
}
|
||
}
|
||
|
||
// Логируем отключение
|
||
var logFile string
|
||
if state.ConfigType == "wireguard" {
|
||
logFile = logger.GetLogPath(logsDir, "wireguard")
|
||
} else if state.ConfigType == "vless" {
|
||
logFile = logger.GetLogPath(logsDir, "vless")
|
||
}
|
||
|
||
if logFile != "" {
|
||
logger.LogMessage(logFile, fmt.Sprintf("Начало отключения от '%s'", state.ConfigName))
|
||
}
|
||
|
||
// Останавливаем процесс в зависимости от типа
|
||
if state.ConfigType == "wireguard" {
|
||
// Отключаем WireGuard
|
||
if err := wireguard.Disconnect(state.Interface, logsDir); err != nil {
|
||
fmt.Printf("%s Ошибка отключения WireGuard: %v\n", "⚠", err)
|
||
}
|
||
} else if state.ProcessPID > 0 {
|
||
// Останавливаем процесс VLESS
|
||
process, err := os.FindProcess(state.ProcessPID)
|
||
if err == nil {
|
||
if runtime.GOOS == "windows" {
|
||
// На Windows используем taskkill
|
||
process.Kill()
|
||
} else {
|
||
// На Unix используем SIGTERM
|
||
process.Signal(syscall.SIGTERM)
|
||
}
|
||
|
||
// Ждем завершения процесса
|
||
time.Sleep(1 * time.Second)
|
||
|
||
if logFile != "" {
|
||
logger.LogMessage(logFile, fmt.Sprintf("Отключено от '%s' (PID: %d)", state.ConfigName, state.ProcessPID))
|
||
}
|
||
|
||
if state.LogFile != "" && logFile != "" {
|
||
logger.LogMessage(logFile, fmt.Sprintf("Лог трафика сохранен: %s", state.LogFile))
|
||
}
|
||
}
|
||
}
|
||
|
||
// Сбрасываем состояние
|
||
newState := &config.ConnectionState{
|
||
Connected: false,
|
||
ConfigName: "",
|
||
ConfigType: "",
|
||
StartTime: "",
|
||
Interface: "",
|
||
ProcessPID: 0,
|
||
LogFile: "",
|
||
}
|
||
|
||
if err := config.SaveState(newState); err != nil {
|
||
return fmt.Errorf("ошибка сохранения состояния: %w", err)
|
||
}
|
||
|
||
// Отключаем системный прокси если был VLESS
|
||
if state.ConfigType == "vless" {
|
||
if err := proxy.DisableSystemProxy(); err != nil {
|
||
fmt.Printf("%s Не удалось отключить системный прокси: %v\n", "⚠", err)
|
||
fmt.Println("Отключите его вручную в настройках Windows")
|
||
} else {
|
||
fmt.Println("✓ Системный прокси отключен")
|
||
}
|
||
}
|
||
|
||
fmt.Println("✓ Отключено от VPN")
|
||
return nil
|
||
}
|
||
|
||
// GetStatus возвращает текущий статус подключения
|
||
func GetStatus() (*config.ConnectionState, error) {
|
||
return config.LoadState()
|
||
}
|
||
|
||
// ShowStatus отображает детальный статус подключения
|
||
func ShowStatus() error {
|
||
state, err := GetStatus()
|
||
if err != nil {
|
||
return fmt.Errorf("ошибка получения статуса: %w", err)
|
||
}
|
||
|
||
if !state.Connected {
|
||
fmt.Println("\n❌ VPN не подключен")
|
||
return nil
|
||
}
|
||
|
||
fmt.Println("\n" + "==================================================")
|
||
fmt.Println("📊 Статус VPN")
|
||
fmt.Println("==================================================")
|
||
fmt.Printf("Статус: ✓ Подключено\n")
|
||
fmt.Printf("Конфиг: %s\n", state.ConfigName)
|
||
fmt.Printf("Тип: %s\n", state.ConfigType)
|
||
|
||
if state.StartTime != "" {
|
||
startTime, err := time.Parse(time.RFC3339, state.StartTime)
|
||
if err == nil {
|
||
duration := time.Since(startTime)
|
||
hours := int(duration.Hours())
|
||
minutes := int(duration.Minutes()) % 60
|
||
seconds := int(duration.Seconds()) % 60
|
||
fmt.Printf("Время подключения: %02d:%02d:%02d\n", hours, minutes, seconds)
|
||
}
|
||
}
|
||
|
||
if state.ConfigType == "vless" {
|
||
fmt.Printf("Прокси: 127.0.0.1:10808\n")
|
||
if state.LogFile != "" {
|
||
fmt.Printf("Лог трафика: %s\n", state.LogFile)
|
||
}
|
||
} else if state.ConfigType == "wireguard" {
|
||
// Получаем статистику WireGuard
|
||
stats, err := wireguard.GetStats(state.Interface)
|
||
if err == nil {
|
||
fmt.Printf("\nСтатистика трафика:\n")
|
||
fmt.Printf(" Получено: %s\n", stats["rx"])
|
||
fmt.Printf(" Отправлено: %s\n", stats["tx"])
|
||
}
|
||
}
|
||
|
||
fmt.Println("==================================================")
|
||
return nil
|
||
}
|