whoizz 0.1.0

Tiny, dependency-light RFC 3912 whois client with best-effort field extraction
Documentation
# whoizz

A tiny, dependency-light [RFC 3912](https://datatracker.ietf.org/doc/html/rfc3912)
whois client for Rust. Supports both **domain whois** (via TLD registries)
and **IP whois** (via the Regional Internet Registries — ARIN, RIPE,
APNIC, LACNIC, AFRINIC). Queries IANA for the authoritative server,
follows the `refer:` line, and returns both the raw response and a
best-effort extraction of the common fields (registrar,
creation/expiry dates, name servers).

No async runtime, no custom protocol crates — just `std::net::TcpStream`
and `thiserror`.

## Usage

### Domain lookup

```rust
use whoizz::lookup;

fn main() -> Result<(), whoizz::WhoisError> {
    let r = lookup("rust-lang.org")?;
    println!("registrar:   {:?}", r.registrar);
    println!("created:     {:?}", r.created);
    println!("expires:     {:?}", r.expires);
    println!("nameservers: {:?}", r.nameservers);
    println!("--- raw ---\n{}", r.raw);
    Ok(())
}
```

### IP lookup

```rust
use std::net::IpAddr;
use whoizz::lookup_ip;

fn main() -> Result<(), whoizz::WhoisError> {
    let ip: IpAddr = "8.8.8.8".parse().unwrap();
    let r = lookup_ip(ip)?;
    println!("server: {}", r.server);      // e.g. "whois.arin.net"
    println!("raw:\n{}", r.raw);
    Ok(())
}
```

## What it does

1. Extracts the TLD from the input domain.
2. Queries `whois.iana.org` to discover the authoritative whois server for
   that TLD (the `refer:` line in the IANA response).
3. Opens a TCP connection to the referred server on port 43, sends
   `<domain>\r\n`, reads the response until EOF.
4. Parses the free-text response heuristically to extract the common
   fields. Whois responses are **not** standardized across registries,
   so field extraction is best-effort — the `raw` field always contains
   the full response if you need to parse more yourself.

## Limitations

- **Best-effort parsing.** Registrars format their responses however they
  like. If a field is missing or formatted oddly, it won't be extracted.
  The raw response is always available.
- **Blocking I/O only.** If you need async, wrap the call in
  `tokio::task::spawn_blocking` or equivalent.
- **Rate limits apply.** Most whois servers rate-limit queries per IP.
  Don't hammer them.
- **Private / reserved IP ranges** (RFC 1918, CGNAT, loopback, etc.)
  return whichever response the RIR gives — usually a reservation
  notice rather than a registration record. Filter client-side if you
  only want public IPs.

## License

MIT — see [LICENSE](LICENSE).