System Design

Design scope

The tunnel transfers a file in one direction, from a client to an authoritative server. Rather than encapsulating arbitrary IP packets, it divides the file into chunks and places each encoded chunk in a DNS query name.

The receiver extracts the encoded labels, recovers the protected data, and places the decoded file fragment in a reassembly buffer. Transfer metadata identifies the file, the position of the fragment, and the number of chunks required for completion.

Architecture

The system has three relevant participants:

  1. Client: reads the file, constructs chunks, protects and encodes them, and sends queries.
  2. DNS resolution infrastructure: forwards queries under the controlled domain to its authoritative nameserver.
  3. Authoritative server: receives query names, decodes and authenticates chunks, and reconstructs the file.

The client can send directly to the authoritative server or to a recursive resolver. Through recursive resolution, the client sends an ordinary DNS request to the resolver, which eventually delivers the queried name to the domain’s authoritative server.

FTExfil processing diagram with client chunk construction, key exchange, encoding, DNS transport, server decoding, and file reassembly

Sequence diagram of the proposed DNS tunneling tool.

Transfer pipeline

The transfer proceeds through the following stages:

  1. Establish a session, either with a fixed development key or an ECDH exchange.
  2. Read the input file as a byte sequence.
  3. Divide it into payload fragments.
  4. Append a metadata section to each fragment.
  5. Encrypt and authenticate the complete chunk.
  6. Transform the protected data into the selected DNS-compatible representation.
  7. Append the controlled domain to form a complete query name.
  8. Send the query to the configured server or resolver.
  9. At the receiver, extract the encoded labels and reverse the representation.
  10. Authenticate, decrypt, and parse the chunk.
  11. Insert the payload at its indexed position in the corresponding transfer buffer.
  12. Reconstruct the file when every expected chunk is present.

The transport and reassembly logic are independent of the selected encoding. This allows regex/FTE, weighted Huffman, and the Base32 baseline to operate within the same file-transfer pipeline.

Chunk sizes

The configured size specifies the total pre-encryption chunk length, including metadata. The metadata length MM is fixed at 16 bytes, so the maximum file-payload length NN in a full chunk is

N=size−M=size−16. N=\mathrm{size}-M=\mathrm{size}-16.

The final fragment can be shorter than NN. Its actual length is recorded in the metadata rather than inferred from the configured size. The encryption envelope and the subsequent encoding expand the chunk further, so the configured size must leave enough capacity for those layers as well.

Metadata layout

Each plaintext chunk has the structure

<file-payload> || <16-byte metadata>
FieldTypeSizePurpose
versionu81 byteIdentifies the metadata format.
flagsu81 byteProvides room for transfer options and extensions.
payload_lenu162 bytesRecords the actual file-payload length in this chunk.
chunk_indexu324 bytesIdentifies the fragment’s position in the file.
total_chunksu324 bytesRecords the number of fragments required for completion.
file_idu324 bytesGroups chunks belonging to the same file.

The metadata supports three important properties. Payload length handles a short final fragment. Indexes permit out-of-order arrival. File identifiers and total counts allow simultaneous receiver state and completion checks without depending on arrival order.

Session establishment

When key exchange is enabled, the client sends an initial TXT query containing a transfer identifier, the chosen curve, and its public key. The server replies with its public key. Both derive the same symmetric encryption and authentication keys from the ECDH secret.

The none mode bypasses this exchange and uses the fixed key provided for development. It is useful for testing the pipeline, but does not provide meaningful secrecy.

Query-name assembly

Each encoded chunk is placed before the controlled domain:

<encoded-chunk>.<controlled-domain>.<TLD>

This structure preserves normal DNS routing. The controlled suffix determines which authoritative server receives the query; the labels before it contain the encoded chunk.

The assembled name must satisfy both the per-label limit and the complete-name limit. A longer suffix leaves less room for encoded payload. With weighted Huffman output, name length also depends on the particular ciphertext bits, so a capacity estimate based on average codeword length requires a margin.

Sending and acknowledging queries

The client generates one DNS query per encoded chunk. Runtime options control query type, timeout, retries, retry delay, and an optional delay between queries. These settings govern delivery and pace without changing the underlying representation.

The server returns a DNS response code indicating success or error. The client tracks the outcome of each query and can retry according to its configured policy. Repeated delivery of an already accepted chunk must not append the same file data a second time.

Receiver processing

The server runs a UDP DNS loop. For each incoming request it:

  1. Parses the DNS packet and obtains the query name.
  2. Checks whether the name belongs to the configured domain.
  3. Extracts the encoded part of a matching name.
  4. Reverses the selected encoding and validates the protected chunk.
  5. Separates payload from metadata and validates its fields.
  6. Inserts the fragment into the buffer selected by file identifier and chunk index.
  7. Returns the appropriate DNS response.

Unrelated DNS traffic is not treated as a transfer chunk. Duplicate indexes are counted but not reinserted. Sessions that do not complete can expire according to the configured lifetime.

Reassembly

Completion occurs when all expected chunk indexes are present. Payloads are concatenated in index order, producing the original file byte stream. The output uses a .bin extension with a generated name:

recv_<file_id>_<total_chunks>_<timestamp>.bin

The receiver’s reconstruction therefore depends on the explicit metadata, not on the lexical order of names or the order of packet arrival.

Design limitations

Encoding can reduce certain payload indicators, but the transport still produces many distinct queries to a controlled domain. Long query names and high DNS volume remain visible. Frequency shaping also requires more characters per encrypted bit on average, increasing the number of chunks needed for a transfer.