Asterisk, and other worldly endeavours.

A blog by Leif Madsen

Posts Tagged ‘dialplan

Converting multiple exten => lines to using same => in Asterisk dialplan

Last week I wanted to start changing some 1.4 based Asterisk dialplan to a 1.8 based Asterisk system, and in that process wanted to convert lines like:

exten => _NXXNXXXXXX,1,NoOp()
exten => _NXXNXXXXXX,2,GotoIf($[...]?reject,1)
exten => _NXXNXXXXXX,3,Dial(SIP/foo/${EXTEN})
...

into using the same => prefix:

exten => _NXXNXXXXXX,1,NoOp()
 same => n,GotoIf($[...]?reject,1)
 same => n,Dial(SIP/foo/${EXTEN})

In order to do that, Mike King helped me out with the following regular expressing which I used in vim:

%s/exten\s*=>\s*[^,]\+,\s*[n2-9]/ same => n/g

Written by Leif Madsen

2012/01/16 at 8:28 am

Cooking With Asterisk (Video)

And I found the Cooking With Asterisk presentation as well! Apologies for the low quality on the video (as this one has a lot more text than the Distributed Call Centre video), but you can follow along with the presentation on your screen as available on my website at:

http://leifmadsen.com/sites/default/files/AstriCon%202011%20-%20Cooking%20With%20Asterisk.pdf

The video presentation is available here:

http://www.tmcnet.com/tmc/videos/default.aspx?vid=5741&title=Cooking+with+Asterisk#

Written by Leif Madsen

2011/12/23 at 11:17 am

Using Dialplan Functions: AES_DECRYPT() and AES_ENCRYPT()

I  recently asked on twitter how many people would be interested in a set of blog posts that focused on how to use the various dialplan functions in Asterisk, and I got quite a positive response. I posted that shortly before getting married, and now that I’m back into the groove of things, I’m going to take a shot at posting a bunch of content focused around Asterisk dialplan functions. If you don’t know what Asterisk dialplan functions are, head on over to the online version of Asterisk: The Definitive Guide (3rd edition) (or buy it) and read the section on dialplan functions. If you’re still starting out with Asterisk, I highly suggest you start with the dialplan basics chapter.

Today we’ll look at the first 2 dialplan functions in my list: AES_DECRYPT() and AES_ENCRYPT()

The AES_DECRYPT() and AES_ENCRYPT() functions work by passing strings to the functions, and they return a result. If you pass an unencrypted string to the AES_ENCRYPT() function it will return an encrypted string; vice-versa for the AES_DECRYPT() function. The two functions operate by passing a string and a key where the result is encoded  in base64.

Use case for these functions probably makes the most sense when you need to store data outside of the dialplan, perhaps passwords, pins, or other data passed in by the caller, but which you want to secure when you go to store it. Let’s take an example where we create some dialplan that allows a caller to set their pin and store it in the database. For the sake of simplicity I’m not going to add any error checking (like to verify we really have data to work with, allow the caller to verify their extension, etc.):

exten => *88,1,NoOp()
 same => n,Playback(silence/1)
 same => n,Read(UserExtension,extension,3)                  ; read persons 3 digit extension unmber
 same => n,Verbose(2,Extension number: ${UserExtension})
 same => n,Read(PinEntry,agent-pass)                        ; ask for a pin number
 same => n,Verbose(2,Pin number: ${PinEntry})
 same => n,SayDigits(${PinEntry})                           ; say pin back to caller
 same => n,Set(DB(pin/${UserExtension})=${PinEntry})        ; store pin in the AstDB
 same => n,Playback(vm-goodbye)
 same => n,Hangup()

After the user enters their extension and pin, we store it in the AstDB. We can verify it was stored correctly by checking from the Asterisk CLI:


scrappy*CLI> database show pin
/pin/100 : 1234
1 results found.

Now let’s modify our dialplan to store the pin in the database using a value returned from AES_ENCRYPT().

exten => *88,1,NoOp()
 same => n,Playback(silence/1)
 same => n,Read(UserExtension,extension,3)
 same => n,Verbose(2,Extension number: ${UserExtension})
 same => n,Read(PinEntry,agent-pass)
 same => n,Verbose(2,Pin number: ${PinEntry})
 same => n,SayDigits(${PinEntry})
 same => n,Set(SpecialKey=1234qwerasdfzxcv)
 same => n,Set(EncryptedPin=${AES_ENCRYPT(${SpecialKey},${PinEntry})})
 same => n,Set(DB(pin/${UserExtension})=${EncryptedPin})
 same => n,Playback(vm-goodbye)
 same => n,Hangup()

And we can see the encoded string stored in the database:


scrappy*CLI> database show pin
/pin/100 : Je2G/qyHuGVKgvvXDwXjHA==
1 results found.

Of course anyone who has access to the AstDB from the Asterisk CLI is also going to have access to the Asterisk dialplan, so you’ll have to do a better job than I have here of hiding the secret key being used for encrypting the data. Really all we’re trying to do here is not make the list of pins and data in our AstDB quite so obvious. We could of course not use AstDB at all, and store the data remotely where we know people will have access to the data, but not access to the secret key on our Asterisk server.

Now lets look at the inverse by decoding the pin to authenticate someone.

exten => *77,1,NoOp()
 same => n,Playback(silence/1)
 same => n,Read(UserExtension,extension,3)                     ; get users extension
 same => n,Set(EncryptedPin=${DB(pin/${UserExtension})})       ; get encrypted pin from AstDB
 same => n,Read(PinEntry,agent-pass)                           ; get pin from user
 same => n,Set(SpecialKey=1234qwerasdfzxcv)
 same => n,Set(DecryptedPin=${AES_DECRYPT(${SpecialKey},${EncryptedPin})})                          ; decrypt the pin
 same => n,Playback(${IF($["${PinEntry}" = "${DecryptedPin}"]?pin-number-accepted:pin-invalid)})    ; if pin is correct, play number accepted, else, pin invalid
 same => n,Playback(vm-goodbye)
 same => n,Hangup()

That’s it for now. Leave a comment if you like this format, and if you found this article useful. Thanks!

Written by Leif Madsen

2011/09/09 at 8:28 am

Set() and Goto() on same line

(Thanks to Jared Smith for answering my question in IRC which is the inspiration for this post.)

Typically when I write dialplan, primarily in the case where I’m using a pattern match, I’ll save the dialed extension to a channel variable using Set(), then do a Goto() where the call logic is handled. The Set() is so that I don’t lose the value of ${EXTEN} throughout the dialplan process, especially if I'm using other mechanics such as GoSub() and others.

I’ve been doing this on two or three lines like this (usually three because I like using a NoOp() or Verbose() for the first priority):

exten => _NXXNXXXXXX,1,NoOp()
   same => n,Set(DialedExtension=${EXTEN})
   same => n,Goto(CallHandler,1)

This is kind of annoying for each pattern match, especially if you’re going to do multiple. Here is a legitimate example of the CallHandler extension:

exten => _NXXNXXXXXX,1,NoOp()
   same => n,Set(DialedExtension=${EXTEN})
   same => n,Goto(CallHandler,1)

exten => _1NXXNXXXXXX,1,NoOp()
   same => n,Set(DialedExtension=${EXTEN})
   same => n,Goto(CallHandler,1)

exten => _NXXXXXX,1,NoOp()
   same => n,Set(DialedExtension=${EXTEN})
   same => n,Goto(CallHandler,1)

exten => CallHandler,1,NoOp()
   same => n,Dial(${GLOBAL(PSTN_CONNECTION)}/${DialedExtension},30)
   same => n,Hangup()

It’s a bit annoying having to either type out the same type of logic multiple times, even if it’s only 2-3 lines (even if you just copy and paste the same => lines it’s a bit better, but still not ideal). So here’s a solution to the same problem of multiple pattern matches and doing a Goto() our CallHandler extension.

exten => _NXXNXXXXXX,1,GotoIf($[${EXISTS(${SET(DialedExtension=${EXTEN})})}]?CallHandler,1:i,1)
exten => _1NXXNXXXXXX,1,GotoIf($[${EXISTS(${SET(DialedExtension=${EXTEN})})}]?CallHandler,1:i,1)
exten => _NXXXXXX,1,GotoIf($[${EXISTS(${SET(DialedExtension=${EXTEN})})}]?CallHandler,1:i,1)

exten => CallHandler,1,NoOp()
   same => n,Dial(${GLOBAL(PSTN_CONNECTION)}/${DialedExtension},30)
   same => n,Hangup()

exten => i,1,Congestion()

While both ways are perfectly reasonable (and some may argue the more verbose method is easier to read), I like embedding dialplan into a single line when I can as I find it easier to maintain. I’m also pretty good at knowing how many brackets to end with when nesting functions, but not everyone is comfortable doing that; in those cases you should probably break it out to multiple lines in order to save debugging time. Both methods are perfectly valid, so enjoy using whichever you prefer!

Written by Leif Madsen

2011/08/12 at 9:14 pm

Posted in Asterisk, Programming

Tagged with , ,

Asterisk: The Definitive Guide, First Draft Complete!

Today we marked the finishing of Asterisk: The Definitive Guide (3rd Edition) by sending the first draft off to the O’Reilly production team. We finished nearly on schedule (within a few days, which is remarkable considering we added 150 more pages than originally intentioned) and have our favourite copy editor Rachel Head (formerly Wheeler) who did the first edition of Asterisk: The Future of Telephony who we emphatically enjoyed working with. We’re looking forward to it again.

With the draft done now, and the copy sent off to production to do their magic, I’m not quite sure what I’m going to do with my mornings! Perhaps I’ll start blogging about some dialplan stuff, or coming up with some sort of project to build. We’ll see! Any suggestions about things to document on my blog are of course welcome.

Don’t stop posting your comments and suggestions to the OFPS site though! (http://ofps.oreilly.com) When the O’Reilly production team is done with the book in a few weeks, we’re going to have time to do additional modifications and editing, so your comments will get addressed then. It’s looking like the estimated shipping date of books for March is realistic at this point.

Thanks for all the support the community has shown us this far. This is going to be by far the best book on Asterisk to date.

Written by Leif Madsen

2011/01/11 at 8:30 pm

Scheduling automated calls between two participants with res_calendar

Here is a little dialplan snippet I wrote this morning for the next edition of the Asterisk book. While I’m not going to delve into all the aspects of setting up res_calendar like we do in the book, I thought for those of you who might already have this working might enjoy it.

(The calendaring modules are available in Asterisk 1.8, which is currently in release candidate status. Check out http://www.asterisk.org/downloads for the current version.)

I started with this little bit of dialplan that gets triggered when a call is answered from the calendaring module:



[AutomatedMeetingSetup]
exten => start,1,Verbose(2,Triggering meeting setup for two participants)
   same => n,Set(DeviceToDial=${FILTER(0-9A-Za-z,${CALENDAR_EVENT(location)})})
   same => n,Dial(SIP/${DeviceToDial},30)
   same => n,Hangup()

The location field in my calendar event (which in this case is labeled as Destination in my Google Calendar) contains the string 0000FFFF0002 which is the device identifier in my example.

Once you get that working, the cool magic happens below. In the following example, a call is placed from the calendaring module when a meeting needs to take place between two participants. The first part of the dialplan allows the first person called to accept or reject the meeting, and if accepted, to record a message for the other party. Once that recording is saved, the dialplan will go ahead and trigger a call to the other meeting participant.

When that meeting participant answers the call, a Macro() is employed to allow them to listen to the recorded message left by the first party, (i.e. “Hey Jim, this is Leif. We have a meeting scheduled right now.”). That person is then presented the same option to accept or reject the call by pressing 1 or 2.

Of course the dialplan could even by further expanded to play back messages when the calls were rejected, the option for the called party to leave a return message of why they are rejecting the call, and maybe even the ability to post-pone the call for a few minutes. All it takes is some clever dialplan!

[AutomatedMeetingSetup]
exten => start,1,Verbose(2,Triggering meeting setup for two participants)
same => n,Read(CheckMeetingAcceptance,to-confirm-wakeup&press-1&otherwise&press-2,,1)
same => n,GotoIf($["${CheckMeetingAcceptance}" != "1"]?hangup,1)

same => n,Playback(silence/1&pls-rcrd-name-at-tone&and-prs-pound-whn-finished)
same => n,Set(__RandomNumber=${RAND()})
same => n,Record(/tmp/meeting-invite-${RandomNumber}.ulaw)

same => n,Set(DeviceToDial=${FILTER(0-9A-Za-z,${CALENDAR_EVENT(location)})})
same => n,Dial(SIP/${DeviceToDial},30,M(CheckConfirm))
same => n,Hangup()

exten => hangup,1,Verbose(2,Call was rejected)
same => n,Playback(vm-goodbye)
same => n,Hangup()

[macro-CheckConfirm]
exten => s,1,Verbose(2,Allowing called party to accept or reject)
same => n,Playback(/tmp/meeting-invite-${RandomNumber})
same => n,Read(CheckMeetingAcceptance,to-confirm-wakeup&press-1&otherwise&press-2,,1)
same => n,GotoIf($["${CheckMeetingAcceptance}" != "1"]?hangup,1)

exten => hangup,1,Verbose(2,Call was rejected by called party)
same => n,Playback(vm-goodbye)
same => n,Hangup()

Written by Leif Madsen

2010/09/29 at 9:25 am

Musings about ENUMplus and ISNs

Recently I’ve been playing around with a couple of technologies that I haven’t used in quite a while; ENUM and ISN. First, a little information on what these technologies are about.

ISNs and ITADs

First, lets talk about ISN (ITAD Subscriber Number) and ITAD (Internet Telephony Administrative Domain). I think the http://www.freenum.org website does a good job of succinctly describing the problem attempting to be addressed:

“The Freenum/ISN system is 12-digit-keypad (telephone handset) friendly method of providing mapping between users. While the eventual use of email-style URI pointers is the eventual goal for communications identifiers, it is still the case that the majority of the world’s telephony users are trapped using a 12-digit keypad for extended numeric entry, and it is for the purposes of these devices that the Freenum system and ISN dialing was designed as a “stopgap” which may last many years. Using ENUM-like methods and IETF standards but not using telephone numbers, the Freenum/ISN dialing system is designed to initially allow SIP-capable proxies and iPBX systems to connect to each other in a free, open, and protocol agnostic manner over the Internet. Initially focused on SIP voice communications, the platform is hoped to eventually extend to other communications methods as those protocols become more widespread.” — freenum.org

An ISN is a number that looks like:  100*460

The ITAD part of that ISN is the number 460 where you can think of the 460 as the domain. If we used an email address (or SIP URI for that matter) as an example, we’d have something like leif@leifmadsen.com where leifmadsen.com would be the domain, and the number 460 would be the equivalent of that. The number 100 would be the unique identifier within that domain, which would represent ‘leif’ prior to the email ‘at’ symbol. And finally, you can think of the asterisk in the ISN as the ‘at’ symbol (@) in the ISN. We could then read 100*460 as “extension 100 at ITAD 460”. And the 100*460 would then map to the SIP URI of sip:leif@leifmadsen.com (for example).

This allows a stopgap measure of permitting traditional telephony devices to dial VoIP addresses from their keypad. Because of the nature of VoIP and how a simple URI can actually ring multiple devices, enter into a queue, or an auto-attendant, it doesn’t always make sense to utilize a telephone number to represent a SIP URI (see more information about this below in the ENUM section). Perhaps you are a small company with approximately 20 devices, but only 2-3 phone numbers. It doesn’t make sense to obtain multiple phone numbers for your company just as a method of dialing a SIP URI from a traditional keypad. In this way, you can assign numbers any which way it makes sense in your company with ISNs.

The company with 20 extensions could then assign ISNs to each device such as:  201*460, 202*460 … 220*460. The operator could be 0*460, and perhaps the auto-attendant could be 1*460. There are no set mechanisms or best practices (yet), but as time evolves, perhaps these will come to fruition.

I’m not sure if ISNs will catch on with the general public, as historically things like ENUM and other services of this nature haven’t progressed as much as they likely should, but the idea is sound, and certainly makes more sense to me than having to remember several phone numbers for each extension or location.

As this is simply an overview about ISNs, I’ll stop here. Future articles will delve into the configurations and testing for outbound and inbound calls with Asterisk, setting up DNS, etc. For now, see the http://freenum.org website for more information about configuring these aspects. Unfortunately the Asterisk information is out of date (in terms of utilizing the best features of the dialplan). I’d be happy to update the information if enough requests and interest is generated.

ENUMplus

ENUMplus (http://www.enumplus.org) is a site which takes the information from several ENUM databases and allows you to perform a single lookup using cURL. Since there is a lot of technology going on in that sentence, lets step back and define each of these aspects.

I think the wikipedia entry about ENUM does more justice about what it is than what I could do, so lets quote a resource 🙂

Telephone number mapping is the process of unifying the telephone number system of the public switched telephone network with the Internet addressing and identification name spaces. Telephone numbers are systematically organized in the E.164 standard, while the Internet uses the Domain Name System for linking domain names to IP addresses and other resource information. Telephone number mapping systems provide facilities to determine applicable Internet communications servers responsible for servicing a given telephone number by simple lookups in the Domain Name System.

The most prominent facility for telephone number mapping is the E.164 NUmber Mapping (ENUM) standard. It uses special DNS record types to translate a telephone number into a Uniform Resource Identifier or IP address that can be used in Internet communications.” — wikipedia entry at http://en.wikipedia.org/wiki/Telephone_Number_Mapping

And cURL is a technology that allows you place a request via a website, and have information returned. It is most typically used by programmers to get information from a website without having to parse through the entire site.

So by marrying these two technologies, ENUM and cURL together, ENUMplus has created a one-stop-shop for performing ENUM lookups. By performing an ENUM lookup prior to placing a call from your Asterisk system, is that if a telephone number has been registered with one of the ENUM organizations and has a SIP URI to point to, then we can utilize that SIP URI instead of calling over the PSTN, which can save toll costs, and free up a circuit for other calls.

I originally wrote a section of dialplan while testing out the service (which is very quick!) and added it to the ENUMplus wiki page for the configuration of Asterisk 1.6 systems. Originally I had this done as a separate path for dialing that would utilize a prefix of 7 prior to dialing out in order to do an ENUM lookup, and then you could dial without the prefix if you didn’t want the lookup (or if the lookup failed). I’ve since realized that this method is the wrong approach for obvious reasons (who wants to dial the number twice?), so I have since modified my dialplan to always utilize ENUM lookups prior to placing a call via the PSTN.

I want to show you the following example from my dialplan, which I’ll eventually clean up and add to the ENUMplus wiki.

exten => _+1NXXNXXXXXX,1,Set(X=${EXTEN:1})
exten => _+1NXXNXXXXXX,n,Goto(setCID,1)
exten => _+NXXNXXXXXX,1,Set(X=1${EXTEN:1})
exten => _+NXXNXXXXXX,n,Goto(setCID,1)
exten => _1NXXNXXXXXX,1,Set(X=${EXTEN})
exten => _1NXXNXXXXXX,n,Goto(setCID,1)
exten => _NXXNXXXXXX,1,Goto(1${EXTEN},1)

exten => setCID,1,NoOp()
exten => setCID,n,Set(CALLERID(name)=LM Enterprises)
exten => setCID,n,Set(CALLERID(num)=4164790259)
exten => setCID,n,Goto(lookup,1)

exten => lookup,1,Verbose(2,Looking up direct dial via ENUM from ENUMPlus: ${X:1})
exten => lookup,n,Playback(silence/1&doing-enum-lookup)
exten => lookup,n,Set(CURL_RESULT=${CURL(${GLOBAL(G_ENUMPLUS_API)}/${X:1},key=${GLOBAL(G_ENUMPLUS_KEY)})})
exten => lookup,n,GotoIf($[${ISNULL(${CURL_RESULT})}]?no_result,1)
exten => lookup,n,Goto(dial,1)

exten => dial,1,Verbose(2,Lookup returned:  ${CURL_RESULT})
exten => dial,n,Playback(enum-lookup-successful)
exten => dial,n,Dial(${CUT(CURL_RESULT,|,1)},30)
exten => dial,n,Hangup()

exten => no_result,1,Verbose(2,ENUMPlus returned no data.)
exten => no_result,n,Playback(silence/1&enum-lookup-failed)
exten => no_result,n,Set(OUTBOUND_ROUTE=SIP/${DEFAULT_ITSP_ROUTE})
exten => no_result,n,Dial(${OUTBOUND_ROUTE}/${X})
exten => no_result,n,Hangup()

Written by Leif Madsen

2010/02/19 at 10:38 am

Posted in Asterisk, Musings

Tagged with , , , , , , ,