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.2.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.2.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.2.1.tar.gz.

File metadata

  • Download URL: rdapify_py-0.2.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.2.1.tar.gz
Algorithm Hash digest
SHA256 c73ff9bf54ba46c9153e2f621efaa8a76c4fcc54869273a390d73e9cd51c48fd
MD5 b7728357b0435203182b6b849578714d
BLAKE2b-256 18c346e819977c3a0ef49682998da94dabd896bf5789dc8d9ea5bc5f8e13b7aa

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rdapify_py-0.2.1-cp38-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 b2dad5fe7c07424bd0336efe52307ca15a8143b55b38a74ba85a293842e34e91
MD5 54c5e2667a453d6eb55b39886fb68990
BLAKE2b-256 c645aa06aacc0407e48ea74024d1dbeb7159299f56dc01c716d34c024e8c42b3

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