Как стать автором
Поиск
Написать публикацию
Обновить

Структура DNS пакета

Время на прочтение7 мин
Количество просмотров45K

Предисловие


Решил как то написать снифер DNS, так сказать just for fun. Просто посмотреть какие адреса в моей системе резолвятся. Протокол старый, документации должно быть много. Много. Но все статьи очень не полные и заканчиваются, на самом интересном моменте. Да, есть rfc1035, но хотелось бы на русском и с пояснениями. Собственно по накоплению опыта и разбора пакета и созрела данная статья. Она рассчитана на тех, кто понимает, что такое DNS и понимает, что бывают запросы и ответы. Для тех, кто хочет немного разобраться в структуре данного протокола.

Статья предполагает теорию, а потом немного практики.

Структура пакета DNS


    +---------------------+
    |        Header       | Заголовок
    +---------------------+
    |       Question      | Секция запросов
    +---------------------+
    |        Answer       | Секция ответа
    +---------------------+
    |      Authority      | Секция ответа об уполномоченных серверах
    +---------------------+
    |      Additional     | Секция ответа дополнительных записей
    +---------------------+

Header — Заголовок DNS пакета, состоящий из 12 октет.

Question section — в этой секции DNS-клиент передает запросы DNS-серверу сообщая о том, для какого имени необходимо разрешить (зарезолвить) запись DNS, а также какого типа (NS, A, TXT и т.д.). Сервер при ответе, копирует эту информацию и отдает клиенту обратно в этой же секции.

Answer section — сервер сообщает клиенту ответ или несколько ответов на запрос, в котором сообщает вышеуказанные данные.

Authoritative Section — содержит сведения о том, с помощью каких авторитетных серверов было получена информация включенная в секцию DNS-ответа.

Additional Record Section — дополнительные записи, которые относятся к запросу, но не являются строго ответами на вопрос.

Записей в секциях может быть как несколько, так и не быть вообще. Всё определяется заголовком.

Структура заголовка DNS


                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      ID                       |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    QDCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ANCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    NSCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ARCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

ID (16 бит) — данное поле используется как уникальный идентификатор транзакции. Указывает на то, что пакет принадлежит одной и той же сессии “запросов-ответов” и занимает 16 бит.

QR (1 бит) — данный бит служит для индентификации того, является ли пакет запросом (QR = 0) или ответом (QR = 1).

Opcode (4 бита) — с помощью данного кода клиент может указать тип запроса, где обычное значение:

  • 0 — стандартный запрос,
  • 1 — инверсный запрос,
  • 2 — запрос статуса сервера.
  • 3-15 – зарезервированы на будущее.

AA (1 бит) — данное поле имеет смысл только в DNS-ответах от сервера и сообщает о том, является ли ответ авторитетным либо нет.

TC (1 бит) — данный флаг устанавливается в пакете ответе в том случае если сервер не смог поместить всю необходимую информацию в пакет из-за существующих ограничений.

RD (1 бит) — этот однобитовый флаг устанавливается в запросе и копируется в ответ. Если он флаг устанавливается в запросе — это значит, что клиент просит сервер не сообщать ему промежуточных ответов, а вернуть только IP-адрес.

RA (1 бит) — отправляется только в ответах, и сообщает о том, что сервер поддерживает рекурсию

Z (3 бита) — являются зарезервированными и всегда равны нулю.

RCODE (4 бита) — это поле служит для уведомления клиентов о том, успешно ли выполнен запрос или с ошибкой.

  • 0 — значит запрос прошел без ошибок;
  • 1 — ошибка связана с тем, что сервер не смог понять форму запроса;
  • 2 — эта ошибка с некорректной работой сервера имен;
  • 3 — имя, которое разрешает клиент не существует в данном домене;
  • 4 — сервер не может выполнить запрос данного типа;
  • 5 — этот код означает, что сервер не может удовлетворить запроса клиента в силу административных ограничений безопасности.

QDCOUNT(16 бит) – количество записей в секции запросов
ANCOUNT(16 бит) – количество записей в секции ответы
NSCOUNT(16 бит) – количество записей в Authority Section
ARCOUNT(16 бит) – количество записей в Additional Record Section

Структура секции запроса


                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                     QNAME                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QTYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     QCLASS                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

QNAME — Каждая запись запроса и ответа начинается с NAME. Это доменное имя, к которому привязана или которому “принадлежит” данная запись. Она закодирована как серия меток. На этом моменте следует остановиться несколько поподробнее.

В статьях, что я видел, забывают сказать, что исходный протокол DNS предусматривает два типа меток, которые определяются первыми двумя битами:

00 (стандартная метка) – значит, остальные 6 бит определяют длину метки, за которым следует данное количество октетов. Соответственно, длина метки не может быть более 63 байта (Например, nslookup выдаст сообщение “is not a legal name (label too long)” при попытке отрезолвить хост с длинной меткой). Заканчивается запись кодом 0x00.
11 (сжатая метка) – тогда последующие 14 бит определяют ссылку на начальный адрес серии меток. Как показал опыт, может так же содержать сжатую метку на другой адрес. В запросе, как правило, таких меток нет.

Так же метка может содержать значение 0x00 (нулевая длина), означает что это корневое доменное имя (root).

Максимальная длина NAME <= 255. Это создано ради простоты реализации.

QTYPE — Тип записи DNS, которую мы ищем (NS, A, TXT и т.д.).
QCLASS — Определяющий класс запроса (IN для Internet).

Структура секции ответов


                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                                               |
    /                                               /
    /                      NAME                     /
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TYPE                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                     CLASS                     |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      TTL                      |
    |                                               |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                   RDLENGTH                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
    /                     RDATA                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

NAME — Такой же формат, что и QNAME в секции запроса.
TYPE — тип ресурсной записи. Определяет формат и назначение данной ресурсной записи.
CLASS — класс ресурсной записи; теоретически считается, что DNS может использоваться не только с TCP/IP, но и с другими типами сетей, код в поле класс определяет тип сети. В основном IN для Internet (Код 0x0001)
TTL — (Time To Live) — допустимое время хранения данной ресурсной записи в кэше неответственного DNS-сервера.
RDLENGTH — длина поля данных (RDATA).
RDATA — поле данных, формат и содержание которого зависит от типа записи.

Практика


Рассмотрим пакет из реального запроса и ответа. Запускаем любимый снифер и резолвим habrahabr.ru.

Запрос



Разберем структуру dns заголовка.

ID транзакции = 0x9bce

Далее идут флаги. 01 00 представим как двоичное значение 0’0000’0’0’1’0’000’0000 (здесь и далее я разделяю биты апострофом для лучшего визуального представления деления флагов)
QR=0 — значит этот пакет является запросом;
Opcode=0000 – Стандартный запрос;
AA=0 — данное поле имеет смысл только в DNS-ответах, поэтому всегда 0;
TC=0 — данное поле имеет смысл только в DNS-ответах, поэтому всегда 0;
RD=1 – Просим вернуть только IP адрес;
RA=0 – отправляется только сервером;
Z=000 – всегда нули, зарезервированное поле;
RCODE=0000 – Все прошло без ошибок

QDCOUNT=00 01 – 1 запись в секции запросов
ANCOUNT=00 00 – В запросе всегда 0, секция для ответов
NSCOUNT=00 00 – В запросе всегда 0, секция для ответов
ARCOUNT=00 00 – В запросе всегда 0, секция для ответов
Дальше у нас идет секции запросов и ответов. С одной записью.
Первым октетом у нас идет 0x09, представим его как двоичное значение 00’001001. Первые два бита идут 00, это значит что это обычная метка. Длина метки 9 байт (b001001). “68 61 62 72 61 68 61 62 72”. Вот эти 9 байт. Тут написано “habrahabr” (в 16-ричном виде). Идем дальше. Октет 0x02. Первых два бита 00, значит снова обычная метка с длиной 2 байта. Вот они: “72 75”. Написано “ru”. Идем дальше. Октет 0x00. Значит конец записи хоста. Мы получили два слова “habrahabr” и “ru”. Объединяем их точкой, получаем “habrahabr.ru”, это и есть хост который мы запросили.
QTYPE=0x0001 – Соответствует типу A (запрос адреса хоста)
QCLASS=0x0001 – Соответствует классу IN.

Ответ



Разберем структуру dns заголовка.

ID транзакции = 0x9bce. Она должна быть ровна ID от запроса.
Снова флаги. 81 80 представим как двоичное значение 1’0000’0’0’1’1’000’0000
QR=1 — значит этот пакет является ответом;
Opcode=0000 – Стандартный запрос;
AA=0 – Сервер не является авторитетным для домена;
TC=0 – Вся информация поместилась в один пакет;
RD=1 – Просим вернуть только IP адрес;
RA=1 – Сервер поддерживает рекурсию;
Z=000 – всегда нули, зарезервированное поле;
RCODE=0000 – Все прошло без ошибок

QDCOUNT=00 01 – 1 запись в секции запросов
ANCOUNT=00 01 – Теперь у нас появилась одна запись в ответе
NSCOUNT=00 00 – В запросе всегда 0, секция для ответов
ARCOUNT=00 00 – В запросе всегда 0, секция для ответов

Дальше у нас идет секции запросов и ответов. С двумя записями. Одна запись запроса, другая запись с ответом. Расписывать секцию запрос я не буду, он будет всегда 1в1 такой же, как и в пакете запроса. Приступим с секции ответов.

Первым октетом у нас идет 0x09, два первых бита 00, значит обычная метка с длиной 9 байт. Читаем 9 байт, получаем “HABRAHABR”. Далее идет 0XC0 (b11000000). Как видим, первые два бита имеют значение 11, это значит что перед нами сжатая ссылка. Смотрим следующие 8 бит (это у нас 0x16 (b00010110)) и объединяем с текущими последними 6бит. Получаем b00000000010110. Ссылка на 22ой байт пакета DNS (02 72 75 00). Начиная с 22 октета, снова получаем метки. По тем же правилам. У нас это получается “.ru”. Объединяем всё что получили, получается “HABRAHABR.ru” Это и есть хост о котором пойдет дальше речь.

QTYPE = 0x0001 – Соответствует типу A (запрос адреса хоста)
QCLASS = 0x0001 – Соответствует классу IN.
TTL = 0x00000c90 – время актуальности данных 3216 секунд.
RDLENGTH = 0x0004 – Длина данных 4 октета.
RDATA = «b2 f8 ed 44».

Как уже было сказано, формат и содержание зависит от типа записи. Тип записи у нас “A”. Значит, чтобы получить IP мы должны считать 4 байта. Каждый байт это и будет соответствующий октет IP адреса, записанный в 16ричном виде.

Получаем IP: b2.f8.ed.44 или «178.248.237.68». Что и требовалось получить.

Например, для типа NS, формат был бы такой:

    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    /                   NSDNAME                     /
    /                                               /
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

И считывали мы бы имя по правилам QNAME.
Теги:
Хабы:
Всего голосов 24: ↑22 и ↓2+27
Комментарии6

Публикации

Ближайшие события