Log in

No account? Create an account
Asterisk - Alex Belits
For a while I had an Asterisk-based VoIP setup at work, that was used for occasional experimenting with SIP and H.323 clients and forwarding local (area codes 303 and 720) calls to my celphone that still has area code 650 (SF Bay Area). It was ridiculously trivial for such a cool technology, so I have decided to put it to a more fitting use, or at least do something more or less practical. That meant, of course, that main office phone should be no longer directly connected to the outside line, and that Asterisk should take over handling of all incoming calls.

I have taken a punchdown tool, and switched the lines on our phone panel -- originally outside and inside lines were connected, now outside line goes to the FXO port, internal phone line to the FXS port.

The problem is, Zoom 5801 SIP adapter that we have is FXO/FXS in name only. FXS is a device that talks to the phone while FXO talks to the outside phone network. PBX is a small phone switch, in this case a computer running Asterisk. It's usually assumed that a call coming from either of those interfaces can reach the VoIP PBX and be handled there. PBX in its turn can send a call through either of those interfaces -- inside or outside. This is not what Zoom 5801 does, at least within its documented capabilities. FXS part works great -- it turns a local analog phone into a SIP device, passes caller ID, ring, etc. FXO is umm... lacking. By default outside call comes straight to the analog phone, bypassing everything that even remotely looks like VoIP. A user can configure a mode where the call from outside is answered, and the caller hears another dialtone that is treated exactly like someone calling from the analog phone. Since this configuration would be a horrible security hole, it's possible to add a numeric password or restriction by called ID (as if it ever was hard to fake a caller ID), however the line is still treated exactly as an internal phone, as opposed to an outside caller that wants to hear something meaningful, not a dialtone. So that mode is of no help at all.

Another problem related to this was with the caller ID. After traveling through kinda-FXO-kinda-FXS bridge caller ID becomes mangled in some way that our analog phone doesn't like, and displays "incomplete data". So even letting those calls pass through means that I am going to lose functionality. Fortunately, there is a simple solution. I have an account with Voicepulse that allows free incoming for $11/mo. The phone number for that account (720-206-0669) can become a new primary incoming number at no additional cost. Our current main phone number (303-400-2936) can be simply forwarded to it by pressing *72, waiting for a dialtone, then calling 720-206-0669. Since 720 and 303 are local area codes, call forwarding is free, and incoming calls at the new number are also free, it does not affect the cost of those calls, however it means that:

1. All calls to the main number will go through VoIP.
2. While an incoming call is passed through Asterisk, analog line still can be used to make an outgoing call or to send a fax.

The fax part is important because VoIP doesn't like passing faxes over anything but a direct link to the phone network or 64Kbps protocol. VoIP providers avoid 64Kbps protocols because they take exactly the same amount of bandwidth as regular phones, so fax is a separate service that I don't have.

I have configured Zoom 5801 to send all calls to Asterisk except ones dialed to 303 and 720 area codes without a 1, so local line still works as outgoing for them. Faxmodem was moved back to the outside line, so nothing that it can do will affect voice calls that go through Asterisk.

So now incoming calls to either number end up at Voicepulse, where they are passed to my Asterisk server over IAX2, the native Asterisk protocol, outgoing calls to local area codes go straight to the analog line, outgoing calls to everything else go through Asterisk. See something missing? There is no way to actually turn the call forwarding on because neither a phone nor Asterisk can dial *72. Again, the solution is simple -- one of the poorly documented configuration pages of Zoom 5801 lists a prefix code for forced dialing out from the phone, it's #8, so *72 becomes #8, wait for dialtone, *72, wait again for dialtone, then a number. Good enough for a one-time operation.

Now, the interesting part -- Asterisk itself. There would be no point in configuring a VoIP PBX at my work if it didn't handle at least two features -- menus and following the user as he becomes available at the different phones. Say, when I am at my desk at work, I can use Ekiga softphone, and the calls will be local. When I am at Starbucks, Paris on Platte or Cafe Sole, I may have a laptop or 802.11b phone on, so calls will go over a less reliable and slower network but will be still free. And if I am driving, the only way to reach me is over a celphone, what means potentially worse quality, dropouts, airtime charges plus 1 cent per minute for outgoing VoIP calls that go through Voicepulse.

Asterisk configuration files look more or less normal for a Unix application (ironically a format known as win.ini) -- except one, extensions.conf. Its format is a reminder to all mortals that they are dealing not with just some piece of open source software, but a telephony application, something that belongs to a realm of unspeakable evil that could only descend from Ma Bell monopoly on passing vibrations over a distance.

The overall format is the same win.ini-like, with sections corresponding to contexts, and lines corresponding to commands that have extensions and priorities defined for them. For anything to happen, extension (whatever was dialed somewhere) should match with some line within the context (that is initially assigned to a call by its origin, and may change as the call progresses). Sequence of commands is determined by a number called "priority".

In other words, the whole thing is a program. In a language. That is not designed as a programming language. I would say that this design sucks, however my all previous experience with telephony software says that this is more or less the only way how telephony is ever done -- with programming languages designed not like programming languages, definitions that are about as intuitive as genders in foreign language for someone speaking English, and data structures designed as if they are supposed to be carved in stone every time a call arrives somewhere. And multiple layers of changes that accumulated between versions due to implementations, incremental changes and reimplementations of both ideas and formats. Thankfully there is no Gordian knot of references to ITU documents that reference other ITU documents, that in practical application never get implemented exactly like they are written, so compared to other pieces of computer telephony software this is extremely simple, streamlined and easy to use. Hooray!

To make a menu, I needed voice prompts. I speak with a Russian accent, so I can never be sure if a phrase assembled from pieces that I have recorded in my own voice is understandable -- and if it is, an accent would be very distracting. On the other hand, Festival's voices sound robotic but are neutral enough to be understood by everyone. Asterisk can call Festival directly, so initially when I was testing things, I just made Festival synthesize all speech on the fly. Once things became usable, I have made a script:

echo -n "Enter the text to pronounce > "
read text
echo ${text} | text2wave | sox -t wav - -r 8000 -c1 -v4 "/var/lib/asterisk/sounds/local/$1.gsm"
if [ -f "/var/lib/asterisk/sounds/local/$1.gsm" ]
 ls -l "/var/lib/asterisk/sounds/local/$1.gsm"
 exit 1

An obvious reason for pre-made prompts was to reduce the load on the server, however it's more important that while Festival is talking Asterisk is deaf to the DTMF (key) tones being entered by the caller, so one has to wait for menu prompts to stop before entering a choice. Playing pre-made files with a Background command has no such problem, so I have replaced all calls to Festival except one (where it recites the Caller ID) to Background playing the files.

Another problem that I have found was more of an obstacle for testing. Caller ID for all "my" extensions was set to my celphone number, so every time I called myself and the call was forwarded to a cellular phone, Verizon seen my caller ID and thought, I am checking my voice mail. Voice mail system asked for a password, and a celphone didn't even ring, so before calling me this "program" had to check for my caller ID and replace it with an impossible number that I have also configured into a celphone to recognize with a special ringtone.

In the end, relevant sections of extensions.conf looked like this:
exten => _1NXXNXXXXXX,1,Set(CALLERID(all)=Belits Computer Systems <7202060669>)
exten => _1NXXNXXXXXX,n(calloutside),Dial(IAX2/xxxxxxxxxx:xxxxxxxxxx@connect01.voicepulse.com/${EXTEN})
exten => _1NXXNXXXXXX,n,Dial(IAX2/xxxxxxxxxx:xxxxxxxxxx@connect02.voicepulse.com/${EXTEN})

; Call from outside to the main number, company menu system
exten => _1NXXNXXXXXX,1,Answer()
exten => _1NXXNXXXXXX,n,Wait(1)
exten => _1NXXNXXXXXX,n,Goto(incoming-menu|s|1)

;"Welcome to Belits Computer Systems."
;"Your phone number is"
exten => s,1,Background(local/welcome1)
exten => s,n,Festival(${CALLERIDNUM})

;"For office press one, for Valeriy press two, for Alex press zero"
exten => s,n(prompt),Background(local/welcome2)

;"Calling Alex"
exten => 0,1,Background(local/callingalex)
exten => 0,n,GotoIf($["${CALLERIDNUM}" = "6505555555"]?mangle:pass)
exten => 0,n(mangle),Set(CALLERID(number)=0001337734)
exten => 0,n(pass),Dial(SIP/abkphone&SIP/abelitswifi&SIP/ablaptop,60,t)
exten => 0,n,Goto(outgoing|16505555555|calloutside)

;"Calling Valeriy"
exten => 2,1,Background(local/callingvaleriy)
exten => 2,n,Goto(outgoing|17205555555|calloutside)

;"Calling Office"
exten => 1,1,Background(local/callingoffice)
exten => 1,n,Dial(SIP/belits-pstn,60,t)
;"Office phone does not answer."
exten => 1,n,Background(local/noansweroffice)
exten => 1,n,Goto(incoming-menu|s|prompt)

exten => _x,1,Playback(local/extension)
exten => _x,n,Playback(local/digits/${EXTEN})
;"does not exist"
exten => _x,n,Playback(local/doesnotexist)
exten => _x,n,Goto(incoming-menu|s|prompt)

;"Extension hash does not exist."
exten => _#,1,Background(local/exthashdoesnotexist)
exten => _#,n,Goto(incoming-menu|s|prompt)

;"Extension star does not exist."
exten => _*,1,Background(local/extstardoesnotexist)
exten => _*,n,Goto(incoming-menu|s|prompt)

;"Thank you for calling Belits Computer Systems."
exten => t,1,Playback(local/thankyouforcalling)
exten => t,n,Wait(1)
exten => t,n,Hangup

This is all very simple, and does not include any voice mail, external checks for availability of users, time, etc., but it serves its purpose well, and can be more or less easily extended. For a few hours of work dealing with a "language" like this it's decent and far above IVR monstrosities that I have seen before, so again I have to admit that this is what counts as the state of the art in computer telephony now. Seriously.

Tags: , ,

6 comments or Leave a comment
From: (Anonymous) Date: July 15th, 2006 07:58 am (UTC) (Link)

Other services

There are several ways to get 'free' services in and out of Asterisk, such as:

- IPKALL (http://www.ipkall.com) - Free numbers from Washington State forwarded to your SIP URI
- VoipStunt (http://www.voipstunt.com) - 'Free' outbound calls to around ~20 countries including Russia last I checked

Also, stop using extensions.conf! And go with extensions.ael, which is a more 'script-like' dialplan generator that is then compiled into the internal extensions.conf logic at load time. Much easier to maintain when making future enhancements. Or, even better, dump the internal dialplan scripting altogether and use AGI to do it in PHP, Ruby or whatever else you fancy.

Jason G
abelits From: abelits Date: July 15th, 2006 11:06 am (UTC) (Link)

Re: Other services

I have looked at AEL, and it looks more like a language than extensions.conf, however pretty much anything looks more like a language than extensions.conf. In the end, it's a better language format but not a much better processing model or expressions syntax. AGI, on the other hand, is interesting, but if I am going to use a real programming language, I would rather treat fastagi as a protocol -- I think, I can add more infrastructure, and make AGI requests to a server that runs real scripts and caches everything it needs to know about calls and everything they refer to.

Manager API looks like a good way to attach a program that implements a logic that is completely external to dialplan, however I haven't looked at it enough to say if it's useful for handling context of the call while dialplan implements dumber aspects of it, or if it's useful for being combined with AGI.

Not unlike certain other event-based interfaces, AGI and manager are more about "this happened in that tiny internal context, and that is all we know for now" rather than "this set of objects is performed/affected by a certain operation in those ways". On the other hand, it can be complete enough to give sufficient information to derive everything an application needs, but then there should be something in between that combines things.
From: (Anonymous) Date: July 17th, 2006 07:55 am (UTC) (Link)

Re: Other services

Yes, FastAGI is good as well and allows you to do database handling better as well as other things. The great thing about AGI is that it is very different than most other switching interfaces, in that anything you may do in the internal dialplan you may do externally, including accessing the 'call object' and all associated variables.

Depending on your language of choice, there is already a whole host of libraries available to handle the interface to Asterisk (although, easy enough to do yourself as well):

- http://phpagi.sourceforge.net
- http://asterisk-java.sourceforge.net
- http://www.snapvine.com/code/ragi/ (Personally, I am a fan of RoR)

Asterisk is far more powerful than any other traditional PBX/ACD CTI link around, as it truly exposes the internals to virtually any programming environment of your choice.

And yes, AGI may be mixed with the internal dialplan whether it be extensions.conf, AEL or RealTime (fed from MySQL or other database tables).

There are a lot of things you may do...

Jason G
abelits From: abelits Date: July 17th, 2006 09:23 pm (UTC) (Link)

Re: Other services

Indeed. It's just when I do development I think of an API and protocols in a way how they can be used to implement some kind of infrastructure. All the data is there -- the interesting part of the task is to organize it in a way that makes it easy to implement a complex functionality.

Storing state in database may be justified for "web applications" because when an unknown number of users simultaneously sends requests, a developer may expect the total amount of data simultaneously being processed to be arbitrarily large -- it is not tied to the available resources other than total bandwidth and storage. Also any failure can be easily recovered from by the next request being served by another server in a cluster, without any visible breakage of session for the user.

Telephony is different -- a developer can be absolutely sure that if he deals with some number of simultaneously existing connections, all the associated data definitely did fit into RAM of something -- they are all objects being processed by PBX, and PBX has limited number of endpoints and limited amount of RAM where all current connections have to live. And fault tolerance is always limited by the continuity of streams -- if the box doing a codecs conversion crashed, another one won't pick its functionality seamlessly, at least one user (or surviving application) will have to redial a dropped link.

This means, the current state of all connections can always be stored in the RAM (nearly infinitely faster to access than a database -- from persistent fastAGI process or not) in the form of native objects of the language used, that correspond to user's application's representation of calls and related parameters (then it's an infrastructure -- we _always_ know who is talking to whom, how they are connected, how they got connected, and what application thinks about them -- decision-making function/method has all information in the objects), not just PBX interface's representation of them (then it's a wrapper -- we know what just happened, and it's application's responsibility to dig into a database to find all relevant data to make a decision, what to do with it). Database can store things that are supposed to last, however placing temporary state variables there can cripple the performance -- database becomes a communication channel, and database are really bad communication channels, their current popularity in this role in web applications notwithstanding.

I see Asterisk's flexibility and completeness of data available as a good possible foundation for infrastructure designs, and those can be much more useful than just relying on every end-user application maintaining its own context or being designed in a way with no context other than one immediately available in request (no matter how factually complete it is). Infrastructure doesn't have to be heavy, it's a matter of design, not set of features or completeness of functionality.

I wanted to be able to work in that direction, however other interfaces stubbornly limited the quality/completeness of available data tying it to their designers' idea of what telephony applications may need in "fire-and-forget" processing model that was obsolete even before those interfaces were designed.

Asterisk's internal dialplan scripting, even enhanced by placing calls to AGI scripts, is uglier than Fortran, AGI scripts with a database mimic semi-stateless HTTP requests processing even though the requirement of statelessness never happens in telephony (what was ever connected will always at some point be mentioned again as being disconnected), however as protocol AGI and Manager API seem to be sufficient to build a stateful environment for object-oriented applications. Of course, here I make distinction between applications in object-oriented languages and object-oriented applications -- java and ruby APIs that you have mentioned are in object-oriented languages, but they are not object-oriented by their design, they reflect data structures used in an existing protocol.
From: (Anonymous) Date: July 20th, 2006 04:20 pm (UTC) (Link)

Re: Other services

When you say 'infrastructure' what exactly do you have in mind in terms of a service?

Jason G
abelits From: abelits Date: July 24th, 2006 12:12 am (UTC) (Link)


1. Class library (ported to multiple languages but keeping the same structure) that reflect the conditions (status, topology of connections) of all persistent objects (endpoints/participants and connections) in a consistent manner. When applications are developed, programmers should be expected to be able to use that library to derive their classes that reflect relationship between telephony and their applications, however those classes should not be _the_ implementation of the application logic if application has non-telephony-related functionality.

For example, traditional trouble ticket processing application should be able to tie "live" and "dead" links, known and anonymous parties to external data and other objects with various external references (accounts, tickets, records, files sent from and to users), and allow creation of completely different objects -- for example, a person sitting in a queue may be given an ID that he enters in a web form where he can start entering data/upload files before the call is dispatched, or even before the decision about call destination is made. This can be done with a database and countless bazillions of queries for possibly related data per operation, but it's much more effective and flexible with custom persistent objects, where all related data can be just enumerated and dereferenced.

In simplier systems it should make it easy for developers to avoid undesired routes such as receptionist transferring a caller to a voice mail when an alternative destination is available but requires a human decision to be used, or overloading/underloading higher levels of technical support, etc -- anything that requires large amount of application-specific information to be available for decision-making procedure or to be displayed for a human making that decision.

2. Format and protocol/event system that allows storage and replication of relevant data in such an object/data system. Application should be able to instantly see which relevant connections are "alive", yet at the same time have access to all data collected about both telephony-related objects (who talked to whom, who is available) and the issue (that may be in a separate object system, or limited to database records referred by their IDs). External application logic should not be directly added to this system, however some processing of external data and notifications probably should be allowed. It's a fine balance between keeping clearly defined telephony stuff insulated from the variety of applications' demands and processing models, and making interfaces useful for those applications.

3. A set of methods/functions that perform telephony-related actions and operations on connections using references to the objects instead of internal telephony-related identifiers or relying on a narrow context of a single connection. Those functions should be able to return something meaningful, such as "Operation valid, in progress, expected outcome updated in operation-in-progress fields for the objects involved", "Operation invalid, instant failure", "Operation invalid, objects changed into a state inconsistent with request before it was sent" or "Operation already performed".

4. Implementation of 1, 2 and 3 as a combination of client libraries, asterisk components (glue that uses asterisk interfaces, applications that perform pieces of functionality) and a separate server.

5. Synchronization server, responsible for passing data between instances of a server to identify and update the state of the same objects (endpoints and connections) appearing on multiple asterisk instances, and allowing a server to build a consistent view of those things. Should be useful for clustered and distributed setups.
6 comments or Leave a comment