RDAP Rust Client
A modern, elegant RDAP (Registration Data Access Protocol) client written in Rust with beautiful colored output.
Features
- Modern & Fast - Asynchronous I/O with Tokio, efficient HTTP client, fast JSON parsing
- Beautiful Output - Colorized terminal output, pretty-printed tables, clear hierarchical display
- WHOIS-style Output - Traditional WHOIS/RPSL-style plain text output (
-f whois)
- Full RDAP Support - Domain, IP (v4/v6), ASN, entity, nameserver, and search queries
- Smart Bootstrap - Automatic IANA bootstrap service discovery for domains, IPs, ASNs, and entities
- Entity Object Tags - RFC 8521 support for automatic entity handle resolution (e.g.
ORG-LA1994-RIPE, YANGY12-ARIN)
- RIR Shortcuts - Built-in
-r/--rir flag with predefined RIR RDAP URLs (no need to memorize server URLs)
- Query Auto-Detection - Automatically detects domains, IPs, ASNs, and tagged entity handles
- Configurable - Custom timeouts, explicit server override, multiple output formats
Installation
From Source
git clone https://github.com/Akaere-NetWorks/rdap.git
cd rdap
cargo build --release
sudo cp target/release/rdap /usr/local/bin/
Using Cargo
cargo install rdap
Usage
Basic Queries
rdap example.com
rdap 192.0.2.1
rdap 2001:db8::1
rdap AS15169
rdap 15169
rdap ORG-LA1994-RIPE
rdap YANGY12-ARIN
Entity Queries
Entity handles with known RIR tag suffixes (e.g. -RIPE, -ARIN, -APNIC) are automatically
detected and resolved via the IANA object tags bootstrap registry (RFC 8521):
rdap ORG-LA1994-RIPE
rdap YANGY12-ARIN
rdap -r ripe LiuHaoRan-MNT
rdap -r arin GOGL
rdap -t entity -s https://rdap.db.ripe.net/ RIPE-NCC-END-MNT
Supported --rir values:
| Name |
RDAP Server |
ripe |
https://rdap.db.ripe.net/ |
arin |
https://rdap.arin.net/registry/ |
apnic |
https://rdap.apnic.net/ |
lacnic |
https://rdap.lacnic.net/rdap/ |
afrinic |
https://rdap.afrinic.net/rdap/ |
frnic |
https://rdap.nic.fr/ |
glauca |
https://whois-web.as207960.net/rdap/ |
norid |
https://rdap.norid.no/ |
When --rir is specified, the query type defaults to entity (can be overridden with -t).
Output Formats
rdap example.com
rdap -f whois AS213605
rdap -f whois ORG-LA1994-RIPE
rdap -f json example.com
rdap -f json-pretty example.com
| Format |
Description |
text |
Colored terminal output (default) |
whois |
Traditional WHOIS/RPSL-style plain text, pipe-friendly |
json |
Compact JSON |
json-pretty |
Pretty-printed JSON |
Advanced Options
rdap -t domain example.com
rdap -t entity -r ripe RIPE-NCC-END-MNT
rdap -s https://rdap.verisign.com/com/v1 example.com
rdap -r ripe LiuHaoRan-MNT
rdap -v example.com
rdap --timeout 60 example.com
Examples
Domain Query
$ rdap example.com
Domain Name: EXAMPLE.COM
Handle: 2336799_DOMAIN_COM-VRSN
Object Class: domain
Status: client delete prohibited
Status: client transfer prohibited
Status: client update prohibited
Nameserver: A.IANA-SERVERS.NET
Nameserver: B.IANA-SERVERS.NET
Delegation Signed: yes
DS Key Tag: 370
DS Algorithm: 13
DS Digest Type: 2
DS Digest: BE74359954660069D5C63D200C39F5603827D7DD02B56F120EE9F3A86764247C
Registration: 1995-08-14T04:00:00Z
Expiration: 2026-08-13T04:00:00Z
Last Changed: 2025-08-14T07:01:39Z
Last Update: 2025-11-04T20:54:25Z
Entity Handle: 376
Role: registrar
Name: RESERVED-Internet Assigned Numbers Authority
IANA Registrar ID: 376
IP Query
$ rdap 8.8.8.8
Handle: NET-8-8-8-0-2
Start Address: 8.8.8.0
End Address: 8.8.8.255
IP Version: v4
Name: GOGL
Type: DIRECT ALLOCATION
Parent Handle: NET-8-0-0-0-0
Status: active
Port43: whois.arin.net
last changed: 2023-12-28T17:24:56-05:00
registration: 2023-12-28T17:24:33-05:00
Entity Handle: GOGL
Role: registrant
Name: Google LLC
AS Number Query
$ rdap AS213605
AS Number: AS213605
Name: Pysio-Research-NetWork
Handle: AS213605
Object Class: autnum
Status: active
Port43: whois.ripe.net
Registration: 2025-01-10T12:53:39Z
Last Changed: 2025-10-14T13:21:47Z
Entity Handle: LA9082-RIPE
Role: administrative
Role: technical
Role: abuse
Name: LiuHaoRan
Email: team@pysio.online
Phone: +86 19934273163
Entity Query
$ rdap ORG-LA1994-RIPE
Entity Handle: ORG-LA1994-RIPE
Name: Liu HaoRan
Email: team@pysio.online
Phone: +86 19934273163
Address: Fuqing East Street, Chenghua District, Chengdu, Sichuan
Port43: whois.ripe.net
registration: 2025-01-07T17:22:50Z
last changed: 2026-01-13T11:23:15Z
WHOIS-style Output
$ rdap -f whois AS213605
aut-num: AS213605
as-name: Pysio-Research-NetWork
org: ORG-LA1994-RIPE
admin-c: LA9082-RIPE
tech-c: LA9082-RIPE
abuse-c: LA9082-RIPE
mnt-by: AKAERE-NETWORKS-MNT
mnt-by: LiuHaoRan-MNT
mnt-by: RIPE-NCC-END-MNT
status: ACTIVE
created: 2025-01-10T12:53:39Z
last-modified: 2026-02-05T03:28:52Z
source: RIPE
organisation: ORG-LA1994-RIPE
org-name: Liu HaoRan
address: Fuqing East Street, Chenghua District, Chengdu, Sichuan
phone: +86 19934273163
source: RIPE
Entity Query with RIR Shortcut
$ rdap -r ripe -f whois LiuHaoRan-MNT
mntner: LiuHaoRan-MNT
descr: LiuHaoRan-MNT
mnt-by: LiuHaoRan-MNT
created: 2025-01-06T08:29:19Z
last-modified: 2026-01-29T12:28:32Z
source: RIPE
Library Usage
Add this to your Cargo.toml:
[dependencies]
rdap = { git = "https://github.com/Akaere-NetWorks/rdap.git" }
tokio = { version = "1.35", features = ["full"] }
Or use a specific version/branch:
[dependencies]
rdap = { git = "https://github.com/Akaere-NetWorks/rdap.git", branch = "main" }
tokio = { version = "1.35", features = ["full"] }
Basic Query
use rdap::{RdapClient, RdapRequest, QueryType};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = RdapClient::new()?;
let request = RdapRequest::new(QueryType::Domain, "example.com");
let result = client.query(&request).await?;
use rdap::display::RdapDisplay;
result.display(false);
Ok(())
}
Auto-Detection
use rdap::{RdapClient, RdapRequest};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = RdapClient::new()?;
let query = "ORG-LA1994-RIPE";
let query_type = RdapRequest::detect_type(query)?;
let request = RdapRequest::new(query_type, query);
let result = client.query(&request).await?;
match result {
rdap::RdapObject::Domain(domain) => {
println!("Domain: {}", domain.ldh_name.unwrap_or_default());
}
rdap::RdapObject::IpNetwork(ip) => {
println!("IP Network: {}", ip.name.unwrap_or_default());
}
rdap::RdapObject::Autnum(asn) => {
println!("AS Number: AS{}", asn.start_autnum.unwrap_or(0));
}
rdap::RdapObject::Entity(entity) => {
println!("Entity: {}", entity.handle.unwrap_or_default());
}
_ => {}
}
Ok(())
}
RIR Server Lookup
use rdap::{RdapClient, RdapRequest, QueryType};
use rdap::bootstrap::rir_to_rdap_url;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = RdapClient::new()?;
let server = rir_to_rdap_url("ripe").expect("Unknown RIR");
let request = RdapRequest::new(QueryType::Entity, "LiuHaoRan-MNT")
.with_server(server);
let result = client.query(&request).await?;
let whois_text = rdap::whois::format_whois(&result);
print!("{}", whois_text);
Ok(())
}
WHOIS Format Output
use rdap::{RdapClient, RdapRequest, QueryType};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = RdapClient::new()?;
let request = RdapRequest::new(QueryType::Autnum, "AS213605");
let result = client.query(&request).await?;
let whois_text = rdap::whois::format_whois(&result);
print!("{}", whois_text);
Ok(())
}
The format_whois() output is plain text with no ANSI escape codes, making it
suitable for piping to other tools like grep, awk, cut, or custom parsers:
rdap -f whois AS213605 | grep "mnt-by" | awk '{print $2}'
Custom Server
use rdap::{RdapClient, RdapRequest, QueryType};
use url::Url;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = RdapClient::new()?;
let server = Url::parse("https://rdap.verisign.com/com/v1")?;
let request = RdapRequest::new(QueryType::Domain, "example.com")
.with_server(server);
let result = client.query(&request).await?;
Ok(())
}
JSON Output
use rdap::{RdapClient, RdapRequest, QueryType};
use serde_json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = RdapClient::new()?;
let request = RdapRequest::new(QueryType::Domain, "example.com");
let result = client.query(&request).await?;
let json = serde_json::to_string_pretty(&result)?;
println!("{}", json);
Ok(())
}
Working with Domain Data
use rdap::{RdapClient, RdapRequest, QueryType, RdapObject};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = RdapClient::new()?;
let request = RdapRequest::new(QueryType::Domain, "example.com");
let result = client.query(&request).await?;
if let RdapObject::Domain(domain) = result {
println!("Domain: {}", domain.ldh_name.unwrap_or_default());
for status in &domain.status {
println!("Status: {}", status);
}
for ns in &domain.nameservers {
if let Some(name) = &ns.ldh_name {
println!("Nameserver: {}", name);
}
}
if let Some(dnssec) = &domain.secure_dns {
if let Some(signed) = dnssec.delegation_signed {
println!("DNSSEC: {}", if signed { "Signed" } else { "Not signed" });
}
}
for entity in &domain.entities {
if let Some(handle) = &entity.handle {
println!("Entity: {} (roles: {:?})", handle, entity.roles);
}
if let Some(vcard) = &entity.vcard {
if let Some(name) = vcard.name() {
println!(" Name: {}", name);
}
if let Some(email) = vcard.email() {
println!(" Email: {}", email);
}
}
}
for event in &domain.events {
println!("Event: {} at {}", event.action, event.date);
}
}
Ok(())
}
Working with IP Network Data
use rdap::{RdapClient, RdapRequest, QueryType, RdapObject};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = RdapClient::new()?;
let request = RdapRequest::new(QueryType::Ip, "8.8.8.8");
let result = client.query(&request).await?;
if let RdapObject::IpNetwork(network) = result {
println!("Network: {}", network.name.unwrap_or_default());
println!("Range: {} - {}",
network.start_address.unwrap_or_default(),
network.end_address.unwrap_or_default()
);
println!("Type: {}", network.network_type.unwrap_or_default());
if let Some(country) = &network.country {
println!("Country: {}", country);
}
}
Ok(())
}
Working with AS Number Data
use rdap::{RdapClient, RdapRequest, QueryType, RdapObject};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = RdapClient::new()?;
let request = RdapRequest::new(QueryType::Autnum, "AS15169");
let result = client.query(&request).await?;
if let RdapObject::Autnum(asn) = result {
if let Some(start) = asn.start_autnum {
println!("AS Number: AS{}", start);
}
println!("Name: {}", asn.name.unwrap_or_default());
println!("Type: {}", asn.as_type.unwrap_or_default());
if let Some(country) = &asn.country {
println!("Country: {}", country);
}
}
Ok(())
}
Error Handling
use rdap::{RdapClient, RdapRequest, QueryType, RdapObject, RdapError};
#[tokio::main]
async fn main() {
let client = RdapClient::new().unwrap();
let request = RdapRequest::new(QueryType::Domain, "example.com");
match client.query(&request).await {
Ok(result) => {
match result {
RdapObject::Error(err) => {
eprintln!("RDAP Error: {}", err.title.unwrap_or_default());
for desc in &err.description {
eprintln!(" {}", desc);
}
}
_ => {
use rdap::display::RdapDisplay;
result.display(false);
}
}
}
Err(e) => {
match e {
RdapError::Bootstrap(msg) => {
eprintln!("Bootstrap error: {}", msg);
}
RdapError::Http(err) => {
eprintln!("HTTP error: {}", err);
}
RdapError::InvalidQuery(msg) => {
eprintln!("Invalid query: {}", msg);
}
_ => {
eprintln!("Error: {}", e);
}
}
}
}
}
Advanced: Custom Timeout and Configuration
use rdap::{RdapClient, RdapRequest, QueryType};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = RdapClient::new()?
.with_timeout(Duration::from_secs(30));
let request = RdapRequest::new(QueryType::Domain, "example.com");
let result = client.query(&request).await?;
Ok(())
}
Architecture
src/
├── lib.rs # Library entry point
├── main.rs # CLI entry point
├── error.rs # Error types
├── models/ # RDAP data models
│ ├── domain.rs
│ ├── entity.rs
│ ├── autnum.rs
│ ├── ip_network.rs
│ ├── nameserver.rs
│ └── ...
├── client.rs # RDAP client
├── request.rs # Request builder & query type auto-detection
├── bootstrap.rs # IANA bootstrap service discovery (incl. RFC 8521 object tags)
├── cache.rs # Bootstrap cache
├── display.rs # Pretty colored output formatting
└── whois.rs # WHOIS/RPSL-style plain text output
RFCs Implemented
License
MIT License - see LICENSE file for details
Author
AKAERE NETWORKS TECHNOLOGY LTD
Links