14 KiB
IMPLEMENTATION.md
0) Project identity
- Project name: WTFnet
- Binary name:
wtfn - Tagline: “What the fck is my networking doing?”*
Target OS (first-class):
- ✅ Linux (Debian/Ubuntu)
- ✅ Windows 10/11 + Windows Server
1) Workspace layout (Cargo)
Recommended workspace structure: keep the CLI thin, keep logic reusable, and isolate OS-specific code.
wtfnet/
├─ Cargo.toml
├─ crates/
│ ├─ wtfnet-cli/ # bin: clap parsing + output formatting
│ ├─ wtfnet-core/ # shared types: errors, output schema, config, logging init
│ ├─ wtfnet-platform/ # platform traits + OS dispatch
│ ├─ wtfnet-platform-linux/ # Linux implementations (netlink/procfs)
│ ├─ wtfnet-platform-windows/ # Windows implementations (Win32 APIs)
│ ├─ wtfnet-geoip/ # GeoLite2 Country+ASN mmdb
│ ├─ wtfnet-probe/ # ping/tcping/trace + geoip enrichment
│ ├─ wtfnet-dns/ # query/detect/watch
│ ├─ wtfnet-http/ # HTTP/1.1, HTTP/2, optional HTTP/3
│ ├─ wtfnet-tls/ # TLS handshake/cert/verify/alpn
│ ├─ wtfnet-discover/ # mdns/ssdp discovery
│ └─ wtfnet-diag/ # diag bundle orchestrator
└─ docs/ # design/usage docs (optional)
Root Cargo.toml (workspace)
[workspace]
resolver = "2"
members = [
"crates/wtfnet-cli",
"crates/wtfnet-core",
"crates/wtfnet-platform",
"crates/wtfnet-platform-linux",
"crates/wtfnet-platform-windows",
"crates/wtfnet-geoip",
"crates/wtfnet-probe",
"crates/wtfnet-dns",
"crates/wtfnet-http",
"crates/wtfnet-tls",
"crates/wtfnet-discover",
"crates/wtfnet-diag",
]
[workspace.package]
edition = "2021"
license = "MIT"
2) Subcrate responsibilities
2.1 wtfnet-cli (binary)
Purpose
- Defines
clapsubcommands & global flags (--json,--pretty, logging flags, timeouts) - Calls into library crates
- Converts results → human tables OR JSON
Strict rule
- No OS-specific code here.
- No heavy logic here.
2.2 wtfnet-core (shared kernel)
Owns
- Output schema wrapper (
meta,command,data,warnings,errors) - Error taxonomy + exit code mapping
- Config loading (flags/env/config.json)
- Logging initialization (
tracing) - Formatting helpers (durations, bytes, IP formatting)
- “human table renderer” utilities (optional)
2.3 wtfnet-platform (traits + dispatch)
Purpose Expose a stable interface for sysadmin-ish data:
- sys: interfaces/IP/route/DNS config snapshot
- ports: listening sockets + PID/process info
- cert roots: enumerate trusted roots
- neigh: ARP/NDP cache
Pattern
- Define Rust traits like
SysProvider,PortsProvider,CertProvider,NeighProvider - Provide a
Platformobject that selects implementation by target OS
2.4 wtfnet-platform-linux
Linux implementations:
-
netlink-based route/IP/neigh: use
rtnetlink([Docs.rs][1]) -
ports/process mapping:
- either use
listeners(cross-platform) or Linux procfs parsing
- either use
-
DNS snapshot: parse
/etc/resolv.conf+ detectsystemd-resolvedbest-effort
2.5 wtfnet-platform-windows
Windows implementations:
-
use
windowscrate / Win32 APIs- interfaces/routes/neigh via IP Helper APIs
- ports/process via Windows socket/process APIs
- trusted roots via Windows cert store APIs
2.6 wtfnet-geoip
-
Loads local GeoLite2 Country + ASN mmdb
-
Provides one
GeoIpServicewithlookup(ip)returning:- country + ISO
- ASN + org
2.7 wtfnet-probe
Implements:
- ping (v4/v6)
- tcping (connect latency)
- trace (traceroute-like)
- optional
--geoipenrichment
For ping, you can start with surge-ping ([Crates][2]) or ping-async ([Docs.rs][3]) (both async-oriented).
2.8 wtfnet-dns
Implements:
dns query(dig-like)dns detect(poisoning compare)dns watch(passive, best-effort)
Use Hickory DNS (Trust-DNS rebrand) ([Docs.rs][4]) for a solid resolver/client base.
2.9 wtfnet-http
Implements:
-
http head|get -
HTTP/2 support is required (via
reqwest/hyper) -
HTTP/3 optional behind feature flag:
h3+h3-quinn+quinn([GitHub][5])
2.10 wtfnet-tls
Implements:
-
tls handshake|cert|verify|alpn -
Use
rustlsfor handshake parsing -
For system trust verification:
rustls-native-certsfor loading OS roots ([Crates][6])- optionally
rustls-platform-verifierfor “verify like the OS” behavior ([GitHub][7])
2.11 wtfnet-discover
Implements bounded local discovery:
- mDNS service discovery:
mdns-sd([Crates][8]) - SSDP discovery:
ssdp-client([Crates][9])
2.12 wtfnet-diag
Orchestrates:
- sys snapshot
- routes
- ports listen
- neigh list
- optional quick checks: DNS detect, TLS handshake, HTTP head
- bundle export to zip
3) Dependency map (crate graph)
High-level dependency graph:
wtfnet-cli
├─ wtfnet-core
├─ wtfnet-platform
│ ├─ wtfnet-platform-linux (cfg linux)
│ └─ wtfnet-platform-windows (cfg windows)
├─ wtfnet-geoip
├─ wtfnet-probe
├─ wtfnet-dns
├─ wtfnet-http
├─ wtfnet-tls
├─ wtfnet-discover
└─ wtfnet-diag
Rule of thumb
wtfnet-coreshould not depend on OS crates.- feature crates should depend on
wtfnet-core+ minimal extras.
4) Shared libraries / crate dependencies (recommended)
4.1 Core stack (almost everywhere)
- CLI:
clap(+clap_completeoptional) - Async runtime:
tokio - Serialization:
serde,serde_json - Errors:
thiserror(libs),anyhow(CLI glue) - URLs:
url - Time:
time(orchrono),humantime - Tables (human output):
tabledorcomfy-table - Zip bundles:
zip(diag bundle)
4.2 Logging / tracing
Use tracing + tracing-subscriber:
tracingtracing-subscriber(fmt + env filter)- optional:
tracing-appender(log file)
Why this choice
- structured logs
- spans for timing each probe stage
- easy JSON logs to stderr
4.3 Sys/platform related crates
-
Interfaces:
network-interface([Crates][10]) (good for standardized interface listing) -
Linux netlink:
rtnetlink([Docs.rs][1]) -
Ports:
listeners(cross-platform “listening process mapping”) ([GitHub][11])- fallback:
netstat2(cross-platform sockets info) ([Docs.rs][12])
-
Windows APIs:
windowscrate
Suggestion: start with
listenersforports listen/who(it directly targets your use-case). ([GitHub][11])
4.4 GeoIP
maxminddb(read GeoLite2 mmdb)
4.5 DNS
hickory-resolver/ Hickory ecosystem ([Docs.rs][4])
Passive dns watch (optional):
pcaporpnet(feature-gated)
4.6 HTTP / TLS
HTTP:
reqwest(easy HTTP/1.1 + HTTP/2)- or
hyperif you want lower-level control
HTTP/3 (optional feature):
h3+h3-quinn+quinn([GitHub][5])
TLS:
rustlsrustls-native-certs([Crates][6])rustls-platform-verifier(optional) ([GitHub][7])
4.7 Probing
-
ping:
surge-ping([Crates][2])- or
ping-async([Docs.rs][3])
-
tcping: plain
tokio::net::TcpStream::connect+timeout -
trace: start simple (UDP/ICMP-based approaches are trickier & permission-sensitive)
4.8 Discovery
- mDNS:
mdns-sd([Crates][8]) - SSDP:
ssdp-client([Crates][9])
5) Feature flags & compile-time options
In root design, define optional features to avoid heavy deps by default.
Suggested features:
http3→ enablesh3,h3-quinn,quinnpcap→ enables passive DNS watch with packet capturediscover→ enable mdns/ssdp features (if you want a “minimal build”)
Example snippet (in wtfnet-http/Cargo.toml):
[features]
default = ["http2"]
http2 = []
http3 = ["dep:h3", "dep:h3-quinn", "dep:quinn"]
[dependencies]
reqwest = { version = "*", features = ["rustls-tls", "http2"] }
h3 = { version = "*", optional = true }
h3-quinn = { version = "*", optional = true }
quinn = { version = "*", optional = true }
6) Core data model + output schema (do this early)
6.1 Unified JSON wrapper (recommended)
Every command returns:
pub struct CommandEnvelope<T> {
pub meta: Meta,
pub command: CommandInfo,
pub data: T,
pub warnings: Vec<Warn>,
pub errors: Vec<ErrItem>,
}
Key principles:
- stable keys
- additive schema evolution
- logs never pollute stdout JSON
6.2 Exit code mapping
Put this in wtfnet-core and make CLI enforce it:
pub enum ExitKind {
Ok,
Failed,
Usage,
Permission,
Timeout,
Partial,
}
7) Logging design (tracing)
7.1 Init once in main()
wtfnet-core::logging::init(...) should:
- respect CLI flags + env vars
- print to stderr
- support
text|jsonformats
7.2 Use spans for “timing breakdown”
Example: HTTP diagnostics
#[tracing::instrument(skip(client))]
async fn http_head(client: &Client, url: &Url) -> Result<HttpReport, Error> {
let _span = tracing::info_span!("http_head", %url).entered();
// measure dns/connect/tls/ttfb where possible
Ok(report)
}
8) Platform abstraction pattern (Rust traits)
In wtfnet-platform:
pub trait SysProvider {
async fn interfaces(&self) -> Result<Vec<NetInterface>, PlatformError>;
async fn routes(&self) -> Result<Vec<RouteEntry>, PlatformError>;
async fn dns_config(&self) -> Result<DnsConfig, PlatformError>;
}
pub trait PortsProvider {
async fn listening(&self) -> Result<Vec<ListenSocket>, PlatformError>;
async fn who_owns(&self, port: u16) -> Result<Vec<ListenSocket>, PlatformError>;
}
pub trait CertProvider {
async fn trusted_roots(&self) -> Result<Vec<RootCert>, PlatformError>;
}
pub trait NeighProvider {
async fn neighbors(&self) -> Result<Vec<NeighborEntry>, PlatformError>;
}
Then provide:
pub struct Platform {
pub sys: Arc<dyn SysProvider + Send + Sync>,
pub ports: Arc<dyn PortsProvider + Send + Sync>,
pub cert: Arc<dyn CertProvider + Send + Sync>,
pub neigh: Arc<dyn NeighProvider + Send + Sync>,
}
OS dispatch:
cfg(target_os = "linux")→wtfnet-platform-linuxcfg(target_os = "windows")→wtfnet-platform-windows
9) Implementation notes per command area
9.1 sys (interfaces/routes/dns)
Interfaces
- start with
network-interface([Crates][10]) for a normalized list - if you need MTU / gateway details not exposed, add platform-native calls
Linux routes/neigh
rtnetlinkcan manage links/addresses/ARP/route tables ([Docs.rs][1])
Windows routes/neigh
- use Win32 IP helper APIs via
windowscrate
9.2 ports (listen/who)
Best path:
- use
listenerscrate for cross-platform “listening sockets → process” mapping ([GitHub][11])
Fallback:
netstat2provides cross-platform socket information ([Docs.rs][12])
9.3 cert roots
Use:
rustls-native-certsto load roots from OS trust store ([Crates][6])
Filtering:
- by subject substring
- by fingerprint sha256
- expired/not-yet-valid
Diff:
- export stable JSON
- compare by fingerprint (sha256)
9.4 geoip (local mmdb)
Use:
maxminddbExpose:GeoIpService::new(country_path, asn_path)lookup(IpAddr) -> GeoInfo
9.5 dns (query/detect/watch)
Resolver:
- Hickory DNS ecosystem ([Docs.rs][4])
Detect logic (keep deterministic):
- Query multiple resolvers
- Normalize answers (A/AAAA/CNAME)
- “suspicious” if major divergence, NXDOMAIN mismatch, private IP injection patterns
Watch:
- feature-gate packet capture
- document privilege needs clearly
9.6 http (head/get)
HTTP/2:
reqwestmakes this trivial
HTTP/3 (optional):
- use
h3+h3-quinn+quinn([GitHub][5]) Keep it behind--http3and fallback to HTTP/2 when UDP is blocked.
Timing breakdown:
- you’ll get total time easily
- fine-grained DNS/connect/TLS timing may need deeper client hooks (ok to be best-effort)
9.7 tls (handshake/cert/verify)
Handshake:
-
use
rustlsto connect and extract:- version, cipher suite, ALPN
- peer cert chain
Verify:
- use
rustls-platform-verifierif you want OS-like verification ([GitHub][7]) - otherwise load roots via
rustls-native-certs([Crates][6]) and verify with webpki
9.8 neigh (ARP/NDP)
Linux:
rtnetlinkincludes ARP/neighbor operations ([Docs.rs][1])
Windows:
- IP Helper API provides neighbor cache info (implementation detail)
9.9 discover (mDNS + SSDP)
mDNS:
mdns-sd([Crates][8]) Bounded--duration, no spam.
SSDP:
ssdp-client([Crates][9]) Send M-SEARCH, collect responses, parse location/server/usn.
10) Testing strategy
10.1 Unit tests (fast, pure)
- subnet math (
calc) - parsing/formatting
- DNS comparison heuristics (test vectors)
10.2 Snapshot tests (JSON stability)
Use insta:
- ensure
--jsonschema doesn’t drift accidentally
10.3 Integration tests (CI)
-
run non-privileged commands only:
sys ifacescalc subnetdns query example.com Ahttp head https://example.com
11) Coding conventions
- Every command handler returns a structured
CommandEnvelope<T> - Never
println!from libs; return data → CLI prints it --jsonmust be clean stdout (no logs mixed in)- Use timeouts everywhere in probe/dns/http/tls
- Prefer “best-effort + warnings” over hard failure
12) Minimal “first coding milestone” plan
wtfnet-core: envelope + logging init + exit mappingwtfnet-cli: clap skeleton +sys ifaceswtfnet-geoip: load mmdb +geoip <ip>ports listen/whousinglisteners([GitHub][11])dns queryvia Hickory ([Docs.rs][4])http headandtls handshakebasic success pathdiagorchestration + zip bundle