Skip to main content

Unified RDAP client for Python, powered by Rust

Project description

rdapify

A fast, secure, production-ready RDAP client library for Rust.

RDAP (Registration Data Access Protocol) is the modern replacement for WHOIS, defined in RFC 9083 and RFC 9224.

Crates.io docs.rs CI License: MIT

rdapify ecosystem

Library Language Package
rdapify-rsyou are here Rust rdapify on crates.io
RDAPify TypeScript / Node.js rdapify on npm
rdapify-nd Node.js (Rust native) rdapify-nd on npm
rdapify-py Python (Rust native) rdapify-py on PyPI

Features

  • 5 query types — domain, IP, ASN, nameserver, entity
  • IANA Bootstrap (RFC 9224) — automatic server discovery, no manual configuration needed
  • SSRF protection — blocks requests to private, loopback, and link-local addresses
  • In-memory cache — configurable TTL and capacity, lock-free via DashMap
  • IDN support — accepts Unicode domain names, normalises to Punycode automatically
  • Retry with back-off — exponential back-off on network errors and 5xx/429 responses
  • Zero OpenSSL — uses rustls (pure Rust TLS)
  • Async-first — built on tokio

Installation

[dependencies]
rdapify = "0.1"

Quick Start

use rdapify::RdapClient;

#[tokio::main]
async fn main() -> rdapify::Result<()> {
    let client = RdapClient::new();

    // Query a domain
    let domain = client.domain("example.com").await?;
    println!("Registrar: {:?}", domain.registrar);
    println!("Expires:   {:?}", domain.expiration_date());

    // Query an IP address
    let ip = client.ip("8.8.8.8").await?;
    println!("Network: {:?}", ip.name);
    println!("Country: {:?}", ip.country);

    // Query an ASN
    let asn = client.asn("AS15169").await?;
    println!("ASN name: {:?}", asn.name);

    Ok(())
}

Usage

Domain Query

let res = client.domain("rust-lang.org").await?;

println!("{}", res.ldh_name.as_deref().unwrap_or("-"));
println!("{:?}", res.status);
println!("{:?}", res.expiration_date());

if let Some(r) = &res.registrar {
    println!("Registrar: {}", r.name.as_deref().unwrap_or("-"));
}

IP Address Query

// IPv4
let res = client.ip("1.1.1.1").await?;

// IPv6
let res = client.ip("2606:4700::1111").await?;

println!("CIDR:    {:?}", res.cidr);
println!("Country: {:?}", res.country);

ASN Query

// Both formats accepted
let res = client.asn("15169").await?;
let res = client.asn("AS15169").await?;

println!("Name: {:?}", res.name);

Nameserver Query

let res = client.nameserver("ns1.example.com").await?;
println!("IPs: {:?}", res.ip_addresses);

Entity Query

let res = client.entity("ARIN-CHA-1", "https://rdap.arin.net/registry").await?;
println!("Name:  {:?}", res.name);
println!("Roles: {:?}", res.roles);

Configuration

use rdapify::{RdapClient, ClientConfig, FetcherConfig, SsrfConfig};
use std::time::Duration;

let client = RdapClient::with_config(ClientConfig {
    cache: true,
    fetcher: FetcherConfig {
        timeout: Duration::from_secs(10),
        max_attempts: 3,
        ..Default::default()
    },
    ssrf: SsrfConfig {
        enabled: true,
        ..Default::default()
    },
    ..Default::default()
})?;

CLI

Enable the cli feature to build the rdapify binary:

rdapify = { version = "0.1", features = ["cli"] }

Or install it directly:

cargo install rdapify --features cli
rdapify domain example.com
rdapify ip 8.8.8.8
rdapify asn AS15169
rdapify nameserver ns1.example.com
rdapify entity ARIN-CHA-1 https://rdap.arin.net/registry

# Machine-readable JSON output
rdapify domain example.com --raw

Performance

All figures are measured with cargo bench (Criterion) on a Linux x86-64 machine. The query benchmarks use a local mock HTTP server (mockito) so results reflect pure Rust overhead — no real network latency is included.

Cache

Benchmark Time
Cache hit (DashMap read, fresh TTL) ~124 ns
Cache miss (key absent) ~24 ns
Cache insert (single write) ~780 ns
Cache eviction (insert at max capacity) ~8.8 µs
Bulk insert — 100 entries ~35 µs
Bulk insert — 1 000 entries ~444 µs

Query pipeline (mock HTTP server)

Benchmark Time Notes
domain() — no cache ~183 µs bootstrap lookup + HTTP fetch + normalise
domain() — cache hit ~2.3 µs ~80× faster than uncached
ip() — no cache ~176 µs
asn() — no cache ~176 µs

Cache brings query latency from ~180 µs → ~2 µs — an 80× speedup for repeated queries within the TTL window.

SSRF validation

Benchmark Time
Public domain URL (allowed) ~141 ns
Public IPv4 URL (allowed) ~220 ns
Private IPv4 URL (blocked at RFC-1918) ~295 ns
Non-HTTPS scheme (blocked immediately) ~145 ns
SSRF disabled (boolean bypass) ~3 ns

Run the benchmarks yourself:

cargo bench
# HTML reports → target/criterion/report/index.html

Language Bindings

Node.js — rdapify-nd

A prebuilt native binding for Node.js. No compiler required — binaries ship for Linux x64, macOS x64/arm64, and Windows x64.

npm install rdapify-nd
const { domain, ip, asn, nameserver, entity } = require('rdapify-nd');

// Domain
const d = await domain('example.com');
console.log(d.registrar?.name);    // "Example Registrar, Inc."
console.log(d.ldhName);            // "example.com"
console.log(d.metadata.timestamp); // "2026-03-21T00:00:00Z"

// IP address
const i = await ip('8.8.8.8');
console.log(i.name);    // "GOOGLE"
console.log(i.country); // "US"

// ASN
const a = await asn('AS15169');
console.log(a.name); // "GOOGLE"

// Nameserver
const ns = await nameserver('ns1.google.com');
console.log(ns.ipAddresses.v4); // ["216.239.32.10"]

// Entity (requires explicit server URL — no global bootstrap for entities)
const e = await entity('ARIN-HN-1', 'https://rdap.arin.net/registry');
console.log(e.handle); // "ARIN-HN-1"

Use with the TypeScript rdapify library for automatic native acceleration:

npm install rdapify rdapify-nd
import { RDAPClient } from 'rdapify';

// backend: 'auto' (default) uses rdapify-nd if installed, falls back to TypeScript
const client = new RDAPClient({ backend: 'auto' });

// Or require it — throws at startup if rdapify-nd is not installed
const client2 = new RDAPClient({ backend: 'native' });

const result = await client.domain('example.com');
console.log(result.metadata.source); // RDAP server URL that served the response

Python — rdapify-py

A prebuilt native extension for Python 3.8+. Ships as abi3 wheels for Linux x64, macOS x64/arm64, and Windows x64.

pip install rdapify-py
import rdapify_py as rdap

# Domain
d = rdap.domain("example.com")
print(d["registrar"]["name"])         # "Example Registrar, Inc."
print(d["ldhName"])                   # "example.com"
print(d["meta"]["queried_at"])        # RFC 3339 timestamp

# IP address
i = rdap.ip("8.8.8.8")
print(i["name"])     # "GOOGLE"
print(i["country"])  # "US"

# ASN
a = rdap.asn("AS15169")
print(a["name"])  # "GOOGLE"

# Nameserver
ns = rdap.nameserver("ns1.google.com")
print(ns["ipAddresses"]["v4"])  # ["216.239.32.10"]

# Entity (requires explicit server URL)
e = rdap.entity("ARIN-HN-1", "https://rdap.arin.net/registry")
print(e["handle"])  # "ARIN-HN-1"

All five functions are synchronous and backed by a tokio runtime under the hood.

MSRV

Minimum supported Rust version: 1.75

License

MIT — see LICENSE

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

rdapify_py-0.1.2.tar.gz (74.0 kB view details)

Uploaded Source

Built Distribution

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

rdapify_py-0.1.2-cp38-abi3-manylinux_2_34_x86_64.whl (2.0 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.34+ x86-64

File details

Details for the file rdapify_py-0.1.2.tar.gz.

File metadata

  • Download URL: rdapify_py-0.1.2.tar.gz
  • Upload date:
  • Size: 74.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.12.6

File hashes

Hashes for rdapify_py-0.1.2.tar.gz
Algorithm Hash digest
SHA256 75c0492eaa825e10ffae139d34d535179f6310d09e69bd23d8f2af71fbb2e5cd
MD5 02311f868c7b3588380a198b7738e15f
BLAKE2b-256 0f7a9f19cf7590335f783dd9e35fb2ac0784f67af55c9869b1d222b1bc94831f

See more details on using hashes here.

File details

Details for the file rdapify_py-0.1.2-cp38-abi3-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for rdapify_py-0.1.2-cp38-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 71b0597db163caeb0086b5b326105b8b8309863b1804d0fee48a769ada0c395c
MD5 459f9d076a40e969278ec9b26d0ac682
BLAKE2b-256 329734fa3b8b6b9a7b8c94c8f506fa27d869c3f59174e824a7cfe2d4f0712c25

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