Skip to content

๐ŸŒ 06-03: DNS Packets Construction


๐Ÿ“Œ Introduction

DNS packets are the core units of communication in the Domain Name System. They carry queries (requests) and responses (answers) between clients and DNS servers.


๐Ÿงฑ DNS Packet Structure

A DNS packet follows a layered structure:

IP Header โ†’ UDP Header โ†’ DNS Header โ†’ DNS Data

  • IP Header โ†’ handles routing
  • UDP Header โ†’ uses port 53
  • DNS Header โ†’ controls behavior
  • DNS Data โ†’ contains actual query/response

DNS Structure


๐Ÿงฉ DNS Header Fields

Field Purpose
id Unique transaction ID
flags Query/response control
qdcount Number of Question Records
ancount Number of Answer Records
nscount Number of Authority Records
arcount Number of Additional Records

โš™๏ธ Important Flags

  • qr โ†’ 0 = query, 1 = response
  • aa โ†’ authoritative answer
  • rd โ†’ recursion desired
  • ra โ†’ recursion available

๐Ÿ“ฆ DNS Data Field

Section Description
qd Question (what we ask)
an Answer (final IP)
ns Authority (who knows)
ar Additional (extra helpful info)

๐Ÿ“„ DNS Records (Concept)

Query: www.example.com

Response breakdown: - Question โ†’ what we asked - Answer โ†’ IP of domain - Authority โ†’ which server is responsible - Additional โ†’ IP of that server


๐Ÿ› ๏ธ DNS Structure in Scapy

Command: ls(DNS)

This shows all DNS fields: - qd โ†’ question - an โ†’ answer - ns โ†’ authority - ar โ†’ additional


๐Ÿงช DNS Query Record (DNSQR)

DNSQR = Question part of DNS packet

Fields: - qname โ†’ domain name - qtype โ†’ record type (A = IPv4) - qclass โ†’ class (IN = Internet)


๐Ÿงช DNS Resource Record (DNSRR)

DNSRR = Actual data in response

Used in: - Answer - Authority - Additional

Fields: - rrname โ†’ domain name - type โ†’ record type - rclass โ†’ class - ttl โ†’ cache duration - rdata โ†’ actual value (IP / NS)


๐Ÿ“ก Example: Sending a DNS Query

Code:

#!/usr/bin/env python3
from scapy.all import *

IPpkt  = IP(dst='8.8.8.8')        # DNS server
UDPpkt = UDP(dport=53)            # DNS port

Qdsec  = DNSQR(qname='www.example.com')

DNSpkt = DNS(
    id=100,
    qr=0,
    qdcount=1,
    qd=Qdsec
)

packet = IPpkt / UDPpkt / DNSpkt

response = sr1(packet, timeout=2)

if response:
    print(response[DNS].summary())

Explanation: - IP โ†’ where to send - UDP โ†’ DNS port - DNSQR โ†’ question - DNS โ†’ full packet - "/" โ†’ combines layers - sr1() โ†’ send + wait for reply


๐Ÿ–ฅ๏ธ Simple DNS Server

Step 1: Receive Query

Code:

#!/usr/bin/env python3
from scapy.all import *
from socket import socket, AF_INET, SOCK_DGRAM

sock = socket(AF_INET, SOCK_DGRAM)
sock.bind(("0.0.0.0", 1053))

while True:
    data, addr = sock.recvfrom(4096)

    dns_req = DNS(data)
    query_name = dns_req.qd.qname.decode()

    print("Query:", query_name)

Explanation: - Opens UDP server - Receives DNS packet - Extracts domain name


Step 2: Build Response

Code:

answer = DNSRR(
    rrname=dns_req.qd.qname,
    type="A",
    rdata="10.2.3.6",
    ttl=300
)

Explanation: - Creates answer record - Maps domain โ†’ IP


Step 3: Send Response

Code:

dns_resp = DNS(
    id=dns_req.id,
    qr=1,
    aa=1,
    qd=dns_req.qd,
    qdcount=1,
    ancount=1,
    an=answer
)

sock.sendto(bytes(dns_resp), addr)

Explanation: - id must match - qr=1 โ†’ response - aa=1 โ†’ authoritative - attaches answer


๐Ÿงช Advanced DNS Response (Answer + Authority + Additional)

from scapy.all import * from socket import socket, AF_INET, SOCK_DGRAM

Create UDP socket (DNS server)

sock = socket(AF_INET, SOCK_DGRAM) sock.bind(("0.0.0.0", 1053)) # Port 1053 (non-root DNS port)

while True: data, addr = sock.recvfrom(4096)

# Parse incoming DNS request
DNSreq = DNS(data)
query_name = DNSreq.qd.qname.decode()
print("Query:", query_name)

# -------------------------------
# ๐ŸŸข Answer Section (Final IP)
# -------------------------------
Anssec = DNSRR(
    rrname=DNSreq.qd.qname,
    type='A',
    rdata='10.2.3.6',
    ttl=259200
)

# -------------------------------
# ๐ŸŸก Authority Section (Name Servers)
# -------------------------------
NSsec1 = DNSRR(
    rrname="example.com",
    type='NS',
    rdata='ns1.example.com',
    ttl=259200
)

NSsec2 = DNSRR(
    rrname="example.com",
    type='NS',
    rdata='ns2.example.com',
    ttl=259200
)

# -------------------------------
# ๐Ÿ”ต Additional Section (IP of NS)
# -------------------------------
Addsec1 = DNSRR(
    rrname='ns1.example.com',
    type='A',
    rdata='10.2.3.1',
    ttl=259200
)

Addsec2 = DNSRR(
    rrname='ns2.example.com',
    type='A',
    rdata='10.2.3.2',
    ttl=259200
)

# -------------------------------
# ๐Ÿ”ด Build Full DNS Response
# -------------------------------
DNSpkt = DNS(
    id=DNSreq.id,      # Match request ID
    qr=1,              # Response
    aa=1,              # Authoritative Answer
    rd=0,              # Recursion not supported

    qdcount=1,
    ancount=1,
    nscount=2,
    arcount=2,

    qd=DNSreq.qd,      # Original question
    an=Anssec,         # Answer section
    ns=NSsec1 / NSsec2,  # Authority section
    ar=Addsec1 / Addsec2 # Additional section
)

# Debug print
print(repr(DNSpkt))

# Send response back to client
sock.sendto(bytes(DNSpkt), addr)

๐Ÿ”„ Communication Flow

Client โ†’ DNS Query โ†’ Server Server โ†’ DNS Response โ†’ Client


๐Ÿง  Key Takeaways

  • DNS = layered protocol
  • DNSQR โ†’ query
  • DNSRR โ†’ answer
  • Header controls everything
  • Scapy allows custom packets

๐Ÿš€ Why This Matters

  • Debugging networks
  • Packet analysis
  • Security research
  • Building DNS systems

โœ… Final Flow

IP โ†’ UDP โ†’ DNS Header โ†’ Question โ†’ Answer โ†’ Authority โ†’ Additional