DNSHide - A Generic Protocol for embedding content in DNS TXT Records
The foundational decision in designing a generic, application-layer protocol that leverages the Domain Name System (DNS) without altering its core infrastructure is the selection of an appropriate transport carrier.
We want to use existing DNS structure and re-purpose it, hence the choice of the standard DNS TXT record. The flexibility of TXT records stems from their specification, which allows insertion of arbitrary text. We encapsulate messages in TXT records which can be chained across multiple (sub)domains.
DNS TXT records are governed by a set of well-defined constraints that directly influence the design of this protocol. One common misconception is that a TXT record is limited to 255 characters, but the specification actually says that this limit applies to each individual string within a record, not the record itself.
According to the original specification, if the content of a TXT record exceeds 255 characters, it must be split into multiple strings, each of which is treated as a separate part of the RDATA field. Maximum record length for TXT recs is limited only by RDLENGTH field in the DNS message header. That field is a 16 bit unsigned integer, so 64k bytes maximum per TXT record.
The original UDP specification had a maximum payload size of 512 bytes, in keeping with the quick and dirty nature of UDP. The failover is to use TCP, although later extensions of the protocol (EDNS) allow clients and servers to negotiate extended payloads up to 4k bytes.
We chose not to exceed 255 chars, despite the ability to do so, based on these considerations:
- older resolvers might not send more than 255 characters
- some resolvers migt be configured to truncate long records
- UDP restrictions might cause issues and TCP has much more overhead
- we can use many individual records and avoid all size constraints
Using thousands of TXT records is problematic in that query results will be slow. This was the rationale that led us to opt for striping data across multiple domains.
To point to the next and previous domains in a sequence, we use a dedicated TXT rec with the key dnav, and the value of the domain name. Next and previous domains must be stored in separate TXT recs.
The payload of a DNShide record is encoded as a sequence of TXT records for one or more domains. The individual TXT records are parsed sequentially in order to re-assemble a file.
We chose to encode our uncompressed text payloads using base58.
To create a protocol that is minimalist, generic, and capable of handling diverse data, we use a hybrid message structure. The core idea is to use the TXT record to carry a small header that declares the protocol's content type and sequencing information, followed by a compact blob containing the actual message content.
-- encoding specs: coming soon --
Implementations must also be aware of the practical transport limitations. When communicating over UDP, the effective payload size per domain is constrained by the EDNS buffer size negotiated between the client and server, which is typically 4096 bytes.