How I used Asterisk to Take Back Control of my Telephone
23rd Jun 2013, 13:11:40
I turned to Asterisk to solve the problem of telemarketers cold calling us. Along the way, I discovered that it can do much more and it saves us money. Read on to see my configuration.
So, what of it?
Asterisk is a complete PBX implemented in software. It allows computers to do what futurologists always promised, it does your busy work and gives you more time. I have become a complete Asterisk fanboy since setting up a PBX for our home in early 2009. This is my setup:
The features I enjoy the most are:
- Internal calls - I can ring the kitchen from the garage and order a cup of coffee (I wish!).
- Callers who withhold their caller ID do not cause the phones to ring.
- Calls received during the night or at dinner time do not ring the phones.
- Known nuisance callers and telemarketers are blocked entirely.
- Answer phone messages get emailed to me as an attachment.
- Outgoing calls are automatically 'least cost' routed to PSTN or VoIP to find the cheapest tariff.
- Multiple concurrent calls can be made over the PSTN and VoIP services.
- Each member of the household can have their own telephone number, each of which can be pre-paid with a specified amount of credit and metered.
- I can make free IP calls over the internet to my Asterisk-enabled friends or family members in 'HD voice'.
- If I can do it with a script, I can do it over the phone with Asterisk!
The least-cost routing, multiple concurrent calls and anonymous call blocking scored a very high WAF for me, so bear that in mind if this is a consideration for you.
Requirements
For home or small business use, Asterisk requires very little processing power. A Pentium III or better is bound to be sufficient. I run my PBX on a Soekris net5501-60, which is a low power embedded AMD Geode i386 platform with 256MB RAM.
It looks industrial enough.
The FXO card is a tight fit, but it does fit with some persuasion.
Most people in the UK already have a telephone line because it's bundled with internet and TV services. You may want to buy an FXO card to interface this line with Asterisk, since most include a free call allowance. An ordinary 'voice' modem is not the same and will not do (OK, there are exceptions to this rule, but let's gloss over that). The cheapest FXO card I'm aware of is the X100P Wildcard, but I do not recommend that you purchase this card. I am very happy with the performance of my OpenVox A400P card, as are others to whom I have recommended it. Yes it's nearly four times the price, but it is well worth £50.00 to retain your sanity.
Otherwise, Asterisk can still be used purely in a VoIP capacity if you do not want or need a PSTN line. Consider how you will make a 999 call in an emergency.
Finally, you really need some proper handsets. Softphones are OK for testing to begin with but they proved to be too impractical for my household. You can reuse your existing PSTN telephones using an analogue telephone adapter. At £25 upwards, it's easy to recommend a Grandstream Handytone 701, 702 or 704 as a good starter device - the last digit of the model number refers to the number of analogue handsets the device can use.
My hardphones are currently a mix of retired Cisco handsets and some Gigaset DECT handsets connected to an N300IP base station. Configuring the Cisco handsets really did leave a lot to be desired, each device requires a number of configuration files and firmware images to be available on a TFTP server. Because of the fiddly configuration, I really wouldn't recommend them for typical home situation, but at least they have proved to be very reliable. The Gigaset system was very simple to configure, very much a web interface point-and-click affair.
Getting Going - a Very Basic Setup
I use Debian wheezy as the operating system because it is my favourite Linux distribution. Asterisk works with any mainstream Linux distribution, so if you wanted to use CentOS or similar it's not going to be a big deal.
As would be expected, there are packaged versions of Asterisk available in Debian and other distributions, but I think it is preferable to compile from source. While it is easy to just 'apt-get install' Asterisk, it will make it more difficult later when you get the VoIP bug and want to start trying out more exotic features like chan_mobile and what-have-you. It is very easy to compile Asterisk, so it may as well be done on day 1.
Complete a standard install of Debian wheezy. Make sure there is some
(2GB will be enough) space in the /usr
partition, since
you'll need the Linux headers and other source code here.
These are the packages we'll need to compile Asterisk:
# apt-get install build-essential linux-headers-`uname -r` libxml2-dev ncurses-dev libssl-dev
Now, download the source from Digium:
# cd /usr/src # wget http://downloads.asterisk.org/pub/telephony/certified-asterisk/certified-asterisk-1.8.15-current.tar.gz # wget http://downloads.asterisk.org/pub/telephony/dahdi-linux-complete/dahdi-linux-complete-current.tar.gz # tar zxf certified-asterisk-1.8.15-current.tar.gz.tar.gz # tar zxf dahdi-linux-complete-current.tar.gz
DAHDI isn't strictly necessary if you don't have any FXO or other line cards, but some Asterisk features like MusicOnHold still rely on it and will break in odd ways if it isn't there. So, compile DAHDI first:
# cd /usr/src/dahdi-linux-complete-2* # make all && make install && make config
When that completes, move on to build Asterisk itself:
# cd /usr/src/certified-asterisk-1.8.15-cert* # ./configure && make && make install && make samples && make config
We've just compiled and installed Asterisk 1.8:
# asterisk -V Asterisk 1.8.15-cert2
make config
installs the necessary scripts to start DAHDI and Asterisk at boot time,
you can reboot the box to check this, or just start the services yourself:
# /etc/init.d/dahdi start # /etc/init.d/asterisk start
To ensure all is well, connect to your asterisk daemon with asterisk -r
or
rasterisk
:
# rasterisk Asterisk 1.8.15-cert2, Copyright (C) 1999 - 2012 Digium, Inc. and others. Created by Mark Spencer >markster@digium.com< Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details. This is free software, with components licensed under the GNU General Public License version 2 and other licenses; you are welcome to redistribute it under certain conditions. Type 'core show license' for details. ========================================================================= Connected to Asterisk 1.8.15-cert2 currently running on asterisk (pid = 1986) asterisk*CLI> quit #
Asterisk's configuration files are stored in /etc/asterisk
. There are a lot of files
here, but really we need only be concerned with a handful of them. extensions.conf
is
your dialplan where most important stuff gets done. I like to start with this:
# cd /etc/asterisk # mkdir backup # mv extensions.conf backup/ # vi extensions.conf [globals] [house-phones] exten => 200,1,Dial(SIP/200) exten => 201,1,Dial(SIP/201)
The above is a minimal extensions.conf; it defines extensions 200 and 201 in the context
house-phones
. Now we may create two SIP users for these phones:
# cd /etc/asterisk # mv sip.conf backup/ # vi sip.conf [200] type=friend host=dynamic username=200 secret=xyzxyz context=house-phones canreinvite=no [201] type=friend host=dynamic username=201 secret=xyzxyz context=house-phones canreinvite=no
This creates our SIP accounts. Any two SIP phones will be able to use these details to log in to asterisk and call each other, provided that we ask asterisk to reload our config changes:
# rasterisk asterisk*CLI> reload ......much output..... asterisk*CLI> quit
OK, we have two SIP phones ringing each other. This is cool, but probably not terribly useful. If you wish to call the outside world, you'll need to configure the A400P device:
# echo "lc_country uk" >> /etc/dahdi/genconf_parameters # dahdi_genconf
This creates /etc/dahdi/system.conf
. You'll need to restart DAHDI later to pick up the
changes. First, we tell Asterisk about the DAHDI device using the chan_dahdi.conf
file:
# cd /etc/asterisk # mv chan_dahdi.conf backup/ # vi chan_dahdi.conf [channels] ; UK centric. ; implementations vary wildly depending on (and sometimes within) country of residence usecallerid=yes ukcallerid=yes cidsignalling=v23 ; Added for UK CLI detection cidstart=polarity ; Added for UK CLI detection sendcalleridafter=2 callerid=asreceived ; propagate the CID received from BT. context=from-pstn rxgain = 7.0 txgain = 6.0 group=0 channel => 1 ; This isn't commented out, it's the stupid include syntax! #include dahdi-channels.conf
Restart everything:
/etc/init.d/asterisk stop /etc/init.d/dahdi restart /etc/init.d/asterisk start
Now we must edit extensions.conf to take account of this new capability:
# cd /etc/asterisk # vi extensions.conf [global] [house-phones] exten => 200,1,Dial(SIP/200) exten => 201,1,Dial(SIP/201) ; Entries in [to-pstn] can be dialled from SIP extensions 200 and 201 include => to-pstn [to-pstn] ; Emergency numbers 999 and 112 need to go to the PSTN, clearly exten => 999,1,Dial(DAHDI/1/${EXTEN},60,rTK) exten => 112,1,Dial(DAHDI/1/${EXTEN},60,rTK) ; Starts with a 0 and contains more than 5 digits - it's a phone number, dial it exten => _0XXXX.,1,Dial(DAHDI/1/${EXTEN},60,rTK) ; Six digits? It's a local call, dial it exten => _XXXXXX,1,Dial(DAHDI/1/${EXTEN},60,rTK) ; BT Test number, very useful, try it exten => 17070,1,Dial(DAHDI/1/${EXTEN},60,rTK) [from-pstn] ; All incoming calls ('s') cause phones 200 and 201 to ring exten=> s,1,Dial(SIP/200&SIP/201)
And reload:
# rasterisk asterisk*CLI> reload ......much output..... asterisk*CLI> quit
We now have the ability to place calls to and answer calls from the PSTN. If it's not working for you, you can monitor what's going on through the asterisk monitor:
# rasterisk asterisk*CLI> core set debug 10 asterisk*CLI> core set verbosity 10 asterisk*CLI>
You'll notice that that the [house-phones]
are in a different context to the PSTN.
Contexts are containers into which extensions are placed in order to partition different groups
of users and devices from each other. Because we want the house phones to be able to dial out,
we use an include =>
statement.
We don't
include =>
the house phones in the [from-pstn]
context because outside
callers always reach the special extension s
which triggers the Dial()
function. We might want to use extension s
for something else in other contexts.
This would be a good point to take a look at features.conf
. This file controls the
codes used to access facilities like transferring, hold etc. during a call. By default, '#' is
mapped to transfer a call. This is convenient, but it causes problems when using IVR systems that
ask you, for example, to enter your account number followed by #. I change the code to be '#1',
circumventing the problem. To do this, uncomment the following lines in features.conf
:
blindxfer => #1 ; Blind transfer (default is #) disconnect => *0 ; Disconnect (default is *)
An Answerphone
How about an answerphone? Of course Asterisk can do this for you. It uses
voicemail.conf
:
# cd /etc/asterisk # mv voicemail.conf backup/ # vi voicemail.conf [general] format = wav ; limit messages to 5 minutes maxmessage=300 ; less than 2 seconds? probably not a valid message minmessage=2 [default] ; This creates mailbox 298 with pin 1234. Specifying an email address causes you to receive ; messages as email attachments 298 => 1234,James Stocks,stocksy@stocksy.co.uk
Add a voicemail line to the [from-pstn]
context and an extension through which
voicemail will be accessed:
# cd /etc/asterisk # vi extensions.conf [global] [house-phones] exten => 200,1,Dial(SIP/200) exten => 201,1,Dial(SIP/201) ; Entries in [to-pstn] can be dialled from SIP extensions 200 and 201 include => to-pstn ; Dial to access voicemail exten => 299,1,VoiceMailMain(298) [to-pstn] ; Emergency numbers 999 and 112 need to go to the PSTN, clearly exten => 999,1,Dial(DAHDI/1/${EXTEN},60,rTK) exten => 112,1,Dial(DAHDI/1/${EXTEN},60,rTK) ; Starts with a 0 and contains more than 5 digits - it's a phone number, dial it exten => _0XXXX.,1,Dial(DAHDI/1/${EXTEN},60,rTK) ; Six digits? It's a local call, dial it exten => _XXXXXX,1,Dial(DAHDI/1/${EXTEN},60,rTK) ; BT Test number, very useful, try it exten => 17070,1,Dial(DAHDI/1/${EXTEN},60,rTK) [from-pstn] ; All incoming calls ('s') cause phones 200 and 201 to ring exten=> s,1,Dial(SIP/200&SIP/201,15) ; Go to voicemail after 15 seconds exten => s,n,VoiceMail(298,u)
This will cause the caller to be diverted to voicemail after 15 seconds. You'll need to set up your voice mailbox by dialling 299. Don't forget to reload Asterisk to pick up the changes. For email to work, you'll need a working MTA set up on your Asterisk box. This is covered in the final section of this page, 'Securing and other Final Touches'.
Adding a VoIP Service
Now you've got an asterisk box you can start to recoup your hardware costs by saving money on your calls. There are many providers to chose from; I ended up choosing VoIPon partly because they had been very helpful when I was choosing my FXO card, but mainly because they offer an IAX connection. For now, steer clear of companies that only offer a SIP connection. For the beginner it isn't worth the aggravation of sorting out all the UDP based RTP streams and attendant NAT issues; IAX is just one UDP port in and out.
Assuming you want to use VoIPon, go along to their web site and sign up for an account. It doesn't cost anything to get them to allocate you an 0871 number and you only need to buy £5.00 worth of credit when you want to start making outgoing calls.
Once your account is set up, it's time to configure the Asterisk box at our end. Firstly, make sure that your firewall or router will allow UDP port 4569 out to the internet and that UDP 4569 is accepted (and NATed) through to your Asterisk server's internal IP address. Then, we put our account details into the IAX configuration file:
# cd /etc/asterisk/ # mv iax.conf backup/ # vi iax.conf [general] ; VoIPon don't seem to support call tokens at present, let's disable them for now calltokenoptional = 0.0.0.0/0.0.0.0 ; Set this to the maximum number of concurrent IAX calls you think you might need to handle ; 10 is probably more than you need for a home system maxcallnumbers = 10 ; This is where our outgoing calls get placed - use your own account details [voipon-stocksy] type=peer secret=aaa000 username=0000000 host=iax.voipon.co.uk context=to-voipon-stocksy qualify=yes ; DTMF seems to work only with ulaw disallow=all allow=ulaw ; Here are our incoming calls - use your own 0871 number here [08710000000] type=friend username=08710000000 context=from-voipon-stocksy ; DTMF seems to work only with ulaw disallow=all allow=ulaw
Reload Asterisk to pick up the changes. You can check the status of your new IAX connection from the Asterisk monitor:
asterisk*CLI> iax2 show peers Name/Username Host Mask Port Status 08710000000/087 (Unspecified) (S) 0.0.0.0 4569 Unmonitored voipon-stocksy/ 217.14.138.130 (S) 255.255.255.255 4569 OK (29 ms) 2 iax2 peers [1 online, 0 offline, 1 unmonitored] asterisk*CLI> iax2 show peer voipon-stocksy * Name : voipon-stocksy Secret : <Set> Context : to-voipon-stocksy Mailbox : Dynamic : No Callnum limit: 0 Calltoken req: No Trunk : No Callerid : "" <> Expire : -1 ACL : No Addr->IP : 217.14.138.130 Port 4569 Defaddr->IP : 0.0.0.0 Port 0 Username : 0000000 Codecs : 0x4 (ulaw) Codec Order : (ulaw) Status : OK (31 ms) Qualify : every 60000ms when OK, every 10000ms when UNREACHABLE (sample smoothing Off) asterisk*CLI> iax2 show peer 0871000000 * Name : 08710000000 Secret : <Not set> Context : from-voipon-stocksy Mailbox : Dynamic : No Callnum limit: 0 Calltoken req: No Trunk : No Callerid : "" <> Expire : -1 ACL : No Addr->IP : (Unspecified) Port 4569 Defaddr->IP : 0.0.0.0 Port 0 Username : 0871000000 Codecs : 0x4 (ulaw) Codec Order : (ulaw) Status : Unmonitored Qualify : every 60000ms when OK, every 10000ms when UNREACHABLE (sample smoothing Off)
So, you can see that we have determined that our outgoing calls will be placed using the
[to-voipon-stocksy]
context and received in to the [from-voipon-stocksy]
context. We need to create those contexts:
# cd /etc/asterisk # vi extensions.conf [global] [house-phones] exten => 200,1,Dial(SIP/200) exten => 201,1,Dial(SIP/201) ; Dial to access voicemail exten => 299,1,VoiceMailMain(298) ; Heres the 'clever' bit I suppose ; ; Weekend calls (between 00:00 on saturday and 23:59 on sunday), go to the PSTN first because they ; are free of charge: include => to-pstn,00:00-23:59,sat-sun,*,* ; Evening calls (between 18:59 and 07:59 go to BT first because they are cheaper: include => to-pstn,18:00-07:59,mon-fri,*,* ; Daytime calls go to VoIPon first because they are cheaper: include => to-voipon,08:00-17:59,mon-fri,*,* include => to-pstn-force ; ; Some stuff always should go to the PSTN ; [to-pstn-force] ; ; **IMPORTANT** We need to make sure emergency calls always work irrespective of what time of ; day it is! exten => 999,1,Dial(DAHDI/1/${EXTEN},60,rTK) exten => 112,1,Dial(DAHDI/1/${EXTEN},60,rTK) ; No point sending BT specific number to VoIPon: exten => 17070,1,Dial(DAHDI/1/${EXTEN},60,rTK) ; Stuff breaks. Prefixing with 9 will send the call through to the ever-reliable PSTN exten => _9.,1,Dial(DAHDI/1/${EXTEN:1},60,rTK) [to-pstn] ; Starts with a 0 and contains more than 5 digits - it's a phone number, dial it exten => _0XXXX.,1,Dial(DAHDI/1/${EXTEN},60,rTK) ; Six digits? It's a local call, dial it exten => _XXXXXX,1,Dial(DAHDI/1/${EXTEN},60,rTK) [from-pstn] ; All incoming calls ('s') cause phones 200 and 201 to ring exten=> s,1,Dial(SIP/200&SIP/201,15) ; Go to voicemail after 15 seconds exten => s,n,VoiceMail(298,u) [to-voipon-stocksy] ; **Note:** VoIPon only accepts calls using the full '+44' format, so if your STD code is 01111, ; local calls need to be prefixed with 441111 - replace 1111 with your own STD code dropping the '0' exten => _XXXXXX,1,Dial(IAX2/voipon-stocksy/441111${EXTEN},60,rTK) ; Same story with out of STD code calls, prefix with 44 and drop the leading '0' with ${EXTEN:1} exten => _0[1-9]XXX.,1,Dial(IAX2/voipon-stocksy/44${EXTEN:1},60,rTK) ; International calls are easy, we just drop the '00' at the start with ${EXTEN:2} and dial it: exten => _00XXX.,1,Dial(IAX2/voipon-stocksy/${EXTEN:2},60,rTK) [from-voipon-stocksy] ; I don't want anyone dialling this number, so it goes straight to voicemail exten => 08713094004,1,VoiceMail(298,u)
Reload Asterisk and you should now find that your calls get routed to the least cost service depending on what time it is. You'll notice that when you place calls through VoIP your caller ID will be presented to the callee as 0871000000; this caused us problems because people would not recognise the number and wouldn't take our call. VoIPon will change your outgoing ID to your PSTN number as long as you send them proof that you own the number and pay them £15.00.
This is fine, but I got annoyed that I had effectively two telephone lines, but couldn't place another call if someone else is on the phone. I took the opportunity to learn about Macros in the dialplan. Here's the relevant bit:
# cd /etc/asterisk # vi extensions.conf ; ; These are our two Macros ; ; 'CONGESTION' and 'CHANUNAVAIL' are special extensions which we use to dial the other line which ; should be free. If both lines are busy, we don't need to do anything special, caller just gets ; fast busy tone. ; [macro-callbt] ; Call BT and fall back to voipon if line busy or otherwise not available exten => s,1,GotoIf($[${ARG1:0:2}=0]?int) exten => s,n,set(NUM=44${ARG1:1}) exten => s,n,goto(dialit) exten => s,n(int),set(NUM=${ARG1:2}) exten => s,n(dialit),Verbose(Full Number = ${NUM}) ; exten => s,n,Dial(DAHDI/1/${ARG1},180,rTK) ; exten => s,n,Goto(${DIALSTATUS},1) exten => CONGESTION,1,Dial(IAX2/voipon-stocksy/${NUM},180,rTK) exten => CHANUNAVAIL,1,Dial(IAX2/voipon-stocksy/${NUM},180,rTK) exten => ANSWER,1,Hangup exten => CANCEL,1,Hangup [macro-callvoipon] ; Call voipon unless it is unavailable, in which case we try the BT line exten => s,1,GotoIf($[${ARG1:0:2}=0]?int) exten => s,n,set(NUM=44${ARG1:1}) exten => s,n,goto(dialit) exten => s,n(int),set(NUM=${ARG1:2}) exten => s,n(dialit),Verbose(Full Number = ${NUM}) ;exten => s,1,Set(NUM=${ARG1}) exten => s,n,Dial(IAX2/voipon-stocksy/${NUM},180,rTK) exten => s,n,Goto(${DIALSTATUS},1) exten => CONGESTION,1,Dial(DAHDI/1/${ARG1},180,rTK) exten => CHANUNAVAIL,1,Dial(DAHDI/1/${ARG1},180,rTK) exten => ANSWER,1,Hangup exten => CANCEL,1,Hangup ; ; I created a macro for our incoming calls since I was fed up of typing SIP/200&SIP/201&SIP/202 etc. ; Now we can just edit this one Dial() line to add and remove extensions to ring: ; [macro-call-house-phones] exten => s,1,Dial(SIP/200&SIP/201,15,tk) exten => s,n,VoiceMail(298,u) ; ; Now we adjust our outgoing contexts: ; [to-pstn] ; Starts with a 0 and contains more than 5 digits - it's a phone number, dial it ; Don't need this now ; exten => _0XXXX.,1,Dial(DAHDI/1/${EXTEN},60,rTK) exten => _0XXXX.,1,Macro(callbt,${EXTEN}) ; Six digits? It's a local call, dial it ; Don't need this now ; exten => _XXXXXX,1,Dial(DAHDI/1/${EXTEN},60,rTK) exten => _XXXXXX,1,Macro(callbt,${EXTEN}) [to-voipon-stocksy] ; **Note:** VoIPon only accepts calls using the full '+44' format, so if your STD code is 01111, ; local calls need to be prefixed with 441111 - replace 1111 with your own STD code dropping the '0' exten => _XXXXXX,1,Dial(IAX2/voipon-stocksy/441111${EXTEN},60,rTK) ; Same story with out of STD code calls, prefix with 44 and drop the leading '0' with ${EXTEN:1} ; don't need this now; exten => _0[1-9]XXX.,1,Dial(IAX2/voipon-stocksy/44${EXTEN:1},60,rTK) exten => _0[1-9]XXX.,1,Macro(callvoipon,${EXTEN}) ; International calls are easy, we just drop the '00' at the start with ${EXTEN:2} and dial it: ; don't need this now ; exten => _00XXX.,1,Dial(IAX2/voipon-stocksy/${EXTEN:2},60,rTK) ; Set 01111 to your STD code exten => _XXXXXX,1,Macro(callvoipon,01111${EXTEN}) [from-pstn] ;;; All incoming calls ('s') cause phones 200 and 201 to ring ;;exten=> s,1,Dial(SIP/200&SIP/201,15) ;;; Go to voicemail after 15 seconds ;;exten => s,n,VoiceMail(298,u) ; ; We don't need any of the above rubbish anymore, just this one Macro: ; exten => s,n,Macro(call-house-phones)
Reload Asterisk and test this.
Before moving on to the next step, a word about emergency calls. Make sure you have emergency calls working properly under all conditions and at all times of the day. I have this set up so that 999 or 112 is always routed to the PSTN under all circumstances because the emergency services use your telephone number to route you through to the correct control room. After I make any major changes, I always unplug the Asterisk box from the PSTN and watch the output of Asterisk's CLI to make sure that 999 would be routed properly.
Finally, make sure you have an old fashioned phone somewhere which you could use in the event of a power outage. Don't leave it plugged in though or it'll ring before the Asterisk server picks up and annoy the tits off you.
Dealing With Unwanted Calls
For me, this is the most interesting part. I started by dreaming up all sorts of elaborate ways to torture cold callers; I'd loop them in infinite menus, set up intelligent agents to pretend to be interested in what they were selling or play recordings of screaming monkeys at them. I certainly had lots of fun creating them, but in the end I didn't use any of these ideas for real, the possibility of accidentally sending legitimate calls into these extensions was just to great. For example, if your employer looks up your number in your employee file and is then told that "all members of the household are currently assisting other telemarketers, please hold " you're sure to have some explainin' to do in the morning.
tl;dr: Please consider what unintended consequences your telemarketer torture dialplan might have.
My first line of defence against unwanted calls is some simple time-based routing. Except for a couple of numbers on my whitelist database, no calls at all ring the phone before 06:00 or after 22:00:
# cd /etc/asterisk # vi extensions.conf ; ; First of all, remove any Dial() lines for your incoming context. Then we set up our routing ; [from-pstn] ; Store the caller's number to use later: exten => s,1,Set(DB(cid/last)=${CALLERID(num)}) ; If it's number we like, always go to 'day' context in case it's an emergency exten => s,n,GotoIf(${DB_EXISTS(whitelist/${CALLERID(num)})}?day,s,1) ; If it's later than 6:00 but earlier than 22:00, go to 'day' context ; else, go to the 'night' context exten => s,n,Set(dayornight=${IFTIME(06:00-22:00,*,*,*?day:night)}) exten => s,n,GotoIf($["${dayornight}" = "day"]?day,s,1:night,s,1) exten => s,n,Hangup ; ; Now we need a day and a night context ; [day] exten => s,1,Macro(call-house-phones) exten => s,n,Hangup [night] exten => s,1,Background(daynight/intro) exten => s,n,Background(silence/5) exten => s,n,VoiceMail(298,u) exten => s,n,Hangup ; The PIN is just an extension, pick something better than 1111! exten => 1111,1,Macro(call-house-phones) exten => 1111,n,Hangup ; If the caller enters anything other than 1111, say "That's not valid, try again" and go back to ; the start (s,1) exten => i,1,Playback(invalid) exten => i,n,Goto(s,1)
Anyone calling outside 06:00 - 22:00 gets a recording and is offered the opportunity to enter a PIN. Obviously, it's pretty easy to extend this setup to block calls during mealtimes, holidays or any other time you want by just adding more contexts and including them at the desired time. You'll probably want to record your own message for this, which you can do by setting up a special extension. Put this in your dialplan under [house-phones]
; For recording prompts ; When dialled, you'll get a tone and then it records. Press # to stop recording and the message ; will be played back to you. exten => *50,1,Answer exten => *50,n,Record(custom-message:alaw) exten => *50,n,Wait(2) exten => *50,n,Playback(custom-message) exten => *50,n,Hangup
Reload Asterisk and dial *50 to record your message. You can move it into the right place with:
# mkdir /var/lib/asterisk/sounds/daynight # mv /var/lib/asterisk/sounds/custom-message.ulaw /var/lib/asterisk/sounds/daynight/intro.ulaw
If you want to use mine just for testing, you can do this, but I'll warn you, I'm no voice actor!
# cd /var/lib/asterisk/sounds # mkdir daynight # cd daynight # wget http://www.stocksy.co.uk/files/asterisk/daynight/intro.ulaw
If you want a list of whitelisted numbers, we can do that on the Asterisk CLI:
# rasterisk asterisk*CLI> database put whitelist 01111222222 1 Updated database successfully asterisk*CLI> database put whitelist 01111333333 1 Updated database successfully asterisk*CLI> database show whitelist /whitelist/01111222222 : 1 /whitelist/01111333333 : 1
If you want to test any of these without having to call in from the outside, you can do so by creating
a special extension in [house-phones]
using the GoTo command for example this
would send you to the night context at priority 1 if you dial *52:
exten => *52,1,Goto(night,s,1)
That was easy enough. Lets tackle anonymous calls next. Just as we did with out-of-hours callers, we send anonymous callers to a different context and handle the call there.
# cd /etc/asterisk # vi extensions.conf [day] ; If the number is withheld or international, go to nocid context exten => s,1,GotoIf($["${CALLERID(name)}" = "WITHHELD"]?nocid,s,1) exten => s,n,GotoIf($["${CALLERID(name)}" = "INTERNATIONAL"]?nocid,s,1) exten => s,n,GotoIf($["${CALLERID(name)}" = "UNAVAILABLE"]?nocid,s,1) exten => s,n,GotoIf($["${CALLERID(name)}" = "PAYPHONE"]?nocid,s,1) exten => s,n,Macro(call-house-phones) exten => s,n,Hangup ; ; Create the nocid context referenced above: ; [nocid] exten => s,1,Answer exten => s,n,Set(CALLERID(name)=0) exten => s,n,Set(CALLERID(num)=0) ; "This number does not accept anonymous calls. Please enter your telephone number followed by the ; # key". ; Tell the caller they need to key in their number. Read in up to 16 digits into ${manualcid}. exten => s,n,Read(manualcid,nocid/intro4,16) exten => s,n,Set(CALLERID(name)=Manual CID) exten => s,n,Set(CALLERID(num)=${manualcid}) ; If nothing gets keyed (${manualcid} is null), go straight to voicemail. exten => s,n,GotoIf($["${manualcid}" = ""]?vm) ; If caller keyed less than 6 digits, go to 'wrong' recording. ; "That doesn't seem like a valid phone number, try again". exten => s,n,GotoIf($[${LEN(${manualcid})} < 6]?wrong:spoof) ; If the caller inputs my own number (many telemarketers try this), tell them to stop being silly. exten => s,n(spoof),GotoIf($[${manualcid} =~ "1785211850"]?mynum:dialit) ; Otherwise, set the caller ID string and dial the phones. exten => s,n(dialit),Set(CALLERID(name)=Manual CID) exten => s,n,Set(CALLERID(num)=${manualcid}) exten => s,n,Macro(call-house-phones) exten => s,n,Hangup ; If our caller was well behaved, it ends with the hangup above. ; Otherwise, we give them one more chance exten => s,n(wrong),Read(manualcid,nocid/wrong2,16) ; If they still enter less than six digits or the entry is null, go to voicemail. exten => s,n,GotoIf($[${LEN(${manualcid})} < 6]?vm:spoof2) ; If the caller inputs my own number, tell them to stop being silly. exten => s,n(spoof2),GotoIf($[${manualcid} =~ "1785211850"]?spoof:dialit2) ; Otherwise, set the caller ID string and dial the phones. exten => s,n(dialit2),Set(CALLERID(name)=Manual CID) exten => s,n,Set(CALLERID(num)=${manualcid}) exten => s,n,Macro(call-house-phones) exten => s,n,Hangup exten => s,n(vm),VoiceMail(298,u) exten => s,n,Hangup ; "No, that's my phone number. Tell me yours". exten => s,n(mynum),Read(manualcid,nocid/mynum,16) exten => s,n,GotoIf($[${LEN(${manualcid})} < 6]?vm:spoof3) exten => s,n(spoof3),GotoIf($[${manualcid} =~ "1785211850"]?mynum:dialit3) exten => s,n(dialit3),Set(CALLERID(name)=Manual CID) exten => s,n,Set(CALLERID(num)=${manualcid}) exten => s,n,Macro(call-house-phones) exten => s,n,Hangup ; If they do something silly tell them off! exten => i,1,Playback(nocid/hose)
OK, that's a big mess of crap, but let me explain a bit. We needed to modify the
[day]
context since night-time calls don't ring the
phones any way. I've included the four caller ID strings that BT send - change to match your
requirements. Once we have established that a call is anonymous, we send it to the nocid
context where the caller hears that we don't allow anonymous calls and gives them the
chance to enter their telephone number using their keypad. If the caller doesn't enter a number
they go straight to voicemail. If a number of six digits or more is entered we ring the phones,
but prefix the incoming ID like Manual CID: 01111 2222222
. So far, I have not had a
single cold caller choose to enter their number - they just hang up whereas genuine callers don't
seem to mind putting in their number.
As before, you'll need to record your own prompts, or you can use my nerdy voice for testing:
# mkdir /var/lib/asterisk/sounds/nocid # cd /var/lib/asterisk/sounds/nocid # wget http://www.stocksy.co.uk/files/asterisk/nocid/prompts.tar.gz # tar zxf prompts.tar.gz && rm prompts.tar.gz
We've covered withheld numbers and night time calls, that just leaves unwanted calls that do display their number. This is my other half's favourite thing about our set up; it deals with the caller in an effective and non-confrontational way.
In a similar way to the whitelist we used earlier, I just store my blacklist in Asterisk's DB. As you would expect, we test the caller ID against this list and move it to another context if it matches:
# cd /etc/asterisk # vi extensions.conf ; ; First, add extensions to [my-phones] that jump to the blacklist contexts. ; [my-phones] ; Add a number to the blacklist exten => *30,1,Goto(blacklist-add,s,1) ; Remove a number exten => *31,1,Goto(blacklist-remove,s,1) ; Blacklist the last caller exten => *32,1,Goto(blacklist-last,s,1) ; Transfer a caller to this extension to blacklist them and politely tell them to go away. exten => *666,1,Goto(blacklisted-live,s,1) ; ; When a blacklisted caller calls, they are twice told to stop calling and then we hang up. ; [blacklisted] exten => s,1,Wait(3) exten => s,n,Playback(extra/privacy-stop-calling-not-welcome) exten => s,n,Wait(3) exten => s,n,Playback(extra/privacy-stop-calling-not-welcome) exten => s,n,Wait(3) exten => s,n,Hangup ; ; Transfer a caller to this extension to: ; 1. Add them to the blacklist ; 2. Tell them five times to stop calling ; 3. Hang up on them ; [blacklisted-live] exten => s,1,Answer exten => s,n,Set(number=${DB(cid/last)}) exten => s,n,GotoIf($["${number}" = ""]?104) ; also if it's blank (caller id blocked) exten => s,n,Set(DB(blacklist/${number})=1) exten => s,n,Goto(104,1) exten => 104,1,Wait(3) exten => 104,n,Playback(extra/privacy-stop-calling-not-welcome) exten => 104,n,Wait(3) exten => 104,n,Playback(extra/privacy-stop-calling-not-welcome) exten => 104,n,Wait(3) exten => 104,n,Playback(extra/privacy-stop-calling-not-welcome) exten => 104,n,Wait(3) exten => 104,n,Playback(extra/privacy-stop-calling-not-welcome) exten => 104,n,Wait(3) exten => 104,n,Playback(extra/privacy-stop-calling-not-welcome) exten => 104,n,Wait(3) exten => 104,n,Hangup ; ; This allows you to manually add any number to the blacklist ; [blacklist-add] exten => s,1,Answer exten => s,n,Wait(1) exten => s,n,Playback(extra/enter-num-blacklist) ; Give 30 seconds to enter the number instead of the default of 5! exten => s,n,Set(TIMEOUT(response)=30) exten => s,n,Read(blacknr,extra/then-press-pound) ; Read back the number to and ask to confirm (jump to extension 1) else hang up if not confirmed exten => s,n,SayDigits(${blacknr}) exten => s,n,Background(extra/if-correct-press) exten => s,n,Background(digits/1) exten => s,n,Background(silence/5) exten => s,n,Hangup exten => 1,1,Set(DB(blacklist/${blacknr})=1) exten => 1,n,Playback(extra/num-was-successfully) exten => 1,n,Playback(extra/added) exten => 1,n,Wait(1) exten => 1,n,Hangup ; ; Manually remove a number from the blacklist ; [blacklist-remove] exten => s,1,Answer exten => s,n,Wait(1) exten => s,n,Playback(extra/entr-num-rmv-blklist) exten => s,n,Set(TIMEOUT(digit)=5) ; Give 30 seconds to enter the number instead of the default of 5! exten => s,n,Set(TIMEOUT(response)=30) ; Read back the number to and ask to confirm (jump to extension 1) else hang up if not confirmed exten => s,n,Read(blacknr,extra/then-press-pound) exten => s,n,SayDigits(${blacknr}) exten => s,n,Background(extra/if-correct-press) exten => s,n,Background(digits/1) exten => s,n,Background(silence/5) exten => s,n,Hangup exten => 1,1,DBdel(blacklist/${blacknr}) exten => 1,n,SayDigits(${blacknr}) exten => 1,n,Playback(extra/num-was-successfully) exten => 1,n,Playback(extra/removed) exten => 1,n,Hangup ; ; This blacklists the last caller ; [blacklist-last] exten => s,1,Answer exten => s,n,Wait(1) exten => s,n,Set(number=${DB(cid/last)}) ; Jump to priority s,104 if no caller ID was available, else carry on exten => s,n,GotoIf($["${number}" = ""]?104) exten => s,n,Playback(extra/privacy-to-blacklist-last-caller) exten => s,n,Playback(extra/telephone-number) ; Read back what the number was and ask to confirm (jump to extension 1) exten => s,n,SayDigits(${number}) exten => s,n,Wait,1 exten => s,n,Background(extra/press-1) exten => s,n,Background(extra/or) exten => s,n,Background(extra/press-star-cancel) ; Give user 30 seconds to decide exten => s,n,Background(silence/10) exten => s,n,Background(silence/10) exten => s,n,Background(silence/10) exten => s,n,Hangup exten => s,104,Playback(extra/unidentified-no-callback) exten => s,n,Playback(extra/goodbye) exten => s,n,Hangup ; ; If user confirms by pressing 1, get to work ; exten => 1,1,Set(DB(blacklist/${number})=1) ; Read the number again and say that it has been added exten => 1,n,Playback(extra/telephone-number) exten => 1,n,Playback(extra/privacy-blacklisted) exten => 1,n,Wait,1 exten => 1,n,Playback(extra/goodbye) exten => 1,n,Hangup ; Handle cases where someone has dialled something daft exten => t,1,Playback(extra/goodbye) exten => t,n,Hangup exten => i,1,Playback(extra/goodbye) exten => i,n,Hangup exten => o,1,Playback(extra/goodbye) exten => o,n,Hangup
Don't forget to reload Asterisk.
With the above in your dialplan, you'll be able to dial *30 - *32 to control entries in your
blacklist. Transferring to *666 plays a message to get rid of the caller and blacklists them.
If you're not using VoIP phones you need to enable transferring in features.conf
for this to work, uncomment the line that says:
blindxfer => #1 ; Blind transfer (default is #)
Then you can just dial #1*666# to kill off the pesky caller.
Security
By default, Asterisk runs as the root user. The horror! Fortunately, it isn't too much work to change this.
Create a user for Asterisk:
# adduser --system --group --home /var/lib/asterisk --no-create-home --gecos "Asterisk PBX" asterisk # adduser asterisk dialout # adduser asterisk audio
Set permissions so that the Asterisk user can access what it needs:
# chown -R asterisk:asterisk /var/lib/asterisk /var/log/asterisk /var/run/asterisk \ /var/spool/asterisk /usr/lib/asterisk # chmod -R u=rwX,g=rX,o= /var/lib/asterisk /var/log/asterisk /var/run/asterisk /var/spool/asterisk \ /usr/lib/asterisk # chown -R root:asterisk /etc/asterisk # chmod -R u=rwX,g=rX,o= /etc/asterisk
udev will take care of the permissions of /dev/dahdi/*
, but only if you restart DAHDI
after creating the asterisk user.
# /etc/init.d/dahdi restart # ls -l /dev/dahdi/ total 0 crw-rw---- 1 asterisk asterisk 196, 1 2010-05-21 19:34 1 crw-rw---- 1 asterisk asterisk 196, 2 2010-05-21 19:34 2 crw-rw---- 1 asterisk asterisk 196, 3 2010-05-21 19:34 3 crw-rw---- 1 asterisk asterisk 196, 4 2010-05-21 19:34 4 crw-rw---- 1 asterisk asterisk 196, 254 2010-05-21 19:34 channel crw-rw---- 1 asterisk asterisk 196, 0 2010-05-21 19:34 ctl crw-rw---- 1 asterisk asterisk 196, 255 2010-05-21 19:34 pseudo crw-rw---- 1 asterisk asterisk 196, 253 2010-05-21 19:34 timer
Edit /etc/asterisk/asterisk.conf
, remove (!)
from the [directories]
line.
# vi /etc/asterisk/asterisk.conf [directories] ; remove the (!) to enable this astetcdir => /etc/asterisk astmoddir => /usr/lib/asterisk/modules astvarlibdir => /var/lib/asterisk astdbdir => /var/lib/asterisk astkeydir => /var/lib/asterisk astdatadir => /var/lib/asterisk astagidir => /var/lib/asterisk/agi-bin astspooldir => /var/spool/asterisk astrundir => /var/run/asterisk astlogdir => /var/log/asterisk
Locate and uncomment the follwing lines in /etc/default/asterisk
:
# vi /etc/default/asterisk AST_USER="asterisk" AST_GROUP="asterisk"
Restart Asterisk/DAHDI and everything should be as desired. You can check that the Asterisk daemon really isn't running as root like so:
Before:
root 18825 0.0 1.8 49984 18916 ? Ssl 2010 18:45 /usr/sbin/asterisk -U asterisk -G asterisk
After:
# ps aux | grep [a]sterisk asterisk 27581 0.0 1.8 49984 18916 ? Ssl 2010 18:45 /usr/sbin/asterisk -U asterisk -G asterisk
Logging
A disadvantage of eschewing the Debian package is the necessary logrotate scripts are not put in place, so the messages and CDR logs will grow without bounds. This is easily corrected:
# vi /etc/logrotate.d/asterisk /var/log/asterisk/cdr-csv/Master.csv /var/log/asterisk/debug /var/log/asterisk/event_log /var/log/asterisk/messages { weekly missingok rotate 52 size 1024k copytruncate endscript }
The above rotates all Asterisk log files once per week, but only if they reach at least 1MB in size.
It will keep the previous 52 weeks' log files before it begins to delete the oldest ones. The
copytruncate
is necessary to prevent Asterisk from continuing to write to the old log
file once it has been moved.
Call Records
Each time you make or receive a call, Asterisk keeps a record of this in the CDR log. I've written/stolen/modified/hacked a PHP script to analyse these logs. Please feel free to suggest or submit improvements, I certainly am no programmer!