убраны флаги -mode и -bond: остался только udp-релей для wireguard.
удалены tcpfwd, tcpfwdserver, bondclient, bondserver, bondframe, kcptun,
wire/relay; proxy/common переехал в udprelay/dial.
BREAKING CHANGE: флаги -mode и -bond удалены; конфиги и ссылки freeturn://
с tcp-режимом больше не принимаются.
Персона строится детерминированно из FingerprintSeed (Client ID) и живёт
между перезапусками; при отказе captcha сжигается, и цепочка проходится
заново новой личностью с чистыми куками.
Телеметрия captcha (internal/captcha/telemetry.go) собирает аналитику
виджета: сэмплы сенсоров, connection-поля, курсор/тапы. Жест проживается
в реальном времени, иначе тайминги массивов не сходятся.
internal/personanet - TLS-клиент персоны (JA3 + split ClientHello), общий
для VK-auth и ручной captcha; порядок заголовков задаёт browserprofile.
BREAKING CHANGE: флаг -browser убран, семейство персоны всегда Chrome -
только Chromium даёт NetworkInformation и Generic Sensors, без которых
телеметрия captcha неполна. Класс устройства по-прежнему за -platform.
Флаг -browser chrome|firefox (default firefox, Chrome - escape-hatch), client-local для VK-auth, в freeturn:// URI не идёт. browserprofile.ForKind даёт канонные UA/JA3/Accept-Language; ApplyFhttp шлёт sec-ch-ua* только для Chrome (Firefox их не отправляет). dial.go выбирает profiles.Firefox_147 / Chrome_146 под UA.
Control-plane TLS-dial переведён на MultiSplitWriteConn (фрагментация SNI).
rtpopus2 несёт RTP header extension (RFC 8285 one-byte: X=1,
ssrc-audio-level + transport-cc) - ближе к современному WebRTC, чем
rtpopus (X=0). Wire несовместим с rtpopus, overhead 52 против 40.
wire.Codec - общий контракт профиля; NewClientCodec/Listen диспатчат
по -obf-profile, RelayPacketConn обобщён по Codec. Профиль тянется
в pipeline (раньше шёл только ключ obf).
Попутно: tcpfwd на 1-based streamID (единый базис с udprelay для
CacheID); cmd/client задаёт probe-host login.vk.ru для vk.
freeturn:// теперь несёт base64url(json) вместо query-string. Поля:
v, provider, peer, transport, mode, bond, obf, key, n, spc, cid,
listen, dns, dnss, mcap, name. Версионирован полем v=1.
config.PeekSubURL: pre-parse -sub до ParseClient, URI первой ноды
подсовывается позиционным аргументом - применение общим путём.
ParseClient применяет все новые поля URI (N, StreamsPerCred,
ClientID, Listen, DNSMode, DNSServers, ManualCaptcha, transport).
VK отдаёт несколько relay-адресов, но стрим использовал ровно один
(streamID%n). Если этот IP душит DPI/RST — allocate падает без альтернативы.
Credentials несёт список адресов (primary-first по streamID), DialTURN
пробует кандидатов по очереди и берёт первый успешный allocate. Балансировка
relay-IP по стримам сохранена, остальные адреса — фоллбэк. HostOverride (-turn)
схлопывает список до одного адреса.
На Android каталог рядом с бинарём read-only, ID не сохранялся и
ротировался на каждый запуск, ломая allowlist. Пробуем каталог бинаря,
UserConfigDir и TempDir по порядку.
Клиент всегда отправляет Client ID первой DTLS app-record, сервер
всегда читает. Флаг -auth и URI-параметр auth удалены — рассинхрон
client/server невозможен. -clients-file на сервере включает только
проверку ID по allowlist clients.json.
Также:
- clients.json и client_config.json пишутся с правами 0o600
- исправлены все находки golangci-lint (errcheck, gosec, govet shadow)
Примечание: удалён флаг -auth и URI-параметр auth; Client ID теперь
всегда в первой записи туннеля — старые клиенты/серверы несовместимы.
- internal/clientsdb: БД клиентов с hot-reload + length-prefixed ID wire
- internal/uri: parse/encode share-ссылок freeturn://
- internal/sub: загрузка и парсинг подписок (sub.md)
- client: флаги -auth, -client-id, -sub; позиционный URI; ID через crypto/rand
- server: подкоманда clients (add/remove/list), чтение ID под -clients-file
- запись/чтение ID учитывают DTLS record-orientation (буфер на запись целиком)
- docs: uri.md, sub.md; правки flags/deploy/quickstart
- снят UTF-8 BOM по всем исходникам
ParseClient/ParseServer возвращают flag.ErrHelp после печати usage, но main
трактовал это как фатальную ошибку: log.Fatalf печатал лишнюю строку
"flag: help requested" и выходил с кодом 1. Ловим flag.ErrHelp и выходим с 0
в обоих бинарях.
Сейчас нужен только VK-провайдер. internal/provider/static и связанные флаги
-static-user/-static-pass/-static-addr удалены, валидация и тесты обновлены.
Интерфейс provider.Provider остаётся — слот для будущих реализаций сохранён в
нейтральных формулировках doc-комментариев.
Переносит VK-specific пакеты под internal/provider/vk/internal/* — теперь
недоступны для импорта извне provider/vk (Go internal/ rule). ish остался в
internal/client/ish — не VK-specific.
vk.SetCaptchaLoggers удалён: логгеры captcha/manual подключаются внутри vk.New,
debug-флаг передаётся через новое поле Config.Debug. cmd/client/main.go больше
не импортирует captcha-пакеты.
Документация обновлена.
Вытаскивает источник TURN-creds из захардкоженной привязки к VK в интерфейс
internal/provider.Provider. Pipeline (proxy/udprelay, proxy/tcpfwd) работает
только через интерфейс — без VK-specific импортов и sentinel'ов.
Реализации:
- internal/provider/vk: фасад над client/vkauth (требует -link)
- internal/provider/static: фиксированные creds для self-hosted coturn /
metered / любого RFC 5766/8656 TURN (флаги -static-user/-static-pass/
-static-addr)
Изменения интерфейса:
- common.GetCredsFunc дропнула link (provider держит сам)
- udprelay AuthHandler: LockoutUntilUnix → BackoffUntilUnix
- vkauth sentinels обёрнуты в provider.ErrBackoffActive / ErrFatalNoStreams
через errors.Join (backward-compat сохранён)
Документация: docs/providers.md (новый), docs/glossary.md и docs/flags.md
обновлены.
- bondserver: errors.Is для проверки smux.ErrGoAway (обёрнутые ошибки)
- bondserver: убрать лишнюю аллокацию в copyBackendToBond, зеркалит bondclient
- cmd/server: прерывать Accept-цикл при отмене ctx вместо warn-спама
- cmd/server: понизить лог выхода после таймаута с Error до Warn
Добавляет dtlsdial.GenerateSelfSignedCert() как единственное место создания
self-signed сертификата. cmd/server теперь использует его вместо прямого импорта
selfsign; Dialer.Dial уже использовал ленивый путь certOnce, который внутри
зовёт ту же функцию.
dtlsdial.Dialer в VLESS-режиме теперь несёт тот же HandshakeSem (cap-3), что и
UDP-диалер, предотвращая неограниченное число конкурентных DTLS-handshake на
пути VLESS/bond.
Все N стримов теперь стартуют конкурентно. okchan оставлен только как лог-сигнал
первого успешного handshake; стримы 2..N больше не ждут завершения
DTLS-handshake стрима 1.
runCaptchaServerAndWait теперь делает select на ctx.Done вместе с keyCh и
закрывает listener в defer. Без этого, когда родительский manualCtx истекал
(3m), внутренняя горутина блокировалась на keyCh навсегда, оставляя
127.0.0.1:8765 занятым до выхода процесса. SolveViaProxy/SolveViaHTTP принимают
ctx и протягивают его; manualCaptchaSolver в cmd/client/main.go форвардит ctx
вместо того, чтобы его терять.
- logx: добавить префиксы [DEBUG]/[INFO]/[WARN]/[ERROR]; уровни были
неразличимы в выводе (всё шло через голый log.Printf).
- logx: добавить хелпер OrNop для nil-проверок на стороне конструктора.
- vkauth/{client,token}.go: заменить прямой log.Printf на c.log.Infof
(97 мест обходили унифицированный логгер после прошлого рефакторинга).
- udprelay.oneTURN: буферизовать error chan (cap 1) + select на ctx.Done в
TURNLoop — небуферизованный send блокировался навсегда, когда TURNLoop
выходил по ctx до чтения, утекали горутина + TURN-стрим.
- udprelay: перенести write-цикл conn2->relay в wg.Go (был голый go func, мог
пережить wg.Wait и читать из закрытого conn).
- udprelay.TURNLoop: заменить time.Sleep(2s) на select+ctx, чтобы shutdown не
блокировался per-stream.
- udprelay.Run / tcpfwd.Run: возвращать error вместо log.Panicf при сбое listen;
cmd/* теперь log.Fatalf на всплывшей ошибке.
- cmd/*: log.Panicf → log.Fatalf для CLI-ошибок (без stack trace на плохой флаг).
- kcptun.NewKCPOverDTLS: закрывать серверный kcp.Listener на error-путях после
SetDeadline/AcceptKCP (утекал при любом сбое).
- doc пакета udprelay: переписать устаревшее утверждение "avoid importing
vkauth" (файл импортирует vkauth ради sentinel-ошибок).
- udprelay: lifecycle-сообщения ("Established/Closed DTLS connection")
Errorf → Infof; строка "relayed-address" под DebugEnabled использует Debugf.
- tcpfwd: lifecycle-сообщения ("connected/disconnected/waiting/listening")
Errorf → Infof, "failed to wrap listener" → Warnf.
- tcpfwd.maintainSession doc: "fixed 2-3s backoff" было неверно (3s при сбое
setup, 2s после disconnect).
- pool.go doc: "bond client (in main)" → internal/proxy/bondclient.
- srtpmimicry.Unwrap: doc предупреждает, что AEAD открывает in-place внутри
wire, поэтому вызыватели должны считать wire потреблённым.
- комментарии-алиасы GetCredsFunc udprelay/tcpfwd: убрать фикцию "cmd/main
wiring stability" (всё в internal/).
Build чисто, go vet чисто, все тесты проходят.