dnscrypt 0.1.1

A pure-Rust DNSCrypt v2 client library — sync and async support.
Documentation

dnscrypt

Crates.io Documentation License

A pure-Rust, high-assurance implementation of the DNSCrypt v2 client protocol. Supporting both synchronous and asynchronous (Tokio-backed) resolution, this crate provides transport-level encryption and cryptographic authentication for all your DNS queries.

No C bindings, no OpenSSL dependencies, and no unsafe code.

Features

  • Pure Rust Cryptography: Built entirely on high-assurance cryptographic implementations (chacha20, poly1305, x25519-dalek, ed25519-dalek).
  • Memory Security: Integrates the zeroize crate to securely wipe sensitive keying material and decrypted buffers from memory on drop.
  • MITM Resistance: Relies on pinned resolver public keys hardcoded at compile-time to prevent CA-level compromise/spoofing.
  • Robust TCP Fallback: Automatically and transparently retries queries over TCP if the decrypted DNSCrypt response indicates truncation (TC bit set).
  • Consensus Voting (max-security mode): Queries multiple DNSCrypt providers in parallel and returns only consensus-voted results, discarding minority/unconfirmed IP addresses.
  • Reqwest Integration: Ships a drop-in DnscryptResolver to route all host lookups in a reqwest::Client through DNSCrypt.
  • Async & Sync Support: Complete async/sync dual API.

Feature Flags

Feature Description Default
tokio Enables asynchronous API, async session establishment, and async DNS querying using Tokio. Yes
zeroize Wraps all sensitive state, keys, nonces, and decrypted payloads in zero-on-drop containers. Yes
reqwest Enables the DnscryptResolver for integration with reqwest::Client. Yes
max-security Aggregates and votes on results from multiple providers, returning only majority-voted answers. No

Cryptographic Specification

Primitive Protocol Role Crate
X25519 DH Per-session key agreement with the resolver x25519-dalek
HChaCha20 Key derivation from the raw Diffie-Hellman secret chacha20
XChaCha20-Poly1305 Authenticated encryption of queries/responses chacha20 / poly1305
Ed25519 Resolver certificate signature verification ed25519-dalek

1. Simple Synchronous Resolution

use dnscrypt::{resolve, HARDCODED_RESOLVERS, Error};

fn main() -> Result<(), Error> {
    // Resolve github.com using default built-in resolvers
    let ips = resolve(HARDCODED_RESOLVERS, "github.com")?;
    println!("github.com => {:?}", ips);
    Ok(())
}

2. Asynchronous (Tokio) Resolution

Make sure the tokio feature flag is enabled:

use dnscrypt::{resolve_async, HARDCODED_RESOLVERS, Error};

#[tokio::main]
async fn main() -> Result<(), Error> {
    let ips = resolve_async(HARDCODED_RESOLVERS, "github.com").await?;
    println!("github.com => {:?}", ips);
    Ok(())
}

3. Reqwest Client Integration

Enable the reqwest feature flag to use DnscryptResolver:

use dnscrypt::DnscryptResolver;
use std::sync::Arc;

#[tokio::main]
async fn main() {
    let resolver = Arc::new(DnscryptResolver::new());
    
    let client = reqwest::Client::builder()
        .dns_resolver(resolver)
        .build()
        .unwrap();

    let res = client.get("https://github.com")
        .send()
        .await
        .unwrap();

    println!("Status: {}", res.status());
}

4. Custom DNSCrypt Resolvers

Bypass the default list of resolvers by supplying your own custom configurations:

use dnscrypt::{resolve, HardcodedResolver};
use std::net::{SocketAddr, IpAddr, Ipv4Addr};

const MY_RESOLVERS: &[HardcodedResolver] = &[
    HardcodedResolver {
        ip: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(9, 9, 9, 9)), 8443),
        provider_name: "2.dnscrypt-cert.quad9.net",
        provider_pk: [
            // Insert your resolver's Ed25519 public key bytes here
            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
            0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
            0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
        ],
    }
];

fn main() {
    let ips = resolve(MY_RESOLVERS, "github.com").unwrap();
    println!("Resolved: {:?}", ips);
}

Security Hardening

This library has undergone systematic hardening to defend against transport-level threats:

  • Zeroization: Sensitive memory containers (keys, nonces, plaintext packets) are systematically zero-filled on drop when the zeroize feature is active.
  • Randomized Padding & TXIDs: Queries are padded to randomized boundaries to prevent side-channel traffic analysis. Transaction IDs are derived from a cryptographically secure random source (getrandom).
  • Encrypted TC Detection: The parser decrypts responses before validating truncation. If a response is truncated inside the secure envelope, the resolver automatically executes a retry over TCP.
  • Majority Voting: When configured with max-security, minority/non-consensus IP responses are automatically discarded to prevent MITM attacks from compromised or corrupted nodes.

License

Licensed under either of:

at your option.