dnscrypt 0.2.0

A DNSCrypt v2 client library
Documentation

dnscrypt

Crates.io Documentation License

A high-assurance implementation of the DNSCrypt v2 client protocol. Built on an asynchronous, Tokio-backed API, this crate provides transport-level encryption and cryptographic authentication for all your DNS queries.

No unsafe code.

Features

  • Audited Cryptography: X25519, Ed25519, constant-time comparison, and randomness are backed by aws-lc-rs. The certificate's ES version selects between XChaCha20-Poly1305 and XSalsa20-Poly1305 (neither supported by aws-lc-rs), provided by the pure-Rust chacha20/salsa20/poly1305 crates.
  • 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).
  • Reqwest Integration: Ships a drop-in DnscryptResolver to route all host lookups in a reqwest::Client through DNSCrypt.
  • Async Tokio API: A single, focused async API — no synchronous entry points to keep in sync.

Feature Flags

Feature Description Default
reqwest Enables the DnscryptResolver for integration with reqwest::Client. Yes

Cryptographic Specification

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

The AEAD construction used per-session is whichever ES version (0x0001 or 0x0002) the resolver's certificate advertises — not a client-side choice.

Default Resolvers

HARDCODED_RESOLVERS ships 5 endpoints across 2 independently operated providers, each with its Ed25519 provider public key pinned at compile time:

Provider Endpoints
Quad9 9.9.9.9:8443, 149.112.112.112:8443, 149.112.112.9:8443
AdGuard DNS 94.140.14.14:5443, 94.140.15.15:5443

Session establishment stops once it has 3 live sessions, so a typical run only contacts a subset of these. Supply your own list via HardcodedResolver to bypass the defaults entirely.

1. Simple Resolution

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

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

2. 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());
}

3. 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,
        ],
    }
];

#[tokio::main]
async fn main() {
    let ips = resolve(MY_RESOLVERS, "github.com").await.unwrap();
    println!("Resolved: {:?}", ips);
}

Security Hardening

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

  • Randomized Padding & TXIDs: Queries are padded to randomized boundaries to prevent side-channel traffic analysis. Transaction IDs are derived from aws-lc-rs's cryptographically secure random source.
  • 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.

License

Licensed under the 0BSD license.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate shall be licensed as above, without any additional terms or conditions.