Internet Call Routing I
One of the attractions of VoIP is the concept of avoiding the use of the PSTN altogether, and routing all calls directly between endpoints using the Internet at little or no cost. While the technology to do this has been around for some time, the reality is that most phone calls still cost money—even those that are routed across VoIP services. From a technology standpoint, there are still many systems out there that cannot handle routing VoIP calls using anything other than a dialpad on a telephone. From a cultural standpoint, we are still used to calling each other using a numerical string (a.k.a. a phone number). With VoIP, the concept of being able to phone somebody using name@domain (just as we do with email) makes sense, but there are a few things to consider before we can get there. So what’s holding everything up?
DNS and SIP URIs
The Domain Name System (DNS) is designed to make it easier for humans to locate resources on the Internet. While ultimately all connections between endpoints are handled through numerical IP addresses, it can be very helpful to associate a name (such as www.google.com) with what may in fact be multiple IP addresses. In the case of VoIP, the use of a domain name can take something like 100@192.168.1.1 ( extension@server ) and make it available as leif@shifteight.org (which looks so much sexier on a business card).
The SIP URI
A SIP URI generally looks like sip:endpoint@domain.tld . Depending on your SIP client, you may be able to dial a SIP URI as endpoint@domain.tld , or even just as endpoint (if you have a proxy server and the endpoint you are calling is part of your domain).
For a SIP telephone, which often only has a numerical dialpad, it can be problematic to dial a SIP URI by name, 2 so it has become common to use numerical dialing to reach external resources. We are also used to making “phone calls” using “phone numbers.” The SIP protocol itself, however, only understands resource@address , so whatever you dial must ultimately be converted to this format before SIP can do anything with it. Usually the only reason you can dial something by “phone number” from your SIP phone is because you are registered to a resource that understands how to convert the numerical strings you dial into SIP URIs.
In Asterisk, the resource part of the URI (the part before the @ ) must match an extension in the dialplan. 3 The address portion will be the address (or hostname) of the Asterisk server itself. So, a URI of sip:100@shifteight.org will end up at an extension called 100 , somewhere in the dialplan of the server that provides SIP service for shifteight.org .
What is dialed ( 100 ) may not in any way relate to the actual identifier of the endpoint being connected to. For example, we might have a user named Leif whose phone may be a device that registers itself by its MAC address, and therefore could be something like 0000FFFF0001@192.168.1.99 . 4 Much of the purpose of the Asterisk dialplan is to simplify addressing for users and to handle the complexities of the various protocols that Asterisk supports.
SRV Records
A Service Record (SRV) is a somewhat new type of DNS record that provides information about available services. Defined in RFC 2782, it is often used by newer protocols (SIP being one of them). If you want to support SIP lookups on your domain, you will require a relevant SRV record in order to properly respond.
When a SIP connection does a lookup on leif@shifteight.org , for the purposes of SIP, the SRV record can respond that the requested service (SIP) is actually found on the server pbx.shifteight.org (or possibly even on a completely different domain, such as pbx.tothemoon.net ).
Internet hosting providers typically offer a web-based interface for setting up DNS re‐ cords, but many of them do not provide a good interface for SRV records (assuming they offer anything at all). You can generally set up A records and MX records easily enough, but SRV records can be trickier. If your host does not support SRV records, you will need to move your DNS hosting to another provider if you want to be able to support SIP SRV lookups for your domain. The majority of DNS servers run BIND (Berkeley Internet Name Daemon). The BIND record for an SRV entry for SIP will look something like this:
_sip._udp.shifteight.org. 86400 IN SRV 0 0 5060 pbx.shifteight.org.
When you configure an SRV record, you can test it with the following Linux command:
# dig SRV _sip._udp.shifteight.org
The result will contain several components, but the section you are interested in is:
;; ANSWER SECTION:
_sip._udp.shifteight.org. 14210 IN SRV 0 0 5060 pbx.shifteight.org.
This means that your DNS server is responding correctly to an SRV lookup for SIP to your domain by responding with the hostname of your PBX (in this case, pbx.shifteight.org ).
Any SIP requests to your domain will be referred to your Asterisk server, which will be responsible for handling incoming SIP connections. 5
If your dialplan does not understand the name/resource/endpoint portion of the SIP URI, calls will fail. This means that if you want to be able to offer resources in your Asterisk system by name, you will need relevant dialplan entries.
Accepting Calls to Your System
When a SIP URI comes into your Asterisk system, the resource portion of the URI will arrive in the dialplan as an ${EXTEN} . So, for example, leif@shifteight.org would arrive in the dialplan as leif within the ${EXTEN} channel variable in whatever context
you use to handle unauthenticated SIP calls (if you are building your dialplan using the examples in this book, that will be the unauthenticated dialplan context).
Modifying sip.conf
Once you are familiar with the security implications of allowing unauthenticated SIP connections, you will need to ensure that your sip.conf file allows for them. While Asterisk allows them by default, in earlier chapters of this book we have instructed you to disable unauthenticated SIP calls. The logic for this is simple: if you don’t need it, don’t enable it.
Since we are now interested in allowing calls from the Internet, we will need to allow unauthenticated SIP calls. We do that by setting a general variable in the /etc/asterisk/sip.conf file, as follows:
[general]
context=unauthenticated ; default context for incoming calls
allowguest=yes ; enable unauthenticated calls
After making this change, don’t forget to reload SIP, using this command from the command line:
$ sudo asterisk -rx “sip reload”
or this one from the Asterisk CLI:
*CLI> sip reload
You can verify that the changes have succeeded using the Asterisk CLI command sip show settings. What you want to see is Allow unknown access: Yes under the Global Settings section, and Context: unauthenticated under the Default Settings header.
Standard dialplan
In order to handle an incoming name, your dialplan needs to contain an extension that matches that name.
A dialplan entry on the pbx.shifteight.org system might look like this:
[unauthenticated]
exten => leif,1,Goto(PublicExtensions,100,1)
exten => jim,1,Goto(PublicExtensions,101,1)
exten => tilghman,1,Goto(PublicExtensions,102,1)
exten => russell,1,Goto(PublicExtensions,103,1)
This is by far the simplest way to implement name dialing, but it is also complex to maintain, especially in systems with hundreds of users.
In order to implement name handling in a more powerful way, you could add something like the following to your extensions.conf file. Note that some lines have been wrapped in this example due to space restrictions. These lines must appear on a single line in the dialplan. All lines should start with exten => , same => , or a comment indicator ( ; ).
[unauthenticated]
exten => _[A-Za-z0-9].,1,Verbose(2,UNAUTHENTICATED REQUEST TO ${EXTEN} FROM
${CALLERID(all)})
same => n,Set(FilteredExtension=${FILTER(A-Za-z0-9,${EXTEN})})
same => n,Set(CheckPublicExtensionResult=${DIALPLAN_EXISTS(PublicExtensions,
${FilteredExtension},1)})
same => n,GotoIf($[“${CheckPublicExtensionResult}” = “0”]?CheckEmailLookup)
same => n,Goto(PublicExtensions,${FilteredExtension},1)
; This is our handler for when someone dials a SIP URI with a name
same => n(CheckEmailLookup),GoSub(subEmailToExtensionLookup,start,1
(${TOLOWER(${FilteredExtension})}))
same => n,GotoIf($[“${GOSUB_RETVAL}” = “NoResult”]?i,1:PublicExtensions,
${GOSUB_RETVAL},1)
same => n,Goto(i,1)
; This handles invalid numbers/names
exten => i,1,Verbose(2,Incoming call from ${CALLERID(all)} to context ${CONTEXT}
found no result)
same => n,Playback(silence/1&invalid)
same => n,Hangup()
; These are explicit extension matches (useful on small systems)
exten => leif,1,Goto(PublicExtensions,100,1)
exten => jim,1,Goto(PublicExtensions,101,1)
exten => tilghman,1,Goto(PublicExtensions,102,1)
exten => russell,1,Goto(PublicExtensions,103,1)
When a call enters the dialplan, it can match in one of two places: it can match our pattern match at the top, or it can match the explicit named extensions closer to the bottom of our example (i.e., leif , jim , tilghman , or russell ). If the call does not explicitly match our named extensions, the pattern match will be utilized. Our pattern match of _[A-Za-z0-9]. matches any string starting with an alphanumeric character followed by one or more other characters.
The incoming string needs to be made safe, so we utilize the FILTER() function to remove nonalphanumeric characters and assign the result to the FilteredExtension channel variable.
The DIALPLAN_EXISTS() function will be used to see if the request matches anything in the PublicExtensions context. This function will return either a 0 (if no match is found) or a 1 (when a match is found) and assign the result to the CheckPublicExtensionResult channel variable.
The next line is a GotoIf() that checks the status of the CheckPublicExtensionRe sult variable. If the result returned was 0 , the dialplan will continue at the CheckEmail Lookup priority label. If the result was anything other than 0 (in this case, the other result could have been a 1 ), the next line of the dialplan will be executed. This line will perform a Goto() and continue execution in the PublicExtensions context (presumably to dial our destination endpoint).
Assuming our CheckPublicExtensionResult variable was a 0 , our dialplan will continue at the CheckEmailLookup priority label, where we use the subroutine subEmailToExtensionLookup via a GoSub() . 6 We pass the value contained within the FilteredExtension channel variable to the subroutine, but you’ll notice that we’ve wrapped it in the TOLOWER() dialplan function (which expects your email addresses to be stored in lowercase as opposed to mixed case).
Upon return from the subEmailToExtensionLookup subroutine, we check the GO SUB_RETVAL channel variable (which was automatically set when the subroutine returned). The result will be one of two things: the extension number that matches the name that was passed to the subroutine, or the string NoResult . Our dialplan checks ${GOSUB_RETVAL} , and if it contains NoResult , the caller is passed to the i (invalid) extension, where we inform the caller that the extension dialed is invalid. If all is well, the call will continue execution in the PublicExtensions context.
File parsing
This little trick will allow you to use the voicemail.conf file to look up valid usernames against their email address. This could end up being kludgy, and it requires that the email field in voicemail.conf is filled out and contains a username (before the @ symbol) that you will support in your dialplan, but it’s simple to code in the dialplan, and if nothing else it will give you some ideas about how you might provide a more automated way to link names to extension numbers for SIP URI dialing. Note that this method will not allow you to exclude some people from name dialing. It’s all or nothing.
We’ve written this as a subroutine, which is invoked something like this:
; where ‘name’ is the username as found in the email address
GoSub(subEmailToExtensionLookup,start,1(name))
The subroutine looks like this:
[subEmailToExtensionLookup]
exten => start,1,Verbose(2,Checking for user in voicemail.conf)
same => n,Set(LOCAL(FilteredExtension)=${FILTER(a-z0-9,${ARG1})})
same => n,Set(LOCAL(Result)=${SHELL(grep “${LOCAL(FilteredExtension)}@”
/etc/asterisk/voicemail.conf)})
same => n,GotoIf($[${ISNULL(${LOCAL(Result)})}]?no_Result,1)
same => n,Set(LOCAL(ExtensionToDial)=${CUT(${LOCAL(Result)},=,1)})
same => n,Set(LOCAL(ExtensionToDial)=${FILTER(0-9,${LOCAL(ExtensionToDial)})})
same => n,Return(${LOCAL(ExtensionToDial)})
exten => no_Result,1,Verbose(2,No user ${ARG1} found in voicemail.conf)
same => n,Return(NoResult)
Let’s go over this code, because there are some useful actions being performed that you may be able to apply to other purposes.
First, a channel variable named FilteredExtension is created. This variable is local to the subroutine:
Set(LOCAL(FilteredExtension)=${FILTER(a-z0-9,${ARG1})})
The FILTER() function looks at the entire ${ARG1} and removes any nonalphanumeric characters. This is primarily for security reasons. We are passing this string out to the shell, so it’s critical to ensure it will only contain characters that we expect.
The next step is where the coolness happens:
Set(LOCAL(Result)=${SHELL(grep “${LOCAL(FilteredExtension)}@”
/etc/asterisk/voicemail.conf)})
The shell is invoked in order to run the grep shell application, which will search through the voicemail.conf file, return any lines that contain name@ , and assign the result to the
variable ${Result} :
GotoIf($[${ISNULL(${LOCAL(Result)})}]?no_result,1)
If no lines contain the string we’re looking for, we’ll return from the subroutine the value NoResult (which will be found in the ${GOSUB_RETVAL} channel variable). The dialplan section that called the subroutine will need to handle this condition.
We’ve created an extension named no_result for this purpose:
exten => no_result,1,Verbose(2,No user ${ARG1} found in voicemail.conf)
same => n,Return(NoResult)
If ${Result} is not null, the next steps will clean up ${Result} in order to extract the extension number 7 of the user with the name passed in ${ARG1} :
Set(LOCAL(ExtensionToDial)=${CUT(${LOCAL(Result)},=,1)})
The CUT() function will use the = symbol as the field delimiter and will assign the value from the first field found in ${Result} to the new variable ExtensionToDial . From there, we simply need to trim any trailing spaces by filtering all nonnumeric characters:
Set(LOCAL(ExtensionToDial)=${FILTER(0-9,${LOCAL(ExtensionToDial)})})
We can now return the extension number of the name we received:
Return(${LOCAL(ExtensionToDial)})
This example was something we whipped up for the purposes of illustrating some methods you can employ in order to easily match names to extension numbers for the purposes of SIP URI dialing. This is by no means the best way of doing this, but it is fairly simple to implement, and in many cases may be all that you need.
Database lookup
Using a database is by far the best way to handle user information on larger, more complex systems.
A database is ideal for handling name lookup, as it makes maintenance of user data (and integration with external systems such as web interfaces) far simpler. However, it does require a bit more effort to design and implement.
The example we will use in this chapter will work, but for a production environment it is probably too simplistic. Our goal here is simply to give you enough information to understand the concept.
First, we’ll need a table to handle our name-to-extension mapping. This could be a separate table from the main user table, or it could be handled in the main user table, provided that that table contains a field that will contain the exact strings that users will publish as their SIP URIs (as an example, some companies have rules regarding how email addresses look, so Leif might have a URI such as lmadsen@shifteight.org , or leif.madsen@shifteight.org ).
Our sample NameMapping table looks like Table 1.
NameMapping table
We believe that having a separate table that only handles name-to-extension/context mapping is the most useful solution, since this table can be used to handle more than just users with telephone sets. You are encouraged to come up with other ways to handle this that may be more suitable to your environment.
In the dialplan, we would refer to this table using Asterisk’s func_odbc function:
[subLookupNameInNameMappingTable]
exten => start,1,Verbose(2,Looking up ${ARG1})
; where ‘name’ is the username as found in the email address
same => n,Set(ARRAY(CalleeExtension,CalleeContext)=${ODBC_NAME_LOOKUP(${ARG1})})
same => n,GotoIf($[${ISNULL(${CalleeExtension})}]?no_result,1)
same => n,GotoIf($[${ISNULL(${CalleeContext})}]?no_result,1)
same => n,Return() ; You’ll need to handle the new CalleeExtension and
; CalleeContext variables in the code that called this
; subroutine
exten => no_result,1,Verbose(2,Name was not found in the database.)
same => n,Return(NoResult)
The /etc/asterisk/func_odbc.conf file will require the following entry:
[NAME_LOOKUP](DB)
readsql=SELECT Extension,Context FROM NameMapping WHERE Name=’${SQL_ESC(${ARG1})}’
Dialing SIP URIs from Asterisk
Asterisk can dial a SIP URI as easily as any other sort of destination, but it is the endpoint (namely, your telephone) that is ultimately going to shoulder the burden of composing the address, and therein lies the difficulty.
Most SIP telephones will allow you to compose a SIP URI using the dialpad. This sounds like a great idea at first, but since there are no typewriter keys on a phone set, in order to dial something like jim.vanmeggelen@shifteight.org , what you would need to actually input into the phone would be something along the lines of:
5-444-6-*-888-2-66(pause)-6-33-4(pause)-4-33-555-33-66-#-7777-44(pause)
-444-333-8-33-
444-(pause)-4(pause)-44-8-*-666-777-4
To support this in your dialplan, you would need something similar to this: 8
exten => _[0-9a-zA-Z].,1,Verbose()
same => n,Set(FilteredExtension=${FILTER(0-9a-zA-Z@-_.,${EXTEN})})
same => n,Dial(SIP/${FilteredExtension})
It’s simple, it’s fun, and it works! … ?
The reality is that until all phones support complex and flexible address books, as well as a QWERTY-style keyboard (perhaps via touchscreen), SIP URI dialing is not going to take off.
If you have a SIP URI that you want to dial on a regular basis (for example, during the writing of this book there were many calls made between Jim and Leif), you could add something like this to your dialplan:
exten => 5343,1,Dial(SIP/leif.madsen@shifteight.org)
With this in your dialplan, you could dial 5343 (LEIF) on your phone and the Asterisk dialplan would translate it into the appropriate SIP URI. It’s not practical for a large number of URIs, but for a few here and there it can be a helpful shortcut.
But keep reading, because there are some very useful components of DNS that simplify the process of dialing directly between systems without the use of the PSTN.
ENUM and E.164
Although the SIP protocol really doesn’t think in terms of phone numbers, the reality is that phone numbers are not going away any time soon, and if you want to properly integrate a VoIP system with as many telephone networks as possible, you’re going to need to handle the PSTN in some way.
ENUM maps telephone numbers onto the Domain Name System (DNS). In theory, ENUM is a great idea. Why not cut out the PSTN altogether, and simply route phone calls directly between endpoints using the same numbering plan? We’re not sure this idea is ever going to become what the emerging telecom community would like it to be, though. The reason? Nobody really can say who owns phone numbers.
E.164 and the ITU
The International Telecommunication Union (ITU) is a United Nations agency that is actually older than the UN itself. It was founded in 1865 as the International Telegraph Union. The ITU-T sector, known for many decades as CCITT (Comité consultatif international téléphonique et télégraphique), is the standards body responsible for all of the protocols used by the PSTN, as well as many that are used in VoIP. Prior to the advent of VoIP, the workings of the ITU-T sector were of little interest to the average person, and membership was generally limited to industries and institutions that had a vested interest in telecommunications standards.
ITU standards tend to follow a letter-dot-number format. ITU-T standards you may
have heard of include H.323, H.264, G.711, G.729, and so forth.
E.164 is the ITU-T standard that defines the international numbering plan for the PSTN. If you’ve ever used a telephone, you’ve used E.164 addressing.
Each country in the world has been assigned a country code, 9 and control of addressing in those countries is handled by the local authorities.
E.164 numbers are limited to 15 digits in length (excluding the prefix).
In Asterisk, there is nothing special that needs to be done in order to handle E.164 addressing, other than to make sure your dialplan is suitable to the needs of any PSTN-compatible channels you may have.
For example, if you’re operating in a NANP country, you will probably need to have the
following pattern matches:
_NXXNXXXXXX
_1NXXNXXXXXX
_011X.
_N11
In the UK, you might need something more like this:
_0[123789]XXXXXXXXX
_0[123789]XXXXXXXX
And in Australia, your dialplan might have these pattern matches:
_NXXXXXXX
_0XXXXXXXXX
ENUM
In order to allow the mapping of E.164 numbers onto the DNS namespace, a way of representing phone numbers as DNS names had to be devised.
This concept is defined in RFC 3761, helpfully named “The E.164 to Uniform Resource Identifiers (URI) Dynamic Delegation Discovery System (DDDS) Application (ENUM).” ENUM reportedly stands for Electronic NUmber Mapping.
According to the RFC, converting a phone number into an ENUM-compatible address
requires the following algorithm:
1. Remove all characters with the exception of the digits.
For example, the First Well Known Rule produced the Key
“+442079460148”. This step would simply remove the
leading “+”, producing “442079460148”.
2. Put dots (“.”) between each digit. Example:
4.4.2.0.7.9.4.6.0.1.4.8
3. Reverse the order of the digits. Example:
8.4.1.0.6.4.9.7.0.2.4.4
4. Append the string “.e164.arpa” to the end. Example:
8.4.1.0.6.4.9.7.0.2.4.4.e164.arpa
Clear as mud?
ENUM has not taken off. The reasons appear to be mostly political in nature. The problem stems from the fact that there is no one organization that controls numbering on the PSTN the way that IANA does for the Internet. Since no one entity has a clear mandate for managing E.164 numbers globally, the challenge of maintaining an accurate and authoritative database for ENUM has proved elusive.
Some countries in Europe have done a good job of delivering reliable ENUM databases, but in country code 1 (NANP), which contains multiple countries and therefore multiple regulatory bodies, the situation has become an illogical mess. This is hardly surprising, since the carriers that control E.164 addressing can’t reasonably be expected to get enthusiastic about allowing you to bypass their networks. The organizations responsible for implementing ENUM in North America have tended to work toward creating a PSTN on the Internet, which could save them money, but not you or me.
This is not at all what is wanted. Why would I want to route VoIP calls from my system to yours across a network that wants to charge me for the privilege? SIP is designed to route calls between endpoints, and has no real use for the concept of a carrier.
The advantage of all this is supposed to be that when an ENUM lookup is performed, a valid SIP URI is returned.
Asterisk and ENUM
Asterisk can perform lookups against ENUM databases using either the ENUMLOOK UP() function or a combination of the ENUMQUERY() and ENUMRESULT() dialplan functions. ENUMLOOKUP() only returns a single value back from the lookup, and is useful when you know there is likely to only be one return value (such as the SIP URI you want the system to dial), or if you simply want to get the number of records available.
An ENUM lookup in the dialplan might look like this:
exten => _X.,1,Set(CurrentExten=${FILTER(0-9,${EXTEN})})
same => n,Set(LookupResult=${ENUMLOOKUP(${CurrentExten},sip,,,e164.arpa)})
same => n,GotoIf($[${EXISTS(${LookupResult})}]?HaveLocation,1)
same => n,Set(LookupResult=${ENUMLOOKUP(${CurrentExten},sip,,,e164.org)})
same => n,GotoIf($[${ISNULL(${LookupResult})}]?NormalCall,1:HaveLocation,1)
exten => HaveLocation,1,Verbose(2,Handle dialing via SIP URI returned)
exten => …
exten => NormalCall,1,Verbose(2,Handle dialing via standard PSTN route)
exten => …
The dialplan code we just looked at will take the number dialed and pass it to the ENUMLOOKUP() function. It requests the method type to be sip (we want the SIP URI returned) and the lookup to be performed first against the listings in DNS found in the
e164.arpa zone, and next against the records found at http://www.e164.org. Outside the countries that have implemented it, there is little uptake of ENUM. As such, many ENUM queries will not return any results. This is not expected to change in the near future, and ENUM will remain a curiosity until more widely implemented.