sip-uri
Zero-dependency SIP/SIPS, tel:, and URN parser for Rust.
Implements RFC 3261 (SIP-URI, SIPS-URI), RFC 3966 (tel-URI), and RFC 8141 (URN) with hand-written parsing and per-component percent-encoding.
use ;
let uri: SipUri = "sip:alice@example.com;transport=tcp".parse.unwrap;
assert_eq!;
assert_eq!;
assert_eq!;
let tel: TelUri = "tel:+15551234567;cpc=emergency".parse.unwrap;
assert_eq!;
assert!;
let urn: UrnUri = "urn:service:sos".parse.unwrap;
assert_eq!;
assert_eq!;
[]
= "0.2"
Types
| Type | Description |
|---|---|
SipUri |
SIP or SIPS URI with user, host, port, params, headers, fragment |
TelUri |
tel: URI with number, params, fragment |
UrnUri |
URN with NID, NSS, and optional r/q/f components |
Uri |
Enum dispatching Sip / Tel / Urn / Other based on scheme |
NameAddr |
Deprecated -- display name + URI ("Alice" <sip:...>), will be removed in 0.3.0 |
Host |
IPv4, IPv6, or hostname |
Scheme |
Sip or Sips |
All types implement FromStr, Display, Debug, Clone, PartialEq, and Eq.
Parsing is case-insensitive for schemes, hosts, and parameter names.
Display output round-trips through FromStr.
SipUri
use ;
// Full SIP URI with user-params, password, port, params, headers
let uri: SipUri = "sips:+15551234567;cpc=emergency:secret@[2001:db8::1]:5061;user=phone?Subject=test"
.parse.unwrap;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
User-params
SIP URIs with user=phone follow the telephone-subscriber production from
RFC 3966. Parameters within the userinfo (before @) are split from the
user part and exposed via user_params():
use SipUri;
// NG911 pattern: user-params carry tel: semantics inside a SIP URI
let uri: SipUri = "sip:+15551234567;cpc=emergency;oli=0@198.51.100.1;user=phone"
.parse.unwrap;
assert_eq!;
assert_eq!;
Builder
use ;
use Ipv4Addr;
let uri = new
.with_scheme
.with_user
.with_port
.with_param;
assert_eq!;
TelUri
use TelUri;
let uri: TelUri = "tel:+15551234567;cpc=emergency;oli=0".parse.unwrap;
assert_eq!;
assert!;
assert_eq!;
// Local numbers (no + prefix)
let local: TelUri = "tel:911".parse.unwrap;
assert!;
UrnUri
URN parsing per RFC 8141. Used in SIP for NG911 service identifiers, 3GPP IMS service types, GSMA IMEI, and NENA call/incident tracking.
use UrnUri;
// NG911 emergency service identifier
let urn: UrnUri = "urn:service:sos.fire".parse.unwrap;
assert_eq!;
assert_eq!;
// NENA call tracking identifier
let urn: UrnUri = "urn:nena:callid:abc123:host.example.com".parse.unwrap;
assert_eq!;
assert_eq!;
// 3GPP IMS service
let urn: UrnUri = "urn:urn-7:3gpp-service.ims.icsi.mmtel".parse.unwrap;
assert_eq!;
// Optional RFC 8141 components (resolution, query, fragment)
let urn: UrnUri = "urn:example:resource?+resolve?=query#section".parse.unwrap;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
NID is validated per RFC 8141 (2-32 chars, alphanum bookends) and stored lowercase. NSS percent-encoding hex digits are uppercased for canonical comparison but never decoded.
NameAddr (deprecated)
NameAddr is deprecated since 0.2.0 and will be removed in 0.3.0.
The name-addr production (RFC 3261 §25.1) appears inside SIP header
fields where it is followed by header-level parameters (;tag=,
;expires=, ;serviceurn=, etc.). This type rejects those parameters,
so it cannot round-trip real SIP header values.
Migration:
- For SIP header parsing (From, To, Contact, Refer-To), use
SipHeaderAddrfromfreeswitch-types, which handles display names, URIs, and header-level parameters. - If you only need the URI, parse it directly with
Uri.
Percent-encoding
Per-component percent-encoding follows RFC 3261 rules:
- Unreserved characters are decoded (
%41->A) - Reserved characters stay encoded (
%40stays%40in user-part) - Hex digits are normalized to uppercase (
%3d->%3D) - Each URI component has its own allowed character set
use SipUri;
// Percent-encoded quotes in user-part are preserved
let uri: SipUri = r#"sip:%22foo%22@example.com"#.parse.unwrap;
assert_eq!;
Design
- Zero dependencies -- not even
percent-encoding. The subset needed is trivial and avoids transitive dep churn. - Hand-written parser -- the SIP URI grammar is regular enough that nom/regex
are unnecessary overhead. Parsing follows the sofia-sip two-phase
@discovery algorithm for correct handling of reserved characters in user-parts. - Case-insensitive where required -- scheme and parameter name lookup are case-insensitive per RFC. Host names are lowercased.
#[non_exhaustive]-- onUri,Host, andSchemeenums for forward-compatible matching.- Fragment support --
SipUriandTelUriparse and round-trip#fragmentcomponents (accepted permissively, matching sofia-sip behavior). - Any-scheme fallback --
Uri::Otherstores unrecognized schemes (http, https, data, etc.) as raw strings, soNameAddrcan parse SIP headers likeCall-Infothat carry non-SIP URIs.
RFC coverage
- RFC 3261 19/25 -- SIP-URI, SIPS-URI syntax, comparison, percent-encoding
- RFC 3966 -- tel-URI (global/local numbers, visual separators, parameters)
- RFC 8141 -- URN syntax (NID, NSS, r/q/f components)
Development
RUSTDOCFLAGS="-D missing_docs -D rustdoc::broken_intra_doc_links"
Other Rust SIP URI crates
- rsip -- full SIP library with heavy deps (nom, bytes, md5, sha2, uuid). No tel: URI support, no user-param extraction.
- rvoip-sip-core -- alpha with a massive dependency tree.
Neither is a focused, zero-dep URI-only parser.
License
MIT OR Apache-2.0 -- see LICENSE-MIT and LICENSE-APACHE.