๐ 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 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