mibus.org geek refuge

10May/100

Asterisk – Specifying outbound routes for fun and profit

...or at least a little savings.

You're in a quandry. You've found VSP1, with brilliant general rates. VSP2 has fantastic rates for mobile calls. VSP3 has the cheapest calls of all to England, where you have family. You can't use just the best bits of each... can you?

With Asterisk, yes you can!

The extension configs here assume that you've set up your VSP peers already as vsp1, vsp2, and vsp3.

Starting with your usual outbound context:

[regular_outbound]
; Handle calls out
exten => _XX.,1,Dial(SIP/${EXTEN}@vsp1,,)
;

Add in some new patterns for the different call types. (0011 is the code to dial an international number from Australia, 44 is the UK "country code", and 04 is the prefix for all mobile phones, which are always ten digits).

[regular_outbound]
; Mobiles go via VSP2
exten => _04XXXXXXXX,1,Dial(SIP/${EXTEN}@vsp2,,)
; UK numbers via VSP3
exten => _001144.,1,Dial(SIP/${EXTEN}@vsp3,,)
; Handle all other calls out
exten => _XX.,1,Dial(SIP/${EXTEN}@vsp1,,)
;

After some time, you find out that VSP3 isn't terribly reliable. Rather than just disallow the call at all, instead let's use VSP1 as a fallback.

Instead of:

; UK numbers via VSP3
exten => _001144.,1,Dial(SIP/${EXTEN}@vsp3,,)
;

Do:

; UK numbers via VSP3, fallback to VSP1
exten => _001144.,1,Dial(SIP/${EXTEN}@vsp3,,)
exten => _001144.,2,Dial(SIP/${EXTEN}@vsp1,,)
;

If the first Dial() statement succeeds, the dialplan execution will not continue. If it fails, the next statement will execute, thus running the second Dial() via VSP1. (This functionality is more commonly used to implement line hunting, or to redirect to voicemail on busy and no-answer).

Note that in some cases, the call may actually happen twice - if the remote end is genuinely busy, for instance. You can check the contents of ${DIALSTATUS} and only redirect in some cases - but then you might not fall through if VSP3 incorrectly gives you a busy tone instead of ringing!

26Apr/100

Asterisk – Email notifications

One of the things I like about Asterisk, is its ability to send email notifications to me if certain things happen. There's no built-in ability to send emails by itself, so we'll use its integration with the underlying server to do it.

Let's start by sending me an email if someone uses my VoIP line to make an international call. In Australia, all international calls are prefixed with "0011"...

[regular_outbound]
; Email me for international calls
exten => _0011.,1,System(mail -s "International Call to ${EXTEN}" example@mibus.org < /home/mibus/Documents/international-call.msg)
exten => _0011.,n,Dial(SIP/${EXTEN}@nodephone,,)
; Handle other calls out
exten => _XX.,1,Dial(SIP/${EXTEN}@nodephone,,)
;

Note that the subject is InternationalCall, the destination email address is example@mibus.org, and the body of the email comes from a file I've already saved to /home/mibus/Documents/international-call.msg. From now on, I'll know whenever an expensive international call is made.

A rarely-used feature of extensions.conf is the built-in h extension. Let's use that (inside whatever context rings your real internal phone) to let us know by email when we've missed a call for any reason. For example, if my phone is rung on extension 99...

exten => 99,1,NoOp(Inbound)
exten => 99,n,Dial(SIP/myphone,30,)
exten => 99,n,Hangup()

exten => h,1,GotoIf($["${DIALSTATUS}" = "ANSWER"]?done)
exten => h,n,System(mail -s "Missed Call From ${CALLERID(num)}" example@mibus.org < /home/mibus/Documents/missed-call.msg)
exten => h,n(done),NoOp()
;

Note that I'm specifically using only the number component of the Caller-ID - if your VSP supports sending textual Caller-ID alongside it, it means that random foreign callers can alter your System() call!

12Apr/100

Asterisk – Dual-dialling to save money

Asterisk saves me money, even using just the one VoIP provider. (Disclaimer: The provider I use is run by my employer, but I'm writing this only as a happy customer! :).

It just so happens that my work desk phone number is a free call, but my wife can never remember the number and is usually in too much of a hurry to stop and think about whether I'm at my desk yet etc. etc. (We have lots of small kids - I'm surprised I have time to write this post :). My VoIP provider (nodephone) also happens to allow two concurrent calls by default... let's see what fun we can have with that?

My outgoing context used to look a bit like this:

[frominside]
exten => _XX.,1,Dial(SIP/${EXTEN}@nodephone,,)
;

What I wanted, was to automatically ring my desk phone whenever I was likely to be at it. Just in case I wasn't, ring my mobile phone at the same time - let me answer whichever I choose.

Move the contents of the frominside context into a new context regular_outbound, and use an include to keep it all working the same...

[frominside]
include => regular_outbound

[regular_outbound]
exten => _XX.,1,Dial(SIP/${EXTEN}@nodephone,,)
;

Next, make a new context that will dual-dial when you call my mobile number. (No, those aren't real phone numbers, they're dummy numbers from ACMA). Include it at the top of the frominside context, and limit it to a "safe" set of working hours.

[frominside]
include => mibus_at_work|08:15-16:45|mon-fri|*|*
include => regular_outbound

[regular_outbound]
exten => _XX.,1,Dial(SIP/${EXTEN}@nodephone,,)

[mibus_at_work]
exten => 0491570156,1,Wait(1)
exten => 0491570156,n,Playback(hang-on-a-second)
exten => 0491570156,n,Set(LIMIT_WARNING_FILE=beep)
exten => 0491570156,n,Dial(SIP/${EXTEN}@nodephone&SIP/0855501234@nodephone,,)
;

Done! Now, trying to ring 0491570156 will also ring 0855501234. They're charged at vastly different rates, so if I can, I'll answer on the "cheaper" one. If not, it'll ring my mobile phone anyway - just as it always would have.

29Mar/100

Plain-text password storage is good

...or at least, not as bad as it's sometimes made out to be.

Please note that I am not discussing any password system used by any employer, past or present. This is based on general industry knowledge only, and is meant to explain why so much software suggests or requires plaintext passwords to be stored.

When I first came across a professional application that used plaintext password storage (about six years ago now) - I thought, "Ugh, why would anyone store passwords cleartext?". There are a huge number of really valid reasons why not to - the easiest one being simply "what happens if/when that database gets stolen?". You may or may not be concerned by "Can the server admins and developers see my password?". I found out why the hard way when I tried to do something similar, maybe a year or so later.

So, why would a sysadmin or software developer choose to use cleartext password storage, with the risks that it inherently has? The same reason as with most security issues - it's overridden by convenience and usability. Password security has two main aspects; protection when stored, and protection when in transit.

In-transit seems pretty easy, but has quite a few pitfalls. A horribly common mechanism is just to pass it cleartext. Eww! Another is to pass it reversibly encrypted over SSL. Better - man-in-the-middle snooping is stopped - but still not really great. What about only sending an MD5 across the wire? Challenge-response systems are yet another solution - the server sends you a secret string, you combine it with your password in a way predictable to the server and send a munged 'response' back to the server. Great, right?

No. Your average challenge-response system uses a function along the lines of MD5(challenge+MD5(password)). So for both of the last two options, all the server needs to store is the MD5 of the password, instead of the password itself. Win. Except for the fail that you've just made your MD5 of your password valuable, because that's all the attacker needs now - and that's exactly what the server is storing!

Moving back to storage - what sort of authentication systems does your software use, and what do you need to store to support it? CRAM-MD5? Plaintext over the wire? APOP? Something custom? MD5? CHAP? What happens when CRAM-SHA256 displaces CRAM-MD5, and you're only storing an MD5? You can only use the authentication methods compatible with what you originally planned for. Maybe you can work around it by storing ten different types of hashes, but it still isn't really future-proof. You certainly aren't protected against requests you could never foresee (Can we auth IMAP users against this forum user database?).

However, if you're storing the password in cleartext, the answer becomes "So what? Can-do, boss!". You can generate whatever sort of hash you need on the fly, you can use any password authentication scheme that you want. You can integrate with any password-based authentication scheme. Any. Not just the one you're using today, or tomorrow, but the one you'll be using five years from now. No matter how it passes over the wire, you can use it.

I've simplified this quite a bit, and there are a bajillion other ways to work around it - but nothing that is as compatible or usable as just storing the passwords in cleartext.

If you do want to work around it... Be inconvenienced! Some options might be:

  • Make a stand and decide what protocols you support
  • Don't use just passwords, use two-factor authentication (passwords plus tokens, SMS codes, biometrics, etc)
  • Accept the cost of forcing the user to visit the "change password" page to generate new hash formats when needed

Even as a user, you have a great option for keeping your own security:

  • Use unique passwords for every account, so a compromised password can only affect the one account

It's universally sound advice, but often ignored.

Remember finally that we (as developers and sysadmins) can make systems that are incredibly secure - just look at banks. It's just inconvenient. Technical security, DMZs, firewalls, physical security policies, segregated backups, employee access policies. All that work! But it can be done.

15Mar/100

Asterisk – Database-driven CallerID

One of the nice things Asterisk can do is manipulate Caller ID information on the fly. Since I'm too lazy to update the stored numbers within my individual cordless handsets, I use Asterisk to cheat. Asterisk looks up the incoming phone number ("08XXXXXXXX") in a MySQL table, finds matching text ("Bob") and passes that along to the handsets for display.

This assumes you have a MySQL database on the same box as asterisk, with a username of asterisk, password of mypassword, your database is called asterisk, and your final internal destination is SIP/myphone. (I'm inventive, I am... ;). Extension 99 in this example is where your inbound calls end up in order to ring your phone.

exten => 99,1,NoOp(Inbound)
exten => 99,n,MYSQL(Connect connid localhost asterisk mypassword asterisk)
exten => 99,n,GotoIf($["${connid}" = ""]?nodb)
exten => 99,n,MYSQL(Query resultid ${connid} SELECT\ name\ FROM\ addressbook\ WHERE\ phone_number="${CALLERID(num)}"\ LIMIT\ 1)
exten => 99,n,MYSQL(Fetch fetchid ${resultid} name)
exten => 99,n,MYSQL(Clear ${resultid})
exten => 99,n,Set(CALLERID(name)=${name})
exten => 99,n,MYSQL(Disconnect ${connid})
exten => 99,n(nodb),NoOp(DoneDB)
exten => 99,n,Dial(SIP/myphone,30,)
exten => 99,n,Congestion()

To actually make it work, you also need the database filled in.

CREATE TABLE addressbook (
    phone_number VARCHAR(40),
    name VARCHAR(40),
    PRIMARY KEY(phone_number)
);
INSERT INTO addressbook VALUES ('08XXXXXXX1', 'Bob');
INSERT INTO addressbook VALUES ('08XXXXXXX2', 'Mary');

(and so forth).

So, what next? Well, you're logging your CDR into MySQL, right? Let's make a 'view' of the CDR that includes the names:

CREATE VIEW cdr_with_names AS
    SELECT cdr.*,ab_src.name AS src_name,ab_dst.name AS dst_name
    FROM cdr
    LEFT JOIN addressbook ab_src
        ON ((cdr.src = ab_src.phone_number)
        OR (CONCAT('08',cdr.src) = ab_src.phone_number))
    LEFT JOIN addressbook ab_dst
        ON ((cdr.dst = ab_dst.phone_number)
        OR (CONCAT('08',cdr.dst) = ab_dst.phone_number))
;

What's this CONCAT('08',cdr.src) stuff? '08' is my local area code, and I can leave it off outbound calls to the same area code if I so choose. This way, my query finds both variants ('XXXXXXXX' and '08XXXXXXXX').

It's such a messy view for direct use, though. Let's clean it up with a view that only shows us the columns we really care that much about:

CREATE VIEW cdr_easy AS SELECT calldate,src,src_name,dst,dst_name,disposition,billsec FROM cdr_with_names

In a few minutes of hacking at extensions.conf and MySQL, you now have database-driven CallerID text and a straightforward CDR view that includes human-readable names. What can't Asterisk do?

28Feb/100

Asterisk – Adding Game

A quick adding game for Asterisk.

Set up an extension (I used "4263") to Goto(game,s,1). It will speak two numbers from 1-8, and expects a single key to be pressed for the sum of those two numbers. I've only used fairly standard sounds, so there's no recording involved.

Hoping that my five-year-old enjoys this :)

[game]
exten => s,1,NoOp(GameSTART)
exten => s,n,Set(num1=${RAND(1,8)});
exten => s,n,Set(maxnum2=$[9-${num1}])
exten => s,n,Set(num2=${RAND(1,${maxnum2})})
exten => s,n,Set(total=$[${num1}+${num2}])
exten => s,n(quest),SayNumber(${num1})
exten => s,n,Playback(letters/plus)
exten => s,n,SayNumber(${num2})
exten => s,n,WaitExten

exten => _X,1,NoOp(GameHazDigits)
exten => _X,n,GotoIf($[${EXTEN} = ${total}]?GotItRight)
exten => _X,n(GotItWrong),NoOp(Awww)
exten => _X,n,Playback(privacy-incorrect)
exten => _X,n,Playback(pls-try-again)
exten => _X,n,Goto(s,quest)
exten => _X,n(GotItRight),NoOp(YAY)
exten => _X,n,Playback(yes-dear2)
exten => _X,n,Goto(s,1)

exten => t,1,Goto(s,quest)
exten => i,1,Goto(s,quest)
29Jan/100

You have a PABX at home? Why!?

When I mention to someone that I have a PABX at home, people always ask me - WHY???

Early Media - this is a fancy way of saying that when someone calls me it doesn't just go "ring ring" - I have it going "rrrrrinngggggg rrrrrrinnnngggg" like an old-school telephone. I've considered having cackling monkeys, but I don't want to confuse people too much.

Free Calls Home - Anywhere I have full Internet access (like at an Internode CityLan hotspot ;) I can connect to the PABX and call the "extension" that rings my home phone. End result, free calls.

Call Announcements - I [can] have announcements for how much a call will cost, or minutely beeps to remind you that you're on a timed call. Great for keeping the bills down!

Simultaneous Ring - Calling my mobile phone, in hours that I've decided I'm likely to be at my desk at work, will actually ring my mobile and my desk phone. Whichever I pick up first, 'gets' the call. End result, cheaper (or in this case, free) calls, without having to call multiple numbers by hand.

Automated Outbound Calls - If I can't get hold of Missy (my darling wife who has had to put up with all this tinkering ;), I can have the phone system repeatedly call the home phone until it's picked up. (Say, every 15 minutes). Once answered, it will play a pre-recorded message (eg. I'm running late and my mobile phone battery is about to die).

Direct Peering - Some friends of ours don't have VoIP, but do have a VoIP-capable ATA. So, we've hooked them in to our PABX much like they normally would a fully-fledged VoIP provider. A quick "calls to this prefix go via VoIP" dialplan later, and we can have free calls between our house phones. This is particularly awesome when using the Siemens C470IP Gigaset that we have - it can do multiple SIP providers, using dialplans to choose which to send a call through.

Calling Card Functionality - I can configure my phone to accept incoming calls from certain numbers, and do different things with them. An example of that, is placing incoming calls from a family member into a part of the PABX that allows outbound calls. End result, a family member makes a local call to me, then can take advantage of the good international call rates I get by using VoIP - without all the hassle with ADSL and ATAs. A variation of this would be to put "normal" outbound calls through a PIN checker, so you could isolate users within your house (and allow through family and emergency numbers directly).

External Notification - I have certain calls also have other events attached - eg., I get sent an email, IM, and SMS if anyone calls 000. No, I don't rely on my cordless phone, VoIP, and PABX for all emergency calls. We have a corded handset by a spare phone jack for "just in case".

But the #1 reason...

Because I can - I'm a giant bloody geek, with a server and a DSL connection. What do you expect? :)

21Nov/08Off

Three months and eight megabits

Well, it's been three months at Internode - a bit over, actually.

And... it's cool here. I really like that I can focus on just sysadmin work, instead of trying to divide my attention sixty ways. (That's Anton's job now :-).

And I still get to spend time on forums (think Whirlpool), though it's not quite as easy to justify! I also have had quite a few freebies already (coffee mugs, shirts, hats, etc - from node itself and vendors).

The SAN failure was... interesting. Everyone was pretty calm, despite the calamity, and everything got done and done well. Despite the email issue continuing through the weekend, almost everything was up and running pretty quickly after the failure.

Other cool things include the company-supplied MBP. I always had a real soft spot for OSX, and now it's my primary OS. I bought myself a Dell Mini 9 for the bus trip, and installed Ubuntu. Totally awesome, ranks amongst the best $600 I ever spent!

Oh, and I'm on a RIM where I'm living - but at least this time, (a) it's DSL-capable, (b) I'm really close, and (c) Internode pays - so I get ~7.5Mbit. I also now have a Nodephone2 account; cheap calls (from Aldinga, the number of STD calls we make suck). More importantly, the number starts with 08-75 instead of the 08-85 normal for the area - the puzzled look on people's faces is worth it!

I do miss my friends though. Hopefully we'll be able to catch up in the near future ;-)

25Jul/08Off

End of an Era

Well, it's done. Over. My last day at Ballistic Media (CGSociety) is now finished.

To my friends - for five years, my family - good bye. Keep in touch!

1Jul/08Off

Changing Jobs!

Wow.

It wasn't that long ago that I never thought I'd do this.

On the 25th of July, 2008, my time at Ballistic Media will come to an end. On the 28th of July, I will begin working for Internode as a Systems Administrator.

Wow.

So many reasons - mostly that I need to explore new things, and try new stuff out. It's been one of the hardest decisions I've ever made: giving up the family and security that I've had here at Ballistic, for the dream of something new and different.

Here's to change.