Chapter 14. Potpourri
The first 90 percent of the task takes 90 percent of the time, and the last 10 percent of the task takes the other 90 percent of the time.
The toughest part of writing this book was not finding things to write about, but rather deciding what we would not be able to write about. Now that we’ve covered the basics, you are ready to be told the truth: we have not taught you anywhere near all that there is to know about Asterisk.
Now please understand, this is not because we didn’t want to give you our very best; it’s merely because Asterisk is, well, limitless (or so we believe).
In this chapter, we want to give you a taste of some of the wonders Asterisk holds in store for you. Nearly every section in this chapter could become a book in itself (and they will become books, if Asterisk succeeds in the way we think it is going to).
Festival
Festival is a popular open source text-to-speech engine. The basic premise of using Festival with Asterisk is that your dialplan can pass a body of text to Festival, which will then “speak” the text to the caller. Probably the most obvious use for Festival would be to have it read your email to you when you are on the road.[147]
Getting Festival Set Up and Ready for Asterisk
There are currently two ways to use Festival with Asterisk. The first (and easiest) method—without having to patch and recompile Festival—is to add the following text to Festival’s configuration file (festival.scm, usually located in /etc/ or /usr/share/festival/):
(define (tts_textasterisk string mode) "(tts_textasterisk STRING MODE) Apply tts to STRING. This function is specifically designed for use in server mode so a single function call may synthesize the string. This function name may be added to the server safe functions." (let ((wholeutt (utt.synth (eval (list 'Utterance 'Text string))))) (utt.wave.resample wholeutt 8000) (utt.wave.rescale wholeutt 5) (utt.send.wave.client wholeutt)))
You may place this text anywhere in the file, as long as it is not between any other parentheses.
The second (and more traditional) way is to compile Festival with an Asterisk-specific patch (located in the contrib/ directory of the Asterisk source).
Information on both of these methods is contained in the README.festival file, located in the contrib/ directory of the Asterisk source.
For either method, you’ll need to modify the Festival access list in the festival.scm file. Simply search for the word “localhost” and replace it with the fully qualified domain name of your server.
Both of these methods set up Festival to be able to correctly
communicate with Asterisk. After setting up Festival, you should start
the Festival server. You can then call the Festival()
application from within your dialplan.
Configuring Asterisk for Festival
The Asterisk configuration file that deals with Festival is aptly called festival.conf. Inside this file, you specify the hostname and port of your Festival server, as well some settings for the caching of Festival speech. For most installations (if you’re going to run Festival on your Asterisk server), the defaults will work just fine.
Starting the Festival Server
To start the Festival server for debugging purposes, simply run festival
with the --server
argument, like this:
[root@asterisk ~]# festival --server
Once you’re sure that the Festival server is running and not rejecting your connections, you can start Festival by typing:
[root@asterisk ~]# festival_server 2>&1 >/dev/null &
Calling Festival from the Dialplan
Now that Festival is configured and the Festival server is started, let’s call it from within a simple dialplan:
exten => 123,1,Answer() exten => 123,2,Festival(Asterisk and Festival are working together)
Tip
You should always call the Answer()
application before calling Festival()
, to ensure that a channel
is established.
As Asterisk connects to Festival, you should see output like this in the terminal where you started the Festival server:
[root@asterisk ~]# festival --server
server Sun May 1 18:38:51 2005 : Festival server started on port 1314
client(1) Sun May 1 18:39:20 2005 : accepted from asterisk.localdomain
client(1) Sun May 1 18:39:21 2005 : disconnected
If you see output like the following, it means you didn’t add the host to the access list in festival.scm:
[root@asterisk ~]# festival --server
server Sun May 1 18:30:52 2005 : Festival server started on port 1314
client(1) Sun May 1 18:32:32 2005 : rejected from asterisk.localdomain not
in access list
Call Files
Call files allow you to create calls through the Linux shell. These powerful events are triggered by depositing a .call file in the directory /var/spool/asterisk/outgoing/. The actual name of the file does not matter, but it’s good form to give the file a meaningful name and to end the filename with .call.
When a call file appears in the outgoing folder, Asterisk will almost immediatelyact on the instructions contained therein.[148]
Call files are formatted in the following manner. First, we define where we want to call:
Channel: channel
We can control how long to wait for a call to be answered (the
default is 45 seconds), how long to wait between call retries, and the
maximum number of retries. If MaxRetries
is omitted, the call will be
attempted only once:
WaitTime:number
RetryTime:number
MaxRetries:number
If the call is answered, we specify where to connect it here:
Context:context-name
Extension:ext
Priority:priority
Alternatively, we can specify a single application and pass arguments to it:
Application: Playback() Data: hello-world
Next, we set the Caller ID of the outgoing call:
CallerID: Asterisk 800-555-1212
Then we set channel variables, as follows:
SetVar:john=Zap/1/5551212
SetVar:sally=SIP/1000
and add a CDR account code:
Account: documentation
Warning
When you create a call file, do not do so
from the spool directory. Asterisk monitors the spool aggressively and
will try to grab your file before you’ve even finished writing it!
Create call files in some other folder, make a copy in the same
folder, and then mv
the copy into
the spool directory. Note that we said mv
, not cp
. This is important, because the way that
Linux copies files means that the file appears in the destination
folder before it is completely there. Contrast that with a mv
operation, which will not allow the file
to appear in the destination folder until the move operation is
complete. If you copy, there is a very good chance that Asterisk will
read the file before it is all there, which will cause unexpected
results.
DUNDi
If there were any concerns that Mark Spencer was in danger of running out of good ideas, Distributed Universal Number Discovery (DUNDi) ought to lay such thoughts to rest. DUNDi is poised to be as revolutionary as Asterisk. The DUNDi web site (http://www.dundi.com) says it best: “DUNDi™ is a peer to peer system for locating Internet gateways to telephony services. Unlike traditional centralized services (such as the remarkably simple and concise ENUM standard; http://www.faqs.org/rfc/rfc2916.txt), DUNDi is fully distributed with no centralized authority whatsoever.” DUNDi is somewhat of a routing protocol for VoIP.
How Does DUNDi Work?
Think of DUNDi as a large phone book that allows you to ask peers if they know of an alternative VoIP route to an extension number or PSTN telephone number.
For example, assume that you are connected to the DUNDi-test network (a free and open network that terminates calls to traditional PSTN numbers). You ask your friend Bob if he knows how to reach 1-212-555-1212, a number for which you have no direct access. Bob replies, “I don’t know how to reach that number, but let me ask my peer Sally.”
Bob asks Sally if she knows how to reach the requested number,
and she responds with, “You can reach that number at IAX2/dundi:
very_long_password@hostname/extension
.”
Bob then stores the address in his database and passes on to you the
information about how to reach 1-800-555-1212 via VoIP, allowing you
an alternative method of reaching the same destination through a
different network.
Because Bob has stored the information he found, he’ll be able to provide it to any peers who later request the same number from him, so the lookup won’t have to go any further. This helps reduce the load on the network and increases response times for numbers that are looked up often. (However, it should be noted that DUNDi creates a rotating key and, thus, stored information is valid for a limited period of time.)
DUNDi performs lookups dynamically, either with a switch =>
statement in your
extensions.conf file or with the use of the
DUNDiLookup()
application. DUNDi is
available only in Asterisk version 1.2 or higher.
You can use the DUNDi protocol in a private network as well. Say you’re the Asterisk administrator of a very large enterprise installation, and you wish to simplify the administration of extension numbers. You could use DUNDi in this situation, allowing multiple Asterisk boxes (presumably located at each of the company’s locations and peered with one another) to perform dynamic lookups for the VoIP addresses of extensions on the network.
Configuring Asterisk for Use with DUNDi
There are three files that need to be configured for DUNDi: dundi.conf, extensions.conf, and iax.conf.[149] The dundi.conf file controls the authentication of peers whom we allow to perform lookups through our system. This file also manages the list of peers to whom we might submit our own lookup requests. Since it is possible to run several different networks on the same box, it is necessary to define a different section for each peer, and then configure the networks in which that peer is allowed to perform lookups. Additionally, we need to define which peers we wish to use to perform lookups.
The General Peering Agreement
The General Peering Agreement (GPA) is a legally binding license agreement that is designed to prevent abuse of the DUNDi protocol. Before connecting to the DUNDi-test group, you are required to sign a GPA. The GPA is used to protect the members of the group and to create a “trust” between the members. It is a requirement of the DUNDi-test group that your complete and accurate contact information be configured in dundi.conf, so that members of your peer group can contact you. The GPA can be found in the doc/ subdirectory of the Asterisk source.
General configuration
The [general]
section
of dundi.conf contains parameters
relating to the overall operation of the DUNDi client and
server:
; DUNDi configuration file ; [general] ; department=IT organization= toronto.example.com locality=Toronto stateprov=ON country=CA email=support@toronto.example.com phone=+19055551212 ; ; Specify bind address and port number. Default is 4520 ;bindaddr=0.0.0.0 port=4520 entityid=FF:FF:FF:FF:FF:FF ttl=32 autokill=yes ;secretpath=dundi
The entity identifier defined by entityid
should generally be the Media Access Control (MAC) address of an interface in
the machine. The entity ID defaults to the first Ethernet address of
the server, but you can override this with entityid
, as long as it is set to the MAC
address of something you own. The MAC address
of the primary external interface is recommended. This is the
address that other peers will use to identify you.
The Time To Live (ttl
)
field defines how many peers away we wish to receive
replies from and is used to break loops. Each time a request is
passed on down the line because the requested number is not known,
the value in the TTL field is decreased by one, much like the TTL
field of an ICMP packet. The TTL field also defines the maximum
number of seconds we are willing to wait for a reply.
When you request a number lookup, an initial query (called a
DPDISCOVER
) is sent to your peers requesting that number. If you do
not receive an acknowledgment (ACK
) of your query (DPDISCOVER
) within 2,000 ms (enough time
for a single transmission only) and autokill
is equal to yes
, Asterisk will send a CANCEL
to the peers. (Note that an
acknowledgment is not necessarily a reply to the query; it is just
an acknowledgment that the peer has received the request.) The
purpose of autokill
is to keep
the lookup from stalling due to hosts with high latency. In addition
to the yes
and no
options, you may also specify the
number of milliseconds to wait.
The pbx_dundi module creates a rotating
key and stores it in the local Asterisk database (AstDB). The key
name secret
is stored in the
dundi
family. The value of the
key can be viewed with the database
show
command at the Asterisk console. The database family
can be overridden with the secretpath
option.
Creating mapping contexts
The dundi.conf file defines DUNDi
contexts that are mapped to dialplan contexts in your extensions.conf file. DUNDi
contexts are a way of defining distinct and separate directory
service groups. The contexts in the mapping section point to
contexts in the extensions.conf file, which
control the numbers that you advertise. When you create a peer, you
need to define which mapping contexts you will allow this peer to
search. You do this with the permit
statement (each peer may contain
multiple permit
statements). Mapping contexts are
related to dialplan contexts in the sense that they are a security
boundary for your peers.
Phone numbers must be advertised in the following format:
<country_code><area_code><prefix><number>
For example, a complete North American number could be advertised as 14165551212.
All DUNDi mapping contexts take the form of:
dundi_context
=>local_context
,weight
,technology
,destination
[,options
]]
The following configuration creates a DUNDi mapping context that we will use to advertise our local phone numbers to the DUNDi-test group. Note that this should all appear on one line:
dundi-test => dundi-local,0,IAX2,dundi:${SECRET}@toronto.example.com/ ${NUMBER}, nounsolicited,nocomunsolicit,nopartial
In this example, the mapping context is dundi-test
, which points to the dundi-local
context within
extensions.conf (providing a listing of phone
numbers to reply to). Numbers that resolve to the PBX should be
advertised with a weight
of zero
(directly connected). Numbers higher than 0 indicate an increased
number of hops or paths to reach the final destination. This is
useful when multiple replies for the same lookup are received at the
end that initially requested the number; a
weight
with a lower number will be the
preferred path.
If we can reply to a lookup, our response will contain the method by which the other end can connect to the system. This includes the technology to use (such as IAX2, SIP, H323, and so on), the username and password with which to authenticate, which host to send the authentication to, and finally the extension number.
Asterisk provides some shortcuts to allow us to create a “template” with which we can build our responses. The following channel variables can be used to construct the template:
Defining DUNDi peers
DUNDi peers are defined in the dundi.conf file. Peers are identified by the unique layer-two MAC address of an interface on the remote system. The dundi.conf file is where we define what context to search for peers requesting a lookup and which peers we want to use when doing a lookup for a particular network:
[00:00:00:00:00:00] ; Remote Office model = symmetric host = montreal.example.com inkey = montreal outkey = toronto include = dundi-test permit = dundi-test qualify = yes dynamic=yes
The remote peer’s identifier (MAC address) is enclosed in
square brackets ([]
). The inkey
and outkey
are the public/private key pairs
that we use for authentication. Key pairs are generated with the astgenkey script, located in the
./asterisk/contrib/scripts/
source directory. Be sure to use the -n
flag so that you don’t have to initialize passwords every
time you start Asterisk:
#cd /var/lib/asterisk/keys
#/usr/src/asterisk/contrib/scripts/astgenkey -n toronto
The resulting keys, toronto.pub and toronto.key, will be placed in your /var/lib/asterisk/keys/ directory. The toronto.pub file is the public key, which you should post to a web server so that it is easily accessible for anyone with whom you wish to peer. When you peer, you can give your peers the HTTP-accessible public key, which they can then place in their /var/lib/asterisk/keys/ directories.
After you have downloaded the keys, you must reload the res_crypto.so and pbx_dundi.so modules in Asterisk:
*CLI>module reload res_crypto.so
-- Reloading module 'res_crypto.so' (Cryptographic Digital Signatures) -- Loaded PRIVATE key 'toronto' -- Loaded PUBLIC key 'toronto' *CLI>module reload pbx_dundi.so
-- Reloading module 'pbx_dundi.so' (Distributed Universal Number Discovery (DUNDi)) == Parsing '/etc/asterisk/dundi.conf': Found
Then, create the dundi
user
in the iax.conf file to allow connections into your Asterisk system. When a
call is authenticated, the extension number being requested is
passed to the dundi-local
context
in the extensions.conf file,
where the call is then handled by Asterisk.
Allowing remote connections
Here is the user definition for the dundi
user:
[dundi] type=user dbsecret=dundi/secret context=dundi-local disallow=all allow=ulaw allow=g726
Instead of using a static password, Asterisk regenerates
passwords every 3,600 seconds (1 hour). The value is stored in
/dundi/secret of the Asterisk database and
advertised using the ${SECRET}
variable defined within the mapping context lines in dundi.conf. You can see the current keys
for all peers, including your local public and private keys, by
performing a show keys
at the
Asterisk CLI.
The context
entry dundi-local
is where authorized callers
are sent in extensions.conf.
From there, we can manipulate the call just as we would in the
dialplan of any other incoming connection.
Configuring the dialplan
The extensions.conf file
handles what numbers you advertise and what you do with the calls
that connect to them. The dundi-local
context performs double
duty:
It controls the numbers we advertise, referenced by the
dundi
mapping context in dundi.conf.It controls what to do with the call, referenced by the
dundi
user in iax.conf.
You have the power of dialplan pattern matching to advertise ranges of numbers and to control the incoming calls. In the following dialplan, we are only advertising the number +1-416-555-1212, but pattern matching could just as easily have been employed to advertise a range of numbers or extensions:
[dundi-local] exten => 14165551212,1,NoOp(dundi-local: Number advertisement and incoming) exten => 14165551212,n,Answer() exten => 14165551212,n(call),Dial(SIP/1000) exten => 14165551212,n,Voicemail(u1000) exten => 14165551212,n,Hangup() exten => 14165551212,n(call)+101,Voicemail(b1000) exten => 14165551212,n,Hangup()
Alternative Voicemail Storage Methods
Asterisk’s normal way of storing voicemail is to simply record the message in a file, which is placed on the local hard drive under the /var/spool/asterisk/voicemail tree. While this works well enough for simple PBX deployments, there are more advanced ways of doing this that can be very useful in larger, distributed networks, or environments where tighter integration with external applications is desired.
Storing Voicemail in an IMAP Server
The ability to store voice messages in the same location as regular email is something that the telecom industry has been promising for a long time. They called it Unified Messaging, and while most PBXes now offer some sort of unified messaging, it is typically very expensive to license and implement.
Naturally, Asterisk cuts through all the silliness and just allows you to have your voicemailbox integrated into an IMAP environment. There are several advantages to storing your voicemail on an IMAP server. When you listen to a voicemail on your phone, the message is set to the read state on the IMAP server. This means that your email client will also note that it has been read. By the same token, if you listen to the message from your email client, the voicemail will turn off the message notification light on any phones that are assigned to that mailbox. Deleting a message from one place will cause it to be deleted from every place. So once deleted, the message is truly gone. This is Unified Messaging, the holy grail of voicemail to email integration, but Asterisk humbly decides not to make a big deal of it.
IMAP integration is still new functionality, so there are a few things that need to be added in order to get it to function. First off, Asterisk needs to have an IMAP client installed so that it can communicate with the IMAP server. Pretty much any IMAP server works (even Exchange Server), and the authors have personally tested IMAP voicemail support with both the Courier-IMAP and Dovecot IMAP servers. The IMAP server may be on the same physical machine as the Asterisk installation, or it may be on the other side of the globe. To be able to access the IMAP server, Asterisk requires an IMAP client library. This library is the University of Washington’s free IMAP client, named c-client. To install the c-client you simply need to navigate to your /usr/src directory and run the following commands:
# wget ftp://ftp.cac.washington.edu/mail/imap.tar.Z
This downloads the source code. Extract it with:
# tar zxvf imap.tar.Z
Note
You’ll want to pay special attention to the name of the directory that is created by this command, as the directory name will probably change again by the time you read this. During the production of this book, the directory name has changed four times. The last time we checked, it was named /usr/src/imap-2006h.
Navigate into the resulting folder and run:
# make lrh IP6=4
This will compile everything Asterisk needs to make use of the IMAP client libraries.[150]
Now we have to recompile Asterisk with the IMAP capabilities. We’ll need to navigate to the location of our Asterisk source files (such as /usr/src/asterisk), and run the following command:
# /configure --with-imap=/usr/src/imap-2006h
The we need to rerun make
menuconfig
to incorporate IMAP storage into the compile.
Under Voicemail Build Options select the IMAP_STORAGE
parameter, and then press
x
to save and exit. This ensures
that when we compile Asterisk, it will build the IMAP module as well.
Obviously, the next step then is to recompile and reinstall Asterisk.
A simple way to do this is, in your terminal, to run:
# make && make install
OK, so we’ve got the module compiled and installed. Now it’s time to configure it.
In the /etc/asterisk
folder, we’ll need to add a few lines to the voicemail.conf file, in the [general]
section:
imapserver=localhost imapport=143 expungeonhangup=yes authuser=vmail authpassword=vmailsecret imapfolder=Voicemail
Since Dovecot is available in the CentOS package repository, installing a small IMAP server to handle your virtual (voicemail) users on your Asterisk box is simple:
# yum install dovecot
Now make sure that IMAP support is enabled in /etc/dovecot.conf by uncommenting the protocols line so that it appears as follows:
protocols = imap imaps
After you’ve enabled IMAP support, create the user account that will be storing the mail:
# groupadd vmail # useradd vmail -g vmail -s /bin/true -c "asterisk voicemail user" -p vmailsecret -d /var/spool/asterisk/imap-voicemail vmail # chown -R vmail.vmail /var/spool/asterisk/imap-voicemail
Now restart Dovecot and Asterisk, and you should be good to go.
#service dovecot restart
#service asterisk restart
Congratulations! You’ve successfully installed basic IMAP
voicemail support with Asterisk! This is just the tip of the iceberg,
though. With IMAP voicemail storage, it is easy to implement shared
(e.g., departmental) voicemail using shared IMAP folders. Many
companies already have departmental email, so having a shared
voicemail box is a very natural and logical progression of the
technology. With IMAP voicemail storage, each employee can manage
several voicemail boxes without becoming confused as to whether a
particular voicemail message is for them personally or for a
department to which they belong. There is nothing unusual to configure
from Asterisk’s point of view; you simply call the VoiceMail()
application with the desired
mailbox and context, and make sure that the department employees have
the shared IMAP folder included in their email client’s folder
list.
Finally, you may want to use per-mailbox authorization (i.e.,
each voicemail box authenticates as a specific user) instead of a
global Asterisk IMAP user. Asterisk supports this through the imapuser
and imappassword
options in the individual
voicemail box definition entries:
[imapvoicemail] 100 => 1234,Sue's Mailbox,,,imapuser=sue@example.tld|imapsecret=suesimapsecret 101 => 5555,Bob's Mailbox,,,imapuser=bob@example.tld|imapsecret=bobsimapsecret
In
this particular example, if a message is left in IMAP mailbox 100 in
the imapvoicemail
context, Asterisk will
authenticate to the IMAP server as sue@example.tld
, using suesimapsecret
as the password. Similarly,
bob@example.tld/bobsimapsecret
will
be used to authenticate if a message is left in mailbox 101 of the
same voicemail context.
Storing Voicemail in an ODBC Database
In case you missed it, you can also store voicemail in a database via the ODBC connector. See Chapter 12 for details!
Asterisk and Jabber (XMPP)
The name Jabber is actually the original name for the IETF XMPP protocols (RFC 3920-3923). Since Jabber is by far a better name than XMPP, the original name has stuck. This protocol was originally designed to be a decentralized, nonproprietary, open-standards messaging and presence framework. It supports offline message delivery and encryption, and has grown to include voice messaging, which Asterisk supports.
It is interesting to note that in the beginning, Jabber was seen as a competitor to the SIMPLE protocol, which is SIP-based. XMPP is designed as a more general protocol, and is of course XML-based.
Asterisk can be configured to utilize XMPP in several capacities. It may utilize XMPP as a presence framework (e.g., Extension 205 is away or on the phone), or, through the voice messaging framework JINGLE, support full voice communications with other services such as Google Talk.
Unlike other messaging networks such as MSN and Yahoo!, XMPP is decentralized. Anyone may have his own Jabber server and run any number of services on his server. You send messages in much the same way that you send email: the Jabber server you use contacts the Jabber server of the other person, and a direct connection is established. If the other person is not online, the message is stored, and when she log in to her Jabber server, any stored messages are delivered. With encryption (the XMPP protocol supports TLS), it is no wonder why many businesses are starting to implement their internal messaging networks using this amazing protocol, and Asterisk is able to seamlessly integrate into this communications network.
Conclusion
That’s pretty much all that this chapter is going to teach you, but it’s nowhere near all there is to learn. Hopefully, you are starting to get an idea of how big this Asterisk thing really is.
In the next chapter, we’re going to try and predict the future of telecom, and we’ll discuss how (and why) we believe that Asterisk is well positioned to play a starring role.
[147] Probably the coolest use of Festival is in Simon Ditner’s ZoIP, a port of the famous Zork game to a fully speech-enabled engine running on Asterisk (ZoIP also uses Sphinx, which we will not be covering in this book). We’re going to have to come up with a new kind of name for this sort of thing. It’s not a video game, since there is no screen, so do we need to call these audio games? Regardless, check it out at http://www.zoip.org.
[148] We’re talking within milliseconds here. Don’t believe us? Try it for yourself!
[149] The dundi.conf and extensions.conf files must be configured. We have chosen to configure iax.conf for our address advertisement on the network, but DUNDi is protocol-agnostic—thus sip.conf, h323.conf, or mgcp.conf could be used instead.
[150] The lrh
option tells the
compiler that this is a Linux Red Hat system. The IP6=4
option tells the compiler that we
don’t want to compile in support for IPV6. Read the
Makefile for other options. For RHEL 5 or
CentOS 5, you should use lr5
instead of lrh
.
Get Asterisk: The Future of Telephony, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.