Files
free-turn-proxy/internal/client/captcha/errors.go
T
samosvalishe 2167ba990b refactor(layout): переезд в cmd/, свернуть client/internal в internal/client
- server/main.go → cmd/vk-turn-server/main.go. Несёт остаточные правки сервера
  (drop параметра useBond, убрано поле startup-лога).
- client/internal/{browserprofile,captcha,dnsdial,namegen,vkauth} →
  internal/client/* (единая иерархия internal/; параллельное дерево
  client/internal/ убрано). Все Go import-пути обновлены.
- .github/workflows/{ci,release}.yml, Dockerfile, README.md — пути сборки
  переключены на ./cmd/vk-turn-client и ./cmd/vk-turn-server.
- README: убран устаревший пример "server -vless-bond" (флаг удалён ранее);
  добавлены рецепты сборки .so для Android arm64 + Linux arm64/amd64 под
  ручную упаковку для мобилок.
- internal/proxy/udp/udp.go, internal/config/config.go: обновлены устаревшие
  ссылки client/internal/* в doc-комментариях.
- notes/GLOSSARY.md (новый, ~20 терминов): фиксирует DTLS-как-обфускацию,
  wrap=AEAD-не-XOR, четыре значения "client", три значения "stream", пять
  ортогональных переключателей "mode", заметки по сборке libvkturn.so, iSH,
  хрупкость init globalBondRegistry и удаления флагов.

cmd/vk-turn-client/ намеренно оставлен вне этого коммита; сворачивается вместе
с извлечением ручной captcha в следующем коммите, т.к. переименование и правки
импортов manualcaptcha.* попадают в один файл.

Wire-форматы не тронуты. Поведение не изменено кроме уже закоммиченных
удалений CLI.
2026-05-19 17:20:26 +03:00

113 lines
2.9 KiB
Go

package captcha
import (
"fmt"
"log"
neturl "net/url"
)
// Error describes a VK API "captcha required" error (error_code 14) and the
// fields the solver needs to drive the challenge.
type Error struct {
ErrorCode int
ErrorMsg string
CaptchaSid string
CaptchaImg string
RedirectURI string
IsSoundCaptchaAvailable bool
SessionToken string
CaptchaTs string
CaptchaAttempt string
}
// ParseError extracts a captcha challenge from a VK API error payload. Returns
// nil if required fields are missing.
func ParseError(errData map[string]any) *Error {
codeFloat, ok := errData["error_code"].(float64)
if !ok {
log.Printf("missing error_code in captcha error data")
return nil
}
code := int(codeFloat)
redirectURI, ok := errData["redirect_uri"].(string)
if !ok {
log.Printf("missing redirect_uri in captcha error data")
return nil
}
captchaSid, ok := errData["captcha_sid"].(string)
if !ok {
if sidNum, ok2 := errData["captcha_sid"].(float64); ok2 {
captchaSid = fmt.Sprintf("%.0f", sidNum)
} else {
log.Printf("missing captcha_sid in captcha error data")
return nil
}
}
captchaImg, ok := errData["captcha_img"].(string)
if !ok {
log.Printf("missing captcha_img in captcha error data")
return nil
}
errorMsg, ok := errData["error_msg"].(string)
if !ok {
log.Printf("missing error_msg in captcha error data")
return nil
}
var sessionToken string
if redirectURI != "" {
if parsed, err := neturl.Parse(redirectURI); err == nil {
sessionToken = parsed.Query().Get("session_token")
} else {
log.Printf("failed to parse redirect_uri: %v", err)
return nil
}
}
if sessionToken == "" {
if st, stOk := errData["session_token"].(string); stOk {
sessionToken = st
}
}
isSound, ok := errData["is_sound_captcha_available"].(bool)
if !ok {
isSound = false
}
var captchaTs string
if tsFloat, ok := errData["captcha_ts"].(float64); ok {
captchaTs = fmt.Sprintf("%.0f", tsFloat)
} else if tsStr, ok := errData["captcha_ts"].(string); ok {
captchaTs = tsStr
}
var captchaAttempt string
if attFloat, ok := errData["captcha_attempt"].(float64); ok {
captchaAttempt = fmt.Sprintf("%.0f", attFloat)
} else if attStr, ok := errData["captcha_attempt"].(string); ok {
captchaAttempt = attStr
}
return &Error{
ErrorCode: code,
ErrorMsg: errorMsg,
CaptchaSid: captchaSid,
CaptchaImg: captchaImg,
RedirectURI: redirectURI,
IsSoundCaptchaAvailable: isSound,
SessionToken: sessionToken,
CaptchaTs: captchaTs,
CaptchaAttempt: captchaAttempt,
}
}
// IsCaptcha reports whether this error represents an actionable captcha
// challenge (error_code 14 with both redirect_uri and session_token present).
func (e *Error) IsCaptcha() bool {
return e.ErrorCode == 14 && e.RedirectURI != "" && e.SessionToken != ""
}