stocksy.co.uk
"the site for those who crave disappointment"

Sponsored Links

How I used Asterisk to Take Back Control of my Telephone

23rd Jun 2013, 13:11:40

By James Stocks

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.

Asterisk logo

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:

My setup

The features I enjoy the most are:

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!

New Comments

Some Rights Reserved