Как отправить dns запрос через сокет
Перейти к содержимому

Как отправить dns запрос через сокет

  • автор:

Send request to DNS server via UDP socket

currently i am learning about DNS servers in my lectures. As we had berkeley sockets before, i thought i may combine my knowledge and try to send a request to a DNS via a socket and process the answer myself. I wrote a little C program on linux which takes 3 arguments: an IP a port (most likely 53 as i found out) and the data which should be send to the given server.

First i tried simply sending the host name as a string. Then i discovered RFC1034 where it becomes clear that a request is more complex and contains several fields. However i somehow can not find out the possible values i can fill in the fields. For example under 6.2.1 in the link it says Header: OPCODE=SQUERY whereas OPCODE is stated to be a 4 bit integer so i believe these codes are implementation defined and determined by the DNS server? Or did i miss a section of the RFC? It only states type and class are "encoded 16 bit values".

when i try to call my program like this

for the google DNS, it actually connects and sends the data but then waits for an answer which never comes. When using TCP i noticed the DNS server instantly closes the connection after receiving the domain name.

As i understand now i need to assemble a request according to the RFC with a Header containing the opcode for a request and the question containing the domain name and further options like type and class as binary information. But where do i get the possible values for OPCODE, QCLASS, QTYPE ?

I am aware that getaddrinfo essentially does this for me and that i can get the IP from the sockaddr structure already but this is a matter of experimenting and learning.

Как отправить dns запрос через сокет

DNS over TLS — Шифруем наши DNS запросы с помощью Stunnel и Lua

DNS (англ. Domain Name System — система доменных имён) — компьютерная распределённая система для получения информации о доменах.

TLS (англ. transport layer security — Протокол защиты транспортного уровня) — обеспечивает защищённую передачу данных между Интернет узлами.

После новости «Google Public DNS тихо включили поддержку DNS over TLS» я решил попробовать его. У меня есть Stunnel который создаст шифрованный TCP туннель. Но программы обычно общаются с DNS по UDP протоколу. Поэтому нам нужен прокси который будет пересылать UDP пакеты в TCP поток и обратно. Мы напишем его на Lua.

Вся разница между TCP и UDP DNS пакетами:

То есть делаем туда:

  1. берём пакет из UDP
  2. добавляем к нему в начале пару байт в которых указан размер этого пакета
  3. отправляем в TCP канал

И в обратную сторону:

  1. читаем из TCP пару байт тем самым получаем размер пакета
  2. читаем пакет из TCP
  3. отправляем его получателю по UDP
Настраиваем Stunnel
  1. Скачиваем корневой сертификат Root-R2.crt в директорию с конфигом Stunnel
  2. Конвертируем сертификат в PEM

Пишем в stunnel.conf:

То есть Stunnel:

  1. примет не шифрованное TCP по адресу 127.0.0.1:53
  2. откроет шифрованный TLS туннель до адреса 8.8.8.8:853 (Google DNS)
  3. будет передавать данные туда и обратно

Работу тунеля можно проверить командой:

Опция ‘-vc’ заставляет nslookup использовать TCP соединение к DNS серверу вместо UDP.

Пишем скрипт

Я пишу на Lua 5.3. В нём уже доступны бинарные операции с числами. Ну и нам понадобится модуль Lua Socket.

Напишем простенькую функцию которая позволит отправить дамп пакета в консоль. Хочется видеть что делает прокси.

UDP в TCP и обратно

Пишем две функции которые будут оперировать двумя каналами передачи данных.

Обе функции сразу после запуска выполняют coroutine.yield(). Это позволяет первым вызовом передать параметры функции а дальше делать coroutine.resume(co) без дополнительных параметров.

А теперь main функция которая выполнит подготовку и запустит главный цикл.

Запускаем главную функцию. Если вдруг будет закрыто соединение мы через секунду установим его заново вызвав main.

проверяем

Запускаем наш скрипт

Проверяем работу скрипта командой

На этот раз без ‘-vc’ так так мы уже написали и запустили прокси который заворачивает UDP DNS запросы в TCP тунель.

Если всё нормально можно указать в настройках соедидения как DNS сервер «127.0.0.1»

заключение

Теперь наши DNS запросы под защитой TLS.

P.S. Не даём гуглу лишней информации о нас

Сразу после соединения Windows пытается зарегистрировать нас на DNS серверах гугла через наш туннель. Это отключается в продвинутых настройках DNS снятием галочки.

Отправить запрос на DNS-сервер через UDP-сокет

В настоящее время я изучаю DNS-серверы в своих лекциях. Поскольку раньше у нас были сокеты Berkeley, я подумал, что могу объединить свои знания и попытаться отправить запрос в DNS через сокет и обработать ответ самостоятельно. Я написал небольшую программу на языке C на linux, которая принимает 3 аргумента: IP порт (скорее всего, 53, как я узнал) и данные, которые должны быть отправлены на данный сервер.

Сначала я попытался просто отправить имя хоста в виде строки. Затем я обнаружил RFC1034, где становится ясно, что запрос более сложный и содержит несколько полей. . Однако я почему-то не могу узнать возможные значения, которые я могу заполнить в полях. Например, в 6.2.1 в ссылке указано Header: OPCODE=SQUERY , тогда как OPCODE указано как 4-битное целое число, поэтому я полагаю, что эти коды определяются реализацией и определяются DNS-сервером? Или я пропустил раздел RFC? В нем только указано, что тип и класс являются «закодированными 16-битными значениями».

Когда я пытаюсь вызвать свою программу вот так

Для DNS Google он фактически подключается и отправляет данные, но затем ждет ответа, который никогда не приходит. При использовании TCP я заметил, что DNS-сервер мгновенно закрывает соединение после получения доменного имени.

Насколько я понимаю теперь, мне нужно собрать запрос в соответствии с RFC с заголовком, содержащим код операции для запроса, и вопрос, содержащий имя домена и другие параметры, такие как тип и класс, в виде двоичной информации. Но где мне взять возможные значения для OPCODE, QCLASS, QTYPE ?

Я знаю, что getaddrinfo , по сути, делает это за меня, и что я уже могу получить IP-адрес из структуры sockaddr, но это вопрос экспериментов и обучения.

DNS-запрос с использованием сокета Google App Engine

Я пытаюсь использовать новую поддержку сокетов для Google App Engine, чтобы выполнять некоторые DNS-запросы. я использую DNSPython для выполнения запроса, и код отлично работает вне GAE.

Когда я запускаю GAE, я получаю следующую ошибку:

Который вызывается dnspython, когда ответ не возвращается в течение установленного срока. Я увеличил ограничение по времени до 60 секунд, а DnsQuery — это задача, но все равно получаю ту же ошибку.

Есть ли какие-либо ограничения в реализации сокетов Google App Engine, препятствующие выполнению DNS-запросов?

У меня также есть аналогичная проблема с Google App Engine. — Uri

@Cyril N: убедитесь, что в дополнение к платному приложению вы также установили для него дневной бюджет — по крайней мере, в прошлом году были сообщения о том, что некоторые платные функции не работали должным образом, пока дневной бюджет не был инициализирован. — Dan Cornilescu

@DanCornilescu Я перешел в раздел «Выставление счетов»> «Бюджеты и оповещения» и уже определил оповещение для определенных трех уровней. Это то, что вы имели в виду? я читал в stackoverflow.com/a/10894376/330867 который Google will wait until the first payment is cleared before increasing your mail quota. . Может быть, это моя проблема. Но затем я перешел к своему App Engine > Quotas и увидел, что под записью Sockets мое приложение выполняло некоторые запросы (возможно, используя пакет Python requests , которым я пользуюсь). Так Sockets должно работать, но не с dnspython ? — Cyril N.

@DanCornilescu У меня есть два похожих проекта (dev/prod), и у prod был включен биллинг (по вашей ссылке), поэтому я тестировал его, но он все еще не работает :/ — Cyril N.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *