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

Alex Alex 04 грудня 2019

Структура 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 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'qc 001001. Перші два біти йдуть 00, це означає, що це звичайна мітка. Довжина мітки 9 байт (b001001). "68 61 62 72 61 68 61 62 72". Ось ці 9 байт. Тут написано "habrahabr" (у 16-ричної вигляді). Йдемо далі. Октет 0x02. Перших два біта 00, значить знову звичайна мітка з довжиною 2 байти. Ось вони: "72 75". Написано "ru". Йдемо далі. Октет 0x00. Значить кінець запису хоста. Ми отримали два слова "habrahabr" та "ua". Об'єднуємо їх точкою, отримуємо "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.

Source: habr.com

dns

Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.