Documentation
¶
Index ¶
- Constants
- Variables
- func CaptchaTimeout(mode string) time.Duration
- func GetCreds(ctx context.Context, link string, streamID int) (string, string, []string, error)
- func ParseHashes(raw string) []string
- func ProbeDNS(ctx context.Context, cfg Config) error
- func RequestConfig(conn net.Conn, localPort, deviceID, password string) (string, error)
- func Run(ctx context.Context, cfg Config, sink Sink, ctrl *Control) error
- func RunHashCheck(ctx context.Context, cfg Config, sink Sink) error
- func RunPing(ctx context.Context, cfg Config, sink Sink) error
- func RunSession(ctx context.Context, tp *TurnParams, peer *net.UDPAddr, d *Dispatcher, ...) (bool, error)
- func SendAuth(conn net.Conn, deviceID, password string) error
- func WorkerGroup(ctx context.Context, groupID int, hashIndex int, tp *TurnParams, ...)
- type CallUnavailableError
- type Config
- type Control
- type Credentials
- type DialControlFunc
- type Dispatcher
- type Event
- type EventCaptchaRequired
- type EventConfig
- type EventHashCheck
- type EventHashFailed
- type EventLog
- type EventPing
- type EventStage
- type EventStats
- type EventVkAuthRequired
- type HashCheckStatus
- type LogLevel
- type NullLogger
- func (n *NullLogger) Debug(_ string)
- func (n *NullLogger) Debugf(_ string, _ ...interface{})
- func (n *NullLogger) Error(_ string)
- func (n *NullLogger) Errorf(_ string, _ ...interface{})
- func (n *NullLogger) Info(_ string)
- func (n *NullLogger) Infof(_ string, _ ...interface{})
- func (n *NullLogger) Trace(_ string)
- func (n *NullLogger) Tracef(_ string, _ ...interface{})
- func (n *NullLogger) Warn(_ string)
- func (n *NullLogger) Warnf(_ string, _ ...interface{})
- type NullLoggerFactory
- type ObfsConfig
- type ObfsState
- type Profile
- type SavedProfile
- type Sink
- type SinkFunc
- type Stage
- type StageStatus
- type Stats
- type StreamCredentialsCache
- type TurnCredentials
- type TurnCreds
- type TurnParams
- type VKCredentials
- type VkCaptchaError
- type WorkerSlot
Constants ¶
const ( VkAuthAnonymous = "anonymous" VkAuthAccount = "account" )
Режимы получения учётных данных VK.
Variables ¶
var CaptchaResultChan = make(chan string, 1)
CaptchaResultChan — канал для получения токена капчи из внешнего решателя (WebView)
var TurnCredsResultChan = make(chan turnCredsPayload, 1)
TurnCredsResultChan receives TURN credentials from Android stdin handler.
Functions ¶
func CaptchaTimeout ¶
CaptchaTimeout — сколько ядро ждёт ответа на EventCaptchaRequired, прежде чем считать попытку провальной и пойти дальше своей цепочкой. Режим приходит в поле Mode того же события.
Значения не дублируются, а берутся у ядра: внешний решатель обязан успеть ответить, а после обновления ядра сдвинувшийся таймаут иначе тихо превратил бы его ответы в опоздавшие — токен, которого никто не ждёт, ядро выбрасывает.
func ProbeDNS ¶
ProbeDNS настраивает резолвер по cfg.DNS и проверяет, что он отвечает: резолвит хост VK. Шаг «dns» на схеме подключения закрывает по ней супервизор (architecture.md §06). Из остальных настроек нужен только cfg.DialControl: запрос к резолверу — такой же сокет ядра, как прочие.
Резолвер настраивается здесь, а не в Run, чтобы проверка была честной: она идёт через тот же net.DefaultResolver, которым потом пользуется ядро, а не через системный. Run настраивает его ещё раз — это безвредно.
Срок ожидания задаёт ctx: сама функция ничего не ограничивает.
func RequestConfig ¶
RequestConfig запрашивает WireGuard конфиг через DTLS-соединение.
func Run ¶
Run поднимает туннель и возвращается, когда тот закончился.
Остановка — только отменой ctx; отдельного Stop нет намеренно. Остановка по запросу ошибкой не считается и даёт nil. Ошибка возвращается, если туннель не удалось поднять или он развалился сам — по ней супервизор решает, повторять ли попытку (architecture.md §07).
sink получает события из горутин воркеров, поэтому обязан быть безопасным для параллельного вызова; см. Sink. Назначается он первым делом: до этого ядро немо.
ctrl — ручка управления запущенным ядром: пауза, ответ на капчу, ответ на запрос TURN-кредов (см. Control). Её создаёт вызывающий, потому что Run возвращается только вместе с туннелем и отдать её сам не успел бы. nil допустим и означает, что управлять ядром не понадобится.
func RunHashCheck ¶
RunHashCheck проверяет хеши звонков VK по одному и возвращается. Заменяет ветку -check-hashes.
Настройки проверяются не целиком: туннель здесь не поднимается, поэтому ни адрес сервера, ни пароль не нужны — нужен только непустой список хешей. Исход каждой проверки уходит событием EventHashCheck; ошибку возвращает только сам разбор настроек.
func RunPing ¶
RunPing замеряет задержку до TURN-relay и возвращается. Заменяет ветку -ping-only, которая заканчивалась os.Exit(0) или os.Exit(1).
Хеши перебираются по очереди: годится первый, по которому замер удался. Результат уходит событием EventPing, и та же ошибка возвращается наружу — чтобы CLI мог выставить код возврата, не разбирая события.
func RunSession ¶
func SendAuth ¶
SendAuth отправляет команду авторизации, чтобы сервер мог связать соединение с устройством
func WorkerGroup ¶
func WorkerGroup( ctx context.Context, groupID int, hashIndex int, tp *TurnParams, peer *net.UDPAddr, d *Dispatcher, localPort string, getConfig bool, configCh chan<- string, workerIDs []int, pauseFlag *int32, deviceID, password string, stats *Stats, waitReady <-chan struct{}, signalReady chan<- struct{}, )
WorkerGroup: Запускает 9 потоков с одними кредами. Ротации нет — работает до смерти воркеров.
Types ¶
type CallUnavailableError ¶
type CallUnavailableError struct {
}
CallUnavailableError is a non-retryable VK error about the call/link itself: the call was ended/deleted or the join link is invalid. Retrying another client_id or solving captcha cannot fix this.
func (*CallUnavailableError) Error ¶
func (e *CallUnavailableError) Error() string
type Config ¶
type Config struct {
// Peer — адрес VPS в виде host:port.
Peer string
// Password — пароль подключения. Из него же выводится WRAP-ключ
// RTP-обфускации (см. deriveWrapKey), поэтому это полноценный секрет.
Password string
// Hashes — хеши звонков VK. Нормализуются и дедуплицируются в Normalize.
Hashes []string
// DeviceID — идентификатор устройства; сервер привязывает к нему пароль.
DeviceID string
// VkAuthMode — anonymous или account.
VkAuthMode string
// VkAnonPath — vkcalls или legacy; учитывается только при anonymous.
VkAnonPath string
// VkCredsFile — файл с готовыми TURN-кредами от аккаунта VK.
VkCredsFile string
// CaptchaMode — auto, rjs или wv.
CaptchaMode string
// TurnHost и TurnPort переопределяют адрес TURN, выданный VK. Пусто = не менять.
TurnHost string
TurnPort string
// TurnTCP — соединяться с TURN-relay по TCP вместо UDP.
// Помогает в сетях, где душат UDP.
TurnTCP bool
// NoDTLS — не поднимать DTLS поверх TURN: RTP-обфускация уже даёт
// шифрование и аутентификацию каждого пакета. Требует сервера,
// запущенного с -listen-direct.
NoDTLS bool
// ObfsMode — audio или video. Различаются payload type и объёмом набивки.
ObfsMode string
// Workers — число воркеров. Normalize приводит его к допустимому значению.
Workers int
// DNS — резолвер для доменов VK: yandex, cloudflare, google,
// doh-yandex, doh-cloudflare, doh-google, custom:IP или doh:URL.
DNS string
// Listen — локальный UDP-адрес, куда datapath отдаёт трафик.
//
// Что именно подаёт в него трафик — userspace WireGuard с SOCKS5, штатный
// wireguard.exe или свой адаптер — ядра не касается: оно только принимает
// UDP и отдаёт наружу конфиг событием EventConfig. Выбор и настройки
// datapath живут в internal/datapath (architecture.md §04).
Listen string
// PacketConn — готовый канал к datapath вместо UDP на Listen: свой
// conn.Bind у wireguard-go (datapath.Pipe, plan-e3.md, блок B). Задан —
// ядро сокета не открывает, а из Listen берёт только номер порта для
// GETCONF: сервер возвращает его в Endpoint конфига. Run закрывает
// PacketConn при любом возврате. nil — UDP на Listen, как раньше: так
// работает external, wireguard.exe — чужой процесс. Из профиля не
// читается.
PacketConn net.PacketConn
// DialControl ставится в поле Control всех собственных сокетов ядра:
// TURN, DNS, DoH и HTTPS к VK (см. netguard_hook.go). Его задаёт CLI,
// когда datapath забирает весь трафик системы, — это netguard.Guard.Control,
// который уводит сокеты мимо туннеля (architecture.md §05). Из профиля не
// читается. nil — сокеты идут по общей таблице маршрутов, как в Э1.
DialControl DialControlFunc
}
Config — настройки запуска ядра; заменяет разбор флагов в main().
Заполняется в internal/cli из профиля и аргументов командной строки. Перед использованием обязательно вызвать Normalize, затем Validate.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig возвращает настройки со значениями по умолчанию — теми же, что были у флагов ядра.
type Control ¶
type Control struct {
// contains filtered or unexported fields
}
Control — управление уже работающим ядром; заменяет команды stdin.
Апстрим читал из stdin пять команд: PAUSE, RESUME, STOP, CAPTCHA_RESULT| и TURN_CREDS|. STOP здесь нет намеренно — его роль играет отмена ctx, переданного в Run. Остальные четыре стали методами.
Control создаётся до запуска и передаётся в Run четвёртым аргументом. Так, а не возвратом из Run: Run возвращается вместе с туннелем, то есть за ручкой управления было бы поздно приходить. И не полем Config: Config — настройки, его копируют, приводят к канону и в задаче C1 будут читать из профиля, а Control — живая ручка с состоянием, общим у нескольких горутин.
Все методы безопасны для параллельного вызова и не блокируются: ни один из них не ждёт, пока ядро прочитает переданное. Вызов на nil-ручке — не падение, а ничего: Run без Control работает, просто управлять им нечем.
Один Control — один Run. Ограничение идёт не от него, а от ядра: канал капчи и креды VK живут пакетными переменными, поэтому и Run в процессе может быть только один (см. applyGlobals).
func NewControl ¶
func NewControl() *Control
NewControl возвращает ручку управления для будущего запуска.
Пауза, выставленная до Run, действует с самого старта: группа проверяет флаг прежде, чем идти за кредами.
func (*Control) Pause ¶
func (c *Control) Pause()
Pause придерживает группы воркеров, которые ещё не пошли за кредами VK.
Уже поднятый туннель пауза не рвёт и трафик через него не останавливает — в апстриме эта команда приходила от Android при уходе в doze-режим, где незачем начинать новые сессии, а старым никто не мешает. Обещать большее было бы неправдой: флаг проверяется в одном месте, перед запросом кредов (group.go).
Вызов повторной паузы ничего не меняет.
func (*Control) Resume ¶
func (c *Control) Resume()
Resume снимает паузу: придержанные группы пойдут за кредами в течение секунды (интервал проверки флага). Снятие непоставленной паузы ничего не меняет.
func (*Control) SubmitCaptcha ¶
SubmitCaptcha отдаёт ядру токен решённой капчи — ответ на EventCaptchaRequired.
Пустой токен означает «решить не удалось»: ядро засчитает попытку провальной и пойдёт дальше своей цепочкой (captchaPlan) или сдастся. Молчание тоже работает — ядро ждёт ответа с таймаутом, — но стоит минуты ожидания.
Токен, которого никто не ждёт, остаётся в канале и будет выброшен перед следующим запросом капчи. Так же вёл себя и апстрим: перед каждым ожиданием он чистит канал от несвежих ответов.
func (*Control) SubmitTurnCreds ¶
SubmitTurnCreds отдаёт ядру TURN-креды — ответ на EventVkAuthRequired.
Креды запоминаются под ссылкой на звонок и живут accountCredsLifetime, поэтому отданные заранее пригодятся и той группе, которая спросит их позже.
Неполные креды (нет ссылки, логина, пароля или адресов) читаются как отказ: ожидающая группа сразу получит ошибку вместо того, чтобы стоять до таймаута в пять минут. Апстрим различал отказ и мусор — на отказ у него была отдельная строка stdin, TURN_CREDS|error:, а мусор он молча выбрасывал; здесь такого различия нет, потому что отдаёт креды не Android, а наш же CLI.
type Credentials ¶
Credentials — учетные данные TURN
type DialControlFunc ¶
DialControlFunc — сигнатура поля Control у net.Dialer.
type Dispatcher ¶
type Dispatcher struct {
ReturnCh chan []byte
// contains filtered or unexported fields
}
func NewDispatcher ¶
func NewDispatcher(ctx context.Context, localConn net.PacketConn, stats *Stats) *Dispatcher
func NewDispatcherPendingTUN ¶
func NewDispatcherPendingTUN(ctx context.Context, stats *Stats) *Dispatcher
NewDispatcherPendingTUN — вариант для -mode rawtun: TUN-fd ещё не получен от Android на момент старта воркеров (Android поднимает TUN только ПОСЛЕ того, как сервер назначит IP/DNS/MTU через RAWCONF — см. protocol.go RequestRawConfig). Горутины readLoop/writeLoop стартуют сразу, но ждут AttachTUN() прежде чем начать реальный ввод-вывод.
func (*Dispatcher) AttachTUN ¶
func (d *Dispatcher) AttachTUN(f *os.File)
AttachTUN подключает полученный от Android TUN-fd к уже запущенному диспетчеру и снимает блокировку с readLoop/writeLoop.
func (*Dispatcher) Register ¶
func (d *Dispatcher) Register(w *WorkerSlot)
func (*Dispatcher) Shutdown ¶
func (d *Dispatcher) Shutdown()
func (*Dispatcher) Unregister ¶
func (d *Dispatcher) Unregister(slot *WorkerSlot)
type Event ¶
type Event interface {
// contains filtered or unexported methods
}
Event — событие ядра. Набор реализаций закрыт: метод-маркер неэкспортируемый, чтобы снаружи нельзя было добавить тип, о котором не знает ни один type switch.
type EventCaptchaRequired ¶
type EventCaptchaRequired struct {
// Session — номер потока, запросившего капчу.
Session int
// Mode — auto, manual или selected: чем занималось ядро до того, как сдалось.
Mode string
// RedirectURI — страница капчи VK, которую нужно открыть.
RedirectURI string
// SessionToken — токен сессии капчи; без него ответ не примут.
SessionToken string
}
EventCaptchaRequired — ядро само капчу не осилило и ждёт ответа снаружи. Заменяет маркер CAPTCHA_SOLVE.
Ответ возвращается через Control.SubmitCaptcha (задача B5). Ядро ждёт его с таймаутом, так что молчание потребителя не вешает подключение — но и подключение тогда не состоится.
type EventConfig ¶
type EventConfig struct {
WireGuard string
}
EventConfig — конфиг WireGuard, выданный сервером. Заменяет рамку, которую ядро печатало в stdout.
Текст содержит приватный ключ, поэтому в лог он не идёт: получатель — datapath, который поднимает по нему туннель (architecture.md §04).
type EventHashCheck ¶
type EventHashCheck struct {
// Index — номер хеша, начиная с единицы; Total — сколько их всего.
Index int
Total int
Hash string
Status HashCheckStatus
Msg string
}
EventHashCheck — шаг команды `tt hashes`. Заменяет пару маркеров HASH_CHECK_START и HASH_CHECK: началу проверки соответствует HashCheckRunning.
type EventHashFailed ¶
type EventHashFailed struct {
// Hash — хеш звонка, по которому не вышло. Целиком: супервизор сопоставляет
// его со своим списком. Печатать его нельзя — хеши лежат в хранилище
// секретов (задача C2).
Hash string
// Group — номер группы воркеров, начиная с единицы.
Group int
// Status — чем кончилась попытка. HashCheckDead означает, что звонок закрыт
// и с тем же хешем не поможет ни повтор, ни перезапуск; остальные статусы
// временные.
Status HashCheckStatus
// Msg — пояснение для пользователя.
Msg string
}
EventHashFailed — группа воркеров не получила креды VK по своему хешу звонка и не стартовала.
Ядро переживает это само: группа возвращается, остальные продолжают работать, и туннель держится на них. Наружу событие уходит затем, что решение «этот хеш больше не предлагать, а когда годных не останется — остановиться» принимает супервизор (architecture.md §07, задача F2). В апстриме сигнала не было вовсе: Android вылавливал отказ из строки лога.
Событие приходит по одному на группу, фильтра повторов у него нет: у каждой группы свой хеш, и терять вторую её не за что.
type EventLog ¶
EventLog — то, что в апстриме уходило в log.Printf.
Tag — префикс сообщения, который ядро и так всегда писало: "КЛИЕНТ", "СЕССИЯ #3", "КАПЧА". Он вынесен в отдельное поле, чтобы потребитель мог фильтровать по подсистеме, не разбирая текст. Подстановку тега делает задача B3.
type EventPing ¶
EventPing — итог команды `tt ping`. Заменяет маркеры PING_RESULT и PING_ERROR. При Err != nil значение RTT не определено.
type EventStage ¶
type EventStage struct {
Stage Stage
Status StageStatus
Session int
Msg string
}
EventStage — переход шага подключения.
Session — номер воркера или сессии, породившей событие; 0 означает событие клиента целиком. Поле нужно для лога: воркеров бывает до 108, и знать, чей именно DTLS не сложился, полезно.
type EventStats ¶
type EventStats struct {
// ActiveWorkers — сколько воркеров сейчас держат соединение.
ActiveWorkers int32
// BytesUp и BytesDown — нарастающий итог с момента запуска.
BytesUp int64
BytesDown int64
}
EventStats — счётчики трафика, снятые раз в несколько секунд. В апстриме они существовали только строкой в логе.
type EventVkAuthRequired ¶
type EventVkAuthRequired struct {
// Link — ссылка на звонок, для которого нужны креды.
Link string
}
EventVkAuthRequired — режиму vk-auth=account нужны TURN-креды от живого аккаунта VK. Заменяет маркер VK_AUTH_REQUIRED.
Ответ возвращается через Control.SubmitTurnCreds (задача B5).
type HashCheckStatus ¶
type HashCheckStatus string
HashCheckStatus — исход проверки одного хеша звонка. Значения повторяют строки из classifyHashCheckError.
const ( HashCheckRunning HashCheckStatus = "running" // проверка началась HashCheckOK HashCheckStatus = "ok" HashCheckCaptcha HashCheckStatus = "captcha" // VK просит капчу HashCheckDead HashCheckStatus = "dead" // звонок не найден или закрыт HashCheckLimited HashCheckStatus = "limited" // VK ограничил запросы HashCheckNetwork HashCheckStatus = "network" HashCheckError HashCheckStatus = "error" )
type LogLevel ¶
type LogLevel string
LogLevel — важность записи. Ядро различает только три уровня: подробностей достаточно в тексте сообщения, а отладочный уровень в апстриме и так перемешан с информационным.
type NullLogger ¶
type NullLogger struct{}
func (*NullLogger) Debug ¶
func (n *NullLogger) Debug(_ string)
func (*NullLogger) Debugf ¶
func (n *NullLogger) Debugf(_ string, _ ...interface{})
func (*NullLogger) Error ¶
func (n *NullLogger) Error(_ string)
func (*NullLogger) Errorf ¶
func (n *NullLogger) Errorf(_ string, _ ...interface{})
func (*NullLogger) Info ¶
func (n *NullLogger) Info(_ string)
func (*NullLogger) Infof ¶
func (n *NullLogger) Infof(_ string, _ ...interface{})
func (*NullLogger) Trace ¶
func (n *NullLogger) Trace(_ string)
func (*NullLogger) Tracef ¶
func (n *NullLogger) Tracef(_ string, _ ...interface{})
func (*NullLogger) Warn ¶
func (n *NullLogger) Warn(_ string)
func (*NullLogger) Warnf ¶
func (n *NullLogger) Warnf(_ string, _ ...interface{})
type NullLoggerFactory ¶
type NullLoggerFactory struct{}
NullLoggerFactory подавляет логи pion
func (*NullLoggerFactory) NewLogger ¶
func (n *NullLoggerFactory) NewLogger(_ string) logging.LeveledLogger
type ObfsConfig ¶
type ObfsConfig struct {
SSRC uint32 // Synchronization Source — random per session
PayloadType uint8 // RTP payload type (111 = OPUS dynamic)
PaddingMax int // Max random padding bytes appended
}
ObfsConfig holds per-session obfuscation parameters.
func NewObfsConfig ¶
func NewObfsConfig(mode string) (*ObfsConfig, error)
NewObfsConfig creates a config with random SSRC and sane defaults. mode: "audio" (OPUS-like, PT 111) or "video" (H264-like, PT 96).
type ObfsState ¶
type ObfsState struct {
// contains filtered or unexported fields
}
ObfsState tracks monotonically increasing RTP sequence number and timestamp using a 48-bit packet counter.
func NewObfsState ¶
NewObfsState creates a state with random initial seq/ts and count=0.
type Profile ¶
type Profile struct {
UserAgent string `json:"user_agent"`
SecChUa string `json:"sec_ch_ua"`
SecChUaMobile string `json:"sec_ch_ua_mobile"`
SecChUaPlatform string `json:"sec_ch_ua_platform"`
}
Profile holds consistent browser fingerprint headers for TLS+HTTP requests.
type SavedProfile ¶
type SavedProfile struct {
Profile
DeviceJSON string `json:"device_json"`
BrowserFp string `json:"browser_fp"`
}
SavedProfile is a saved real browser profile loaded from disk.
func LoadProfileFromDisk ¶
func LoadProfileFromDisk() (*SavedProfile, error)
type Sink ¶
type Sink interface{ Emit(Event) }
Sink принимает события ядра.
Emit вызывается из горутин воркеров, поэтому обязан быть безопасным для параллельного вызова. Блокироваться надолго ему нельзя: пока Emit не вернулся, воркер стоит.
type Stage ¶
type Stage string
Stage — шаг подключения. Список повторяет ConnectionStep из Android (app/.../ConnectionPipeline.kt) один в один, чтобы обе оболочки показывали пользователю одну и ту же схему.
const ( StageDNS Stage = "dns" // резолвер до VK живой StageVK Stage = "vk" // получены TURN-креды StageCaptcha Stage = "captcha" // решается Smart Captcha StageWrap Stage = "wrap" // выведен ключ RTP-обфускации StageTURN Stage = "turn" // получен relay на TURN-сервере VK StageDTLS Stage = "dtls" // рукопожатие с сервером StageWorkers Stage = "workers" // воркеры вышли на линию StageVPN Stage = "vpn" // поднят WireGuard StageSocks Stage = "socks" // поднят локальный SOCKS5 StageDone Stage = "done" // туннель работает )
type StageStatus ¶
type StageStatus string
StageStatus — что случилось с шагом.
const ( StageRunning StageStatus = "running" StageOK StageStatus = "ok" StageFailed StageStatus = "failed" )
type Stats ¶
type StreamCredentialsCache ¶
type StreamCredentialsCache struct {
// contains filtered or unexported fields
}
type TurnCredentials ¶
type TurnCreds ¶
type TurnCreds struct {
// Link — ссылка на звонок, для которого креды добыты; та же, что пришла
// в EventVkAuthRequired. Под ней ядро их и запомнит.
Link string
// Username и Password — учётные данные TURN-relay.
Username string
Password string
// URLs — адреса relay в виде turn:host:port или host:port.
URLs []string
}
TurnCreds — учётные данные TURN, добытые живым аккаунтом VK. Нужны только в режиме VkAuthAccount, где ядро само их получить не может.
type TurnParams ¶
type TurnParams struct {
Host string
Port string
Hashes []string
WrapKey []byte // Password-derived WRAP key (32 bytes), nil = disabled
ObfsMode string // "audio" or "video" — RTP masking mode
// NoDTLS: пропустить DTLS и идти RTP-obfs AEAD напрямую поверх TURN relay.
// Требует сервер, который умеет принимать прямые (без DTLS) сессии на
// отдельном порту/слушателе — см. server/main.go -listen-direct.
NoDTLS bool
// TCPTransport: соединяться с TURN-relay по TCP вместо UDP (см.
// dialTURNConn в session.go). На некоторых сетях (замечено на
// Ростелекоме) UDP до TURN душится/дропается провайдером агрессивнее,
// чем TCP на тот же relay — этот флаг обходит именно это.
TCPTransport bool
}
TurnParams — конфигурация TURN
type VKCredentials ¶
type VkCaptchaError ¶
type VkCaptchaError struct {
ErrorCode int
ErrorMsg string
CaptchaSid string
CaptchaImg string
RedirectURI string
SessionToken string
CaptchaTs string
CaptchaAttempt string
}
VkCaptchaError represents a VK captcha challenge
func (*VkCaptchaError) Error ¶
func (e *VkCaptchaError) Error() string