commit d0646af85d933dc8d3a5ecba768051b79c7ac274 Author: by-sonic Date: Mon Mar 16 23:54:42 2026 +0300 TG Unblock v0.2.0: Telegram WebSocket bypass proxy Made-with: Cursor: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e201400 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,64 @@ +name: Build & Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Build release + run: cargo build --release + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: tg_unblock-windows-x64 + path: target/release/tg_unblock.exe + + release: + needs: build + runs-on: ubuntu-latest + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: tg_unblock-windows-x64 + + - name: Create Release + uses: softprops/action-gh-release@v2 + with: + files: tg_unblock.exe + generate_release_notes: true + body: | + ## TG Unblock ${{ github.ref_name }} + + Обход блокировки Telegram через WebSocket-туннель. + + ### Установка + 1. Скачайте `tg_unblock.exe` + 2. Запустите + 3. Нажмите "Запустить обход" + 4. Нажмите "Настроить автоматически" + + --- + **[by sonic VPN](https://t.me/bysonicvpn_bot)** — полный обход блокировок для всех приложений diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e429fd6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/target +/tools +*.zip +*.exe +!*.rs +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..da19339 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "tg_unblock" +version = "0.2.0" +edition = "2021" + +[dependencies] +eframe = "0.31" +egui = "0.31" +tokio = { version = "1", features = ["full"] } +reqwest = { version = "0.12", features = ["blocking"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +open = "5" +tokio-tungstenite = { version = "0.24", features = ["native-tls"] } +native-tls = "0.2" +futures-util = "0.3" + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["winuser"] } + +[[bin]] +name = "tg_unblock" +path = "src/main.rs" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..80bff63 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 by sonic + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8cefc24 --- /dev/null +++ b/README.md @@ -0,0 +1,159 @@ +

+

TG Unblock

+

+ Обход блокировки Telegram через WebSocket-туннель
+ Без VPN. Без серверов. Без абонентки. Один клик. +

+

+ Release + License + Stars + Rust + Windows +

+

+ +--- + +## Что это? + +**TG Unblock** — десктопное приложение на Rust, которое обходит блокировку Telegram через локальный WebSocket-прокси. Провайдер видит обычный HTTPS к `web.telegram.org`, а не MTProto — DPI не может обнаружить и заблокировать трафик. + +### Почему не GoodbyeDPI / Zapret? + +| | GoodbyeDPI | Zapret | **TG Unblock** | +|---|---|---|---| +| Метод | Фрагментация пакетов | Desync пакетов | WebSocket-туннель | +| DPI видит MTProto? | Нет (обфускация) | Нет (desync) | **Нет (обычный HTTPS)** | +| IP-шейпинг обходит? | Нет | Нет | **Да** | +| Скорость | Зависит от DPI | Зависит от DPI | **Полная** | +| Переподключения | Возможны | Возможны | **Нет** | +| Настройка | Много параметров | Стратегии | **Один клик** | + +## Скачать + +> **[Скачать последний релиз](https://github.com/by-sonic/tglock/releases)** + +Или собрать из исходников: + +```bash +git clone https://github.com/by-sonic/tglock.git +cd tglock +cargo build --release +``` + +Готовый `.exe` будет в `target/release/tg_unblock.exe`. + +## Как пользоваться + +1. Запустите `tg_unblock.exe` +2. Нажмите **"Запустить обход"** +3. Нажмите **"Настроить автоматически"** — откроется Telegram, нажмите "Подключить" +4. Готово. Telegram работает на полной скорости. + +### Ручная настройка прокси + +Если автонастройка не сработала: + +**Telegram Desktop** → Настройки → Продвинутые → Тип соединения → **Использовать SOCKS5-прокси** + +| Параметр | Значение | +|---|---| +| Сервер | `127.0.0.1` | +| Порт | `1080` | +| Логин | *пусто* | +| Пароль | *пусто* | + +## Как это работает + +``` +Telegram Desktop + │ + ▼ (SOCKS5) +┌──────────────────┐ +│ TG Unblock │ 127.0.0.1:1080 +│ WS-прокси │ +└──────┬───────────┘ + │ + ▼ (определяет DC по IP) + │ + ├── Telegram IP? ──► WSS-туннель к {dc}.web.telegram.org/apiws + │ (провайдер видит обычный HTTPS) + │ + └── Другой IP? ────► Прямое TCP-соединение (без изменений) +``` + +### DC-маппинг + +Приложение автоматически определяет Data Center по IP-адресу и маршрутизирует через правильный WebSocket-эндпоинт: + +| DC | Подсеть | WebSocket | +|---|---|---| +| DC1 | `149.154.160.0/22` | `wss://pluto.web.telegram.org/apiws` | +| DC2 | `149.154.164.0/22` | `wss://venus.web.telegram.org/apiws` | +| DC3 | `149.154.168.0/22` | `wss://aurora.web.telegram.org/apiws` | +| DC4 | `91.108.12.0/22` | `wss://vesta.web.telegram.org/apiws` | +| DC5 | `91.108.56.0/22` | `wss://flora.web.telegram.org/apiws` | + +Имена DC (`pluto`, `venus`, `aurora`, `vesta`, `flora`) — из [официальной документации MTProto](https://core.telegram.org/mtproto/transports). + +## Стек + +| Что | Зачем | +|---|---| +| **Rust** | Скорость, безопасность, один бинарник без зависимостей | +| **egui / eframe** | Нативный GUI без Electron, без браузера | +| **tokio** | Async I/O для высокопроизводительного проксирования | +| **tokio-tungstenite** | WebSocket-клиент с TLS | +| **native-tls** | TLS через системные сертификаты Windows | + +## Структура проекта + +``` +tglock/ +├── Cargo.toml # Зависимости +├── src/ +│ ├── main.rs # GUI + управление прокси +│ ├── ws_proxy.rs # SOCKS5-сервер + WebSocket-туннель +│ ├── bypass.rs # DNS-настройка, утилиты Windows +│ └── network.rs # Сетевая диагностика +└── tg_blacklist.txt # IP-подсети и домены Telegram +``` + +## Требования + +- Windows 10/11 +- [Rust 1.70+](https://rustup.rs/) (для сборки из исходников) +- Права администратора (для смены DNS, опционально) + +## FAQ + +**Q: Это VPN?** +A: Нет. Трафик не идёт через сторонние серверы. Прокси работает локально и туннелирует только Telegram-трафик через WebSocket к официальным серверам Telegram. + +**Q: Это безопасно?** +A: Весь код открыт. Никакой телеметрии. Никаких данных не отправляется. Соединение с Telegram остаётся end-to-end зашифрованным (MTProto). + +**Q: Будет ли работать с мобильным Telegram?** +A: Пока только Telegram Desktop. Для мобильных устройств рекомендуем [by sonic VPN](https://t.me/bysonicvpn_bot). + +**Q: Замедляется ли интернет?** +A: Нет. Проксируется только трафик к серверам Telegram. Весь остальной трафик идёт напрямую. + +## VPN для полного обхода + +Если нужен обход блокировок для **всех** приложений (YouTube, Discord, Instagram и др.) — попробуйте **[by sonic VPN](https://t.me/bysonicvpn_bot)**. Быстрый, без ограничений скорости. + +## Лицензия + +MIT — делайте что хотите. + +## Автор + +**by sonic** — [@bysonicvpn_bot](https://t.me/bysonicvpn_bot) + +--- + +

+ Если пригодилось — поставьте ⭐ на GitHub +

diff --git a/src/bypass.rs b/src/bypass.rs new file mode 100644 index 0000000..dde9913 --- /dev/null +++ b/src/bypass.rs @@ -0,0 +1,201 @@ +use std::path::{Path, PathBuf}; +use std::process::Command; + +pub fn check_admin() -> bool { + let output = Command::new("net") + .args(["session"]) + .output(); + matches!(output, Ok(o) if o.status.success()) +} + +pub fn set_dns(adapter: &str, primary: &str, secondary: &str) -> Result<(), String> { + let out1 = Command::new("netsh") + .args([ + "interface", "ipv4", "set", "dnsservers", + adapter, "static", primary, "primary", "validate=no", + ]) + .output() + .map_err(|e| format!("netsh error: {}", e))?; + + if !out1.status.success() { + let stderr = String::from_utf8_lossy(&out1.stderr); + return Err(format!("Failed to set primary DNS: {}", stderr)); + } + + let out2 = Command::new("netsh") + .args([ + "interface", "ipv4", "add", "dnsservers", + adapter, secondary, "index=2", "validate=no", + ]) + .output() + .map_err(|e| format!("netsh error: {}", e))?; + + if !out2.status.success() { + // Non-critical: secondary DNS may already exist + } + + Ok(()) +} + +pub fn reset_dns(adapter: &str) -> Result<(), String> { + let out = Command::new("netsh") + .args([ + "interface", "ipv4", "set", "dnsservers", + adapter, "dhcp", + ]) + .output() + .map_err(|e| format!("netsh error: {}", e))?; + + if !out.status.success() { + let stderr = String::from_utf8_lossy(&out.stderr); + return Err(format!("Failed to reset DNS: {}", stderr)); + } + Ok(()) +} + +pub fn flush_dns() { + let _ = Command::new("ipconfig") + .args(["/flushdns"]) + .output(); +} + +pub fn find_goodbyedpi() -> Option { + let exe_dir = std::env::current_exe() + .ok() + .and_then(|p| p.parent().map(|p| p.to_path_buf())) + .unwrap_or_else(|| PathBuf::from(".")); + + let search_dirs = vec![ + exe_dir.join("tools"), + exe_dir.join("tools").join("goodbyedpi"), + exe_dir.clone(), + PathBuf::from("tools"), + PathBuf::from("tools").join("goodbyedpi"), + PathBuf::from("."), + ]; + + for dir in &search_dirs { + // Check common locations + for sub in &["x86_64", "x86", ""] { + let candidate = if sub.is_empty() { + dir.join("goodbyedpi.exe") + } else { + dir.join(sub).join("goodbyedpi.exe") + }; + if candidate.exists() { + return Some(candidate.to_string_lossy().to_string()); + } + } + } + + // Recursive search in tools/ + if let Ok(entries) = find_file_recursive(Path::new("tools"), "goodbyedpi.exe") { + if !entries.is_empty() { + return Some(entries[0].to_string_lossy().to_string()); + } + } + + None +} + +fn find_file_recursive(dir: &Path, filename: &str) -> Result, std::io::Error> { + let mut results = Vec::new(); + if !dir.exists() { + return Ok(results); + } + for entry in std::fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_file() && path.file_name().map(|n| n == filename).unwrap_or(false) { + results.push(path); + } else if path.is_dir() { + results.extend(find_file_recursive(&path, filename)?); + } + } + Ok(results) +} + +pub fn get_blacklist_path() -> Option { + let candidates = vec![ + PathBuf::from("tg_blacklist.txt"), + PathBuf::from("tools").join("tg_blacklist.txt"), + std::env::current_exe() + .ok() + .and_then(|p| p.parent().map(|p| p.join("tg_blacklist.txt"))) + .unwrap_or_default(), + ]; + + for path in candidates { + if path.exists() { + return Some(path.to_string_lossy().to_string()); + } + } + None +} + +pub fn start_goodbyedpi(exe_path: &str, args: &[&str], blacklist: Option<&str>) -> Result<(), String> { + let mut cmd = Command::new(exe_path); + cmd.args(args); + + if let Some(bl) = blacklist { + cmd.args(["--blacklist", bl]); + } + + cmd.spawn().map_err(|e| format!("Failed to start GoodbyeDPI: {}", e))?; + Ok(()) +} + +pub fn kill_goodbyedpi() { + let _ = Command::new("taskkill") + .args(["/f", "/im", "goodbyedpi.exe"]) + .output(); +} + +pub fn download_goodbyedpi() -> Result { + let tools_dir = PathBuf::from("tools"); + std::fs::create_dir_all(&tools_dir) + .map_err(|e| format!("Cannot create tools dir: {}", e))?; + + let zip_path = tools_dir.join("goodbyedpi.zip"); + let url = "https://github.com/ValdikSS/GoodbyeDPI/releases/download/0.2.3rc3/goodbyedpi-0.2.3rc3-2.zip"; + + // Download using powershell + let dl_script = format!( + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri '{}' -OutFile '{}' -UseBasicParsing", + url, + zip_path.to_string_lossy() + ); + + let output = Command::new("powershell") + .args(["-Command", &dl_script]) + .output() + .map_err(|e| format!("Download failed: {}", e))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(format!("Download failed: {}", stderr)); + } + + // Extract + let extract_script = format!( + "Expand-Archive -Path '{}' -DestinationPath '{}' -Force", + zip_path.to_string_lossy(), + tools_dir.to_string_lossy() + ); + + let output = Command::new("powershell") + .args(["-Command", &extract_script]) + .output() + .map_err(|e| format!("Extraction failed: {}", e))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(format!("Extraction failed: {}", stderr)); + } + + // Clean up zip + let _ = std::fs::remove_file(&zip_path); + + // Find the exe + find_goodbyedpi().ok_or_else(|| "goodbyedpi.exe not found after extraction".to_string()) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e753bc9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,358 @@ +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +mod bypass; +mod network; +mod ws_proxy; + +use std::sync::atomic::Ordering; +use std::sync::{Arc, Mutex}; + +use eframe::egui; + +const PROXY_PORT: u16 = 1080; + +fn main() -> eframe::Result<()> { + let options = eframe::NativeOptions { + viewport: egui::ViewportBuilder::default() + .with_inner_size([680.0, 560.0]) + .with_min_inner_size([580.0, 460.0]) + .with_title("TG Unblock"), + ..Default::default() + }; + + eframe::run_native( + "TG Unblock", + options, + Box::new(|cc| { + setup_fonts(&cc.egui_ctx); + Ok(Box::new(App::new())) + }), + ) +} + +fn setup_fonts(ctx: &egui::Context) { + let mut fonts = egui::FontDefinitions::default(); + fonts.font_data.insert( + "system".to_owned(), + std::sync::Arc::new(egui::FontData::from_static(include_bytes!( + "C:\\Windows\\Fonts\\segoeui.ttf" + ))), + ); + fonts + .families + .entry(egui::FontFamily::Proportional) + .or_default() + .insert(0, "system".to_owned()); + fonts + .families + .entry(egui::FontFamily::Monospace) + .or_default() + .insert(0, "system".to_owned()); + ctx.set_fonts(fonts); +} + +#[derive(Clone)] +struct LogEntry { + text: String, + is_error: bool, + ts: String, +} + +struct App { + log: Arc>>, + proxy_stats: Arc, + is_admin: bool, + adapter_name: Arc>>, + dns_set: Arc>, +} + +impl App { + fn new() -> Self { + let is_admin = bypass::check_admin(); + let app = Self { + log: Arc::new(Mutex::new(Vec::new())), + proxy_stats: ws_proxy::ProxyStats::new(), + is_admin, + adapter_name: Arc::new(Mutex::new(None)), + dns_set: Arc::new(Mutex::new(false)), + }; + log_msg(&app.log, "Запущено", false); + if !is_admin { + log_msg(&app.log, "Нет прав администратора — DNS менять не получится", true); + } + { + let adapter = app.adapter_name.clone(); + let log = app.log.clone(); + std::thread::spawn(move || { + if let Some(name) = network::detect_adapter() { + log_msg(&log, &format!("Адаптер: {}", name), false); + *adapter.lock().unwrap() = Some(name); + } + }); + } + app + } + + fn proxy_running(&self) -> bool { + self.proxy_stats.running.load(Ordering::SeqCst) + } + + fn start_proxy(&self) { + if self.proxy_running() { + return; + } + let stats = self.proxy_stats.clone(); + let log = self.log.clone(); + let adapter = self.adapter_name.clone(); + let dns_set = self.dns_set.clone(); + let is_admin = self.is_admin; + + std::thread::spawn(move || { + // DNS + if is_admin { + let aname = adapter.lock().unwrap().clone().or_else(network::detect_adapter); + if let Some(ref name) = aname { + if bypass::set_dns(name, "1.1.1.1", "1.0.0.1").is_ok() { + bypass::flush_dns(); + log_msg(&log, "DNS → Cloudflare 1.1.1.1", false); + *dns_set.lock().unwrap() = true; + } + } + } + + log_msg(&log, &format!("Запускаю WS-прокси на 127.0.0.1:{}...", PROXY_PORT), false); + + let rt = tokio::runtime::Runtime::new().unwrap(); + let result = rt.block_on(ws_proxy::run_proxy(PROXY_PORT, stats)); + if let Err(e) = result { + log_msg(&log, &format!("Прокси остановлен: {}", e), true); + } + }); + + std::thread::sleep(std::time::Duration::from_millis(300)); + if self.proxy_running() { + log_msg(&self.log, "Прокси запущен! Настройте Telegram.", false); + } + } + + fn stop_proxy(&self) { + self.proxy_stats.running.store(false, Ordering::SeqCst); + log_msg(&self.log, "Прокси остановлен", false); + + if *self.dns_set.lock().unwrap() { + let adapter = self.adapter_name.clone(); + let log = self.log.clone(); + let dns_set = self.dns_set.clone(); + std::thread::spawn(move || { + let aname = adapter.lock().unwrap().clone().or_else(network::detect_adapter); + if let Some(ref name) = aname { + let _ = bypass::reset_dns(name); + bypass::flush_dns(); + *dns_set.lock().unwrap() = false; + log_msg(&log, "DNS сброшен", false); + } + }); + } + } + + fn open_tg_proxy_link(&self) { + let url = format!("tg://socks?server=127.0.0.1&port={}", PROXY_PORT); + log_msg(&self.log, "Открываю настройку прокси в Telegram...", false); + let _ = open::that(&url); + } +} + +fn log_msg(log: &Arc>>, text: &str, err: bool) { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(); + let ts = format!("{:02}:{:02}:{:02}", (now % 86400) / 3600, (now % 3600) / 60, now % 60); + log.lock().unwrap().push(LogEntry { + text: text.to_string(), + is_error: err, + ts, + }); +} + +impl eframe::App for App { + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + ctx.request_repaint_after(std::time::Duration::from_millis(400)); + + let running = self.proxy_running(); + let active = self.proxy_stats.active_conn.load(Ordering::Relaxed); + let total = self.proxy_stats.total_conn.load(Ordering::Relaxed); + let ws = self.proxy_stats.ws_active.load(Ordering::Relaxed); + + // --- Top bar --- + egui::TopBottomPanel::top("top").show(ctx, |ui| { + ui.horizontal(|ui| { + ui.heading("TG Unblock"); + ui.separator(); + if running { + ui.colored_label( + egui::Color32::from_rgb(80, 220, 120), + egui::RichText::new("ПРОКСИ РАБОТАЕТ").strong(), + ); + ui.separator(); + ui.label(format!("Соединений: {} (WS: {}) | Всего: {}", active, ws, total)); + } else { + ui.label("Прокси не запущен"); + } + }); + }); + + // --- Log panel --- + egui::TopBottomPanel::bottom("log") + .min_height(130.0) + .show(ctx, |ui| { + ui.label(egui::RichText::new("Лог").strong()); + ui.separator(); + egui::ScrollArea::vertical() + .auto_shrink([false, false]) + .stick_to_bottom(true) + .show(ui, |ui| { + let logs = self.log.lock().unwrap(); + for e in logs.iter() { + let color = if e.is_error { + egui::Color32::from_rgb(255, 100, 100) + } else { + egui::Color32::from_rgb(170, 215, 170) + }; + ui.colored_label(color, format!("[{}] {}", e.ts, e.text)); + } + }); + }); + + // --- Main panel --- + egui::CentralPanel::default().show(ctx, |ui| { + ui.add_space(15.0); + + ui.vertical_centered(|ui| { + if !running { + ui.label(egui::RichText::new("Обход блокировки Telegram через WebSocket-прокси").size(15.0)); + ui.add_space(5.0); + ui.label("Трафик идёт через web.telegram.org — провайдер видит обычный HTTPS"); + ui.add_space(15.0); + + let btn = ui.add_sized( + [340.0, 55.0], + egui::Button::new(egui::RichText::new("Запустить обход").size(20.0).strong()), + ); + if btn.clicked() { + self.start_proxy(); + } + } else { + ui.colored_label( + egui::Color32::from_rgb(80, 220, 120), + egui::RichText::new("Обход работает").size(22.0).strong(), + ); + ui.add_space(5.0); + ui.label(format!("SOCKS5 прокси на 127.0.0.1:{}", PROXY_PORT)); + ui.label(format!("WebSocket-туннелей: {} | Соединений: {}", ws, active)); + ui.add_space(12.0); + + // Stop button + let stop = ui.add_sized( + [340.0, 42.0], + egui::Button::new(egui::RichText::new("Остановить").size(17.0)), + ); + if stop.clicked() { + self.stop_proxy(); + } + } + }); + + ui.add_space(20.0); + ui.separator(); + ui.add_space(8.0); + + // --- Telegram setup --- + ui.heading("Настройка Telegram Desktop"); + ui.add_space(6.0); + + if running { + ui.horizontal(|ui| { + if ui.button(" Настроить автоматически ").clicked() { + self.open_tg_proxy_link(); + } + ui.label("(откроет Telegram, нажмите \"Подключить\")"); + }); + ui.add_space(8.0); + } + + ui.label("Или вручную: Настройки → Продвинутые → Тип соединения → SOCKS5"); + ui.add_space(4.0); + egui::Grid::new("manual_setup") + .num_columns(2) + .spacing([15.0, 4.0]) + .show(ui, |ui| { + ui.label("Сервер:"); + ui.monospace("127.0.0.1"); + ui.end_row(); + ui.label("Порт:"); + ui.monospace(format!("{}", PROXY_PORT)); + ui.end_row(); + ui.label("Логин/Пароль:"); + ui.label("оставить пустыми"); + ui.end_row(); + }); + + ui.add_space(15.0); + ui.separator(); + ui.add_space(5.0); + + // --- How it works --- + ui.heading("Как это работает"); + ui.add_space(4.0); + ui.label("1. Локальный SOCKS5-прокси принимает соединения от Telegram"); + ui.label("2. Трафик к серверам Telegram заворачивается в WebSocket (WSS)"); + ui.label("3. Подключение идёт через web.telegram.org — обычный HTTPS"); + ui.label("4. Провайдер/DPI не видит MTProto, не может замедлить"); + ui.add_space(4.0); + ui.colored_label( + egui::Color32::from_rgb(170, 170, 170), + "Не-Telegram трафик проходит напрямую без изменений", + ); + + ui.add_space(12.0); + ui.separator(); + ui.add_space(6.0); + + // --- VPN ad --- + egui::Frame::new() + .fill(egui::Color32::from_rgb(30, 35, 50)) + .corner_radius(10.0) + .inner_margin(14.0) + .show(ui, |ui| { + ui.horizontal(|ui| { + ui.colored_label( + egui::Color32::from_rgb(100, 180, 255), + egui::RichText::new("by sonic VPN").size(16.0).strong(), + ); + ui.label(egui::RichText::new(" — ").size(14.0)); + ui.label( + egui::RichText::new("Полный обход блокировок для всех приложений") + .size(13.0), + ); + }); + ui.add_space(4.0); + ui.horizontal(|ui| { + ui.label("Быстрый VPN без ограничений скорости:"); + let link = ui.add( + egui::Hyperlink::from_label_and_url( + egui::RichText::new("@bysonicvpn_bot") + .size(14.0) + .strong() + .color(egui::Color32::from_rgb(100, 200, 255)), + "https://t.me/bysonicvpn_bot", + ), + ); + if link.clicked() { + let _ = open::that("https://t.me/bysonicvpn_bot"); + } + }); + }); + }); + } +} diff --git a/src/network.rs b/src/network.rs new file mode 100644 index 0000000..065bf33 --- /dev/null +++ b/src/network.rs @@ -0,0 +1,176 @@ +use std::net::{TcpStream, SocketAddr}; +use std::process::Command; +use std::time::{Duration, Instant}; + +pub fn detect_adapter() -> Option { + let output = Command::new("powershell") + .args([ + "-Command", + "(Get-NetAdapter | Where-Object {$_.Status -eq 'Up'} | Select-Object -First 1).Name", + ]) + .output() + .ok()?; + + let name = String::from_utf8_lossy(&output.stdout).trim().to_string(); + if name.is_empty() { + None + } else { + Some(name) + } +} + +pub fn get_current_dns() -> Option { + let output = Command::new("powershell") + .args([ + "-Command", + "Get-DnsClientServerAddress -AddressFamily IPv4 | Where-Object {$_.ServerAddresses.Count -gt 0} | Select-Object -First 1 -ExpandProperty ServerAddresses | Out-String", + ]) + .output() + .ok()?; + + let result = String::from_utf8_lossy(&output.stdout).trim().to_string(); + if result.is_empty() { + Some("Не определено".to_string()) + } else { + Some(result.replace('\n', ", ").replace('\r', "")) + } +} + +pub fn ping_host(ip: &str) -> (bool, Option) { + let start = Instant::now(); + let output = Command::new("ping") + .args(["-n", "1", "-w", "3000", ip]) + .output(); + + match output { + Ok(out) => { + let elapsed = start.elapsed().as_millis() as u64; + let stdout = String::from_utf8_lossy(&out.stdout); + let ok = out.status.success() && (stdout.contains("TTL=") || stdout.contains("ttl=")); + + if ok { + // Try to extract actual time from ping output + if let Some(time_str) = extract_ping_time(&stdout) { + (true, Some(time_str)) + } else { + (true, Some(elapsed)) + } + } else { + (false, None) + } + } + Err(_) => (false, None), + } +} + +fn extract_ping_time(output: &str) -> Option { + // Match patterns like "time=46ms" or "time<1ms" or "время=46мс" + for line in output.lines() { + let lower = line.to_lowercase(); + if let Some(pos) = lower.find("time=").or_else(|| lower.find("time<")) { + let after = &lower[pos + 5..]; + let num: String = after.chars().take_while(|c| c.is_ascii_digit()).collect(); + if let Ok(ms) = num.parse::() { + return Some(ms); + } + } + // Russian locale + if let Some(pos) = lower.find("=").filter(|_| lower.contains("ms") || lower.contains("мс")) { + let after = &lower[pos + 1..]; + let num: String = after.chars().take_while(|c| c.is_ascii_digit()).collect(); + if let Ok(ms) = num.parse::() { + if ms < 10000 { + return Some(ms); + } + } + } + } + None +} + +pub fn tcp_check(ip: &str, port: u16) -> (bool, Option) { + let addr: SocketAddr = format!("{}:{}", ip, port).parse().unwrap(); + let start = Instant::now(); + match TcpStream::connect_timeout(&addr, Duration::from_secs(5)) { + Ok(_stream) => { + let elapsed = start.elapsed().as_millis() as u64; + (true, Some(elapsed)) + } + Err(_) => (false, None), + } +} + +pub fn https_check(url: &str) -> (bool, Option) { + let start = Instant::now(); + let client = reqwest::blocking::Client::builder() + .timeout(Duration::from_secs(10)) + .danger_accept_invalid_certs(true) + .build(); + + match client { + Ok(c) => match c.get(url).send() { + Ok(resp) => { + let elapsed = start.elapsed().as_millis() as u64; + (resp.status().is_success(), Some(elapsed)) + } + Err(_) => (false, None), + }, + Err(_) => (false, None), + } +} + +/// Benchmarks Telegram connectivity: runs multiple TCP+HTTPS checks, +/// returns (works: bool, score: u64) where lower score = faster connection. +/// Score is average latency across all successful checks. u64::MAX if nothing works. +pub fn benchmark_telegram() -> (bool, u64) { + let tcp_targets = [ + ("149.154.167.51", 443u16), + ("149.154.175.50", 443), + ("149.154.167.91", 443), + ("91.108.56.100", 443), + ]; + + let mut total_ms: u64 = 0; + let mut ok_count: u64 = 0; + let mut fail_count: u64 = 0; + + // TCP checks (x2 rounds for stability) + for _ in 0..2 { + for (ip, port) in &tcp_targets { + let (ok, latency) = tcp_check(ip, *port); + if ok { + total_ms += latency.unwrap_or(5000); + ok_count += 1; + } else { + fail_count += 1; + } + } + } + + // HTTPS check — the real indicator of usable speed + let https_urls = [ + "https://web.telegram.org", + "https://t.me", + ]; + for url in &https_urls { + let (ok, latency) = https_check(url); + if ok { + // Weight HTTPS 3x heavier since it's closer to real usage + let ms = latency.unwrap_or(10000); + total_ms += ms * 3; + ok_count += 3; + } else { + fail_count += 3; + } + } + + if ok_count == 0 { + return (false, u64::MAX); + } + + // Penalize failures: each fail adds 2000ms to the score + let penalty = fail_count * 2000; + let avg = (total_ms + penalty) / (ok_count + fail_count); + + (true, avg) +} diff --git a/src/ws_proxy.rs b/src/ws_proxy.rs new file mode 100644 index 0000000..32d1d5f --- /dev/null +++ b/src/ws_proxy.rs @@ -0,0 +1,265 @@ +use std::net::Ipv4Addr; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; +use tokio::net::{TcpListener, TcpStream}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio_tungstenite::tungstenite; +use tungstenite::client::IntoClientRequest; + +pub struct ProxyStats { + pub running: AtomicBool, + pub active_conn: AtomicU32, + pub total_conn: AtomicU32, + pub ws_active: AtomicU32, +} + +impl ProxyStats { + pub fn new() -> Arc { + Arc::new(Self { + running: AtomicBool::new(false), + active_conn: AtomicU32::new(0), + total_conn: AtomicU32::new(0), + ws_active: AtomicU32::new(0), + }) + } +} + +pub async fn run_proxy(port: u16, stats: Arc) -> Result<(), String> { + let addr = format!("127.0.0.1:{}", port); + let listener = TcpListener::bind(&addr) + .await + .map_err(|e| format!("Не удалось занять порт {}: {}", port, e))?; + + stats.running.store(true, Ordering::SeqCst); + + loop { + if !stats.running.load(Ordering::SeqCst) { + break; + } + + tokio::select! { + result = listener.accept() => { + if let Ok((stream, _)) = result { + let st = stats.clone(); + st.active_conn.fetch_add(1, Ordering::Relaxed); + st.total_conn.fetch_add(1, Ordering::Relaxed); + tokio::spawn(async move { + let _ = handle_socks5(stream, &st).await; + st.active_conn.fetch_sub(1, Ordering::Relaxed); + }); + } + } + _ = tokio::time::sleep(std::time::Duration::from_millis(200)) => {} + } + } + + stats.running.store(false, Ordering::SeqCst); + Ok(()) +} + +/// DC name mapping from official Telegram MTProto transport docs +fn dc_ws_url(dc: u8) -> String { + let name = match dc { + 1 => "pluto", + 2 => "venus", + 3 => "aurora", + 4 => "vesta", + 5 => "flora", + _ => "venus", + }; + format!("wss://{}.web.telegram.org/apiws", name) +} + +async fn handle_socks5( + mut stream: TcpStream, + stats: &ProxyStats, +) -> Result<(), Box> { + stream.set_nodelay(true)?; + + let mut buf = [0u8; 258]; + let n = stream.read(&mut buf).await?; + if n < 2 || buf[0] != 0x05 { + return Err("Not SOCKS5".into()); + } + stream.write_all(&[0x05, 0x00]).await?; + + let n = stream.read(&mut buf).await?; + if n < 7 || buf[0] != 0x05 || buf[1] != 0x01 { + stream.write_all(&[0x05, 0x07, 0x00, 0x01, 0, 0, 0, 0, 0, 0]).await?; + return Err("Bad CONNECT".into()); + } + + let (dest_addr, dest_port) = parse_dest(&buf[3..n])?; + + let dc = dest_addr + .parse::() + .ok() + .and_then(telegram_dc); + + if let Some(dc_id) = dc { + stream + .write_all(&[0x05, 0x00, 0x00, 0x01, 127, 0, 0, 1, 0x04, 0x38]) + .await?; + + stats.ws_active.fetch_add(1, Ordering::Relaxed); + let result = relay_via_ws(stream, dc_id).await; + stats.ws_active.fetch_sub(1, Ordering::Relaxed); + + if let Err(e) = result { + return Err(format!("WS tunnel DC{}: {}", dc_id, e).into()); + } + } else { + let target = format!("{}:{}", dest_addr, dest_port); + match TcpStream::connect(&target).await { + Ok(remote) => { + let _ = remote.set_nodelay(true); + stream + .write_all(&[0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0]) + .await?; + relay_tcp(stream, remote).await; + } + Err(_) => { + stream + .write_all(&[0x05, 0x05, 0x00, 0x01, 0, 0, 0, 0, 0, 0]) + .await?; + } + } + } + + Ok(()) +} + +fn parse_dest(data: &[u8]) -> Result<(String, u16), Box> { + match data[0] { + 0x01 => { + if data.len() < 7 { return Err("short".into()); } + let ip = format!("{}.{}.{}.{}", data[1], data[2], data[3], data[4]); + let port = u16::from_be_bytes([data[5], data[6]]); + Ok((ip, port)) + } + 0x03 => { + let len = data[1] as usize; + if data.len() < 2 + len + 2 { return Err("short".into()); } + let domain = std::str::from_utf8(&data[2..2 + len])?.to_string(); + let port = u16::from_be_bytes([data[2 + len], data[3 + len]]); + Ok((domain, port)) + } + 0x04 => { + if data.len() < 19 { return Err("short".into()); } + let port = u16::from_be_bytes([data[17], data[18]]); + let mut segs = [0u16; 8]; + for i in 0..8 { + segs[i] = u16::from_be_bytes([data[1 + i * 2], data[2 + i * 2]]); + } + let ip = std::net::Ipv6Addr::new( + segs[0], segs[1], segs[2], segs[3], segs[4], segs[5], segs[6], segs[7], + ); + Ok((ip.to_string(), port)) + } + _ => Err("unknown addr type".into()), + } +} + +fn telegram_dc(ip: Ipv4Addr) -> Option { + let o = ip.octets(); + match (o[0], o[1]) { + (149, 154) => Some(match o[2] { + 160..=163 => 1, + 164..=167 => 2, + 168..=171 => 3, + 172..=175 => 1, + _ => 2, + }), + (91, 108) => Some(match o[2] { + 56..=59 => 5, + 8..=11 => 3, + 12..=15 => 4, + _ => 2, + }), + (91, 105) => Some(2), + (185, 76) => Some(2), + _ => None, + } +} + +async fn relay_via_ws( + tcp_stream: TcpStream, + dc_id: u8, +) -> Result<(), Box> { + use futures_util::{SinkExt, StreamExt}; + + let ws_url = dc_ws_url(dc_id); + let mut request = ws_url.as_str().into_client_request()?; + + // Required by the Telegram WebSocket transport protocol + request.headers_mut().insert( + "Sec-WebSocket-Protocol", + "binary".parse()?, + ); + request.headers_mut().insert( + "Origin", + "https://web.telegram.org".parse()?, + ); + + let connector = tokio_tungstenite::Connector::NativeTls( + native_tls::TlsConnector::new().map_err(|e| format!("TLS: {}", e))?, + ); + + let (ws, _resp) = tokio_tungstenite::connect_async_tls_with_config( + request, + None, + false, + Some(connector), + ) + .await?; + + let (mut ws_tx, mut ws_rx) = ws.split(); + let (mut tcp_rx, mut tcp_tx) = tokio::io::split(tcp_stream); + + let up = async { + let mut buf = vec![0u8; 32768]; + loop { + match tcp_rx.read(&mut buf).await { + Ok(0) => break, + Ok(n) => { + let msg = tungstenite::Message::Binary(buf[..n].to_vec()); + if ws_tx.send(msg).await.is_err() { + break; + } + } + Err(_) => break, + } + } + let _ = ws_tx.close().await; + }; + + let down = async { + while let Some(Ok(msg)) = ws_rx.next().await { + match msg { + tungstenite::Message::Binary(data) => { + if tcp_tx.write_all(&data).await.is_err() { + break; + } + } + tungstenite::Message::Close(_) => break, + _ => {} + } + } + }; + + tokio::select! { + _ = up => {} + _ = down => {} + } + + Ok(()) +} + +async fn relay_tcp(client: TcpStream, remote: TcpStream) { + let (mut cr, mut cw) = tokio::io::split(client); + let (mut rr, mut rw) = tokio::io::split(remote); + tokio::select! { + _ = tokio::io::copy(&mut cr, &mut rw) => {} + _ = tokio::io::copy(&mut rr, &mut cw) => {} + } +} diff --git a/tg_blacklist.txt b/tg_blacklist.txt new file mode 100644 index 0000000..f554635 --- /dev/null +++ b/tg_blacklist.txt @@ -0,0 +1,21 @@ +91.108.56.0/22 +91.108.4.0/22 +91.108.8.0/22 +91.108.16.0/22 +91.108.12.0/22 +91.108.20.0/22 +149.154.160.0/20 +185.76.151.0/24 +91.105.192.0/23 +core.telegram.org +web.telegram.org +desktop.telegram.org +macos.telegram.org +updates.telegram.org +venus.web.telegram.org +pluto.web.telegram.org +flora.web.telegram.org +vesta.web.telegram.org +telegram.org +t.me +telegram.me diff --git a/tg_unblock.bat b/tg_unblock.bat new file mode 100644 index 0000000..c15f7de --- /dev/null +++ b/tg_unblock.bat @@ -0,0 +1,852 @@ +@echo off +chcp 65001 >nul 2>&1 +setlocal enabledelayedexpansion + +:: ============================================================ +:: TG Unblock — обход блокировки/замедления Telegram +:: Требуется запуск от имени администратора +:: ============================================================ + +:: --- Проверка прав администратора --- +net session >nul 2>&1 +if %errorlevel% neq 0 ( + echo [!] Скрипт требует прав администратора. + echo Перезапускаю с повышенными правами... + powershell -Command "Start-Process -Verb RunAs -FilePath '%~f0'" + exit /b +) + +set "SCRIPT_DIR=%~dp0" +set "TOOLS_DIR=%SCRIPT_DIR%tools" +set "GDPI_DIR=%TOOLS_DIR%\goodbyedpi-0.2.3rc3-2" +set "GDPI_EXE=%GDPI_DIR%\x86_64\goodbyedpi.exe" +set "BLACKLIST=%SCRIPT_DIR%tg_blacklist.txt" +set "GDPI_URL=https://github.com/ValdikSS/GoodbyeDPI/releases/download/0.2.3rc3/goodbyedpi-0.2.3rc3-2.zip" +set "GDPI_ZIP=%TOOLS_DIR%\goodbyedpi.zip" + +title TG Unblock — Обход блокировки Telegram + +:MENU +cls +echo ============================================================ +echo TG Unblock — Обход блокировки Telegram +echo ============================================================ +echo. +echo [*] ЗАПУСТИТЬ ОБХОД (авто) — нажмите 7 +echo. +echo [1] Сменить DNS (Cloudflare / Google) +echo [2] Запустить GoodbyeDPI (обход DPI) +echo [3] Комбинированный режим (DNS + GoodbyeDPI) +echo [4] Тест соединения с Telegram +echo [5] Сбросить настройки (вернуть DNS, остановить GoodbyeDPI) +echo [6] Показать текущие настройки сети +echo [7] === АВТО-ОБХОД (одна кнопка) === +echo [0] Выход +echo. +echo ============================================================ +set /p "choice=Выберите действие [0-7]: " + +if "%choice%"=="1" goto DNS_MENU +if "%choice%"=="2" goto DPI_START +if "%choice%"=="3" goto COMBINED +if "%choice%"=="4" goto TEST +if "%choice%"=="5" goto RESET +if "%choice%"=="6" goto SHOW_NET +if "%choice%"=="7" goto AUTO_BYPASS +if "%choice%"=="0" goto EXIT +echo [!] Неверный выбор. +timeout /t 2 >nul +goto MENU + +:: ============================================================ +:: 1. СМЕНА DNS +:: ============================================================ +:DNS_MENU +cls +echo ============================================================ +echo Смена DNS +echo ============================================================ +echo. +echo [1] Cloudflare DNS (1.1.1.1 / 1.0.0.1) +echo [2] Google DNS (8.8.8.8 / 8.8.4.4) +echo [3] Quad9 DNS (9.9.9.9 / 149.112.112.112) +echo [4] Cloudflare DoH (1.1.1.1 + DNS-over-HTTPS) +echo [0] Назад +echo. +set /p "dns_choice=Выберите DNS провайдера [0-4]: " + +if "%dns_choice%"=="1" ( + set "DNS1=1.1.1.1" + set "DNS2=1.0.0.1" + set "DNS_NAME=Cloudflare" +) +if "%dns_choice%"=="2" ( + set "DNS1=8.8.8.8" + set "DNS2=8.8.4.4" + set "DNS_NAME=Google" +) +if "%dns_choice%"=="3" ( + set "DNS1=9.9.9.9" + set "DNS2=149.112.112.112" + set "DNS_NAME=Quad9" +) +if "%dns_choice%"=="4" ( + set "DNS1=1.1.1.1" + set "DNS2=1.0.0.1" + set "DNS_NAME=Cloudflare DoH" + goto SET_DNS_DOH +) +if "%dns_choice%"=="0" goto MENU + +if not defined DNS1 ( + echo [!] Неверный выбор. + timeout /t 2 >nul + goto DNS_MENU +) + +:SET_DNS +echo. +echo [*] Определяю активный сетевой адаптер... + +for /f "tokens=1,2,3,4,*" %%a in ('netsh interface ipv4 show interfaces ^| findstr /i "connected"') do ( + set "ADAPTER_NAME=%%e" +) + +if not defined ADAPTER_NAME ( + for /f "tokens=*" %%a in ('powershell -Command "(Get-NetAdapter | Where-Object {$_.Status -eq 'Up'} | Select-Object -First 1).Name"') do ( + set "ADAPTER_NAME=%%a" + ) +) + +if not defined ADAPTER_NAME ( + echo [!] Не удалось определить сетевой адаптер. + echo Введите имя адаптера вручную (например: Ethernet, Wi-Fi): + set /p "ADAPTER_NAME=" +) + +echo [*] Адаптер: !ADAPTER_NAME! +echo [*] Устанавливаю DNS: %DNS_NAME% (%DNS1%, %DNS2%)... + +netsh interface ipv4 set dnsservers "!ADAPTER_NAME!" static %DNS1% primary validate=no >nul 2>&1 +netsh interface ipv4 add dnsservers "!ADAPTER_NAME!" %DNS2% index=2 validate=no >nul 2>&1 + +echo [*] Очищаю DNS-кеш... +ipconfig /flushdns >nul 2>&1 + +echo. +echo [OK] DNS успешно изменён на %DNS_NAME% (%DNS1%, %DNS2%) +echo. +pause +goto MENU + +:SET_DNS_DOH +echo. +echo [*] Определяю активный сетевой адаптер... + +for /f "tokens=*" %%a in ('powershell -Command "(Get-NetAdapter | Where-Object {$_.Status -eq 'Up'} | Select-Object -First 1).Name"') do ( + set "ADAPTER_NAME=%%a" +) + +if not defined ADAPTER_NAME ( + echo [!] Не удалось определить адаптер. Введите имя вручную: + set /p "ADAPTER_NAME=" +) + +echo [*] Адаптер: !ADAPTER_NAME! +echo [*] Устанавливаю DNS: Cloudflare (1.1.1.1, 1.0.0.1)... + +netsh interface ipv4 set dnsservers "!ADAPTER_NAME!" static 1.1.1.1 primary validate=no >nul 2>&1 +netsh interface ipv4 add dnsservers "!ADAPTER_NAME!" 1.0.0.1 index=2 validate=no >nul 2>&1 +ipconfig /flushdns >nul 2>&1 + +echo [*] Включаю DNS-over-HTTPS в системе (Windows 11+)... +powershell -Command "try { Set-DnsClientDohServerAddress -ServerAddress '1.1.1.1' -DohTemplate 'https://cloudflare-dns.com/dns-query' -AllowFallbackToUdp $true -AutoUpgrade $true -ErrorAction Stop; Set-DnsClientDohServerAddress -ServerAddress '1.0.0.1' -DohTemplate 'https://cloudflare-dns.com/dns-query' -AllowFallbackToUdp $true -AutoUpgrade $true -ErrorAction Stop; Write-Host '[OK] DoH активирован' } catch { Write-Host '[!] DoH недоступен на этой версии Windows, DNS установлен без DoH' }" + +echo. +echo [OK] DNS установлен на Cloudflare с DoH. +echo. +pause +goto MENU + +:: ============================================================ +:: 2. ЗАПУСК GoodbyeDPI +:: ============================================================ +:DPI_START +cls +echo ============================================================ +echo GoodbyeDPI — обход DPI +echo ============================================================ +echo. + +:: Проверяем наличие GoodbyeDPI +if not exist "%GDPI_EXE%" ( + echo [!] GoodbyeDPI не найден. + goto DOWNLOAD_GDPI +) + +:DPI_MODE_MENU +echo. +echo Выберите режим GoodbyeDPI: +echo. +echo [1] Режим -9 (максимальная защита, рекомендуется) +echo [2] Режим -5 (средний) +echo [3] Режим -1 (базовый, совместимый) +echo [4] Кастомный (-e 2 -f 2 -q -r -m) +echo [5] Авто-перебор (попробует все режимы) +echo [0] Назад +echo. +set /p "dpi_mode=Выберите режим [0-5]: " + +if "%dpi_mode%"=="0" goto MENU + +:: Убиваем старый процесс если запущен +taskkill /f /im goodbyedpi.exe >nul 2>&1 +timeout /t 1 >nul + +if "%dpi_mode%"=="1" ( + echo [*] Запускаю GoodbyeDPI в режиме -9 (максимальная защита)... + start "" /B "%GDPI_EXE%" -9 --blacklist "%BLACKLIST%" + goto DPI_STARTED +) +if "%dpi_mode%"=="2" ( + echo [*] Запускаю GoodbyeDPI в режиме -5 (средний)... + start "" /B "%GDPI_EXE%" -5 --blacklist "%BLACKLIST%" + goto DPI_STARTED +) +if "%dpi_mode%"=="3" ( + echo [*] Запускаю GoodbyeDPI в режиме -1 (базовый)... + start "" /B "%GDPI_EXE%" -1 --blacklist "%BLACKLIST%" + goto DPI_STARTED +) +if "%dpi_mode%"=="4" ( + echo [*] Запускаю GoodbyeDPI в кастомном режиме... + start "" /B "%GDPI_EXE%" -e 2 -f 2 -q -r -m --blacklist "%BLACKLIST%" + goto DPI_STARTED +) +if "%dpi_mode%"=="5" goto DPI_AUTO + +echo [!] Неверный выбор. +timeout /t 2 >nul +goto DPI_MODE_MENU + +:DPI_STARTED +timeout /t 2 >nul +tasklist /fi "imagename eq goodbyedpi.exe" 2>nul | findstr /i "goodbyedpi" >nul +if %errorlevel%==0 ( + echo. + echo [OK] GoodbyeDPI запущен успешно! + echo Работает в фоне. Для остановки используйте пункт 5 (Сброс). +) else ( + echo. + echo [!] GoodbyeDPI не удалось запустить. + echo Проверьте что антивирус не блокирует WinDivert. +) +echo. +pause +goto MENU + +:: ============================================================ +:: АВТО-ПЕРЕБОР РЕЖИМОВ +:: ============================================================ +:DPI_AUTO +echo. +echo [*] Авто-перебор режимов GoodbyeDPI... +echo [*] Буду пробовать каждый режим и тестировать соединение. +echo. + +set "MODES=-9;-5;-1;-e 2 -f 2 -q -r -m;-e 1 -f 1 -q -r -m -s;-p -f 2 -e 2 -q" +set "MODE_NAMES=Режим -9 (макс);Режим -5 (средний);Режим -1 (базовый);Кастом1 (-e2 -f2 -q -r -m);Кастом2 (-e1 -f1 -q -r -m -s);Кастом3 (-p -f2 -e2 -q)" +set "mode_idx=0" +set "best_mode=" + +for %%m in ("-9" "-5" "-1") do ( + set /a mode_idx+=1 + echo --- Тест !mode_idx!: GoodbyeDPI %%~m --- + + taskkill /f /im goodbyedpi.exe >nul 2>&1 + timeout /t 1 >nul + + start "" /B "%GDPI_EXE%" %%~m --blacklist "%BLACKLIST%" + timeout /t 3 >nul + + tasklist /fi "imagename eq goodbyedpi.exe" 2>nul | findstr /i "goodbyedpi" >nul + if !errorlevel! neq 0 ( + echo [FAIL] Не запустился + ) else ( + powershell -Command "$r = try { (Invoke-WebRequest -Uri 'https://web.telegram.org' -TimeoutSec 10 -UseBasicParsing).StatusCode } catch { 0 }; if ($r -eq 200) { Write-Host ' [OK] web.telegram.org доступен!'; exit 0 } else { Write-Host ' [FAIL] web.telegram.org недоступен'; exit 1 }" + if !errorlevel!==0 ( + set "best_mode=%%~m" + echo [***] Рабочий режим найден: %%~m + ) + ) +) + +:: Пробуем кастомные режимы +for %%p in ( + "-e 2 -f 2 -q -r -m" + "-e 1 -f 1 -q -r -m -s" + "-p -f 2 -e 2 -q" +) do ( + set /a mode_idx+=1 + echo --- Тест !mode_idx!: GoodbyeDPI %%~p --- + + taskkill /f /im goodbyedpi.exe >nul 2>&1 + timeout /t 1 >nul + + start "" /B "%GDPI_EXE%" %%~p --blacklist "%BLACKLIST%" + timeout /t 3 >nul + + tasklist /fi "imagename eq goodbyedpi.exe" 2>nul | findstr /i "goodbyedpi" >nul + if !errorlevel! neq 0 ( + echo [FAIL] Не запустился + ) else ( + powershell -Command "$r = try { (Invoke-WebRequest -Uri 'https://web.telegram.org' -TimeoutSec 10 -UseBasicParsing).StatusCode } catch { 0 }; if ($r -eq 200) { Write-Host ' [OK] web.telegram.org доступен!'; exit 0 } else { Write-Host ' [FAIL] web.telegram.org недоступен'; exit 1 }" + if !errorlevel!==0 ( + if not defined best_mode ( + set "best_mode=%%~p" + echo [***] Рабочий режим найден: %%~p + ) + ) + ) +) + +echo. +if defined best_mode ( + echo ============================================================ + echo [OK] Лучший рабочий режим: !best_mode! + echo Перезапускаю GoodbyeDPI с этим режимом... + echo ============================================================ + taskkill /f /im goodbyedpi.exe >nul 2>&1 + timeout /t 1 >nul + start "" /B "%GDPI_EXE%" !best_mode! --blacklist "%BLACKLIST%" +) else ( + echo ============================================================ + echo [!] Ни один режим GoodbyeDPI не помог. + echo Возможно, нужен VPN или MTProxy. + echo ============================================================ + taskkill /f /im goodbyedpi.exe >nul 2>&1 +) +echo. +pause +goto MENU + +:: ============================================================ +:: 3. КОМБИНИРОВАННЫЙ РЕЖИМ +:: ============================================================ +:COMBINED +cls +echo ============================================================ +echo Комбинированный режим (DNS + GoodbyeDPI) +echo ============================================================ +echo. + +:: --- DNS --- +echo [*] Устанавливаю DNS Cloudflare (1.1.1.1)... +for /f "tokens=*" %%a in ('powershell -Command "(Get-NetAdapter | Where-Object {$_.Status -eq 'Up'} | Select-Object -First 1).Name"') do ( + set "ADAPTER_NAME=%%a" +) +if defined ADAPTER_NAME ( + netsh interface ipv4 set dnsservers "!ADAPTER_NAME!" static 1.1.1.1 primary validate=no >nul 2>&1 + netsh interface ipv4 add dnsservers "!ADAPTER_NAME!" 1.0.0.1 index=2 validate=no >nul 2>&1 + ipconfig /flushdns >nul 2>&1 + echo [OK] DNS установлен: Cloudflare (1.1.1.1, 1.0.0.1) +) else ( + echo [!] Не удалось определить сетевой адаптер для DNS. +) + +:: --- GoodbyeDPI --- +if not exist "%GDPI_EXE%" ( + echo. + echo [!] GoodbyeDPI не найден. Скачиваю... + call :DO_DOWNLOAD_GDPI + if not exist "%GDPI_EXE%" ( + echo [!] Не удалось скачать GoodbyeDPI. Работаю только с DNS. + pause + goto MENU + ) +) + +echo. +echo [*] Запускаю GoodbyeDPI в режиме -9... +taskkill /f /im goodbyedpi.exe >nul 2>&1 +timeout /t 1 >nul +start "" /B "%GDPI_EXE%" -9 --blacklist "%BLACKLIST%" +timeout /t 2 >nul + +tasklist /fi "imagename eq goodbyedpi.exe" 2>nul | findstr /i "goodbyedpi" >nul +if %errorlevel%==0 ( + echo [OK] GoodbyeDPI запущен. +) else ( + echo [!] GoodbyeDPI не запустился. Пробую режим -5... + start "" /B "%GDPI_EXE%" -5 --blacklist "%BLACKLIST%" + timeout /t 2 >nul +) + +echo. +echo ============================================================ +echo [OK] Комбинированный режим активирован: +echo DNS: Cloudflare 1.1.1.1 +echo DPI: GoodbyeDPI +echo ============================================================ +echo. + +:: Быстрый тест +echo [*] Проверяю доступность Telegram... +powershell -Command "$r = try { $sw = [System.Diagnostics.Stopwatch]::StartNew(); $resp = Invoke-WebRequest -Uri 'https://web.telegram.org' -TimeoutSec 15 -UseBasicParsing; $sw.Stop(); Write-Host \"[OK] web.telegram.org доступен (${($sw.ElapsedMilliseconds)}ms)\"; } catch { Write-Host '[!] web.telegram.org пока недоступен, подождите немного...' }" +echo. +pause +goto MENU + +:: ============================================================ +:: 4. ТЕСТ СОЕДИНЕНИЯ +:: ============================================================ +:TEST +cls +echo ============================================================ +echo Тест соединения с Telegram +echo ============================================================ +echo. + +echo [*] Проверяю GoodbyeDPI... +tasklist /fi "imagename eq goodbyedpi.exe" 2>nul | findstr /i "goodbyedpi" >nul +if %errorlevel%==0 ( + echo GoodbyeDPI: ЗАПУЩЕН +) else ( + echo GoodbyeDPI: не запущен +) +echo. + +echo [*] Текущий DNS: +powershell -Command "Get-DnsClientServerAddress -AddressFamily IPv4 | Where-Object {$_.ServerAddresses.Count -gt 0} | Format-Table InterfaceAlias, ServerAddresses -AutoSize" +echo. + +echo [*] Пинг серверов Telegram (DC1-DC5)... +echo. + +set "TG_IPS=149.154.175.50 149.154.167.51 149.154.175.100 149.154.167.91 91.108.56.100 91.108.4.100" + +for %%i in (%TG_IPS%) do ( + echo Пинг %%i... + ping -n 1 -w 3000 %%i >nul 2>&1 + if !errorlevel!==0 ( + for /f "tokens=*" %%t in ('ping -n 1 -w 3000 %%i ^| findstr /i "time="') do ( + echo [OK] %%i — %%t + ) + ) else ( + echo [FAIL] %%i — недоступен + ) +) + +echo. +echo [*] Проверяю TCP-соединение (порт 443)... +echo. + +for %%i in (149.154.175.50 149.154.167.51 91.108.56.100) do ( + powershell -Command "$tcp = New-Object System.Net.Sockets.TcpClient; try { $tcp.ConnectAsync('%%i', 443).Wait(5000) | Out-Null; if ($tcp.Connected) { Write-Host ' [OK] %%i:443 — TCP доступен' } else { Write-Host ' [FAIL] %%i:443 — таймаут' } } catch { Write-Host ' [FAIL] %%i:443 — отказано' } finally { $tcp.Dispose() }" +) + +echo. +echo [*] Проверяю HTTPS доступность... +echo. + +for %%u in ( + "https://web.telegram.org" + "https://core.telegram.org" + "https://t.me" +) do ( + powershell -Command "$url = '%%~u'; try { $sw = [System.Diagnostics.Stopwatch]::StartNew(); $r = Invoke-WebRequest -Uri $url -TimeoutSec 10 -UseBasicParsing; $sw.Stop(); $ms = $sw.ElapsedMilliseconds; Write-Host \" [OK] $url — HTTP $($r.StatusCode) (${ms}ms)\" } catch { Write-Host \" [FAIL] $url — недоступен\" }" +) + +echo. +echo [*] Проверяю DNS резолвинг... +echo. + +for %%d in (web.telegram.org core.telegram.org t.me telegram.org) do ( + powershell -Command "try { $ip = [System.Net.Dns]::GetHostAddresses('%%d') | Select-Object -First 1; Write-Host \" [OK] %%d -> $($ip.IPAddressToString)\" } catch { Write-Host ' [FAIL] %%d — DNS не резолвится' }" +) + +echo. +echo ============================================================ +echo Тест завершён +echo ============================================================ +echo. +pause +goto MENU + +:: ============================================================ +:: 5. СБРОС НАСТРОЕК +:: ============================================================ +:RESET +cls +echo ============================================================ +echo Сброс настроек +echo ============================================================ +echo. + +echo [*] Останавливаю GoodbyeDPI... +taskkill /f /im goodbyedpi.exe >nul 2>&1 +echo [OK] GoodbyeDPI остановлен. + +echo. +echo [*] Возвращаю DNS к автоматическому (DHCP)... +for /f "tokens=*" %%a in ('powershell -Command "(Get-NetAdapter | Where-Object {$_.Status -eq 'Up'} | Select-Object -First 1).Name"') do ( + set "ADAPTER_NAME=%%a" +) +if defined ADAPTER_NAME ( + netsh interface ipv4 set dnsservers "!ADAPTER_NAME!" dhcp >nul 2>&1 + ipconfig /flushdns >nul 2>&1 + echo [OK] DNS сброшен на DHCP (автоматический). +) else ( + echo [!] Не удалось определить адаптер. Сбросьте DNS вручную. +) + +echo. +echo [OK] Все настройки сброшены. +echo. +pause +goto MENU + +:: ============================================================ +:: 6. ПОКАЗАТЬ ТЕКУЩИЕ НАСТРОЙКИ +:: ============================================================ +:SHOW_NET +cls +echo ============================================================ +echo Текущие сетевые настройки +echo ============================================================ +echo. + +echo [*] Сетевые адаптеры: +powershell -Command "Get-NetAdapter | Where-Object {$_.Status -eq 'Up'} | Format-Table Name, InterfaceDescription, Status, LinkSpeed -AutoSize" + +echo [*] DNS серверы: +powershell -Command "Get-DnsClientServerAddress -AddressFamily IPv4 | Where-Object {$_.ServerAddresses.Count -gt 0} | Format-Table InterfaceAlias, ServerAddresses -AutoSize" + +echo [*] IP-конфигурация: +ipconfig | findstr /i "IPv4 Subnet Gateway DNS" + +echo. +echo [*] GoodbyeDPI: +tasklist /fi "imagename eq goodbyedpi.exe" 2>nul | findstr /i "goodbyedpi" >nul +if %errorlevel%==0 ( + echo Статус: ЗАПУЩЕН + for /f "tokens=2" %%p in ('tasklist /fi "imagename eq goodbyedpi.exe" /fo list ^| findstr "PID"') do ( + echo PID: %%p + ) +) else ( + echo Статус: не запущен +) + +echo. +pause +goto MENU + +:: ============================================================ +:: СКАЧИВАНИЕ GoodbyeDPI +:: ============================================================ +:DOWNLOAD_GDPI +echo. +echo [*] GoodbyeDPI необходим для обхода DPI. +echo Скачать автоматически с GitHub? +echo. +echo [1] Да, скачать +echo [0] Нет, назад +echo. +set /p "dl_choice=Выбор [0-1]: " +if "%dl_choice%"=="0" goto MENU +if "%dl_choice%"=="1" ( + call :DO_DOWNLOAD_GDPI + if exist "%GDPI_EXE%" goto DPI_MODE_MENU + pause + goto MENU +) +goto MENU + +:DO_DOWNLOAD_GDPI +echo. +echo [*] Создаю папку tools... +if not exist "%TOOLS_DIR%" mkdir "%TOOLS_DIR%" + +echo [*] Скачиваю GoodbyeDPI с GitHub... +echo URL: %GDPI_URL% +echo. + +powershell -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; try { Invoke-WebRequest -Uri '%GDPI_URL%' -OutFile '%GDPI_ZIP%' -UseBasicParsing; Write-Host '[OK] Скачано успешно' } catch { Write-Host \"[!] Ошибка скачивания: $($_.Exception.Message)\"; exit 1 }" + +if not exist "%GDPI_ZIP%" ( + echo [!] Не удалось скачать файл. + echo Попробуйте скачать вручную: + echo %GDPI_URL% + echo и распаковать в %GDPI_DIR% + exit /b 1 +) + +echo [*] Распаковываю... +powershell -Command "try { Expand-Archive -Path '%GDPI_ZIP%' -DestinationPath '%TOOLS_DIR%' -Force; Write-Host '[OK] Распаковано' } catch { Write-Host \"[!] Ошибка распаковки: $($_.Exception.Message)\"; exit 1 }" + +:: Ищем goodbyedpi.exe рекурсивно +if not exist "%GDPI_EXE%" ( + echo [*] Ищу goodbyedpi.exe... + for /f "tokens=*" %%f in ('powershell -Command "Get-ChildItem -Path '%TOOLS_DIR%' -Recurse -Filter 'goodbyedpi.exe' | Select-Object -First 1 | ForEach-Object { $_.DirectoryName }"') do ( + set "FOUND_DIR=%%f" + ) + if defined FOUND_DIR ( + echo [*] Найден в: !FOUND_DIR! + set "GDPI_DIR=!FOUND_DIR!" + set "GDPI_EXE=!FOUND_DIR!\goodbyedpi.exe" + + :: Обновим путь для x86_64 + if not exist "!FOUND_DIR!\goodbyedpi.exe" ( + for /f "tokens=*" %%f in ('powershell -Command "Get-ChildItem -Path '%TOOLS_DIR%' -Recurse -Filter 'goodbyedpi.exe' | Select-Object -First 1 | ForEach-Object { $_.FullName }"') do ( + set "GDPI_EXE=%%f" + echo [*] Путь к exe: %%f + ) + ) + ) +) + +if exist "%GDPI_EXE%" ( + echo. + echo [OK] GoodbyeDPI установлен: %GDPI_EXE% + del "%GDPI_ZIP%" >nul 2>&1 +) else ( + echo. + echo [!] goodbyedpi.exe не найден после распаковки. + echo Проверьте папку: %TOOLS_DIR% +) +exit /b 0 + +:: ============================================================ +:: 7. АВТО-ОБХОД (одна кнопка) — замеряет ВСЕ режимы, +:: выбирает самый быстрый +:: ============================================================ +:AUTO_BYPASS +cls +echo ============================================================ +echo АВТО-ОБХОД — тестирую ВСЕ режимы, выбираю лучший... +echo Это займёт 2-3 минуты. +echo ============================================================ +echo. + +:: --- Шаг 1: Определяю адаптер --- +echo [1] Определяю сетевой адаптер... +set "ADAPTER_NAME=" +for /f "tokens=*" %%a in ('powershell -Command "(Get-NetAdapter | Where-Object {$_.Status -eq 'Up'} | Select-Object -First 1).Name"') do ( + set "ADAPTER_NAME=%%a" +) +if not defined ADAPTER_NAME ( + echo [!] Не удалось определить адаптер. + pause + goto MENU +) +echo Адаптер: !ADAPTER_NAME! + +:: --- Шаг 2: DNS --- +echo. +echo [2] Устанавливаю DNS Cloudflare (1.1.1.1)... +netsh interface ipv4 set dnsservers "!ADAPTER_NAME!" static 1.1.1.1 primary validate=no >nul 2>&1 +netsh interface ipv4 add dnsservers "!ADAPTER_NAME!" 1.0.0.1 index=2 validate=no >nul 2>&1 +ipconfig /flushdns >nul 2>&1 +echo OK + +:: --- Шаг 3: GoodbyeDPI --- +echo. +echo [3] Ищу GoodbyeDPI... +if not exist "%GDPI_EXE%" ( + echo Не найден, скачиваю... + call :DO_DOWNLOAD_GDPI + if not exist "%GDPI_EXE%" ( + echo [!] Не удалось скачать GoodbyeDPI. + pause + goto MENU + ) +) +echo OK: %GDPI_EXE% + +:: --- Шаг 4: Бенчмарк каждого режима --- +echo. +echo ============================================================ +echo Замеряю скорость каждого режима... +echo ============================================================ +echo. + +set "BEST_MODE=" +set "BEST_SCORE=99999" +set "BEST_LABEL=" +set "MODE_NUM=0" + +:: Benchmark function is a powershell one-liner that does +:: 2x TCP + 1x HTTPS, returns average ms (lower=better), 99999 if fail +set "BENCH_CMD=powershell -Command "$total=0; $ok=0; foreach($ip in @('149.154.167.51','149.154.175.50','91.108.56.100')) { foreach($i in 1..2) { $tcp=New-Object Net.Sockets.TcpClient; $sw=[Diagnostics.Stopwatch]::StartNew(); try { [void]$tcp.ConnectAsync($ip,443).Wait(4000); if($tcp.Connected){$sw.Stop();$total+=$sw.ElapsedMilliseconds;$ok++} } catch {} finally {$tcp.Dispose()} } }; try { $sw2=[Diagnostics.Stopwatch]::StartNew(); $r=Invoke-WebRequest 'https://web.telegram.org' -TimeoutSec 8 -UseBasicParsing; $sw2.Stop(); $total+=$sw2.ElapsedMilliseconds*3; $ok+=3 } catch {}; if($ok -eq 0){Write-Host 99999}else{Write-Host([math]::Floor($total/$ok))}"" + +:: Mode 1: без GoodbyeDPI (только DNS) +set /a MODE_NUM+=1 +echo [%MODE_NUM%/8] Только DNS (без GoodbyeDPI)... +taskkill /f /im goodbyedpi.exe >nul 2>&1 +timeout /t 1 >nul +for /f "tokens=*" %%s in ('%BENCH_CMD%') do set "SCORE=%%s" +echo Скорость: !SCORE!ms +if !SCORE! LSS !BEST_SCORE! ( + set "BEST_SCORE=!SCORE!" + set "BEST_MODE=dns_only" + set "BEST_LABEL=Только DNS" +) + +:: Mode 2: -9 +set /a MODE_NUM+=1 +echo [%MODE_NUM%/8] GoodbyeDPI -9 (максимальная защита)... +taskkill /f /im goodbyedpi.exe >nul 2>&1 +timeout /t 1 >nul +start "" /B "%GDPI_EXE%" -9 --blacklist "%BLACKLIST%" +timeout /t 2 >nul +for /f "tokens=*" %%s in ('%BENCH_CMD%') do set "SCORE=%%s" +echo Скорость: !SCORE!ms +if !SCORE! LSS !BEST_SCORE! ( + set "BEST_SCORE=!SCORE!" + set "BEST_MODE=-9" + set "BEST_LABEL=GoodbyeDPI -9" +) + +:: Mode 3: -5 +set /a MODE_NUM+=1 +echo [%MODE_NUM%/8] GoodbyeDPI -5 (средний)... +taskkill /f /im goodbyedpi.exe >nul 2>&1 +timeout /t 1 >nul +start "" /B "%GDPI_EXE%" -5 --blacklist "%BLACKLIST%" +timeout /t 2 >nul +for /f "tokens=*" %%s in ('%BENCH_CMD%') do set "SCORE=%%s" +echo Скорость: !SCORE!ms +if !SCORE! LSS !BEST_SCORE! ( + set "BEST_SCORE=!SCORE!" + set "BEST_MODE=-5" + set "BEST_LABEL=GoodbyeDPI -5" +) + +:: Mode 4: -1 +set /a MODE_NUM+=1 +echo [%MODE_NUM%/8] GoodbyeDPI -1 (базовый)... +taskkill /f /im goodbyedpi.exe >nul 2>&1 +timeout /t 1 >nul +start "" /B "%GDPI_EXE%" -1 --blacklist "%BLACKLIST%" +timeout /t 2 >nul +for /f "tokens=*" %%s in ('%BENCH_CMD%') do set "SCORE=%%s" +echo Скорость: !SCORE!ms +if !SCORE! LSS !BEST_SCORE! ( + set "BEST_SCORE=!SCORE!" + set "BEST_MODE=-1" + set "BEST_LABEL=GoodbyeDPI -1" +) + +:: Mode 5: custom -e2 -f2 -q -r -m +set /a MODE_NUM+=1 +echo [%MODE_NUM%/8] GoodbyeDPI -e2 -f2 -q -r -m... +taskkill /f /im goodbyedpi.exe >nul 2>&1 +timeout /t 1 >nul +start "" /B "%GDPI_EXE%" -e 2 -f 2 -q -r -m --blacklist "%BLACKLIST%" +timeout /t 2 >nul +for /f "tokens=*" %%s in ('%BENCH_CMD%') do set "SCORE=%%s" +echo Скорость: !SCORE!ms +if !SCORE! LSS !BEST_SCORE! ( + set "BEST_SCORE=!SCORE!" + set "BEST_MODE=-e 2 -f 2 -q -r -m" + set "BEST_LABEL=GoodbyeDPI -e2 -f2 -q -r -m" +) + +:: Mode 6: custom -p -f2 -e2 -q +set /a MODE_NUM+=1 +echo [%MODE_NUM%/8] GoodbyeDPI -p -f2 -e2 -q... +taskkill /f /im goodbyedpi.exe >nul 2>&1 +timeout /t 1 >nul +start "" /B "%GDPI_EXE%" -p -f 2 -e 2 -q --blacklist "%BLACKLIST%" +timeout /t 2 >nul +for /f "tokens=*" %%s in ('%BENCH_CMD%') do set "SCORE=%%s" +echo Скорость: !SCORE!ms +if !SCORE! LSS !BEST_SCORE! ( + set "BEST_SCORE=!SCORE!" + set "BEST_MODE=-p -f 2 -e 2 -q" + set "BEST_LABEL=GoodbyeDPI -p -f2 -e2 -q" +) + +:: Mode 7: custom -e40 -f2 -q -r -m +set /a MODE_NUM+=1 +echo [%MODE_NUM%/8] GoodbyeDPI -e40 -f2 -q -r -m... +taskkill /f /im goodbyedpi.exe >nul 2>&1 +timeout /t 1 >nul +start "" /B "%GDPI_EXE%" -e 40 -f 2 -q -r -m --blacklist "%BLACKLIST%" +timeout /t 2 >nul +for /f "tokens=*" %%s in ('%BENCH_CMD%') do set "SCORE=%%s" +echo Скорость: !SCORE!ms +if !SCORE! LSS !BEST_SCORE! ( + set "BEST_SCORE=!SCORE!" + set "BEST_MODE=-e 40 -f 2 -q -r -m" + set "BEST_LABEL=GoodbyeDPI -e40 -f2 -q -r -m" +) + +:: Mode 8: -9 --set-ttl 5 +set /a MODE_NUM+=1 +echo [%MODE_NUM%/8] GoodbyeDPI -9 --set-ttl 5... +taskkill /f /im goodbyedpi.exe >nul 2>&1 +timeout /t 1 >nul +start "" /B "%GDPI_EXE%" -9 --set-ttl 5 --blacklist "%BLACKLIST%" +timeout /t 2 >nul +for /f "tokens=*" %%s in ('%BENCH_CMD%') do set "SCORE=%%s" +echo Скорость: !SCORE!ms +if !SCORE! LSS !BEST_SCORE! ( + set "BEST_SCORE=!SCORE!" + set "BEST_MODE=-9 --set-ttl 5" + set "BEST_LABEL=GoodbyeDPI -9 --set-ttl 5" +) + +:: --- Применяем лучший --- +echo. +echo ============================================================ + +if "!BEST_SCORE!"=="99999" ( + echo [!] Ни один режим не сработал. Попробуйте VPN или MTProxy. + taskkill /f /im goodbyedpi.exe >nul 2>&1 + pause + goto MENU +) + +echo ЛУЧШИЙ РЕЖИМ: !BEST_LABEL! +echo СКОРОСТЬ: !BEST_SCORE!ms +echo ============================================================ +echo. + +taskkill /f /im goodbyedpi.exe >nul 2>&1 +timeout /t 1 >nul + +if "!BEST_MODE!"=="dns_only" ( + echo [OK] DNS Cloudflare достаточно, GoodbyeDPI не нужен. +) else ( + echo [*] Запускаю GoodbyeDPI с лучшими параметрами... + start "" /B "%GDPI_EXE%" !BEST_MODE! --blacklist "%BLACKLIST%" + timeout /t 2 >nul + echo [OK] GoodbyeDPI запущен: !BEST_MODE! +) + +echo. +echo ============================================================ +echo [OK] ОБХОД АКТИВИРОВАН! +echo DNS: Cloudflare 1.1.1.1 +echo Режим: !BEST_LABEL! (!BEST_SCORE!ms) +echo Для остановки — пункт 5 в меню. +echo ============================================================ +pause +goto MENU + +:: ============================================================ +:: ВЫХОД +:: ============================================================ +:EXIT +echo. +echo [*] GoodbyeDPI будет остановлен при выходе? (y/n) +set /p "exit_choice=" +if /i "%exit_choice%"=="y" ( + taskkill /f /im goodbyedpi.exe >nul 2>&1 + echo [OK] GoodbyeDPI остановлен. +) +echo. +echo Bye! +endlocal +exit /b 0