dnslib
A pure-Python library for encoding, decoding, and building DNS wire-format packets, with a lightweight framework for writing custom DNS servers.
Repository Health
Technical Analysis
dnslib gives Python code direct access to the DNS wire protocol: it parses raw DNS packet bytes into typed Python objects (DNSRecord, DNSHeader, DNSQuestion, and dozens of resource-record types covering A, AAAA, MX, TXT, SOA, SRV, DNSSEC records, and more) and packs them back into bytes for sending over the wire. Beyond raw encode/decode, it also converts between wire format, BIND-style zone-file text, and dig output, which makes it useful for building test fixtures, debugging tools, and DNS-aware scripts without hand-rolling packet parsing.
The library ships a small socketserver-based framework (dnslib.server) for standing up custom DNS resolvers — implementing a resolver is usually just subclassing BaseResolver and overriding one resolve() method. Several ready-to-run examples are included (fixed-response, zone-file-backed, shell-driven, proxying, and intercepting resolvers), making it a common building block for DNS proxies, test servers, and traffic-inspection tools rather than a full production nameserver implementation.
What You Get
- Full parse/pack support for DNS packets — header, question, answer, authority, and additional sections — round-tripping to and from raw wire-format bytes.
- Typed resource-record classes for the common record types (A, AAAA, CNAME, MX, TXT, SOA, NS, PTR, SRV, NAPTR, DNSKEY, RRSIG, and more), each validated against its RFC-defined structure.
- Conversion helpers between wire format, BIND-style zone-file text (
RR.fromZone), anddigcommand output, useful for generating and comparing test fixtures. - A minimal
dnslib.serverframework (DNSServer/DNSHandler/BaseResolver) for writing a custom resolver by overriding a singleresolve()method. - Ready-to-run example resolvers and utilities — fixedresolver, zoneresolver, shellresolver, a DNS proxy, and an intercepting proxy — usable directly or as a starting point.
- A DNSSEC-aware EDNS0/OPT helper for adding the DO flag and other extended DNS mechanisms to outgoing queries.
Common Use Cases
- Parsing and inspecting raw DNS packets captured from the wire or a pcap for debugging or analysis.
- Programmatically constructing DNS queries and synthetic responses for testing DNS-aware application code.
- Standing up a lightweight local or test-environment DNS resolver that returns fixed, zone-file-driven, or dynamically computed answers.
- Building a DNS proxy or intercepting proxy to inspect, log, or rewrite DNS traffic in transit.
- Converting between DNS wire format and BIND zone-file text when generating or validating test fixtures.
Under The Hood
Architecture
dnslib centers on dnslib/dns.py, a large module defining DNSRecord, DNSHeader, DNSQuestion, and dozens of resource-record (RD) subclasses, one per DNS record type, each implementing its own RFC-defined parse/pack logic. This sits on top of a handful of small, focused low-level modules: buffer.py (a position-tracking byte buffer with error handling), label.py (DNSLabel/DNSBuffer, handling DNS name compression), bit.py (bit-field get/set helpers for packed header flags), bimap.py (the two-way code<->name lookup used for QTYPE/CLASS/RCODE/OPCODE tables), and lex.py/digparser.py (zone-file and dig-output tokenizing). server.py layers a socketserver-based DNSServer/DNSHandler on top of this parsing core, delegating all protocol logic to a BaseResolver.resolve() interface that example resolvers (fixedresolver, zoneresolver, shellresolver, proxy, intercept) subclass. Data flows from raw bytes through Buffer/DNSBuffer into typed objects via DNSRecord.parse(), and back out via DNSRecord.pack(); because every RD type delegates its own encoding to the shared buffer/label abstractions, changes to compression-pointer handling in those low-level modules would ripple across every record type.
Tech Stack
The library has no runtime dependencies outside the Python standard library — it uses socket, socketserver (via server.py), struct, binascii, collections, and similar stdlib modules for all binary handling, and is packaged with a plain setup.py (no pyproject.toml or modern build backend). It targets both Python 2.7 and Python 3.2+, reflecting its long maintenance history, and ships its own small CLI utilities (client.py, proxy.py, intercept.py) rather than depending on a third-party CLI framework. There is no database layer or web framework involved; the deployment target is either as an imported library or as a standalone process via dnslib.server.
Code Quality
Testing is unconventional but thorough: rather than a pytest/unittest suite, most modules embed executable doctests in their docstrings (run individually via python dnslib/<module>.py), and a dedicated differential test harness (test_decode.py, driven by run_tests.sh and the GitHub Actions workflow) replays pre-recorded dig output against dnslib’s own parse/pack cycle to catch encode/decode regressions across many real-world record types, including DNSSEC ones. A fuzz.py script adds fuzz-testing of the parser. Error handling is explicit and centralized through a single DNSError exception raised at parse/validation boundaries rather than swallowed. There are no type hints, no mypy/flake8/ruff configuration, and no automated formatter — code style is consistent but manually maintained rather than enforced by tooling. CI runs the full doctest, round-trip, and fuzz suite on every push.
What Makes It Unique
dnslib’s main distinguishing trait is scope discipline: it stays a pure-stdlib wire-format library plus a genuinely minimal resolver framework, rather than growing into a full DNS resolution stack with caching, upstream querying, or DNSSEC validation logic. Its differential testing approach — validating decode/encode output against real dig output rather than only self-consistency — is a practical technique for catching subtle wire-format bugs that pure round-trip tests would miss. The bundled example resolvers (especially the proxy and intercepting proxy) make it a common lightweight choice for DNS traffic inspection and test tooling rather than production nameserver deployment.