> По какому протоколу работает DNS (Go)
Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: VK
Стек: Go
> Пример ответа
Короткий ответ
DNS работает преимущественно по протоколу UDP на порту 53, с fallback на TCP для больших ответов, zone transfer и DNSSEC. Запросы и ответы используют единый формат сообщений, определённый в RFC 1035. UDP обеспечивает низкую задержку и лёгкость, TCP - надёжность для ответов, превышающих 512 байт (или 1232 байта с учётом современных рекомендаций по EDNS0).
Подробное объяснение
DNS использует два транспортных протокола:
-
UDP (порт 53) - основной транспорт для обычных запросов. Это stateless протокол: клиент отправляет запрос, сервер отвечает одним датаграммой. UDP не требует установки соединения, поэтому latency минимальна. Ретрансмиссию на уровне приложения выполняет сам DNS-резолвер (обычно через таймаут в 1-5 секунд).
-
TCP (порт 53) - используется в следующих случаях:
- Ответ превышает максимальный размер UDP-датаграммы (исторически 512 байт, с EDNS0 - до 4096, современная рекомендация - 1232 байта для избежания IP fragmentation).
- Zone transfer (AXFR/IXFR) между первичным и вторичным DNS-серверами.
- DNSSEC, когда ответ содержит много RRsets и не помещается в UDP.
- Когда сервер явно указывает в ответе флаг TC (truncated) - клиент обязан повторить запрос по TCP.
Формат сообщения одинаков для UDP и TCP: 12-байтовый заголовок (ID, флаги, счётчики) плюс секции question, answer, authority, additional. В TCP перед сообщением добавляется 2-байтовый префикс длины.
Современные резолверы (например, Go's net.Resolver) автоматически переключаются на TCP при получении TC-флага. Также существует DNS over HTTPS (DoH) и DNS over TLS (DoT), но это уже поверх HTTP/3 или TLS, а не классический DNS-транспорт.
На практике
В Go стандартная библиотека net уже реализует корректное поведение: сначала UDP, при TC - TCP. Однако при написании собственного DNS-клиента или сервера нужно учитывать:
- Для UDP-сервера - обрабатывать каждый запрос в отдельной горутине, так как пакеты могут приходить одновременно.
- Для TCP-сервера - устанавливать таймауты чтения/записи, чтобы избежать hanging connections.
- При использовании EDNS0 - проверять, поддерживает ли сервер OPT record, иначе ответ может быть обрезан.
- Для высоконагруженных резолверов - использовать connection pooling для TCP, так как установка соединения на каждый запрос дорога.
Пример кода
GOpackage mainimport ("context""fmt""net""time")func main() {resolver := &net.Resolver{PreferGo: true,Dial: func(ctx context.Context, network, address string) (net.Conn, error) {d := net.Dialer{Timeout: 5 * time.Second,}// Принудительно используем UDP, но с fallback на TCP через стандартный механизмreturn d.DialContext(ctx, "udp", "8.8.8.8:53")},}ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)defer cancel()ips, err := resolver.LookupIPAddr(ctx, "example.com")if err != nil {fmt.Printf("error: %v\n", err)return}for _, ip := range ips {fmt.Println(ip.IP)}}
Этот код демонстрирует кастомный dial для резолвера. Обратите внимание: net.Resolver сам обработает TC-флаг и переключится на TCP, но только если в Dial передать network из параметра (в примере выше мы жёстко задали "udp", что сломает fallback - в реальном коде нужно использовать network из аргумента).
Как отвечать на собеседовании
Начни с прямого ответа: "UDP, порт 53, с TCP как fallback". Затем кратко объясни, почему UDP - основной: низкая latency, отсутствие handshake, простота. Упомяни триггеры для TCP: truncation, zone transfer, DNSSEC. Если спросят про EDNS0 - скажи, что он расширяет размер UDP-пакета, но не отменяет TCP. Для senior-позиции добавь детали: как Go-резолвер обрабатывает TC, какие таймауты ставить, как избежать amplification attacks на UDP-сервере. Не углубляйся в DoH/DoT, если не спросят - это отдельная тема.
Что проверяет интервьюер
- Понимание транспортных протоколов и их trade-off.
- Знание ограничений UDP (размер, потеря пакетов) и механизмов компенсации в DNS.
- Практический опыт: как работает резолвер, что происходит при обрезанном ответе.
- Умение объяснить, почему нельзя использовать только TCP (latency, overhead) или только UDP (надёжность).
- Для Go - знание стандартной библиотеки и особенностей
net.Resolver.
Типичные ошибки
- Утверждение, что DNS работает только по UDP - неверно, TCP обязателен для ряда случаев.
- Игнорирование EDNS0: без него лимит 512 байт, и многие современные ответы (DNSSEC, много A-записей) будут обрезаться.
- Непонимание, что TC-флаг - это сигнал клиенту повторить запрос по TCP, а не ошибка.
- В Go - жёсткое задание "udp" в кастомном Dial без учёта параметра
network, что ломает fallback. - Забывают про таймауты: UDP-запрос без ответа может висеть бесконечно, если не установить deadline.
> Похожие задачи по Go
Какие типы записей существуют в DNS
Какой статус у продукта
Используешь ли пайпы в Unix и для чего
Как понять, что сервис в продакшн работает корректно
> Похожие задачи по backend
Какие типы записей существуют в DNS
Какой статус у продукта
Используешь ли пайпы в Unix и для чего
Как понять, что сервис в продакшн работает корректно
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью