rust-stunclient

A lightweight synchronous and async Rust client for STUN (RFC 5389) that resolves a UDP socket's public IP address and port.

Library
Cargo
v0.4.2
23stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
22/100Needs Attention
Development Activity0
Maintenance0
Community16
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
60/100Good
Architecture65
Code Quality52
Innovation58
Learning Curve65

stunclient is a minimal Rust library that implements the client side of the STUN (Session Traversal Utilities for NAT) protocol defined in RFC 5389, letting an application discover the public-facing IP address and port for one of its own UDP sockets. It builds STUN binding requests with the stun_codec crate, sends them to any conventional STUN server (the public Google server is bundled as a convenient default), retries on timeout, and decodes the XOR-MAPPED-ADDRESS or MAPPED-ADDRESS attribute out of the reply.

The crate exposes both a blocking, std::net::UdpSocket-based API and, behind the default “async” Cargo feature, a Tokio-based async API with the same configuration surface (timeout, retry interval, custom SOFTWARE attribute). It has no dependency on any particular STUN server operator, making it a small building block for NAT traversal, hole punching, and peer-to-peer connectivity code.

What You Get

  • A synchronous StunClient built on std::net::UdpSocket for blocking binding requests
  • An async StunClient built on tokio, gated behind the default “async” Cargo feature, with the same configuration surface
  • Configurable timeout, retry_interval, and SOFTWARE attribute via builder-style setter methods
  • A with_google_stun_server() convenience constructor and a just_give_me_the_udp_socket_and_its_external_address() one-liner for quick experiments
  • Runnable examples (client.rs, client_async.rs) demonstrating both sync and async usage from the command line

Common Use Cases

  • Discovering a host’s public IP:port before establishing a peer-to-peer UDP connection (WebRTC-style signaling, VoIP, game netcode)
  • Implementing NAT hole punching by learning the externally mapped address and port of a locally bound UDP socket
  • Diagnosing NAT behavior from a script or CLI tool by querying a known STUN server
  • Building higher-level NAT traversal or STUN/TURN tooling on top of a small, dependency-light STUN client primitive

Under The Hood

Architecture The crate is a single-file library (src/lib.rs, roughly 300 lines) built around one StunClient struct holding configuration (timeout, retry_interval, stun_server, software) with builder-style setters. Two largely independent code paths implement the actual query: query_external_address (blocking, std::net::UdpSocket, a manual retry loop driven by socket read timeouts) and query_external_address_async (behind the “async” feature, a tokio::select! loop). Both share internal get_binding_request() and decode_address() helpers that wrap the stun_codec crate’s Message/MessageEncoder/MessageDecoder types, and all failure modes funnel into a single #[non_exhaustive] Error enum. There’s no shared trait between the sync and async query paths, so the retry/timeout logic is duplicated rather than abstracted — an acceptable tradeoff at this size, but a maintenance point if the protocol logic grows.

Tech Stack Rust, 2018 edition, published on crates.io. Core dependencies are stun_codec 0.4 (RFC 5389 message encoding/decoding) and bytecodec 0.5 (the encode/decode trait machinery stun_codec is built on), plus rand 0.9 for STUN transaction ID generation. tokio 1.x (features: time, net, macros) is an optional dependency behind the “async” feature, which is also the crate’s default feature; a dev-dependency on tokio’s “rt” feature supports the async test. No build script, ORM, database, or web framework is involved — this is a narrow protocol-client crate with a minimal dependency footprint.

Code Quality The crate has two tests in a #[cfg(test)] module (one sync, one async) that exercise the real query path against the live public Google STUN server rather than a mock — useful as smoke tests but network-dependent and not isolated unit tests. No dedicated tests/ directory and no CI configuration (no .github/workflows) were found in the repository. Error handling is explicit throughout the library code: every fallible operation returns Result<T, Error> with a typed, #[non_exhaustive] Error enum implementing std::error::Error and Display, rather than swallowing failures or using strings. The crate enforces #![deny(missing_docs)], so every public item carries a doc comment; naming follows idiomatic Rust snake_case conventions. The two convenience functions that call .unwrap() internally (with_google_stun_server, just_give_me_the_udp_socket_and_its_external_address) are explicitly documented as prototype/demo-only and “may panic,” which is an honest tradeoff rather than a hidden one.

API Design The public surface is deliberately small and easy to pick up: StunClient::new(stun_server) plus builder-style setters covers the configurable path, while with_google_stun_server() and just_give_me_the_udp_socket_and_its_external_address() let a caller get a working example running in one or two lines. The sync and async methods are named symmetrically (query_external_address / query_external_address_async), which keeps the mental model consistent when switching between blocking and async call sites. Every public item is documented thanks to the crate-level lint, and the README mirrors both usage patterns directly. On the downside, callers must import std::net (and, for async, tokio::net) themselves since the crate doesn’t re-export them, and a couple of error variants (NoAddress(()), Timeout(())) use an odd unit-typed newtype instead of a plain unit variant.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search