Skip to main content

RFC Compliance Checker for DNS Protocol Implementations

Project description

rfc-lint

RFC Compliance Checker for DNS Protocol Implementations

Feed it a pcap. It checks every DNS packet against MUST/SHOULD/MAY requirements from RFC 1035 and RFC 6891, flags violations, and produces structured compliance reports.

Quick Start

# Install
pip install -e ".[dev]"

# Lint a pcap
rfc-lint capture.pcap

# Filter by RFC, output JSON
rfc-lint capture.pcap --rfc 1035 --format json -o report.json

What It Checks

RFC 1035 — DNS Message Format (17 rules)

Rule ID Section Level What It Checks
RFC1035-4.1.1-OPCODE §4.1.1 MUST OPCODE is a defined value (0,1,2,4,5)
RFC1035-4.1.1-RCODE §4.1.1 MUST Response RCODE is defined (0-5)
RFC1035-4.1.1-ZBITS §4.1.1 MUST Z bits are zero in non-EDNS messages
RFC1035-4.1.1-QR §4.1.1 MUST QR bit is 0 or 1
RFC1035-4.1.1-TC §4.1.1 MUST TC bit not set in queries
RFC1035-4.1.1-AA §4.1.1 SHOULD AA bit only in responses
RFC1035-4.1.1-RA §4.1.1 SHOULD RA bit only in responses
RFC1035-4.1.2-QDCOUNT §4.1.2 MUST Standard query has QDCOUNT=1
RFC1035-4.1.2-QCLASS §4.1.2 SHOULD QCLASS is a recognized value
RFC1035-4.1-QDCOUNT-RESP §4.1 SHOULD Response echoes question section
RFC1035-4.1-COUNTS §4.1 MUST Header counts match actual records
RFC1035-4.1-NOERROR-ANS §4.1 SHOULD NODATA detection (informational)
RFC1035-2.3.4-LABEL-LEN §2.3.4 MUST Label <= 63 octets
RFC1035-2.3.4-NAME-LEN §2.3.4 MUST Domain name <= 253 characters
RFC1035-2.3.1-LDH §2.3.1 SHOULD LDH syntax (underscore-prefix exempt)
RFC1035-4.2.1-UDP-SIZE §4.2.1 MUST UDP message <= 512 bytes without EDNS
RFC1035-3.6.2-CNAME-ONLY §3.6.2 MUST CNAME doesn't coexist with other types

RFC 6891 — EDNS(0) (8 rules)

Rule ID Section Level What It Checks
RFC6891-6.1.1-OPT-COUNT §6.1.1 MUST At most one OPT record
RFC6891-6.1.1-OPT-SECTION §6.1.1 MUST OPT in additional section only
RFC6891-6.1.1-OPT-NAME §6.1.1 MUST OPT NAME is root ('.')
RFC6891-6.1.3-VERSION §6.1.3 MUST EDNS version is 0
RFC6891-6.2.3-UDP-SIZE §6.2.3 SHOULD UDP payload size >= 512, != 0
RFC6891-6.1.1-EDNS-Z §6.1.1 MUST EDNS Z reserved bits are zero
RFC6891-7-EDNS-RESP §7 MUST EDNS response requires EDNS query (placeholder)
RFC6891-7-FORMERR-OPT §7 MUST FORMERR response has no OPT

Transaction-Level Rules (4 rules)

These pair queries with responses by (IP, TXID):

Rule ID What It Checks
RFC1035-4.1.1-TXID-MATCH Response TXID matches query
RFC1035-4.1-QUESTION-ECHO Response question matches query question
RFC6891-7-EDNS-PAIR EDNS in response only if query had EDNS
RFC1035-4.1.1-TC-TRUNCATION Truncated response flagged for TCP retry

Total: 29 rules across 2 RFCs + stateful transaction analysis.

Architecture

rfc_lint/
├── _scapy.py          # Scapy compatibility shim
├── models.py          # Severity, Verdict, RuleMetadata, Violation, LintReport
├── parser.py          # DNS packet parser (Scapy → structured dict)
├── engine.py          # BaseRule, RuleRegistry
├── transaction.py     # Stateful query↔response pairing + cross-packet rules
├── linter.py          # Orchestrator + text/JSON report formatters
├── cli.py             # Click CLI
└── rules/
    ├── rfc1035.py     # 17 RFC 1035 rules
    └── rfc6891.py     # 8 RFC 6891 rules
tests/
├── fixtures.py        # 30 crafted pcap generators
└── test_rules.py      # 75 tests (positive + negative for every rule)

Design principles:

  • Each rule is a self-contained class with metadata (RFC, section, severity) and a check() method
  • Rules are declarative — adding new RFCs means adding rule files, not modifying the engine
  • Stateful analysis (transaction pairing) is separate from per-packet rules
  • MUST violations → FAIL, SHOULD violations → WARN

Adding New Rules

from rfc_lint.engine import BaseRule
from rfc_lint.models import RuleMetadata, Severity, Violation

class MyNewRule(BaseRule):
    @property
    def metadata(self):
        return RuleMetadata(
            rule_id="RFC7858-4.2-TLS-VERSION",
            rfc=7858, section="4.2", severity=Severity.MUST,
            title="DoT must use TLS 1.2+",
            description="DNS over TLS connections must use TLS 1.2 or later.")

    def check(self, p: dict) -> list:
        # Your check logic here
        return []

Output Formats

Text — human-readable report with violation details and summary by rule.

JSON — machine-parseable with full violation metadata for CI/CD integration.

Test Suite

pytest tests/ -v    # 75 tests, ~1 second

Every rule has both positive (violation detected) and negative (clean packet passes) tests. Edge cases include IPv6, TCP DNS, EDNS DO bit, underscore service labels, NOTIFY/UPDATE opcodes, NXDOMAIN/SERVFAIL responses, and multi-violation packets.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dns_rfc_lint-1.0.0.tar.gz (32.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dns_rfc_lint-1.0.0-py3-none-any.whl (31.5 kB view details)

Uploaded Python 3

File details

Details for the file dns_rfc_lint-1.0.0.tar.gz.

File metadata

  • Download URL: dns_rfc_lint-1.0.0.tar.gz
  • Upload date:
  • Size: 32.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for dns_rfc_lint-1.0.0.tar.gz
Algorithm Hash digest
SHA256 0084121dc2fe4d8c6771591dc54f1927c070ed32b1a88f48ecc01a0fdbb7c363
MD5 ec52562756994360e4dd8f7b8ed9c541
BLAKE2b-256 9ff30eadc6adf3e8e859b7ebd7e9cb014a9136572690d0e9050b536e65e35cc1

See more details on using hashes here.

File details

Details for the file dns_rfc_lint-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: dns_rfc_lint-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 31.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for dns_rfc_lint-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 00f219f2ee3f23ffb9431d15e7a452268049950fc9d1d7483e8b63369dbf31d7
MD5 0460b4b24f1cfea054c9dd96750e293c
BLAKE2b-256 4d215a28e3e37e02900cba4457164a894b3952dbd624614b09ccc911dc31f3d9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page