try hiccup

E.164, SIP URIs and ENUM: how a dialled number becomes a route

Short version: a phone number is a name, not an address. Every call spends its first milliseconds having that name translated — normalised to E.164, maybe looked up in ENUM or a portability database, wrapped in a URI, and resolved through DNS to an actual host. Most "wrong number" faults are one of those translations going quietly wrong, and the evidence is the Request-URI mutating hop by hop.

E.164: the global numbering plan

ITU-T E.164 defines the shape: at most 15 digits — a country code (1–3 digits), then a national destination code and subscriber number whose split each country chooses. +33 6 12 34 56 78 is a French mobile; the + means "fully qualified, no dialling prefixes". The trouble is that users do not dial E.164 — they dial 0612345678, or an extension, or with a trunk access 9 in front. Somewhere, every network runs normalisation rules turning dialled strings into canonical + numbers, and those rules — usually regex on an SBC or PBX — are the most-edited, most-breakable config in voice. A number that works dialled one way and fails another is a normalisation bug, essentially always.

Two URI shapes for the same number

tel:+33612345678 (RFC 3966) names the number itself, no host — "someone will have to route this". sip:[email protected];user=phone names the number at a domain: the user=phone parameter is the flag that says "the user part is a telephone number, not an account name". Networks flip between the two constantly, and the choice matters at borders: a tel: URI forces the next hop to make a routing decision, while a sip: URI has already made one. Alongside them travels identity: From is what the caller claimed; P-Asserted-Identity is what the network verified — which is why call screening and CLI features trust the latter.

How a SIP URI's domain becomes a host: RFC 3263

StepLookupDecides
1NAPTR for the domainWhich transports the domain offers and in what order of preference (SIP+D2T → TCP, SIP+D2U → UDP, SIPS+D2T → TLS), and the SRV name to use next.
2SRV (_sip._tcp.domain etc.)Which hosts serve that transport, with priorities and weights — this is where failover and load-sharing live.
3A/AAAAThe actual addresses.

Two field notes. First, failover behaviour "not working" between SIP peers is very often just a client that skips NAPTR/SRV and does a bare A lookup — perfectly legal, silently different. Second, when a trace shows a request going to a host no configuration mentions, run the RFC 3263 ladder by hand before blaming routing: SRV weights may be doing exactly what they were told.

ENUM: the beautiful idea that half-survived

ENUM (RFC 6116) mapped E.164 into DNS: reverse the digits, dot-separate, append e164.arpa — 8.7.6.5.4.3.2.1.6.3.3.e164.arpa — and publish NAPTR records saying "this number is reachable at sip:[email protected]". Public ENUM assumed subscribers would want their numbers globally mapped to internet endpoints; regulators, carriers and spam reality disagreed, and the public tree is essentially empty. But the mechanism thrives privately: carrier-internal and consortium ENUM roots are how large operators decide "is this number on-net, and where?", and an infrastructure ENUM dip is the first step of many interconnect routing engines. When an on-net call inexplicably breaks out to the PSTN and back, a stale private ENUM zone is a leading suspect.

Number portability: the database in the middle

Porting broke "the prefix tells you the operator" forever. Someone in the call path must dip a portability database and learn the routing number of the current operator; in SIP the result rides tel-URI parameters from RFC 4694 — rn= (route to this network) and npdi (the dip was done, do not dip again). A missing npdi causes double dips and double-charging arguments; a wrong rn sends the call to the donor network, which may loop it, reject it, or — worst — complete it with wrong billing. If a ported number misbehaves, find who did the dip and what rn they attached: it is right there in the Request-URI.

STIR/SHAKEN: signing the caller identity

Because From was always forgeable, RFC 8224 adds an Identity header: the originating operator signs the calling and called numbers (a PASSporT — a JWT with a certificate chain rooted in the numbering authority) with an attestation level: A "this is my subscriber and their number", B "my subscriber, can't vouch for the number", C "it merely transited me". Terminating networks verify and feed the result into spam scoring and the little "verified caller" checkmarks. Deployment reality: strong in North America by mandate, patchy elsewhere, and broken signatures at interconnects (a B2BUA that touched a signed field) are a brand-new failure class riding old plumbing.

Reading a numbering fault in a trace

  1. Follow the Request-URI hop by hop: dialled string → normalised E.164 → rn-decorated → final. The hop where it goes wrong names the guilty box.
  2. Compare From, P-Asserted-Identity and any Identity header — three versions of "who is calling", and the disagreements are informative.
  3. 404/484 from a proxy that should know the number: check digits and user=phone first, portability second, stale ENUM third.

hiccup shows the Request-URI's evolution across every hop and leg in one ladder, so the exact box that mangled the number is a glance away. Self-hosted, free for individual users.

upload a trace