System nazw domenowych (DNS) stanowi podstawę nawigacji w internecie, tłumacząc czytelne dla człowieka nazwy domen na adresy IP zrozumiałe dla maszyn. Niniejszy raport szczegółowo analizuje przygotowanie pakietu zapytania DNS, skupiając się na komponentach strukturalnych, mechanizmach kodowania, polach nagłówka oraz praktycznych strategiach implementacyjnych. Korzystając z dokumentacji technicznej, specyfikacji RFC oraz analizy rzeczywistych pakietów, opracowanie wyjaśnia zawiłości konstrukcji zapytań DNS, w tym nagłówków protokołu, sekcji pytań, technik kodowania nazw oraz programowalnego generowania zapytań z użyciem frameworków takich jak Scapy.

Przegląd protokołu DNS

DNS działa jako rozproszona, hierarchiczna baza danych, umożliwiająca efektywne mapowanie nazw domen na adresy IP w modelu zapytanie-odpowiedź klient-serwer. Zapytania są transmitowane przez UDP lub TCP, przy czym UDP dominuje ze względu na mniejsze narzuty przy krótkich wiadomościach. Każdy pakiet DNS składa się z nagłówka, sekcji pytań oraz opcjonalnych sekcji odpowiedzi, autorytatywnej i dodatkowej. Komponenty istotne dla zapytania znajdują się głównie w nagłówku i sekcji pytań, definiując takie parametry jak żądanie rekursji, typ zapytania czy nazwa domeny.

Kontekst historyczny i standaryzacja

Protokół DNS został sformalizowany w RFC 1035, który określił podstawową strukturę pakietu i semantykę operacyjną. Kolejne RFC wprowadziły rozszerzenia, takie jak EDNS (większe ładunki, dodatkowe flagi), jednak rdzeń formatu zapytania pozostał niezmienny. Prostota i elastyczność protokołu zapewniły mu długowieczność, mimo wykładniczego wzrostu ruchu w internecie.

Struktura wiadomości DNS

Sekcja nagłówka

Nagłówek DNS to 12-bajtowa struktura zawierająca pola sterujące i liczniki dla każdej sekcji. Jego układ, zgodny z RFC 1035 i odwzorowany w frameworkach programistycznych, wygląda następująco:

typedef struct _DNS_HEADER {
    WORD Xid;              // Identyfikator transakcji
    WORD Flags;            // Flagi sterujące
    WORD QuestionCount;    // Liczba pytań
    WORD AnswerCount;      // Liczba odpowiedzi (0 w zapytaniach)
    WORD NameServerCount;
    WORD AdditionalCount;
} DNS_HEADER;

Rys. 1: Struktura nagłówka DNS w C.

Kluczowe pola nagłówka

  • Transaction ID (Xid) – 16-bitowy identyfikator korelujący zapytania i odpowiedzi. Klient generuje losowe ID, by utrudnić spoofing.
  • Flags – 16-bitowa mapa bitowa kodująca wskaźniki zapytania/odpowiedzi, opkody, żądania rekursji i kody odpowiedzi. Kluczowe flagi to:
  • QR (Bit 0) – 0 dla zapytań, 1 dla odpowiedzi.
  • OPCODE (Bity 1-4) – Typ zapytania (np. standardowe, odwrotne, status serwera).
  • RD (Bit 7) – Rekursja żądana, wymusza iteracyjne rozwiązywanie przez serwer.
  • Licznik – Cztery 16-bitowe pola (QuestionCount, AnswerCount itd.) liczą wpisy w każdej sekcji. W zapytaniach QuestionCount=1, reszta=0.

Interakcje flag i ich znaczenie

Ustawienie RD=1 włącza rekursję, wymuszając na serwerze przeszukiwanie hierarchii DNS, jeśli odpowiedź nie jest w cache. Zapytania nierrekursywne (RD=0) ograniczają odpowiedź do wiedzy serwera i są użyteczne do sprawdzeń autorytatywnych. Atakujący mogą manipulować flagami jak TC (Truncation), by wykorzystywać mechanizmy fallback UDP/TCP.

Projekt sekcji pytań

Sekcja pytań określa domenę i typ rekordu. Jej struktura to trzy pola o stałej długości poprzedzone zmienną QNAME:


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

Rys. 2: Format sekcji pytań.

Kodowanie QNAME

Nazwy domen (np. www.example.com) kodowane są jako sekwencja etykiet, z każdą poprzedzoną bajtem długości i zakończoną etykietą o długości zero. Przykład:


3 w w w 7 e x a m p l e 3 c o m 0

To kodowanie eliminuje delimitery, zmniejsza rozmiar pakietu i ułatwia parsowanie. Implementacje muszą obsługiwać kompresję (odwołania do wcześniejszych offsetów), choć zapytania rzadko ją stosują.

QTYPE i QCLASS

  • QTYPE – 16-bitowa wartość określająca typ rekordu (np. 1 dla A, 28 dla AAAA).
  • QCLASS – Zwykle 1 (IN) dla internetu, choć istnieją historyczne klasy jak CH (ChaosNet).

Kodowanie nazw i kompresja

Struktura etykietowa

Każda etykieta QNAME jest ograniczona do 63 bajtów, a cała nazwa do 255 bajtów. To zapewnia zgodność ze starszymi serwerami DNS i chroni przed przepełnieniem bufora. Przykład: www.subdomain.example.com dzieli się na etykiety o długościach 3, 9, 7, 3, po czym następuje etykieta zerowa.

Techniki kompresji

Aby ograniczyć redundancję, DNS stosuje kompresję, gdzie powtarzające się fragmenty domen zastępowane są 2-bajtowymi wskaźnikami do wcześniejszych pozycji. Zapytania rzadko ją wykorzystują (dla uproszczenia), ale odpowiedzi – bardzo często. Przykład: powtórzenie example.com może być zakodowane jako 0xC00C, wskazujące offset pierwszego wystąpienia.

Praktyczne strategie implementacyjne

Programistyczna generacja w C

Implementacje w C zwykle tworzą nagłówki i pytania DNS ręcznie, stosując sieciową kolejność bajtów:

#pragma pack(push, 1)
struct DNS_Header {
    uint16_t xid;
    uint16_t flags;
    uint16_t qdcount;
    uint16_t ancount;
    uint16_t nscount;
    uint16_t adcount;
};
#pragma pack(pop)
void build_query(const char *domain, uint8_t *packet) {
    struct DNS_Header *hdr = (struct DNS_Header*) packet;
    hdr->xid = htons(0x1234);
    hdr->flags = htons(0x0100); // RD=1
    hdr->qdcount = htons(1);
    // Dopisz QNAME, QTYPE, QCLASS...
}

Rys. 3: Budowa nagłówka DNS w C.

Python i framework Scapy

Scapy upraszcza generowanie zapytań DNS dzięki wysokopoziomowym abstrakcjom:

from scapy.all import DNS, DNSQR, IP, UDP
def build_query(domain):
    return IP(dst="8.8.8.8")/UDP(dport=53)/DNS(
        rd=1,
        qd=DNSQR(qname=domain)
    )

Rys. 4: Zapytanie DNS w Scapy.

Klasa DNSQR w Scapy automatycznie koduje QNAME, pozwalając na szybkie prototypowanie. Zaawansowani użytkownicy mogą manipulować flagami jak cd (wyłączenie walidacji DNSSEC).

Aspekty bezpieczeństwa i ataki

Podszywanie się pod pakiety

Atakujący mogą fałszować zapytania DNS ze sfałszowanym IP źródłowym, by wymusić odpowiedzi na serwery ofiary, wzmacniając ataki refleksyjne:

spoofed_pkt = IP(src="192.168.1.100", dst="8.8.8.8")/UDP()/DNS(qd=DNSQR("example.com"))
send(spoofed_pkt)

Rys. 5: Spoofing DNS w Scapy.

Ochrona obejmuje wdrożenie Response Rate Limiting (RRL) oraz weryfikację legalności adresów źródłowych.

Zatrucie cache DNS

Przewidując identyfikatory transakcji i wstrzykując fałszywe odpowiedzi, atakujący mogą skazić cache DNS. Stosowanie nieprzewidywalnych Xid oraz podpisów DNSSEC ogranicza to ryzyko.

Wnioski

Konstrukcja zapytań DNS wymaga precyzyjnej konfiguracji flag nagłówka, formatowania sekcji pytań i kodowania nazw. Prostota protokołu kryje jego odporność – obsługuje zarówno proste zapytania A, jak i złożone, skompresowane wymiany. W miarę rozwoju sieci zrozumienie mechaniki pakietów DNS pozostaje kluczowe dla deweloperów, administratorów i specjalistów ds. bezpieczeństwa. Przyszłe prace mogą dotyczyć transportu DNS przez QUIC lub wykrywania anomalii zapytań z użyciem AI.

Warto eksperymentować z narzędziami typu Scapy i Wireshark, by praktycznie zgłębić te zagadnienia. Zaleca się także lekturę RFC 1035 w celu zrozumienia przypadków brzegowych i historycznych decyzji projektowych.

Autor
Adam M.
Pasjonat cyberbezpieczeństwa z 20-letnim stażem w branży IT. Swoją przygodę rozpoczynał od legendarnego mks_vir, a dziś odpowiada za ochronę systemów w renomowanej polskiej instytucji finansowej. Specjalizuje się w analizie zagrożeń i wdrażaniu polityk bezpieczeństwa. Ceni prywatność, dlatego o szczegółach mówi niewiele – woli, aby przemawiały za niego publikacje i wyniki pracy.