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


    Предисловие


    Решил как то написать снифер 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.

    Комментарии 6

      +2
      полагаю, отхвачу от всех, но это реально эпидемия — публиковать текст RFC? RFC-1035 в данном случае.
      image
      PS. я про всю статью — это просто изложение текста RFC + пример парсинга пакета.
      • НЛО прилетело и опубликовало эту надпись здесь
          0

          Только я увидел разницу в языке? Ваш пример на английском, у автора на русском.

            –1
            я вижу бескрайний простор для деятельности





            IMHO, перевод обязательной к знанию профессионалами в данной области *справочной* информации — это уже перебор.
            Тем более, что знание *технического англ* в IT-сфере — это просто conditio sine qua non

              0
              «обязательной к знанию профессионалами» — да, а что делать студентам, как я? В справочную документацию на английском мне лезть бесполезно(язык слабоват), да и незачем пока, но узнать что-то интересное я всегда рада :)
                0
                Вопросов нет: что справочные нужны — никто не спорит. У меня просто создавалось впечатление, что habr — это НЕ справочник. Вы здесь можете найти неожиданное решение, обсуждение (объективно) сложной темы или даже целый курс, найденный (написанный) кем-то. Но, блин, не перевод инструкций. У любого ресурса должна быть целевая аудитория; мне казалось, что в случае habr'а — это достаточно технически подкованные специалисты. Для азов есть книги, справочники и, наконец, RFC. И я просто не представляю, как вас «спасет» одна эта публикация про DNS. Следующая нужна про DHCP? а еще ARP не охвачен. Перефразируя Атоса, «для справочника слишком мало/скудно, для кейса — слишком примитивно».

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое