Asterisk Outbound Blacklist

Step by step instructions to add a realtime outbound blacklist feature to Asterisk Open Source.

Thursday, June 11, 2009

How-To: Implement an Outbound Blacklist. - Asterisk 1.4.23

It started as a need to block users from dialing 900 numbers, and other toll charging numbers, then it turned into an all day event....

After several attempts to implement methods by "others", for an outbound blacklist feature in Asterisk, I decided to create my own which i'm going to share with you.

FAILURE

One of these failed ideas, was to create an "#include blacklist.conf" at the top of the dialplan. This file would have a bunch of blacklisted pattern matching entries that do nothing but hangup when called..."exten => _5554443321,1,Hangup()".

This worked by pressing "*9", feature.conf assigned key sequence. Pressing this key sequence invoked a macro on the channel to write the hangup extension, as appears above, appended to "blacklist.conf". Then the dialplan was reloaded using the System() application after a 5 second wait time.

For about 6 months it behaved flawlessly. Then one day, the PBX ceased to function in an acceptable manner. The list had grown to over 25000 numbers. Numbers were being added during a reload from a previous add, before Asterisk could fully reparse the file. Duplicate numbers were being added....multiple reloads queued by the system, dropped calls, choppy calls...... system failure.


SUCCESS
'MySQL is your friend'

Of course that many numbers should be in a database table not a file !
We don't want the overhead on the server of reloading the entire diaplan everytime a number is added, or the hogging by Asterisk having to commit 25000 numbers to memory every 30 seconds.
So here's the fix.....

NOTE: Some of the code seen below is word "wrapped", which is why I tried to make it the smallest font possible. Copying and Pasting these code lines DOES work however.
WARNING !!!, Performing a full server backup is highly recommended.

Step 1: Download and setup Mysql server on your box.

Step 2: Make sure you have the MySQL Add-on for Asterisk and that it is installed and working.

This is an ADD-ON of asterisk. It is not installed by default and must be downloaded and installed with the add-on's package.

Step 3: Add a feature to features.conf

[applicationmap]
blackout => *9,caller,macro,blockout


Step 4: Add the following global variables to extensions.conf

[globals]
AsteriskDB_User=root
AsteriskDB_Host=localhost
AsteriskDB_Db=Asterisk

;I don't think this variable does anything, but I left it in because it's working with it.
SEMICOLN=;
DYNAMIC_FEATURES=blackout#blindxfer


Step 5: Add a "blockout" macro to extensions.conf

[macro-blockout]
exten => s,1,System(mysql -u ${AsteriskDB_User} -e "INSERT INTO blacklist (phonenumber) values ('${DIALEDNUM}')${SEMICOLN}" ${AsteriskDB_Db})
exten => s,n,NoOp(User entered ${DIALEDNUM} into the blacklist)
exten => s,n,Playback(beep)

Step 6: Incorporate this code routine into your dialplan, where your outbound dialing takes place.

[Outbound]
;SET the number dialed as "dialednum" inheirited channel variable.
exten => _NXXNXXXXXX,1,Set(__DIALEDNUM=${CDR(dst)})
;Check the database for the number. You may not need to include the password if asterisk is running under root.
exten => _NXXNXXXXXX,n,MYSQL(Connect connid ${AsteriskDB_Host} ${AsteriskDB_User} '' ${AsteriskDB_Db})
exten => _NXXNXXXXXX,n,MYSQL(Query resultid ${connid} SELECT\ phonenumber\ FROM blacklist\ WHERE\ phonenumber='${DIALEDNUM}')
exten => _NXXNXXXXXX,n,MYSQL(Fetch fetchid ${resultid} var1)
exten => _NXXNXXXXXX,n,NoOp(FetchID: ${fetchid} Var1: ${var1} ConnID: ${connid} ResultID: ${resultid})
exten => _NXXNXXXXXX,n,GotoIf($["${DIALEDNUM}" = "${var1}"]?blacklisted)
exten => _NXXNXXXXXX,n,MYSQL(clear ${resultid})
exten => _NXXNXXXXXX,n,MYSQL(disconnect ${connid})
exten => _NXXNXXXXXX,n,Goto(dialthenumber)
;extension not within range or number was blacklisted ..so ...HANGUP !!
exten => _NXXNXXXXXX,n(blacklisted),NoOp(Cannot dial - ${DIALEDNUM} is blacklisted !)
exten => _NXXNXXXXXX,n,MYSQL(clear ${resultid})
exten => _NXXNXXXXXX,n,MYSQL(disconnect ${connid})
exten => _NXXNXXXXXX,n,Hangup()

;Dial the number
exten => _NXXNXXXXXX,n(dialthenumber),Dial(Zap/r3/${EXTEN},90)

Step 7: Create a manual extension "9000", that can be dialed to enter numbers into the blacklist.

[manualblacklist]
exten => s,1,Answer()
exten => s,n,Wait(.5)
exten => s,n,Goto(begin)
exten => s,n(blreadstart),NoOp(added ${DIALEDNUM} to blacklist)
exten => s,n(begin),NoOp(begin BL application)
exten => s,n,Background(enter-num-blacklist)
exten => s,n,Read(DIALEDNUM,,10)
exten => s,n,GotoIf($["${DIALEDNUM}" < "201200000"]?unsuccessful:successful) exten => s,n(successful),NoOp(user entered all ten digits to blacklist)
exten => s,n,System(mysql -u ${AsteriskDB_User} -e "INSERT INTO blacklist (phonenumber) values ('${DIALEDNUM}')${SEMICOLN}" ${AsteriskDB_Db})
exten => s,n,Playback(beep)
exten => s,n,Goto(blreadstart)
exten => s,n(unsuccessful),NoOp(user did not enter all ten digits to blacklist)
exten => s,n,Playback(oops1)
exten => s,n,Playback(you-dialed-wrong-number)
exten => s,n,Playback(please-try-again)
exten => s,n,Goto(blreadstart)

[features]
;Manual Blacklisting feature
exten => 9000,1,Goto(manualblacklist,s,1)

Step 8: Reload Asterisk by issuing the following commands.

service asterisk stop
service asterisk start

Step 9: Test a call. After the call is answered, you can press *9 to add the dialed number to the blacklist. After the "beep", hangup.

Step 10: Try to call the number again, and watch the Asterisk CLI. This dial attempt should fail, and hangup on you immediately (failure indicates success).
You can modify that to play a message instead of hanging up, but I found that most users quickly learn that an immediate hangup = "blacklisted number". On X-lite softphones, it will show "Declined".

If your calls never make it to your outbound context(s), it might have something to do with your ( include => contexts ). Make sure you have these set properly.

http://www.the-asterisk-book.com/unstable/includes-im-dialplan.html

For this example, the includes might look like this....
[default]
include => outbound
include => incoming
include => features
(Note: yours may be different.)

TIP: Always keep one eye on the Asterisk CLI, looking for dialplan errors when testing. Most errors should be obvious on the CLI, and some minor "tweaks" will probably be needed if you are incorporating this into an existing dialplan.
Feel free to submit your own suggestions on making this better.
Currently, i'm working on load balancing the blacklist table lookups. This should be accomplished by making the lookup table a dialplan variable based on "dialed number" ...Example: Table name "BL220NXXXXXX". The original list will be programatically split into smaller lists based on area code when the list record count reaches variable "maxrecord".

Followers