Tuesday, April 23, 2019
The feeling when your new task is already done
“The ‘Project: Heimdall’ team now want all the numbers,” said TS1, my fellow cow-orker.
“Really?” I asked. “They can finally deal with international phone numbers?”
“Apparently yes. So ‘Project: Sippy-Cup’ needs to open the flood gates and let all the numbers through. But make it configurable.”
“Okay.”
So I dive into the code for “Project: Sippy-Cup” and … what's this? The code is already in place. From last year. When it was clear that “Project: Heimdall” could not, in fact, handle all the numbers! I remember it was annoying having to send all NANP numbers (those that are 10 digits, like “501-555-1212”), even the malformed, invalid NANP numbers (like “501-511-1212”), while making sure I didn't pass along valid, international numbers that also happened to be 10 digits long (like “501-555-1212”). Now that the “Project: Heimdall” team has to deal with the crap we get … well … good luck. We're all counting on you.
Dealing with phone numbers
“Project: Wolowizard only supports NANP numbers, but since those numbers come via The Protocol Stack From Hell clearly marked as NANP, it's easy to determine there if a number is NANP or not. It's not quite as simple in “Project: Sippy-Cup” since SIP is … a bit loose with the data formatting.
There,
the numbers are formatted as a tel:
URI
(or a sip:
URI but the differences are minor).
If the number is “global,”
it's easy to determine a NANP number because it will be marked with a “+1”
(“1” being the country code for North America).
So,
tel:+1-501-555-1212
is most definitely a NANP number,
while tel:+501-555-1212
is not.
Things get a bit more muddy when we receive a so-called “local” number.
RFC-3966 clearly states that a “local” tel:
URI MUST
(as defined in RFC-2119)
contain a phone-context
attribute—except when it doesn't
(I swear—the RFC contradicts itself on that point; tel:8005551212
is valid,
even though it's a “local” number and missing a phone-context
attribute because it's a “national freephone number”).
So tel:555-1212;phone-context=+1501
is NANP,
while tel:555-1212;phone-context=+501
is not
(look closely at the two—one has a country code of “1” while the other has a country code of “501”).
It's worse though,
because while tel:555-1212;phone-context=+1501
is NANP,
you cannot use the phone-context
attribute to reconstruct a global number
(the RFC contains the following example: tel:863-1234;phone-context=+1-914-555
—um … yeah).
To further complicate things,
the phone-context
attribute does not have to contain digits—it can be a domain name.
So tel:555-1212;phone-context=example.com
is a valid number.
Is it NANP?
International?
Who knows?
So what does “Project: Sippy-Cup” do?
If it receives a “local” number with a “+1” country code in the phone-context
attribute,
it's marked as NANP;
any other country code is it marked as non-NANP.
If the phone-context
attribute contains a domain name,
it is treated as a NANP number (based on what I saw in production).
And if there's a missing phone-context
attribute for a “local”
number, “Project: Sippy-Cup” treats it as a NANP number if it has at least 10 digits.
Now, why do I care about this? Because we want to avoid doing an expensive database query for non-NANP and invalid NANP numbers, but “Project: Heimdall” wants all the numbers for tracking potentially fraudulent calls.