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.2"

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.1.tar.gz (80.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.1-cp38-abi3-manylinux_2_34_x86_64.whl (2.2 MB view details)

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

File details

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

File metadata

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

File hashes

Hashes for rdapify_py-0.1.1.tar.gz
Algorithm Hash digest
SHA256 e1a1734105bd918667c7cc016343830a806a87c5c8dd6ec5cf930bbb89589831
MD5 0d8949239b9739d04cb16597e3e227bb
BLAKE2b-256 0c39b73a5073c5cdf6e3547d9d5bd8e61d94b20108cef49e68884748f8e828a1

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdapify_py-0.1.1-cp38-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 74ea7f8261e35a0f95cd89053726b67f63e2d9a925c0dd283f379848afedf722
MD5 9bf261a1521a807b9cb9fc700b72220c
BLAKE2b-256 fd98947877a4500075c47728b36dbb15aacee0c244ac6042321bcbcb0f9a873f

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