1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
//! Domain WHOIS and RDAP lookups, with availability detection you can audit.
//!
//! ```no_run
//! # #[cfg(feature = "blocking")] {
//! use monovm_whois::WhoisClient;
//!
//! let client = WhoisClient::new()?;
//! let lookup = client.lookup("example.com")?;
//!
//! println!("{} is {}", lookup.domain, lookup.availability());
//! if let Some(record) = &lookup.record {
//! println!("registrar: {:?}", record.registrar);
//! println!("expires: {:?}", record.expires);
//! }
//! # }
//! # Ok::<(), monovm_whois::Error>(())
//! ```
//!
//! # The problem this crate is about
//!
//! WHOIS has no status codes. A registry answering "that domain is free", one
//! answering "you are querying too fast", and one answering "I do not serve that
//! suffix" all send prose over the same socket, and every one of them can contain
//! the word *available*. Libraries in this space overwhelmingly resolve that
//! ambiguity the same way — anything that is not recognisably a record is treated as
//! availability — which means a rate-limited registry reports its entire zone as
//! free to register.
//!
//! This crate never does that. A response that cannot be interpreted produces
//! [`Error::Inconclusive`], a refusal produces [`Error::Refused`], and neither is
//! ever an [`Availability`]. There is deliberately no `Availability::Unknown`,
//! because an uncertain answer that renders as "available" is the one outcome a
//! caller must not be handed.
//!
//! # How it is put together
//!
//! Six layers, each with one job and no knowledge of the others:
//!
//! | Layer | Responsibility | Key abstraction |
//! |---|---|---|
//! | [`domain`] | Validated values — a name, a suffix, a verdict | [`DomainName`], [`Tld`] |
//! | [`registry`] | Which registry serves a suffix, and how to reach it | [`RegistryProvider`](registry::RegistryProvider) |
//! | [`transport`] | Talking to servers. The only I/O in the crate | [`Transport`](transport::Transport) |
//! | [`cache`] | Not asking twice | [`ResponseCache`](cache::ResponseCache) |
//! | [`detect`] | Deciding what a response said | [`AvailabilityRule`](detect::AvailabilityRule) |
//! | [`parser`] | Turning a record into data | [`RecordParser`](parser::RecordParser) |
//!
//! [`client`] composes them. Every layer is a trait with a bundled implementation,
//! so a caller can replace any one of them — a private registry list, a transport
//! over a proxy, a Redis cache, an extra detection rule for a registry that words
//! things unusually — without forking the crate.
//!
//! # What you get
//!
//! - **Coverage.** 872 curated suffixes plus IANA's RDAP bootstrap registry, for
//! over 1600 in total.
//! - **RDAP.** A full RFC 9083 client and typed model, used as a fallback when port
//! 43 refuses and preferred when [`Preference::Rdap`] is set. RDAP's 404 makes availability a fact rather than an inference.
//! - **Structured records.** [`WhoisRecord`] with typed dates,
//! statuses, name servers and contacts, instead of the server's raw text.
//! - **Referral chasing.** Thin registries answer with a pointer to the registrar;
//! following it is the difference between knowing a domain is taken and knowing
//! who holds it.
//! - **Auditable verdicts.** Every answer names the rule that produced it and why,
//! and [`WhoisClient::explain`] shows what every rule thought.
//! - **Rate limiting, retries and caching**, composed as transport decorators.
//! - **Both runtimes.** [`WhoisClient`] and [`AsyncWhoisClient`].
//!
//! # Features
//!
//! | Feature | Default | Gives you |
//! |---|---|---|
//! | `blocking` | yes | [`WhoisClient`] and the synchronous transports |
//! | `rdap` | yes | RDAP over HTTPS, and the typed [`rdap`] model |
//! | `parser` | yes | [`WhoisRecord`] and record parsing |
//! | `async` | no | [`AsyncWhoisClient`] and the Tokio transports |
//! | `iana-bootstrap` | no | Refreshing the RDAP registry from IANA at runtime |
//! | `cli` | no | The `monovm-whois` command line tool |
//! | `mock` | no | [`MockTransport`](transport::MockTransport), for your own tests |
//!
//! # A note on what a verdict means
//!
//! Availability detection over WHOIS is inference, and this crate is explicit about
//! how much. Every [`Verdict`](detect::Verdict) carries a
//! [`Confidence`](detect::Confidence): `Definitive` for a structured RDAP answer,
//! `High` for wording curated for that specific registry, `Medium` for a pattern
//! that generalises, `Low` for the one inference drawn from absence of evidence. A
//! caller who needs certainty can require `Definitive` and use
//! [`Preference::RdapOnly`].
pub use ;
pub use ;
pub use ;
pub use CheckReport;
pub use ;
pub use ;
pub use ;
/// The crate version, from `Cargo.toml`.
pub const VERSION: &str = env!;
/// Look one domain up with a default client.
///
/// A convenience for a one-off query. Anything repeated should build a
/// [`WhoisClient`] and keep it: a client carries the rate limiter and the cache, and
/// a fresh one per query has neither.
///
/// ```no_run
/// # #[cfg(feature = "blocking")] {
/// let lookup = monovm_whois::lookup("example.com")?;
/// println!("{}", lookup.availability());
/// # }
/// # Ok::<(), monovm_whois::Error>(())
/// ```
/// Whether one domain is free to register, with a default client.
///
/// A premium or reserved name answers `false`; a query that could not be answered is
/// an error rather than `false`.
/// The availability of one domain, with a default client.