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.2.0.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.2.0-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.2.0.tar.gz.

File metadata

  • Download URL: rdapify_py-0.2.0.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.2.0.tar.gz
Algorithm Hash digest
SHA256 7aeaeb132806476be57ae170d86a8d4db8fc60309a5785284f327732e15b6a37
MD5 526e5e6ecd4e51792ca2e154dc4d3229
BLAKE2b-256 94f30ba991f1c8096b8c40ad5c6391f570dc6e0a0d88e9d7068786dfd3db14de

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdapify_py-0.2.0-cp38-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 08d7d5b210861f686066a5726dc6b54f7e4551f2b2c228751757e7eee03b10dd
MD5 f4ca10bee88cf6620b0812d7fd18cbda
BLAKE2b-256 f1ebdfb198748947e09c7f48499330720b0981abb54239f923d874e2301e6e01

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