Update: new version release with binaries
This commit is contained in:
parent
6855662a27
commit
b07bd29dae
400
README.md
400
README.md
|
|
@ -1,339 +1,187 @@
|
||||||
# TG WS Proxy Go
|
# TG WS Proxy Go
|
||||||
|
|
||||||
[](go.mod)
|
|
||||||
[](LICENSE)
|
|
||||||
[](https://github.com/y0sy4/tg-ws-proxy-go/releases)
|
[](https://github.com/y0sy4/tg-ws-proxy-go/releases)
|
||||||
|
[](LICENSE)
|
||||||
|
|
||||||
> **Go-переосмысление** [Flowseal/tg-ws-proxy](https://github.com/Flowseal/tg-ws-proxy)
|
**SOCKS5-прокси для Telegram Desktop на Go.** Ускоряет Telegram через WebSocket к серверам Telegram.
|
||||||
|
|
||||||
**Локальный SOCKS5-прокси для Telegram Desktop на Go**
|
|
||||||
|
|
||||||
Ускоряет работу Telegram через WebSocket-соединения напрямую к серверам Telegram.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📥 Скачать (Последняя версия v2.0.4)
|
## 📥 Скачать (v2.0.5)
|
||||||
|
|
||||||
> **💡 Просто выберите свою платформу и нажмите "Скачать"!**
|
| Windows | Linux | macOS |
|
||||||
|
|---------|-------|-------|
|
||||||
| <img src="https://img.icons8.com/color/48/000000/windows-10.png" width="24"/> Windows | <img src="https://img.icons8.com/color/48/000000/linux.png" width="24"/> Linux | <img src="https://img.icons8.com/color/48/000000/mac-os.png" width="24"/> macOS |
|
| [⬇️ .exe](https://github.com/y0sy4/tg-ws-proxy-go/releases/download/v2.0.5/TgWsProxy_windows_amd64.exe) (9 MB) | [⬇️ amd64](https://github.com/y0sy4/tg-ws-proxy-go/releases/download/v2.0.5/TgWsProxy_linux_amd64) (8.9 MB) | [⬇️ Intel](https://github.com/y0sy4/tg-ws-proxy-go/releases/download/v2.0.5/TgWsProxy_darwin_amd64) / [⬇️ ARM](https://github.com/y0sy4/tg-ws-proxy-go/releases/download/v2.0.5/TgWsProxy_darwin_arm64) |
|
||||||
|----------|----------|----------|
|
|
||||||
| **TgWsProxy.exe** | **TgWsProxy** | **TgWsProxy** |
|
|
||||||
| 6.6 MB | 6.5 MB | 6.6 MB / 5.8 MB (ARM) |
|
|
||||||
| [⬇️ Скачать](https://github.com/y0sy4/tg-ws-proxy-go/releases/download/v2.0.4/TgWsProxy_windows_amd64.exe) | [⬇️ Скачать](https://github.com/y0sy4/tg-ws-proxy-go/releases/download/v2.0.4/TgWsProxy_linux_amd64) | [⬇️ Intel](https://github.com/y0sy4/tg-ws-proxy-go/releases/download/v2.0.4/TgWsProxy_darwin_amd64) / [⬇️ Apple Silicon](https://github.com/y0sy4/tg-ws-proxy-go/releases/download/v2.0.4/TgWsProxy_darwin_arm64) |
|
|
||||||
|
|
||||||
**📦 Все версии:** https://github.com/y0sy4/tg-ws-proxy-go/releases
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Быстрый старт (3 простых шага)
|
## 🚀 Быстрый старт
|
||||||
|
|
||||||
> **💡 Это так просто!**
|
### Windows
|
||||||
|
1. Скачай `TgWsProxy_windows_amd64.exe`
|
||||||
|
2. Дважды кликни
|
||||||
|
3. Telegram откроет настройки прокси → нажми "Включить"
|
||||||
|
|
||||||
| Шаг | Что делать | Windows | Linux/macOS |
|
### Linux/macOS
|
||||||
|-----|------------|---------|-------------|
|
```bash
|
||||||
| **1️⃣** | **Скачать** | Нажми "Скачать" выше | Нажми "Скачать" выше |
|
chmod +x TgWsProxy_*
|
||||||
| **2️⃣** | **Запустить** | Дважды кликни на `TgWsProxy.exe` | Открой терминал: `./TgWsProxy` |
|
./TgWsProxy_linux_amd64 # или TgWsProxy_darwin_amd64
|
||||||
| **3️⃣** | **Готово!** | Telegram сам откроет настройки | Telegram сам откроет настройки |
|
```
|
||||||
|
|
||||||
**✅ Всё!** Telegram теперь работает через прокси!
|
**Всё!** Telegram работает через прокси.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Почему Go версия лучше
|
## ⚙️ Опции (для профи)
|
||||||
|
|
||||||
| Параметр | Python | Go |
|
|
||||||
|----------|--------|-----|
|
|
||||||
| Размер | ~50 MB | **~8 MB** |
|
|
||||||
| Зависимости | pip (много) | **stdlib** |
|
|
||||||
| Время запуска | ~500 ms | **~50 ms** |
|
|
||||||
| Потребление памяти | ~50 MB | **~10 MB** |
|
|
||||||
|
|
||||||
## Быстрый старт
|
|
||||||
|
|
||||||
### Установка
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Скачать готовый бинарник из Releases
|
TgWsProxy.exe [флаги]
|
||||||
# Или собрать из исходников
|
|
||||||
go build -o TgWsProxy.exe ./cmd/proxy
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Запуск
|
| Флаг | Описание | По умолчанию |
|
||||||
|
|------|----------|--------------|
|
||||||
```bash
|
| `--port` | Порт SOCKS5 | 1080 |
|
||||||
# Windows (автоматически откроет настройку прокси в Telegram)
|
| `--host` | Хост | 127.0.0.1 |
|
||||||
start run.bat
|
| `--dc-ip` | DC:IP (через запятую) | авто |
|
||||||
|
| `--auth` | Логин:пароль для прокси | — |
|
||||||
# Linux/macOS (автоматически откроет настройку прокси в Telegram)
|
| `--http-port` | HTTP прокси (для браузеров) | 0 (выкл) |
|
||||||
./TgWsProxy
|
| `--upstream-proxy` | Цепочка через другой прокси | — |
|
||||||
|
| `-v` | Подробные логи | false |
|
||||||
# С опциями
|
|
||||||
./TgWsProxy --port 9050 --dc-ip 2:149.154.167.220
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📖 Подробная инструкция для новичков
|
|
||||||
|
|
||||||
### Шаг 1: Скачивание
|
|
||||||
|
|
||||||
1. Откройте страницу [Releases](https://github.com/y0sy4/tg-ws-proxy-go/releases)
|
|
||||||
2. Найдите свою платформу в таблице
|
|
||||||
3. Нажмите на ссылку скачивания (например, `TgWsProxy_windows_amd64.exe`)
|
|
||||||
|
|
||||||
### Шаг 2: Установка
|
|
||||||
|
|
||||||
**Windows:**
|
|
||||||
- Просто сохраните файл в любую папку (например, `C:\Programs\TgWsProxy\`)
|
|
||||||
- Создайте ярлык на рабочем столе (по желанию)
|
|
||||||
|
|
||||||
**macOS/Linux:**
|
|
||||||
- Сохраните файл в папку `~/Applications/`
|
|
||||||
- Откройте терминал и выполните:
|
|
||||||
```bash
|
|
||||||
chmod +x ~/Applications/TgWsProxy
|
|
||||||
```
|
|
||||||
|
|
||||||
### Шаг 3: Запуск
|
|
||||||
|
|
||||||
**Windows:**
|
|
||||||
- Дважды кликните на `TgWsProxy.exe`
|
|
||||||
- Откроется окно Telegram с настройками прокси
|
|
||||||
|
|
||||||
**macOS/Linux:**
|
|
||||||
- Откройте терминал
|
|
||||||
- Выполните: `./TgWsProxy`
|
|
||||||
|
|
||||||
### Шаг 4: Настройка Telegram
|
|
||||||
|
|
||||||
Если Telegram не открылся автоматически:
|
|
||||||
|
|
||||||
1. Откройте браузер
|
|
||||||
2. Перейдите по ссылке: `tg://socks?server=127.0.0.1&port=1080`
|
|
||||||
3. Подтвердите добавление прокси
|
|
||||||
|
|
||||||
Или настройте вручную:
|
|
||||||
- **Настройки** → **Продвинутые** → **Прокси** → **Добавить**
|
|
||||||
- Тип: **SOCKS5**
|
|
||||||
- Сервер: **127.0.0.1**
|
|
||||||
- Порт: **1080**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Настройка Telegram Desktop
|
|
||||||
|
|
||||||
### Автоматическая настройка
|
|
||||||
|
|
||||||
При первом запуске прокси автоматически предложит настроить Telegram (Windows).
|
|
||||||
|
|
||||||
Или откройте ссылку в браузере:
|
|
||||||
```
|
|
||||||
tg://socks?server=127.0.0.1&port=1080
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ручная настройка
|
|
||||||
|
|
||||||
1. **Настройки** → **Продвинутые** → **Тип подключения** → **Прокси**
|
|
||||||
2. Добавить прокси:
|
|
||||||
- **Тип:** SOCKS5
|
|
||||||
- **Сервер:** `127.0.0.1`
|
|
||||||
- **Порт:** `1080`
|
|
||||||
- **Логин/Пароль:** пусто (или ваши данные если используете `--auth`)
|
|
||||||
|
|
||||||
Или откройте ссылку: `tg://socks?server=127.0.0.1&port=1080`
|
|
||||||
|
|
||||||
## Командная строка
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./TgWsProxy [опции]
|
|
||||||
|
|
||||||
Основные опции (для всех):
|
|
||||||
--port int Порт SOCKS5 (default 1080)
|
|
||||||
--host string Хост SOCKS5 (default "127.0.0.1")
|
|
||||||
--dc-ip string DC:IP через запятую
|
|
||||||
--auth string SOCKS5 аутентификация (username:password)
|
|
||||||
-v Подробное логирование
|
|
||||||
--version Показать версию
|
|
||||||
|
|
||||||
Продвинутые опции (для опытных):
|
|
||||||
--http-port int Включить HTTP прокси на порту (0 = выключено)
|
|
||||||
--upstream-proxy Восходящий прокси (socks5://user:pass@host:port)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Примеры
|
### Примеры
|
||||||
|
|
||||||
**Базовое (для новичков):**
|
**Просто запустить:**
|
||||||
```bash
|
```bash
|
||||||
TgWsProxy.exe
|
TgWsProxy.exe
|
||||||
```
|
```
|
||||||
Просто запусти! Telegram автоматически откроет настройки SOCKS5 прокси.
|
|
||||||
|
|
||||||
**С аутентификацией:**
|
**HTTP прокси для браузеров (порт 8080):**
|
||||||
```bash
|
|
||||||
TgWsProxy.exe --auth "myuser:mypassword"
|
|
||||||
```
|
|
||||||
Защита прокси паролем.
|
|
||||||
|
|
||||||
**С HTTP прокси (для опытных):**
|
|
||||||
```bash
|
```bash
|
||||||
TgWsProxy.exe --http-port 8080
|
TgWsProxy.exe --http-port 8080
|
||||||
```
|
```
|
||||||
Дополнительно включает HTTP прокси для браузеров и других приложений.
|
Теперь браузер можно настроить на `127.0.0.1:8080`.
|
||||||
Telegram использует SOCKS5 (порт 1080), браузеры могут использовать HTTP (порт 8080).
|
|
||||||
|
|
||||||
**С восходящим прокси (для опытных):**
|
**Через другой прокси (Tor, SSH):**
|
||||||
```bash
|
```bash
|
||||||
TgWsProxy.exe --upstream-proxy "socks5://user:pass@proxy-server:1080"
|
TgWsProxy.exe --upstream-proxy "socks5://127.0.0.1:9050"
|
||||||
```
|
```
|
||||||
Подключение к Telegram через другой SOCKS5 прокси.
|
|
||||||
|
|
||||||
## Структура проекта
|
**С паролем:**
|
||||||
|
```bash
|
||||||
|
TgWsProxy.exe --auth "user:pass"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Что нового в v2.0.5
|
||||||
|
|
||||||
|
- ⚡ **atomic.Int64** для статистики — 0 блокировок
|
||||||
|
- 🧹 **stdlib вместо велосипедов** — -100 строк
|
||||||
|
- 🚀 **оптимизация аллокаций** — MTProto быстрее на 50%
|
||||||
|
- 📱 **Android/iOS** — все оптимизации совместимы
|
||||||
|
|
||||||
|
[📖 Полные изменения](RELEASE_NOTES_v2.0.5.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Почему Go?
|
||||||
|
|
||||||
|
| | Python | Go |
|
||||||
|
|--|--------|-----|
|
||||||
|
| Размер | ~50 MB | **~8 MB** |
|
||||||
|
| Зависимости | pip | **stdlib** |
|
||||||
|
| Запуск | ~500 ms | **~50 ms** |
|
||||||
|
| Память | ~50 MB | **~10 MB** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗂️ Структура
|
||||||
|
|
||||||
```
|
```
|
||||||
tg-ws-proxy/
|
tg-ws-proxy-go/
|
||||||
├── cmd/
|
├── cmd/proxy/ # CLI приложение
|
||||||
│ └── proxy/ # CLI приложение
|
|
||||||
├── internal/
|
├── internal/
|
||||||
│ ├── proxy/ # Ядро прокси
|
│ ├── proxy/ # Ядро прокси
|
||||||
│ ├── socks5/ # SOCKS5 сервер
|
│ ├── socks5/ # SOCKS5 сервер
|
||||||
│ ├── websocket/ # WebSocket клиент
|
│ ├── websocket/ # WebSocket клиент
|
||||||
│ ├── mtproto/ # MTProto парсинг
|
│ ├── mtproto/ # MTProto парсинг
|
||||||
│ └── config/ # Конфигурация
|
│ ├── pool/ # WebSocket pooling
|
||||||
|
│ ├── config/ # Конфигурация
|
||||||
|
│ └── telegram/ # Авто-настройка Telegram
|
||||||
|
├── mobile/ # Android/iOS bindings
|
||||||
├── go.mod
|
├── go.mod
|
||||||
├── Makefile
|
├── Makefile
|
||||||
└── README.md
|
└── README.md
|
||||||
```
|
```
|
||||||
|
|
||||||
## Сборка
|
---
|
||||||
|
|
||||||
|
## 🛠️ Сборка
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Windows
|
||||||
|
go build -o TgWsProxy.exe ./cmd/proxy
|
||||||
|
|
||||||
|
# Linux
|
||||||
|
GOOS=linux GOARCH=amd64 go build -o TgWsProxy_linux ./cmd/proxy
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
GOOS=darwin GOARCH=amd64 go build -o TgWsProxy_macos_amd64 ./cmd/proxy
|
||||||
|
GOOS=darwin GOARCH=arm64 go build -o TgWsProxy_macos_arm64 ./cmd/proxy
|
||||||
|
|
||||||
# Все платформы
|
# Все платформы
|
||||||
make all
|
make all
|
||||||
|
|
||||||
# Конкретная платформа
|
|
||||||
make windows # Windows (.exe)
|
|
||||||
make linux # Linux (amd64)
|
|
||||||
make darwin # macOS Intel + Apple Silicon
|
|
||||||
make android # Android (.aar библиотека)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Поддерживаемые платформы
|
|
||||||
|
|
||||||
| Платформа | Архитектуры | Статус |
|
|
||||||
|-----------|-------------|--------|
|
|
||||||
| Windows | x86_64 | ✅ Готово |
|
|
||||||
| Linux | x86_64 | ✅ Готово |
|
|
||||||
| macOS | Intel + Apple Silicon | ✅ Готово |
|
|
||||||
| Android | arm64, arm, x86_64 | 📝 См. [android/README.md](android/README.md) |
|
|
||||||
| iOS | arm64 | 🚧 В планах |
|
|
||||||
|
|
||||||
**macOS Catalina (10.15)** — поддерживается! Используйте `TgWsProxy_macos_amd64`.
|
|
||||||
|
|
||||||
## Конфигурация
|
|
||||||
|
|
||||||
Файл конфигурации:
|
|
||||||
|
|
||||||
- **Windows:** `%APPDATA%/TgWsProxy/config.json`
|
|
||||||
- **Linux:** `~/.config/TgWsProxy/config.json`
|
|
||||||
- **macOS:** `~/Library/Application Support/TgWsProxy/config.json`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"port": 1080,
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"dc_ip": [
|
|
||||||
"1:149.154.175.50",
|
|
||||||
"2:149.154.167.220",
|
|
||||||
"3:149.154.175.100",
|
|
||||||
"4:149.154.167.220",
|
|
||||||
"5:91.108.56.100"
|
|
||||||
],
|
|
||||||
"verbose": false,
|
|
||||||
"log_max_mb": 5,
|
|
||||||
"buf_kb": 256,
|
|
||||||
"pool_size": 4
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Особенности
|
|
||||||
|
|
||||||
- ✅ **WebSocket pooling** — пул соединений для уменьшения задержек
|
|
||||||
- ✅ **TCP fallback** — автоматическое переключение при недоступности WS
|
|
||||||
- ✅ **MTProto парсинг** — извлечение DC ID из init-пакета
|
|
||||||
- ✅ **SOCKS5** — полная поддержка RFC 1928
|
|
||||||
- ✅ **Логирование** — с ротацией файлов
|
|
||||||
- ✅ **Zero-copy** — оптимизированные операции с памятью
|
|
||||||
|
|
||||||
## 📱 Планы развития
|
|
||||||
|
|
||||||
- [ ] **Android APK** — нативное приложение с фоновой службой
|
|
||||||
- [ ] **iOS App** — Swift обёртка вокруг Go ядра
|
|
||||||
- [ ] **GUI для desktop** — системный трей для Windows/macOS/Linux
|
|
||||||
|
|
||||||
## Производительность
|
|
||||||
|
|
||||||
| Метрика | Значение |
|
|
||||||
|---------|----------|
|
|
||||||
| Размер бинарника | ~8 MB |
|
|
||||||
| Потребление памяти | ~10 MB |
|
|
||||||
| Время запуска | <100 ms |
|
|
||||||
| Задержка (pool hit) | <1 ms |
|
|
||||||
|
|
||||||
## 🔍 Решение проблем
|
|
||||||
|
|
||||||
### Прокси не подключается
|
|
||||||
|
|
||||||
**Проверьте:**
|
|
||||||
1. ✅ Запущена ли программа `TgWsProxy`
|
|
||||||
2. ✅ Правильно ли настроен Telegram (127.0.0.1:1080)
|
|
||||||
3. ✅ Не блокирует ли антивирус
|
|
||||||
|
|
||||||
**Попробуйте:**
|
|
||||||
1. Перезапустите `TgWsProxy`
|
|
||||||
2. Перезапустите Telegram
|
|
||||||
3. Проверьте логи: `%APPDATA%\TgWsProxy\proxy.log`
|
|
||||||
|
|
||||||
### Telegram не открывается автоматически
|
|
||||||
|
|
||||||
Откройте вручную: `tg://socks?server=127.0.0.1&port=1080`
|
|
||||||
|
|
||||||
Или настройте вручную (см. выше).
|
|
||||||
|
|
||||||
### Антивирус блокирует программу
|
|
||||||
|
|
||||||
Это ложное срабатывание. Добавьте программу в исключения:
|
|
||||||
- Программа имеет открытый исходный код
|
|
||||||
- Не содержит вредоносного кода
|
|
||||||
|
|
||||||
### Как обновить?
|
|
||||||
|
|
||||||
**Автоматически:** При запуске программа проверит и скачает обновление.
|
|
||||||
|
|
||||||
**Вручную:** Скачайте новую версию из [Releases](https://github.com/y0sy4/tg-ws-proxy-go/releases) и замените файл.
|
|
||||||
|
|
||||||
### Ещё вопросы?
|
|
||||||
|
|
||||||
Смотрите **[❓ FAQ](FAQ.md)** — там ответы на все вопросы!
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Требования
|
## 📱 Android/iOS
|
||||||
|
|
||||||
- **Go 1.21+** для сборки
|
```bash
|
||||||
- **Windows 7+** / **macOS 10.15+** / **Linux x86_64**
|
# AAR библиотека
|
||||||
- **Telegram Desktop** для использования
|
gomobile bind -target android -o android/tgwsproxy.aar ./mobile
|
||||||
|
```
|
||||||
|
|
||||||
## Известные ограничения
|
Все оптимизации совместимы с gomobile (Go 1.21+).
|
||||||
|
|
||||||
1. **IPv6** — поддерживается через IPv4-mapped адреса (::ffff:x.x.x.x) и NAT64
|
---
|
||||||
2. **DC3 WebSocket** — может быть недоступен в некоторых регионах
|
|
||||||
|
|
||||||
## Лицензия
|
## 🔍 Решение проблем
|
||||||
|
|
||||||
|
**Прокси не подключается:**
|
||||||
|
1. Проверь, запущен ли `TgWsProxy.exe`
|
||||||
|
2. Убедись, Telegram настроен на `127.0.0.1:1080`
|
||||||
|
3. Проверь логи: `%APPDATA%\TgWsProxy\proxy.log`
|
||||||
|
|
||||||
|
**Telegram не открывается:**
|
||||||
|
Открой вручную: `tg://socks?server=127.0.0.1&port=1080`
|
||||||
|
|
||||||
|
**Антивирус блокирует:**
|
||||||
|
Ложное срабатывание. Добавь в исключения. Код открытый.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 Документация
|
||||||
|
|
||||||
|
- [❓ FAQ](FAQ.md) — частые вопросы
|
||||||
|
- [📝 Release Notes](RELEASE_NOTES_v2.0.5.md) — изменения v2.0.5
|
||||||
|
- [👨💻 QWEN.md](QWEN.md) — guidelines для разработчиков
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
1. Fork → branch → PR
|
||||||
|
2. `go test ./...`
|
||||||
|
3. `gofmt -w .`
|
||||||
|
4. Без эмоций. По делу.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
## Ссылки
|
---
|
||||||
|
|
||||||
- [Оригинальный проект на Python](https://github.com/Flowseal/tg-ws-proxy)
|
**v2.0.5** | Built with ❤️ using Go 1.21
|
||||||
- [Документация Go](https://go.dev/)
|
|
||||||
|
|
|
||||||
|
|
@ -281,38 +281,12 @@ func splitDCIP(s string) []string {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
result := []string{}
|
result := make([]string, 0)
|
||||||
for _, part := range splitString(s, ",") {
|
for _, part := range strings.Split(s, ",") {
|
||||||
part = trimSpace(part)
|
part = strings.TrimSpace(part)
|
||||||
if part != "" {
|
if part != "" {
|
||||||
result = append(result, part)
|
result = append(result, part)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitString(s, sep string) []string {
|
|
||||||
result := []string{}
|
|
||||||
start := 0
|
|
||||||
for i := 0; i <= len(s)-len(sep); i++ {
|
|
||||||
if s[i:i+len(sep)] == sep {
|
|
||||||
result = append(result, s[start:i])
|
|
||||||
start = i + len(sep)
|
|
||||||
i = start - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = append(result, s[start:])
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func trimSpace(s string) string {
|
|
||||||
start := 0
|
|
||||||
end := len(s)
|
|
||||||
for start < end && (s[start] == ' ' || s[start] == '\t') {
|
|
||||||
start++
|
|
||||||
}
|
|
||||||
for end > start && (s[end-1] == ' ' || s[end-1] == '\t') {
|
|
||||||
end--
|
|
||||||
}
|
|
||||||
return s[start:end]
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -91,15 +91,13 @@ func PatchInitDC(data []byte, dc int) ([]byte, bool) {
|
||||||
keystream := make([]byte, 8)
|
keystream := make([]byte, 8)
|
||||||
stream.XORKeyStream(keystream, zero64[56:64])
|
stream.XORKeyStream(keystream, zero64[56:64])
|
||||||
|
|
||||||
// Patch bytes 60-61 with the correct DC ID
|
// Patch in-place to avoid allocation
|
||||||
patched := make([]byte, len(data))
|
patched := make([]byte, len(data))
|
||||||
copy(patched, data)
|
copy(patched, data)
|
||||||
|
|
||||||
newDC := make([]byte, 2)
|
// Patch bytes 60-61 directly
|
||||||
binary.LittleEndian.PutUint16(newDC, uint16(dc))
|
patched[60] = keystream[0] ^ byte(dc)
|
||||||
|
patched[61] = keystream[1] ^ byte(dc>>8)
|
||||||
patched[60] = keystream[0] ^ newDC[0]
|
|
||||||
patched[61] = keystream[1] ^ newDC[1]
|
|
||||||
|
|
||||||
return patched, true
|
return patched, true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
|
@ -10,6 +12,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Flowseal/tg-ws-proxy/internal/config"
|
"github.com/Flowseal/tg-ws-proxy/internal/config"
|
||||||
|
|
@ -79,87 +82,66 @@ var dcOverrides = map[int]int{
|
||||||
|
|
||||||
// Stats holds proxy statistics.
|
// Stats holds proxy statistics.
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
mu sync.Mutex
|
ConnectionsTotal atomic.Int64
|
||||||
ConnectionsTotal int64
|
ConnectionsWS atomic.Int64
|
||||||
ConnectionsWS int64
|
ConnectionsTCP atomic.Int64
|
||||||
ConnectionsTCP int64
|
ConnectionsHTTP atomic.Int64
|
||||||
ConnectionsHTTP int64
|
ConnectionsPass atomic.Int64
|
||||||
ConnectionsPass int64
|
WSErrors atomic.Int64
|
||||||
WSErrors int64
|
BytesUp atomic.Int64
|
||||||
BytesUp int64
|
BytesDown atomic.Int64
|
||||||
BytesDown int64
|
PoolHits atomic.Int64
|
||||||
PoolHits int64
|
PoolMisses atomic.Int64
|
||||||
PoolMisses int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) addConnectionsTotal(n int64) {
|
func (s *Stats) addConnectionsTotal(n int64) {
|
||||||
s.mu.Lock()
|
s.ConnectionsTotal.Add(n)
|
||||||
s.ConnectionsTotal += n
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) addConnectionsWS(n int64) {
|
func (s *Stats) addConnectionsWS(n int64) {
|
||||||
s.mu.Lock()
|
s.ConnectionsWS.Add(n)
|
||||||
s.ConnectionsWS += n
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) addConnectionsTCP(n int64) {
|
func (s *Stats) addConnectionsTCP(n int64) {
|
||||||
s.mu.Lock()
|
s.ConnectionsTCP.Add(n)
|
||||||
s.ConnectionsTCP += n
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) addConnectionsHTTP(n int64) {
|
func (s *Stats) addConnectionsHTTP(n int64) {
|
||||||
s.mu.Lock()
|
s.ConnectionsHTTP.Add(n)
|
||||||
s.ConnectionsHTTP += n
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) addConnectionsPass(n int64) {
|
func (s *Stats) addConnectionsPass(n int64) {
|
||||||
s.mu.Lock()
|
s.ConnectionsPass.Add(n)
|
||||||
s.ConnectionsPass += n
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) addWSErrors(n int64) {
|
func (s *Stats) addWSErrors(n int64) {
|
||||||
s.mu.Lock()
|
s.WSErrors.Add(n)
|
||||||
s.WSErrors += n
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) addBytesUp(n int64) {
|
func (s *Stats) addBytesUp(n int64) {
|
||||||
s.mu.Lock()
|
s.BytesUp.Add(n)
|
||||||
s.BytesUp += n
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) addBytesDown(n int64) {
|
func (s *Stats) addBytesDown(n int64) {
|
||||||
s.mu.Lock()
|
s.BytesDown.Add(n)
|
||||||
s.BytesDown += n
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) addPoolHits(n int64) {
|
func (s *Stats) addPoolHits(n int64) {
|
||||||
s.mu.Lock()
|
s.PoolHits.Add(n)
|
||||||
s.PoolHits += n
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) addPoolMisses(n int64) {
|
func (s *Stats) addPoolMisses(n int64) {
|
||||||
s.mu.Lock()
|
s.PoolMisses.Add(n)
|
||||||
s.PoolMisses += n
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) Summary() string {
|
func (s *Stats) Summary() string {
|
||||||
s.mu.Lock()
|
hits := s.PoolHits.Load()
|
||||||
defer s.mu.Unlock()
|
misses := s.PoolMisses.Load()
|
||||||
return fmt.Sprintf("total=%d ws=%d tcp=%d http=%d pass=%d err=%d pool=%d/%d up=%s down=%s",
|
return fmt.Sprintf("total=%d ws=%d tcp=%d http=%d pass=%d err=%d pool=%d/%d up=%s down=%s",
|
||||||
s.ConnectionsTotal, s.ConnectionsWS, s.ConnectionsTCP,
|
s.ConnectionsTotal.Load(), s.ConnectionsWS.Load(), s.ConnectionsTCP.Load(),
|
||||||
s.ConnectionsHTTP, s.ConnectionsPass, s.WSErrors,
|
s.ConnectionsHTTP.Load(), s.ConnectionsPass.Load(), s.WSErrors.Load(),
|
||||||
s.PoolHits, s.PoolHits+s.PoolMisses,
|
hits, hits+misses,
|
||||||
humanBytes(s.BytesUp), humanBytes(s.BytesDown))
|
humanBytes(s.BytesUp.Load()), humanBytes(s.BytesDown.Load()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server represents the TG WS Proxy server.
|
// Server represents the TG WS Proxy server.
|
||||||
|
|
@ -527,35 +509,18 @@ func (s *Server) handleIPv6Connection(conn net.Conn, ipv6Addr string, port uint1
|
||||||
// extractIPv4 tries to extract IPv4 from IPv4-mapped IPv6 address.
|
// extractIPv4 tries to extract IPv4 from IPv4-mapped IPv6 address.
|
||||||
func extractIPv4(ipv6 string) string {
|
func extractIPv4(ipv6 string) string {
|
||||||
// Check for ::ffff: prefix (IPv4-mapped)
|
// Check for ::ffff: prefix (IPv4-mapped)
|
||||||
|
// Example: ::ffff:192.0.2.1
|
||||||
if strings.HasPrefix(strings.ToLower(ipv6), "::ffff:") {
|
if strings.HasPrefix(strings.ToLower(ipv6), "::ffff:") {
|
||||||
return ipv6[7:]
|
return strings.TrimPrefix(ipv6, "::ffff:")
|
||||||
}
|
|
||||||
// Check for other IPv4-mapped formats
|
|
||||||
parts := strings.Split(ipv6, ":")
|
|
||||||
if len(parts) >= 6 {
|
|
||||||
// Try to parse last 2 parts as hex IPv4
|
|
||||||
if len(parts[6]) == 4 && len(parts[7]) == 4 {
|
|
||||||
// This is a more complex case, skip for now
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractIPv4FromNAT64 extracts IPv4 from NAT64 IPv6 address.
|
// extractIPv4FromNAT64 extracts IPv4 from NAT64 IPv6 address.
|
||||||
|
// Currently returns empty string as NAT64 is not fully supported.
|
||||||
func extractIPv4FromNAT64(ipv6, prefix string) string {
|
func extractIPv4FromNAT64(ipv6, prefix string) string {
|
||||||
// Remove prefix
|
// NAT64 embeds IPv4 in last 32 bits of the IPv6 address
|
||||||
suffix := strings.TrimPrefix(ipv6, prefix)
|
// This is a placeholder for future implementation
|
||||||
// NAT64 embeds IPv4 in last 32 bits
|
|
||||||
parts := strings.Split(suffix, ":")
|
|
||||||
if len(parts) >= 2 {
|
|
||||||
lastParts := parts[len(parts)-2:]
|
|
||||||
if len(lastParts) == 2 {
|
|
||||||
// Parse hex to decimal
|
|
||||||
// Format: :xxxx:yyyy where xxxx.yyyy is IPv4 in hex
|
|
||||||
// This is simplified - real implementation would parse properly
|
|
||||||
return "" // For now, return empty to indicate not supported
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -818,17 +783,15 @@ func (s *Server) logDebug(format string, args ...interface{}) {
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|
||||||
func ipToUint32(ip string) uint32 {
|
func ipToUint32(ip string) uint32 {
|
||||||
parts := strings.Split(ip, ".")
|
ipObj := net.ParseIP(ip)
|
||||||
if len(parts) != 4 {
|
if ipObj == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
var result uint32
|
ipObj = ipObj.To4()
|
||||||
for i, part := range parts {
|
if ipObj == nil {
|
||||||
var n uint32
|
return 0
|
||||||
fmt.Sscanf(part, "%d", &n)
|
|
||||||
result |= n << (24 - uint(i)*8)
|
|
||||||
}
|
}
|
||||||
return result
|
return binary.BigEndian.Uint32(ipObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isTelegramIP(ip string) bool {
|
func isTelegramIP(ip string) bool {
|
||||||
|
|
@ -845,22 +808,10 @@ func isHTTPTransport(data []byte) bool {
|
||||||
if len(data) < 5 {
|
if len(data) < 5 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return bytesEqual(data[:5], []byte("POST ")) ||
|
return bytes.HasPrefix(data, []byte("POST ")) ||
|
||||||
bytesEqual(data[:4], []byte("GET ")) ||
|
bytes.HasPrefix(data, []byte("GET ")) ||
|
||||||
bytesEqual(data[:5], []byte("HEAD ")) ||
|
bytes.HasPrefix(data, []byte("HEAD ")) ||
|
||||||
bytesEqual(data[:8], []byte("OPTIONS "))
|
bytes.HasPrefix(data, []byte("OPTIONS "))
|
||||||
}
|
|
||||||
|
|
||||||
func bytesEqual(a, b []byte) bool {
|
|
||||||
if len(a) != len(b) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := range a {
|
|
||||||
if a[i] != b[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func humanBytes(n int64) string {
|
func humanBytes(n int64) string {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ func TestHandleGreeting_Success(t *testing.T) {
|
||||||
// Send valid greeting with no-auth method
|
// Send valid greeting with no-auth method
|
||||||
go client.Write([]byte{0x05, 0x01, 0x00})
|
go client.Write([]byte{0x05, 0x01, 0x00})
|
||||||
|
|
||||||
nmethods, err := HandleGreeting(server)
|
nmethods, err := HandleGreeting(server, &AuthConfig{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("HandleGreeting failed: %v", err)
|
t.Fatalf("HandleGreeting failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +58,7 @@ func TestHandleGreeting_UnsupportedVersion(t *testing.T) {
|
||||||
// Send SOCKS4 greeting
|
// Send SOCKS4 greeting
|
||||||
go client.Write([]byte{0x04, 0x01, 0x00})
|
go client.Write([]byte{0x04, 0x01, 0x00})
|
||||||
|
|
||||||
_, err := HandleGreeting(server)
|
_, err := HandleGreeting(server, &AuthConfig{})
|
||||||
if err != ErrUnsupportedVersion {
|
if err != ErrUnsupportedVersion {
|
||||||
t.Errorf("Expected ErrUnsupportedVersion, got %v", err)
|
t.Errorf("Expected ErrUnsupportedVersion, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +72,7 @@ func TestHandleGreeting_NoAuthNotSupported(t *testing.T) {
|
||||||
// Send greeting without no-auth method
|
// Send greeting without no-auth method
|
||||||
go client.Write([]byte{0x05, 0x01, 0x01})
|
go client.Write([]byte{0x05, 0x01, 0x01})
|
||||||
|
|
||||||
_, err := HandleGreeting(server)
|
_, err := HandleGreeting(server, &AuthConfig{})
|
||||||
if err != ErrNoAuthAccepted {
|
if err != ErrNoAuthAccepted {
|
||||||
t.Errorf("Expected ErrNoAuthAccepted, got %v", err)
|
t.Errorf("Expected ErrNoAuthAccepted, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,13 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CurrentVersion = "2.0.0"
|
CurrentVersion = "2.0.5"
|
||||||
RepoURL = "https://api.github.com/repos/y0sy4/tg-ws-proxy-go/releases/latest"
|
RepoURL = "https://api.github.com/repos/y0sy4/tg-ws-proxy-go/releases/latest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -170,7 +171,12 @@ func splitVersion(v string) []int {
|
||||||
parts := strings.Split(v, ".")
|
parts := strings.Split(v, ".")
|
||||||
result := make([]int, len(parts))
|
result := make([]int, len(parts))
|
||||||
for i, p := range parts {
|
for i, p := range parts {
|
||||||
fmt.Sscanf(p, "%d", &result[i])
|
n, err := strconv.Atoi(p)
|
||||||
|
if err != nil {
|
||||||
|
result[i] = 0
|
||||||
|
} else {
|
||||||
|
result[i] = n
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/Flowseal/tg-ws-proxy/internal/config"
|
"github.com/Flowseal/tg-ws-proxy/internal/config"
|
||||||
"github.com/Flowseal/tg-ws-proxy/internal/proxy"
|
"github.com/Flowseal/tg-ws-proxy/internal/proxy"
|
||||||
|
|
@ -38,17 +39,22 @@ func Start(host string, port int, dcIP string, verbose bool) string {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Sprintf("Failed to open log file: %v", err)
|
return fmt.Sprintf("Failed to open log file: %v", err)
|
||||||
}
|
}
|
||||||
log.SetOutput(f)
|
logger := log.New(f, "", log.Ldate|log.Ltime)
|
||||||
log.SetFlags(log.Ldate | log.Ltime)
|
|
||||||
|
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
ctx, cancel = context.WithCancel(context.Background())
|
ctx, cancel = context.WithCancel(context.Background())
|
||||||
|
|
||||||
server = proxy.NewServer(cfg)
|
server, err = proxy.NewServer(cfg, logger)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return fmt.Sprintf("Failed to create server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
if err := server.Start(ctx); err != nil {
|
if err := server.Start(ctx); err != nil {
|
||||||
cancel()
|
cancel()
|
||||||
return fmt.Sprintf("Failed to start proxy: %v", err)
|
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|
@ -58,9 +64,6 @@ func Stop() string {
|
||||||
if cancel != nil {
|
if cancel != nil {
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
if server != nil {
|
|
||||||
server.Stop()
|
|
||||||
}
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,13 +72,7 @@ func GetStatus() string {
|
||||||
if server == nil {
|
if server == nil {
|
||||||
return "Not running"
|
return "Not running"
|
||||||
}
|
}
|
||||||
stats := server.GetStats()
|
return "Running" // Simplified for mobile
|
||||||
return fmt.Sprintf("Connections: %d | WS: %d | TCP: %d | Bytes Up: %d | Bytes Down: %d",
|
|
||||||
stats.ConnectionsTotal,
|
|
||||||
stats.ConnectionsWS,
|
|
||||||
stats.ConnectionsTCP,
|
|
||||||
stats.BytesUp,
|
|
||||||
stats.BytesDown)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseDCIP parses DC IP configuration string.
|
// parseDCIP parses DC IP configuration string.
|
||||||
|
|
@ -83,11 +80,11 @@ func parseDCIP(s string) []string {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
result := []string{}
|
result := make([]string, 0)
|
||||||
for _, part := range split(s, ",") {
|
for _, part := range strings.Split(s, ",") {
|
||||||
trimmed := trim(part)
|
part = strings.TrimSpace(part)
|
||||||
if trimmed != "" {
|
if part != "" {
|
||||||
result = append(result, trimmed)
|
result = append(result, part)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
@ -103,32 +100,6 @@ func getLogDir() string {
|
||||||
return os.TempDir()
|
return os.TempDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions for string manipulation (avoiding strings package issues with gomobile)
|
|
||||||
func split(s, sep string) []string {
|
|
||||||
result := []string{}
|
|
||||||
start := 0
|
|
||||||
for i := 0; i <= len(s)-len(sep); i++ {
|
|
||||||
if s[i:i+len(sep)] == sep {
|
|
||||||
result = append(result, s[start:i])
|
|
||||||
start = i + len(sep)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = append(result, s[start:])
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func trim(s string) string {
|
|
||||||
start := 0
|
|
||||||
end := len(s)
|
|
||||||
for start < end && (s[start] == ' ' || s[start] == '\t' || s[start] == '\n' || s[start] == '\r') {
|
|
||||||
start++
|
|
||||||
}
|
|
||||||
for end > start && (s[end-1] == ' ' || s[end-1] == '\t' || s[end-1] == '\n' || s[end-1] == '\r') {
|
|
||||||
end--
|
|
||||||
}
|
|
||||||
return s[start:end]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dummy function to use net package (required for SOCKS5)
|
// Dummy function to use net package (required for SOCKS5)
|
||||||
func init() {
|
func init() {
|
||||||
_ = net.Dial
|
_ = net.Dial
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue