Skip to content

feat(dns): DNS-over-HTTPS resolver for mobile networks#151

Open
samosvalishe wants to merge 1 commit intocacggghp:mainfrom
samosvalishe:feat/doh
Open

feat(dns): DNS-over-HTTPS resolver for mobile networks#151
samosvalishe wants to merge 1 commit intocacggghp:mainfrom
samosvalishe:feat/doh

Conversation

@samosvalishe
Copy link
Copy Markdown
Contributor

На мобильном трафике некоторых операторов клиент падает ещё до
стадии TURN-а - на этапе получения VK-credentials (#144):

  • Логин/аутентификация к login.vk.ru, api.vk.ru,
    calls.okcdn.ru не проходит.
  • В отдельных случаях подключение уходит в «не тот» IP и ловит RST
    на TLS-handshake.
  • На том же телефоне через Wi-Fi всё работает штатно.

Первопричина

До этого патча имена резолвились единственным способом - UDP/53 к
набору публичных резолверов (Яндекс/Google/Cloudflare), зашитых в
getCustomNetDialer():

dnsServers := []string{"77.88.8.8:53", "77.88.8.1:53",
    "8.8.8.8:53", "8.8.4.4:53", "1.1.1.1:53", "1.0.0.1:53"}

На мобильных сетях этот путь не работает по двум причинам:

  1. UDP/53 режется или не возвращает ответы. Connect к :53
    вроде проходит (UDP connectionless — SendTo не падает), а реальный
    ответ не приходит.
  2. DNS-spoofing оператором. Даже если пакет до резолвера дошёл,
    оператор прозрачно перехватывает UDP/53 и подставляет свой ответ.
    Смена DNS в настройках Android не спасает, так как перехват происходит на уровне сети.

TCP/53 блокируется ещё агрессивнее, так что как fallback не годится.


Решение: DNS-over-HTTPS (RFC 8484) с авто-переключением

Что сделано

  1. Новый модуль client/doh.go - DoH-резолвер:

    • POST application/dns-message на заранее выбранные endpoint'ы.
    • Bootstrap-IP для каждого endpoint, чтобы сам DoH-транспорт не
      зависел от системного DNS.
    • Параллельный A + AAAA, IPv4 сортируется первым (лучше для
      IPv4-only CGNAT).
    • TTL-кэш с clamp'ом [10s, 1h].
    • TLS 1.2+, embedded Mozilla CA roots
      (golang.org/x/crypto/x509roots/fallback) - нужно для
      Android-сборок с CGO_ENABLED=0.
  2. Локальный UDP/TCP-форвардер на 127.0.0.1 - Go-резолвер
    подключается к нему как к обычному DNS-серверу, он заворачивает
    приходящую wire-форму запроса в DoH и отдаёт ответ обратно.
    Все edge-кейсы Go-резолвера (RESINFO, EDNS, TCP length-prefix,
    повторы) обрабатываются штатно.

  3. Единая точка входа appDialer() в main.go - все сетевые
    клиенты проекта теперь резолвят одинаково:

    • tls-client для VK-auth;
    • http.Transport для Telemost-конференции;
    • websocket.Dialer для Telemost WSS;
    • http.Transport для прокси ручной капчи.
  4. Флаг -dns=udp|doh|auto (default auto).
    В auto-режиме при старте делается один реальный DNS round-trip по
    UDP/53 под дедлайном 1.5 с. Если ответ не пришёл - процесс на
    всё время жизни
    переключается на DoH.

  5. Список endpoints - сознательно в таком порядке:

    common.dot.dns.yandex.net  →  Yandex
    secure.dot.dns.yandex.net  →  Yandex
    family.dot.dns.yandex.net  →  Yandex
    dns.google                 →  Google
    cloudflare-dns.com         →  Cloudflare
    

    Яндекс первым - лучше остаётся доступен с мобильных
    операторов, чем Google/CF.

  6. Удалена зависимость bschaatsbergen/dnsdialer и её
    транзитивки (hashicorp/golang-lru/v2, google.golang.org/grpc,
    google.golang.org/genproto/...) - DoH-резолвер полностью
    покрывает функциональность, которую мы использовали.

@samosvalishe
Copy link
Copy Markdown
Contributor Author

Потестить можно тут https://github.com/samosvalishe/turn-proxy-android/releases/tag/v1.8.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant