Category Archives: Development

Handling SIP URI Dialing in Asterisk

Asterisk, by design, is very “extension” orientated- that is, if you want to dial an end-point, it requires an extension to route the call to. These extensions (defined in the asterisk extensions.conf file), can be extensions registered to phones, DIDs (XXXYYYZZZZ), or simply usernames assigned to users by the network administrator. Extensions are used for both incoming, and outgoing phone calls.asterisk

For example, if I place a call through my SIP phone to 1-444-555-1212, then asterisk will look up the “extension” 14445551212 in the extensions.conf file, to determine how to route the call.

Similarly to how e-mail works, an artifact of these extensions is a direct SIP address, which is basically your SIP extension @ your SIP server- so, if my phone was extension 555, and my SIP server had the IP address 192.168.1.1, then my SIP address would be: sip:555@192.168.1.1, and (if it’s not implictely blocked), I can be dialed directly using that SIP address.

But, because Asterisk is so extension orientated, it doesn’t easily allow for outbound dialing, using remote SIP addresses; If I try to dial the address sip:444@somedomain.com, Asterisk will immediately strip off the host portion (@somedomain.com), and try to route the call based simply on the extension “444”- which, since it’s an extension on a remote server (@somedomain.com), it won’t be able to route it locally, and will fail.

The solution? Well, it’s not ideal, but Asterisk provides the ability to use wild cards in the extensions.conf file (it refers to them as “patterns“) when doing extension look ups; this is handy when you have blocks of extension or DID’s- you can use the wildcard to map like extensions to the same config, keeping your config file small.

The issue, is that the wild card only compares against the local part of the SIP URI, which can look like almost anything, including other phone numbers.

First, define some general config

[general]
;
; Your termination provider (defined in sip.conf)
;
TERM_PROVIDER = SIP/company_peer

;
; The IP address of this asterisk server
;
ASTERISK_IP = 192.168.1.1

The “ASTERISK_IP” address is important later, as we’ll use it to validate outgoing SIP addresses.

Then in your default config, you should have something like this configured already- handling NANPA style dialing, and international dialing

[default]
;
; Dial NANPA style phone numbers directly
;
exten => _1NXXNXXXXXX,n,Dial(${TERM_PROVIDER}/${EXTEN},60)
exten => _1NXXNXXXXXX,n,HangUp()

;
; Dial international numbers directly
;
exten => _011.,n,Dial(${TERM_PROVIDER}/${EXTEN}, 60)
exten => _011.,n,HangUp()

After all the specific matches are done, then add:

;
; very last, assume anything else is a SIP URI
;
exten => _.,n,GotoIf($[${SIPDOMAIN} = ${ASTERISK_IP}]?unhandled)
exten => _.,n,GotoIf($[${SIPDOMAIN} = ${ASTERISK_IP}:5060]?unhandled)
exten => _.,n,Macro(uri-dial,${EXTEN}@${SIPDOMAIN})
exten => _.,n,HangUp()

;
; if the call doesn't match anything
;
[unhandled]
exten => s,n,Congestion()

The reason this has to be last, is because matching the extension “_.” will match *anything*- basically it’s a catch-all. You’re saying that if it doesn’t match anything else before this, then assume it must be a SIP URI.

This section also compares the ${SIPDOMAIN} variable to your ASTERISK_IP address; this ensures that only SIP URI’s with remote hosts are processed as SIP URI’s. If the host matches our ASTERISK_IP address value (ie- it’s a local extension), then it should have already matched something above this catch-all config.

Pass any SIP URI dials to the uri-dial MACRO, merging back together the extension and the SIP domain value.

;
; handle dialing SIP uri's directly
;
[macro-uri-dial]
exten => s,n,NoOp(Calling as SIP address: ${ARG1})
exten => s,n,Dial(SIP/${ARG1},60)

This solution works, but isn’t ideal, as it will match anything that didn’t already match; a better solution would be for it to NOT strip off the SIP domain, and allow for using a regular expression of some kind to check the extension, but there is currently no better way of handling this in Asterisk.

Memcache Access From PostgreSQL

A big part of my job is to spend time figuring out how new technologies work, build on it, and then figure out new and inventive ways of merging it into new and existing systems- so I spent a lot of time “playing” around with stuff.

One thing I recently threw together, was some simple stored procedures for PostgreSQL, to give us built-in access to our memcached cluster, using the PL/PERL procedural language, and the Cache::Memcached module. None of this is rocket science, but might be useful for somebody out there that is looking to implement something similar.

First off, you need to build PostgreSQL with PERL support, and create the plperlu (un-trusted) language in our database; you need to use the “un-trusted” language, as we need to “use” the Cache::Memcached module;

postgres=# create language plperlu ;
CREATE LANGUAGE

Then you need to install the Cache::Memcached PERL module; I’ll assume you know how to do that; you can either install it through CPAN, or get it from the cpan.org site.

You also need at least one instance of memcached running somewhere; it doesn’t have to be on your local network, it just needs to be accessible by your database.

Then create the following procedures; I’ve only listed the “set” and “get” procedures, but you could easily follow the Cache::Memcached module man page, and create a “replace” and “delete” (and any other) procedures you needed.

So first the “set” procedure:

create or replace function memcache_set(_key text, _value bytea) returns int as $$

        use Cache::Memcached;

        my ($_key, $_value) = @_;

        $m = new Cache::Memcached {
                'debug'	=> 0
        };

        my @list = ("127.0.0.1:9996", "127.0.0.1:9997");
        my $servers = \@list;

        $m->set_servers($servers);
        $m->enable_compress(0);

        if ($m->set($_key, $_value))
        {
                return 0;
        } else
        {
                return 1;
        }

$$ language plperlu;

And then a “get” procedure:

create or replace function memcache_get(_key text) returns bytea as $$

        use Cache::Memcached;       

        my ($_key) = @_;

        $m = new Cache::Memcached {
                'debug'	=> 0
        };

	my @list = ("127.0.0.1:9996", "127.0.0.1:9997");
        my $servers = \@list;       

        $m->set_servers($servers);
        $m->enable_compress(0);

        my $val = $m->get($_key);
        if (defined($val))
        {
                return $val;
        } else
        {
                return undef;
        }

$$ language plperlu;

Now, in this case, I used a list of two different servers, both running on localhost, one on port 9996 and the other on port 9997; these servers could be anywhere, and you could only list one- but a really important thing to remember: if you do list more than one server, make sure you always list it in the same order, between all your procedures and all applications that may use this same data.

memcached calculates which server to store the data on, based on the order of this list; so you’ll get un-expected results if you list these servers differently between your applications.

A Simple Test

Now that you have the functions defined, you can use them to do some really interesting caching, directly from your database. Starting off with a simple test:

postgres=# select memcache_set('foo', 'bar');
 memcache_set 
---------------
               0
(1 row)

Then do a “get” to retrieve the data

postgres=# select memcache_get('foo');
 memcache_get
---------------
 bar
(1 row)

Database Session Handler

Now, lets say you had a database-backed session handler for your website; say maybe a PHP site, using something like this, which simply uses the PHP session_set_save_handler() method. Assume a simple session table like this:

create table sessions (
	session_id char(32) not null,
	data bytea not null default ''
);

“session_id” is the PHP session id (an MD5 string in this case), and “data” is the serialized contents of the PHP session.

Then add two rules- one for insert and one for update; this triggers the new memcache_set() procedure, so that new session data is always pushed out to your memcached servers

create rule session_insert_rule as on insert to sessions
	do also select memcache_set(NEW.session_id::text, NEW.data);

create rule session_update_rule as on update to sessions
	do also select memcache_set(NEW.session_id::text, NEW.data);

Testing this is simple:

postgres=# insert into sessions values ('5cc27b0285c9d2e6dc4125456d308548', 
     'This would be the session data');
INSERT 0 1

postgres=# select memcache_get('5cc27b0285c9d2e6dc4125456d308548');
              memcache_get
--------------------------------
 This would be the session data
(1 row)

And then subsequent updates

postgres=# update sessions set data = 'This is the new data' where
   session_id = '5cc27b0285c9d2e6dc4125456d308548';
UPDATE 1

postgres=# select memcache_get('5cc27b0285c9d2e6dc4125456d308548');
   memcache_get
-------------------
 This is the new data
(1 row)

Now just make your PHP session handler (or whatever) checks memcached first, before selecting from the sessions table; if you build it right, and set your expiration times correctly, you should be able to build a system that (almost) *never* reads this data from the database, as the data is always available from memcached.

VoicePHP by TringMe

TringMe, an Indian start-up just released its new VoicePHP service, which allows you to develop voice applications using PHP.

This is definitely cool stuff, though I see quite a few issues with implementation. HTTP, is, by design, a non-persistent transaction, and a phone call requires persistency while the call is in progress; therefore, there is no way for PHP alone, to handle “Voice” requests.

The only way to really make this work, is to have an external server (effectively a PBX of some kind) that handles the call traffic, that is simply polled (in some fashion) by PHP on a per request basis. Those requests could be polled via Ajax, or even a comet-like system, but it’s still not handled directly via PHP. I assume this is where their VoicePHP “Server” comes in.

voicephparch

So, in this sense, it sounds simply like a PHP wrapper around the TringMe REST API, which is something that can already be done in other ways, like accessing the Asterisk Manager interface via PHP using asterisk-php-api, but with one interesting twist-

It looks like (according to their FAQ) you can access the audio streams directly from PHP- which is pretty interesting, depending on how they’ve implemented it. Given the (generally) short transaction lengths of an HTTP request, I’m not sure how you would realistically pull audio from a real-time source (like a phone call), or play-back an audio clip on to a call, unless you used some sort of scheduling system.

ie- use PHP to decode and audio file (say .wav or .mp3), and then send the file to the VoicePHP server, which schedules the audio clip for playback, and returned immediately. It’s then played over the call in “real-time”. PHP could then poll the VoicePHP server to get status about the playback.

I’ve signed up for a beta account to play around with it, hopefully I’ll see something from them soon.

memcached (and facebook)

I’m really impressed that so many big companies, like facebook and google, have taken the time to release patches and source code, back out to the open-source community. Being able to improve memcached throughput from 50,000 requests/s to 200,000 requests/s is an amazing achievement.

Facebook

The changes that the facebook guys have made to memcache are going to help a lot of people out there, including fonolo. We’re using memcache heavily in-house, not only for web-based caching, but for a lot of the back-end VoIP and speech recognition applications.

We, obviously, don’t require the throughput that facebook does- given they just recently released data that showed they were adding more than 600,000 new users per day, more than 70% of which are coming from outside the USA. That’s growth at a staggering rate!

I have to say, that I’m a fan of facebook, and what they’ve managed to achieve, in a relatively short amount of time- I just have to wonder how long it will all last? Increasing your user-base by 600,000 users per-day; where is the plateau? Will it all eventually just topple over on itself?

Imagine when the next big social networking app comes out; will there be a mass-exodus from one to the other? or will facebook be so far in-grained in it’s users, that they won’t have any other choice but to stay?

No matter what happens,  it’s nice to know facebook is working out all the bugs for us.

Net::fonolo 1.3 Released

I justed released the latest and greatest version of Net::fonolo, a PERL module for accessing the fonolo.com developer program.

This release was pretty basic; just added support for the new API method company_list(), which returns a list of all the companies in the fonolo database, either by page, or by a date (so you can do incremental updates of a local cache).

If you’re interested in signing up for the fonolo developer program, click here.

The Net::fonolo module is available on CPAN , or here:

[download#2]