Expand description
hostaddr
§Overview
hostaddr provides type-safe, validated types for working with network host identifiers:
Domain<S>– A validated DNS domain name, with IDNA/punycode and percent-encoding supportHost<S>– Either a domain name or an IP address (v4/v6)HostAddr<S>– A host with an optional port number (e.g.example.com:8080,[::1]:443)
All types are generic over their storage backend S, allowing you to choose the representation that best fits your use case – from zero-alloc stack buffers (Buffer) to shared smart pointers (Arc<str>).
Domain, Host, and HostAddr use representation-sensitive, case-sensitive
Eq, Ord, and Hash implementations. DNS queries are commonly
case-insensitive, but callers should normalize names explicitly before using
them as identity keys when that is the desired policy.
§Release status
1.0.0 (2026-10-15) is the latest published stable release. It was promoted
from the published 1.0.0-rc.3 validation candidate; production API,
features, runtime dependencies, and wire semantics are unchanged. It:
- validates the frozen host/address and IPC taxonomy APIs;
- requires Rust
1.89or newer; - keeps
no_std/no-allocandallocfeature combinations buildable; - documents the Serde representation and compatibility policy;
- validates ASCII
xn--A-labels through UTS46 on allocating entry points; and - preserves the stack-only structural behavior of
try_from_ascii_*andverify_ascii_domain.
§Features
no_stdandno-alloccompatible: Use theBuffertype for stack-allocated domains without any heap allocation- Generic storage: Supports the storage types implemented by each address
family, including
String,Arc<str>,Box<str>,Vec<u8>,SmolStr,Bytes, andBuffer - IDNA/Punycode support: Automatic conversion of international domain names (e.g.
测试.中国to punycode) - Type-safe validation: Domain names follow this crate’s ASCII and UTS46/IDNA validation policy
- Percent-encoding: Transparent decoding of percent-encoded domains
- IPv4/IPv6: Full support for IP addresses, including proper
[::1]:portbracket syntax - Semantic IP/socket wrappers:
LoopbackIpAddr/LoopbackAddr,PrivateIpAddr/PrivateAddr, and other validated address classes - Transport taxonomy:
Addr,IpcAddr, andLocalAddrmodel host, IPC, and local-only addresses - Serde: Optional serialization/deserialization support
- Fuzzing: Built-in
arbitraryandquickcheckgenerators for property-based testing
§Installation
[dependencies]
hostaddr = "1"§Feature Flags
| Feature | Description |
|---|---|
std (default) | Standard library support layered over alloc, enables IDNA and percent-decoding |
vsock (default) | Linux VM socket address support through VsockAddr and IpcAddr::Vsock |
alloc | Allocation support without std |
serde | Serialize/deserialize support |
arbitrary | Fuzzing with arbitrary crate |
quickcheck | Property-based generators for host builds; also compile-checked for wasm with the wasm_js backend |
cheap-clone | CheapClone trait for smart pointer types |
smol_str | SmolStr storage support |
bytes | Bytes storage support |
triomphe | triomphe::Arc storage support |
tinyvec | TinyVec storage support |
smallvec | SmallVec storage support |
Only the features listed in this table are public. The old implicit idna
feature was removed; enable alloc or std to use the crate’s IDNA support.
§Quick Start
#[cfg(feature = "std")]
{
use hostaddr::HostAddr;
// Domain with port
let addr: HostAddr<String> = "example.com:8080".parse().unwrap();
assert!(addr.is_domain());
assert_eq!(addr.port(), Some(8080));
// IPv4 with port
let addr: HostAddr<String> = "127.0.0.1:3000".parse().unwrap();
assert!(addr.is_ipv4());
// IPv6 with port (bracket syntax)
let addr: HostAddr<String> = "[::1]:443".parse().unwrap();
assert!(addr.is_ipv6());
assert_eq!(addr.to_string(), "[::1]:443");
// International domain names (auto punycode)
let addr: HostAddr<String> = "测试.中国:80".parse().unwrap();
}§Address Taxonomy
Addr separates Internet host addresses from IPC transports while preserving
generic storage for each address family. LocalAddr::Loopback is validated as a
loopback IP socket address, while LocalAddr::Ipc groups non-IP IPC transport
addresses without treating every IPC endpoint as a same-machine trust boundary.
Semantic address classes are available in IP-only and socket-with-port forms.
The *IpAddr types wrap core::net::IpAddr; the matching *Addr types wrap
core::net::SocketAddr and validate the socket IP component. Common classes
include Loopback, Private, LinkLocal, Documentation, Benchmark,
Shared, Multicast, Unspecified, and Broadcast.
The transparent IPC wrappers support borrowed DST storage such as
&UnixAddr<Path> on Unix and &AbstractAddr<[u8]> on Linux. Windows named-pipe
addresses are exposed through NamedPipeAddr; Linux VsockAddr support is
available through the default-enabled vsock feature.
IPC values are representation wrappers only. Constructing one does not open a socket, inspect the filesystem, normalize a platform pathname, or prove that an endpoint is local or trusted. Callers must perform any OS-level validation and security checks required by their application.
For Linux AbstractAddr, the payload is interpreted as the name after the
kernel’s leading-NUL marker. The wrapper stores the supplied bytes unchanged:
it does not add, remove, or reject leading or interior NUL bytes, and
as_bytes returns the original payload.
HostAddr conversions from SocketAddrV6 retain the IPv6 address and port but
discard flowinfo and scope_id, because HostAddr stores only an IP address
and optional port. Keep the original SocketAddrV6 when those fields matter.
The parsing and TryFrom entry points, plus From<Domain<_>>, validate domain
invariants. The allocating domain parsers validate ASCII xn-- A-labels with
UTS46; the try_from_ascii_*, verify_ascii_domain, and
verify_ascii_domain_allow_percent_encoding APIs are deliberately structural
and do not decode ACE payloads. Address constructors that accept caller-provided
storage are documented with their own invariant requirements; storage support
is limited to the types and trait combinations implemented by each address
family.
§Serde compatibility
Serde is optional. Human-readable formats use named variants (host, ipc,
loopback) and textual IPC tags (unix, abstract, named_pipe, vsock).
Binary formats use two-element tuples and stable numeric tags:
| Type | Tags |
|---|---|
IpcAddr | unix = 0, abstract = 1, named_pipe = 2, vsock = 3 |
Addr | host = 0, ipc = 1 |
LocalAddr | loopback = 0, ipc = 1 |
These tags and variant names are part of the 1.0.0 wire-format contract. New
variants must not reuse an existing tag; deserialization also re-checks the
validated domain and semantic-address invariants.
The stable 1.0.0 semantic socket writers use the RC2 object forms and
readers accept RC1 strings and legacy binary tags. RC1 readers cannot read the
stable object forms or binary tag 2; metadata omitted by an RC1 writer
cannot be reconstructed.
Semantic IP and socket wrappers use the following data model and Serde contract:
| Model | Validated data | Human-readable writer | Human-readable reader | Binary representation |
|---|---|---|---|---|
*IpAddr | IpAddr | IP string | IP string | existing IpAddr representation |
*Addr V4 | SocketAddrV4 | stable RC2 object {"v4":{"ip":"...","port":...}} | stable object or RC1 string | tag 0: V4(ip, port) |
*Addr V6 | SocketAddrV6 | stable RC2 object {"v6":{"ip":"...","port":...,"flowinfo":...,"scope_id":...}} | stable object or RC1 string | tag 1: legacy V6(ip, port); tag 2: V6Extended(ip, port, flowinfo, scope_id) |
Stable 1.0.0 readers accept RC1 socket strings and legacy binary tags. RC1
cannot read the stable object forms or binary tag 2. RC1 strings and legacy
binary tag 1 do not carry IPv6 flowinfo or scope_id, so stable readers
read them as zero; metadata already lost by an RC1 writer cannot be
reconstructed.
use hostaddr::{Addr, HostAddr, LocalAddr, LoopbackAddr, PrivateIpAddr};
let host = HostAddr::<&str>::from(("127.0.0.1".parse::<core::net::IpAddr>().unwrap(), 8080));
let addr: Addr<&str, &str, &[u8]> = host.into();
let private = PrivateIpAddr::try_from("10.0.0.1".parse::<core::net::IpAddr>().unwrap()).unwrap();
#[cfg(unix)]
{
use hostaddr::{IpcAddr, UnixAddr};
let unix: IpcAddr<&str, &[u8]> = UnixAddr::new("/tmp/app.sock").into();
}
let local_ip = LoopbackAddr::try_from("127.0.0.1:8080".parse::<core::net::SocketAddr>().unwrap()).unwrap();
let local: LocalAddr<&str, &[u8]> = local_ip.into();
§Examples
§Working with Domains
#[cfg(feature = "std")]
{
use hostaddr::Domain;
// Validated at construction -- invalid names are rejected
assert!(Domain::<String>::try_from("example.com").is_ok());
assert!(Domain::<String>::try_from("-invalid.com").is_err());
// International domain names are converted to punycode
let domain: Domain<String> = "测试.中国".parse().unwrap();
assert_eq!(domain.as_inner().as_str(), "xn--0zwm56d.xn--fiqs8s");
// FQDN support
let domain: Domain<String> = "example.com.".parse().unwrap();
assert!(domain.is_fqdn());
// ASCII-only fast path (no punycode or percent-decoding)
let domain = Domain::try_from_ascii_str("example.com").unwrap();
}Domain parsing is independent of URL-specialness: allocating
try_from/verify_domain entry points decode percent-encoded octets and then
apply the same UTS46/ASCII policy, while try_from_ascii_*,
verify_ascii_domain, and verify_ascii_domain_allow_percent_encoding accept
only structural ASCII input. A ParseDomainError
therefore means the supplied domain representation is invalid; it is not a
URL-parser error and does not perform URL authority parsing.
§Choosing a Storage Type
#[cfg(feature = "std")]
{
use hostaddr::HostAddr;
use std::sync::Arc;
// Heap-allocated String
let addr: HostAddr<String> = "example.com:8080".parse().unwrap();
// Shared ownership with Arc<str>
let addr: HostAddr<Arc<str>> = "example.com:8080".parse().unwrap();
// Byte storage
let addr: HostAddr<Vec<u8>> = "example.com".parse().unwrap();
}§Stack-Allocated Domains (no_std / no-alloc)
use hostaddr::{HostAddr, Buffer, Domain};
// No heap allocation -- 255-byte stack buffer
let domain: Domain<Buffer> = Domain::try_from("example.com").unwrap();
let addr: HostAddr<Buffer> = HostAddr::try_from("example.com:443").unwrap();§Manipulating Addresses
#[cfg(feature = "std")]
{
use hostaddr::HostAddr;
let mut addr: HostAddr<String> = "example.com".parse().unwrap();
addr.set_port(8080);
assert_eq!(addr.to_string(), "example.com:8080");
// Builder-style with default port
let addr = "example.com".parse::<HostAddr<String>>().unwrap()
.with_default_port(443);
assert_eq!(addr.port(), Some(443));
}§Verification Functions
#[cfg(any(feature = "std", feature = "alloc"))]
{
use hostaddr::{verify_domain, verify_ascii_domain};
assert!(verify_domain(b"example.com").is_ok());
assert!(verify_domain("测试.中国".as_bytes()).is_ok());
assert!(verify_ascii_domain(b"example.com").is_ok());
assert!(verify_ascii_domain("测试.中国".as_bytes()).is_err());
}§License
hostaddr is under the terms of both the MIT license and the
Apache License (Version 2.0).
See LICENSE-APACHE, LICENSE-MIT for details.
Copyright (c) 2026 Al Liu.
Structs§
- Abstract
Addr Linux - A Linux abstract socket name payload.
- Benchmark
Addr - A socket address whose IP address is reserved for benchmarking.
- Benchmark
IpAddr - An IP address reserved for benchmarking.
- Broadcast
Addr - A socket address whose IP address is the IPv4 limited broadcast address.
- Broadcast
IpAddr - The IPv4 limited broadcast address.
- Buffer
- An immutable buffer which contains a valid domain.
- Documentation
Addr - A socket address whose IP address is reserved for documentation or examples.
- Documentation
IpAddr - An IP address reserved for documentation or examples.
- Domain
- A DNS domain name, as
.dot-separated labels. Non-ASCII labels are converted to ASCII using UTS46/IDNA. Allocating parsing entry points also decode percent-encoded input before applying that policy. - Host
Addr - A host address, which consists of a
Hostand an optional port number. - Link
Local Addr - A socket address whose IP address is link-local.
- Link
Local IpAddr - An IPv4 or IPv6 link-local address.
- Loopback
Addr - A socket address whose IP address is loopback.
- Loopback
IpAddr - An IP address in the loopback range.
- Multicast
Addr - A socket address whose IP address is multicast.
- Multicast
IpAddr - An IPv4 or IPv6 multicast address.
- Parse
Ascii Domain Error - The provided input could not be parsed because it is not an ASCII syntactically-valid DNS Domain.
- Parse
Ascii Host Error - An error which can be returned when parsing an ASCII
Host. - Parse
Benchmark Addr Error - The provided socket address is not reserved for benchmarking.
- Parse
Benchmark IpAddr Error - The provided IP address is not reserved for benchmarking.
- Parse
Broadcast Addr Error - The provided socket address is not the IPv4 limited broadcast address.
- Parse
Broadcast IpAddr Error - The provided IP address is not the IPv4 limited broadcast address.
- Parse
Documentation Addr Error - The provided socket address is not reserved for documentation.
- Parse
Documentation IpAddr Error - The provided IP address is not reserved for documentation.
- Parse
Domain Error - The provided input could not be parsed because it is not a syntactically-valid DNS Domain.
- Parse
Host Error - An error which can be returned when parsing a
Host. - Parse
Link Local Addr Error - The provided socket address is not link-local.
- Parse
Link Local IpAddr Error - The provided IP address is not link-local.
- Parse
Loopback Addr Error - The provided socket address is not a loopback address.
- Parse
Loopback IpAddr Error - The provided IP address is not a loopback address.
- Parse
Multicast Addr Error - The provided socket address is not multicast.
- Parse
Multicast IpAddr Error - The provided IP address is not multicast.
- Parse
Private Addr Error - The provided socket address is not private-use or unique-local.
- Parse
Private IpAddr Error - The provided IP address is not private-use or unique-local.
- Parse
Shared Addr Error - The provided socket address is not in IPv4 shared address space.
- Parse
Shared IpAddr Error - The provided IP address is not in IPv4 shared address space.
- Parse
Unspecified Addr Error - The provided socket address is not the exact unspecified address.
- Parse
Unspecified IpAddr Error - The provided IP address is not the exact unspecified address.
- Private
Addr - A socket address whose IP address is private-use or unique-local.
- Private
IpAddr - An RFC 1918 IPv4 private-use or RFC 4193 IPv6 unique-local address.
- Shared
Addr - A socket address whose IP address is in IPv4 shared address space.
- Shared
IpAddr - An IPv4 shared address space address.
- TryUnwrap
Error - Error returned by the derived
TryUnwrapimplementation. - Unix
Addr Unix - A Unix-domain socket pathname.
- Unspecified
Addr - A socket address whose IP address is the exact unspecified address.
- Unspecified
IpAddr - The exact IPv4 or IPv6 unspecified address.
- Vsock
Addr Linux and vsock - A VM socket address.
Enums§
- Addr
- Any address accepted by a runtime.
- Host
- The host name.
- IpcAddr
- A non-IP communication address.
- Local
Addr - An address in the local-address taxonomy.
- Parse
Ascii Host Addr Error - An error which can be returned when parsing a
HostAddr. - Parse
Host Addr Error - An error which can be returned when parsing a
HostAddr.
Functions§
- verify_
ascii_ domain - Verifies that the input is a valid ASCII domain name.
- verify_
ascii_ domain_ allow_ percent_ encoding - Verifies that the input is a valid ASCII domain name. The input can be a percent-encoded domain name.
- verify_
domain allocorstd - Verifies that the input is a valid domain name.