You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python读取DNS数据包:手动解码缺失QR、OpCode等字段问题

Manually Parsing DNS Packet Flags & Structure in Python

Great question—let's walk through exactly how to extract those DNS flags and parse the packet structure without relying on external libraries. The core issue here is that DNS packets are binary structures, not plain ASCII text. Converting the entire packet to ASCII will only reveal the human-readable domain part, while ignoring the critical binary header fields like QR, OpCode, and others you're trying to access.

Let's break down your example packet step by step:
b'\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x10googletagmanager\x03com\x00\x00\x01\x00\x01'

Step 1: Understand DNS Packet Structure

A basic DNS query packet has three core components:

  • Header (12 bytes): Stores the packet ID, flags, and counters for questions/responses
  • Question Section: Contains the target domain, query type, and network class
  • Answer/Authority/Additional Sections: Only present in DNS responses (your packet is a query, so these are empty)

Step 2: Parse the Header Fields

First, isolate the header (dns_packet[:12]) and extract its components:

  1. ID (first 2 bytes): \x01\x01 → 0x0101 = 257 (a unique identifier for matching queries to responses)
  2. Flags (next 2 bytes): \x00\x00 → this 16-bit integer holds all the flags you want to extract (QR, OpCode, etc.)
  3. QDCOUNT (next 2 bytes): \x01\x00 = 1 (number of questions in the packet)
  4. ANCOUNT/NSCOUNT/ARCOUNT: All \x00\x00 (no responses included in this query)

Extracting Flags with Bitwise Operations

The 16-bit flags field follows this strict bit structure (from highest to lowest bit):

Bit: 15 14-11 10  9  8  7  6-4 3  2  1-0
     QR OpCode AA TC RD RA  Z  AD CD RCODE

To pull out each value, we use bitwise shifts and masks to isolate specific bits:

  • QR: Is this a query (0) or response (1)? → (flags >> 15) & 1
  • OpCode: Type of query (0 = standard, 1 = reverse, etc.) → (flags >> 11) & 0b1111
  • AA: Authoritative Answer (only relevant for responses) → (flags >> 10) & 1
  • TC: Truncated (packet was too large for the connection) → (flags >> 9) & 1
  • RD: Recursion Desired (ask the server to resolve the query recursively) → (flags >> 8) & 1
  • RA: Recursion Available (server supports recursive queries) → (flags >> 7) & 1
  • Z: Reserved field (should always be 0) → (flags >> 4) & 0b111
  • AD: Authenticated Data (DNSSEC verified) → (flags >> 3) & 1
  • CD: Checking Disabled (skip DNSSEC validation) → (flags >> 2) & 1
  • RCODE: Response Code (only relevant for responses) → flags & 0b1111

Step 3: Python Code to Manually Parse the Packet

Here's a complete, library-free example that extracts all the fields you care about:

# Your captured DNS packet
dns_packet = b'\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x10googletagmanager\x03com\x00\x00\x01\x00\x01'

# Parse header components
header = dns_packet[:12]
packet_id = int.from_bytes(header[:2], byteorder='big')
flags = int.from_bytes(header[2:4], byteorder='big')
qd_count = int.from_bytes(header[4:6], byteorder='big')
an_count = int.from_bytes(header[6:8], byteorder='big')
ns_count = int.from_bytes(header[8:10], byteorder='big')
ar_count = int.from_bytes(header[10:12], byteorder='big')

# Extract individual flags from the 16-bit integer
qr = (flags >> 15) & 1
opcode = (flags >> 11) & 0b1111
aa = (flags >> 10) & 1
tc = (flags >> 9) & 1
rd = (flags >> 8) & 1
ra = (flags >> 7) & 1
z = (flags >> 4) & 0b111
ad = (flags >> 3) & 1
cd = (flags >> 2) & 1
rcode = flags & 0b1111

# Print header information
print("DNS Header Info:")
print(f"ID: {packet_id}")
print(f"QR: {qr} (0 = Query, 1 = Response)")
print(f"OpCode: {opcode} (0 = Standard Query)")
print(f"AA: {aa}")
print(f"TC: {tc}")
print(f"RD: {rd}")
print(f"RA: {ra}")
print(f"Z: {z}")
print(f"AD: {ad}")
print(f"CD: {cd}")
print(f"RCODE: {rcode}")
print(f"Question Count: {qd_count}")

# Parse the question section (domain, QTYPE, QCLASS)
question_section = dns_packet[12:]
domain_parts = []
ptr = 0

# Extract domain name (labels are prefixed with length bytes)
while True:
    label_length = question_section[ptr]
    if label_length == 0:
        ptr += 1
        break
    # Extract and decode the label to ASCII
    label = question_section[ptr+1:ptr+1+label_length].decode('ascii')
    domain_parts.append(label)
    ptr += 1 + label_length

domain = '.'.join(domain_parts)
qtype = int.from_bytes(question_section[ptr:ptr+2], byteorder='big')
qclass = int.from_bytes(question_section[ptr+2:ptr+4], byteorder='big')

# Print question information
print("\nDNS Question Info:")
print(f"Domain: {domain}")
print(f"QTYPE: {qtype} (1 = A Record)")
print(f"QCLASS: {qclass} (1 = Internet Class)")

Step 4: Expected Output

Running this code will give you clear, parsed data:

DNS Header Info:
ID: 257
QR: 0 (0 = Query, 1 = Response)
OpCode: 0 (0 = Standard Query)
AA: 0
TC: 0
RD: 0
RA: 0
Z: 0
AD: 0
CD: 0
RCODE: 0
Question Count: 1

DNS Question Info:
Domain: www.googletagmanager.com
QTYPE: 1 (1 = A Record)
QCLASS: 1 (1 = Internet Class)

Key Takeaway

You can't decode the entire DNS packet as ASCII because the header uses binary numerical data. Instead, you need to:

  1. Split the packet into its structured sections (header, question, etc.)
  2. Use int.from_bytes() to convert binary bytes to integers (DNS uses big-endian byte order)
  3. Use bitwise operations to extract individual flags from the 16-bit flags field

内容的提问来源于stack exchange,提问作者allineone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:28:08