
Передмова
Вирішив написати сніфер 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
Ще немає коментарів