Wednesday, April 9, 2014

Installing Asterisk 12 on Ubuntu 12.04 with pjproject and SRTP

Today I had to install an Asterisk that could deal with WebRTC. I read on the Asterisk wiki that in order for it to work, it needs to be installed with pjproject and SRTP. Until today, I always used menuselect to choose what to install, but these two buddies are kind of different... they aren't selectable unless you install them before Asterisk.

As I couldn't find a guide with the steps to follow (took bits of information from different sources and figured some things by myself) here's what worked for me. I took a lot from
Asterisk 12 on a Raspberry Pi | MatthewJordan.net so thanks to Matthew ;) I also enjoyed how he shows what he's doing, so I'm copying that from there too.

Install the Asterisk dependencies and more stuff we're going to need (I'm also installing libbfd-dev b/c want to use BETTER_BACKTRACES)

 
gmc@blog:~$ sudo apt-get install build-essential libsqlite3-dev libxml2-dev libncurses5-dev libncursesw5-dev libiksemel-dev libssl-dev libeditline-dev libedit-dev curl libcurl4-gnutls-dev libjansson4 libjansson-dev libuuid1 uuid-dev libxslt1-dev liburiparser-dev liburiparser1 git autoconf libbfd-dev -y

Reading package lists... Done
...
Processing triggers for libc-bin ...
ldconfig deferred processing now taking place

gmc@blog:~$ 
 

Install libsrtp

We first donwload and uncompress the files (thanks to Alexander Traud for pointing me out that libsrtp moved to github)
 
gmc@blog:~$ cd ~
gmc@blog:~$ git clone https://github.com/cisco/libsrtp.git
Cloning into 'libsrtp'...
remote: Reusing existing pack: 2037, done.
remote: Total 2037 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (2037/2037), 3.17 MiB | 1.30 MiB/s, done.
Resolving deltas: 100% (1249/1249), done.
Checking connectivity... done.
gmc@blog:~$
 
Then configure and make it!... I figured out the flags for ./configure by trial and error (at one point, Asterisk complained about the -fPIC flag, so I just added it)
 
gmc@blog:~$ cd libsrtp/
gmc@blog:~/libsrtp$ autoconf
gmc@blog:~/libsrtp$ ./configure CFLAGS=-fPIC --prefix=/usr

checking for ranlib... ranlib
checking for gcc... gcc
checking whether the C compiler works... yes
...
config.status: creating doc/Makefile
config.status: creating crypto/include/config.h

gmc@blog:~/libsrtp$ make

gcc -DHAVE_CONFIG_H -Icrypto/include -I./include -I./crypto/include  -fPIC -c srtp/srtp.c -o srtp/srtp.o
gcc -DHAVE_CONFIG_H -Icrypto/include -I./include -I./crypto/include  -fPIC -c crypto/cipher/cipher.c -o crypto/cipher/cipher.o
gcc -DHAVE_CONFIG_H -Icrypto/include -I./include -I./crypto/include  -fPIC -c crypto/cipher/null_cipher.c -o crypto/cipher/null_cipher.o
...
gcc -DHAVE_CONFIG_H -Icrypto/include -I./include -I./crypto/include  -fPIC -L. -o test/rtpw test/rtpw.c test/rtp.c libsrtp.a  -lsrtp
Build done. Please run 'make runtest' to run self tests.

gmc@blog:~$ 
 
I saw that there was a make runtest... it's pretty cool :) here's what you should see
 
gmc@blog:~/libsrtp$ make runtest

gcc -DHAVE_CONFIG_H -Icrypto/include -I./include -I./crypto/include  -fPIC -c crypto/math/math.c -o crypto/math/math.o
crypto/math/math.c: In function 'bitvector_print_hex':
crypto/math/math.c:854:5: warning: format not a string literal and no format arguments [-Wformat-security]
...
libsrtp test applications passed.
...
libcryptomodule test applications passed.
make[1]: Leaving directory `/home/test/srtp/crypto'

gmc@blog:~$ 
 
and then... just install it
 
gmc@blog:~/srtp$ sudo make install

/usr/bin/install -c -d /usr/include/srtp
/usr/bin/install -c -d /usr/lib
cp include/*.h /usr/include/srtp  
cp crypto/include/*.h /usr/include/srtp
if [ -f libsrtp.a ]; then cp libsrtp.a /usr/lib/; fi

gmc@blog:~$ 
 

Install pjproject

Clone the project from its git repo
 
gmc@blog:~/srtp$ cd ~
gmc@blog:~$ git clone https://github.com/asterisk/pjproject pjproject

Cloning into 'pjproject'...
remote: Reusing existing pack: 3636, done.
remote: Total 3636 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3636/3636), 7.76 MiB | 2.05 MiB/s, done.
Resolving deltas: 100% (1167/1167), done.

gmc@blog:~$ 
 
Configure and make it... again, after a few attempts, I got here
 
gmc@blog:~$ cd pjproject/
gmc@blog:~/pjproject$ ./configure --prefix=/usr --enable-shared --disable-sound --disable-resample --disable-video --disable-opencore-amr --with-external-srtp

checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking target system type... x86_64-unknown-linux-gnu
...
Further customizations can be put in:
  - 'user.mak'
  - 'pjlib/include/pj/config_site.h'
The next step now is to run 'make dep' and 'make'.

gmc@blog:~/pjproject$ make

for dir in pjlib/build pjlib-util/build pjnath/build third_party/build pjmedia/build pjsip/build pjsip-apps/build ; do \
  if make  -C $dir all; then \
      true; \
...
make[2]: Leaving directory `/home/test/pjproject/pjsip-apps/build'
make[1]: Leaving directory `/home/test/pjproject/pjsip-apps/build'

gmc@blog:~/pjproject$ sudo make install

mkdir -p /usr/lib/
...
sed -e "s!@PJ_LDLIBS@!-lpjsua -lpjsip-ua -lpjsip-simple -lpjsip -lpjmedia-codec -lpjmedia -lpjmedia-videodev -lpjmedia-audiodev -lpjmedia -lpjnath -lpjlib-util  -lgsmcodec -lspeex -lilbccodec -lg7221codec  -lsrtp -lpj -luuid -lm -lrt -lpthread  -lcrypto -lssl!" | \
sed -e "s!@PJ_INSTALL_CFLAGS@!-I/usr/include -DPJ_AUTOCONF=1 -O2 -DPJ_IS_BIG_ENDIAN=0 -DPJ_IS_LITTLE_ENDIAN=1 -fPIC!" > //usr/lib/pkgconfig/libpjproject.pc

gmc@blog:~$ 
 
That should be it! this is an extra step to verify that's correctly set up
 
gmc@blog:~/pjproject$ pkg-config --list-all | grep pjproject
libpjproject     libpjproject - Multimedia communication library

gmc@blog:~$ 
 
Done! pjproject is installed! just one more thing...

Install Asterisk

Download an uncompress...
 
gmc@blog:~/pjproject$ cd ~
gmc@blog:~$ wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-12-current.tar.gz

--2014-04-09 21:46:57--  http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-12-current.tar.gz
Resolving downloads.asterisk.org (downloads.asterisk.org)... 76.164.171.238, 2001:470:e0d4::ee
Connecting to downloads.asterisk.org (downloads.asterisk.org)|76.164.171.238|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 56483961 (54M) [application/x-gzip]
Saving to: `asterisk-12-current.tar.gz'
100%[========================================================================================================================>] 56,483,961  10.6M/s   in 5.9s    
2014-04-09 21:47:03 (9.10 MB/s) - `asterisk-12-current.tar.gz' saved [56483961/56483961]

gmc@blog:~$ tar -xzf asterisk-12-current.tar.gz
gmc@blog:~$ 
 
Configure and make menuselect...
 
gmc@blog:~$ cd asterisk*
gmc@blog:~/asterisk-12.1.1$ ./configure --with-pjproject --with-ssl --with-srtp

checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking for gcc... gcc
...
configure: build-cpu:vendor:os: x86_64 : unknown : linux-gnu :
configure: host-cpu:vendor:os: x86_64 : unknown : linux-gnu :

gmc@blog:~/asterisk-12.1.1$ make menuselect
CC="cc" CXX="" LD="" AR="" RANLIB="" CFLAGS="" LDFLAGS="" make -C menuselect CONFIGURE_SILENT="--silent" cmenuselect
make[1]: Entering directory `/home/test/asterisk-12.1.1/menuselect'
gcc  -g -D_GNU_SOURCE -Wall   -c -o menuselect.o menuselect.c

...
 
This is where you can see, under Resource Modules, that we have the res_pjsip_* modules enabled and res_srtp enabled too... you can do changes and tune your Asterisk how you like and quit by hitting x (that's save and quit)... then, we only have to...
 
gmc@blog:~/asterisk-12.1.1$ make

Generating embedded module rules ...
   [CC] astcanary.c -> astcanary.o
   [LD] astcanary.o -> astcanary
...
 +--------- Asterisk Build Complete ---------+
 + Asterisk has successfully been built, and +
 + can be installed by running:              +
 +                                           +
 +                make install               +
 +-------------------------------------------+

gmc@blog:~/asterisk-12.1.1$ sudo make install

Installing modules from channels...
Installing modules from pbx...
...
 +---- Asterisk Installation Complete -------+
 +                                           +
 +    YOU MUST READ THE SECURITY DOCUMENT    +
 +                                           +
 + Asterisk has successfully been installed. +
 + If you would like to install the sample   +
 + configuration files (overwriting any      +
 + existing config files), run:              +
 +                                           +
 +                make samples               +
 +                                           +
 +-----------------  or ---------------------+
 +                                           +
 + You can go ahead and install the asterisk +
 + program documentation now or later run:   +
 +                                           +
 +               make progdocs               +
 +                                           +
 + **Note** This requires that you have      +
 + doxygen installed on your local system    +
 +-------------------------------------------+

gmc@blog:~$ 
 
If you're like me and want it to just set up the init.d scripts (so that a simple service asterisk start works) you can do
 
gmc@blog:~/asterisk-12.1.1$ sudo make config

 Adding system startup for /etc/init.d/asterisk ...
   /etc/rc0.d/K91asterisk -> ../init.d/asterisk
   /etc/rc1.d/K91asterisk -> ../init.d/asterisk
   /etc/rc6.d/K91asterisk -> ../init.d/asterisk
   /etc/rc2.d/S50asterisk -> ../init.d/asterisk
   /etc/rc3.d/S50asterisk -> ../init.d/asterisk
   /etc/rc4.d/S50asterisk -> ../init.d/asterisk
   /etc/rc5.d/S50asterisk -> ../init.d/asterisk

gmc@blog:~$ 
 
And if you're just getting started and want to see some samples...
 
gmc@blog:~/asterisk-12.1.1$ sudo make samples

Installing adsi config files...
/usr/bin/install -c -d "/etc/asterisk"
Installing configs/asterisk.adsi
...
Installing file phoneprov/polycom.xml
Installing file phoneprov/snom-mac.xml

gmc@blog:~$ 
 
And that's it! Congrats! You have what should be a WebRTC compatible Asterisk :)

Here are all the commands without their outputs so that you can run everything without copy/pasting one by one (and b/c I'm sure I'm going to do it again and don't want to remove the responses :P)
 
# dependencies
sudo apt-get install build-essential libsqlite3-dev libxml2-dev libncurses5-dev libncursesw5-dev libiksemel-dev libssl-dev libeditline-dev libedit-dev curl libcurl4-gnutls-dev libjansson4 libjansson-dev libuuid1 uuid-dev libxslt1-dev liburiparser-dev liburiparser1 git autoconf libbfd-dev -y

# srtp
cd ~
git clone https://github.com/cisco/libsrtp.git
cd libsrtp/
autoconf
./configure CFLAGS=-fPIC --prefix=/usr
make

# check that the tests pass
make runtest

sudo make install

# pjproject
cd ~
git clone https://github.com/asterisk/pjproject pjproject
cd pjproject/
./configure --prefix=/usr --enable-shared --disable-sound --disable-resample --disable-video --disable-opencore-amr --with-external-srtp
make
sudo make install

# the following command should return
# libpjproject     libpjproject - Multimedia communication library
pkg-config --list-all | grep pjproject

# asterisk
cd ~
wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-12-current.tar.gz
tar -xzf asterisk-12-current.tar.gz
cd asterisk*
./configure --with-pjproject --with-ssl --with-srtp

# after this command, you can select what you want on your Asterisk
make menuselect

make
sudo make install

# if you want the init.d scripts created
sudo make config
 

Saturday, March 22, 2014

Installing paramiko on Windows 8 64-bits witn MinGW

A few weeks ago, I recommended a friend Python as a great language to code... multiplatform and all that. He asked me about connecting to an SSH2 server and I told him that there were tons of libraries for everything, and that there def was one library for that.

I wasn't wrong... but when we met again, he told me he had a big headache installing any of them... I then decided to give the one that looked best (paramiko) and wow... things can go pretty nasty... but I'd like to think that's just b/c of the C part of the world.

I finally figured it out, and as it wasn't straightforward (much less pythonic) at all, I decided to write down my steps here... it may save a lot of hours to the next folk that wants to give it a try.

The (general) problem

paramiko can't be easily installed on windows because it uses pycrypto... which is a C library that deals with the encryption part.

The first approach

Their website has a link to a bunch of precopiled packages... so I gave that a try first. The right way of installing a package is by using easy_install. It did the set up, but when I tried to import the module, it failed with the message ImportError: DLL load failed: %1 is not a valid Win32 application. when its code imported winrandom.

On their site they also say that on some 64-bits systems winrandom just fails, and the only option is to manually compile it. Oh, what a luck.

Compiling it

Ok, if that's what it takes... let's try it out. Even if I have Visual Studio, I'd like to keep it out of the ecuation. This answer pointed me in the right direction, I had to do it with MinGW.

I had never used it before, but it's extremely straightforward. On the Instalation Manager, select the packages
  • mingw32-base
  • mingw32-gcc-gcc+
  • msys-base
We're also going to need another package that's not there. You should go to all packages and select mingw32-gmp (the dev one, triple check that you select that one).

Once those packages are selected, go to the Instalation menu and then to Apply Changes. After the setup, you should add c:\mingw\bin;c:\mingw\mingw32\bin;C:\MinGW\msys\1.0;c:\mingw\msys\1.0\bin;c:\mingw\msys\1.0\sbin to your path.

Now, to enter into the beautiful console, you just need to go to run (Windows + R) and write msys. If that doesn't open a console for you, there's probably something wrong with your path. Be sure to add it after what you already have there.

If you try pip install pycrypto you'll see it fails (and it's actually trying to use Visual Studio). You need to add a file named distutils.cfg inside C:\Python33\Lib\distutils (or whatever Python folder you're using). It should have
 
[build]
compiler=mingw32
 
That will tell python to use mingw32 to compile whatever it needs to compile... and we're one step closer!

Unfortunately, doing pip install pycrypto also throws all types of errors again... at least, they're different :) The message is always error: unknown type name 'off64_t'... which I didn't have a clue of what it meant... but fortunately I found this answer on SO. As he said, it's brutal... time to modify the sys/types.h file :P

Let me save you a few minutes, the same thing happens with the off_t type. Open the file C:\MinGW\include\sys\types.h and search for off_t. You'll find something like
 
#ifndef _OFF_T_
#define _OFF_T_
typedef long _off_t;
#ifndef __STRICT_ANSI__
typedef _off_t off_t;
#endif /* __STRICT_ANSI__ */
#endif /* Not _OFF_T_ */

#ifndef _OFF64_T_
#define _OFF64_T_
typedef __int64 _off64_t;
#ifndef __STRICT_ANSI__
typedef __int64 off64_t;
#endif /* __STRICT_ANSI__ */
#endif /* ndef _OFF64_T */
 

The problem is that the compiler is setting the strict mode... but on the types.h file, if that mode is set, it doesn't add the off_t alias for the _off_t type (I couldn't care less about the strict mode... just want it to run!). In order to fix it, replace that code with
 
#ifndef _OFF_T_
#define _OFF_T_
typedef long _off_t;
typedef _off_t off_t;
#endif /* Not _OFF_T_ */

#ifndef _OFF64_T_
#define _OFF64_T_
typedef __int64 _off64_t;
typedef __int64 off64_t;
#endif /* ndef _OFF64_T */
 
And now... we're always declaring the aliases the pycrypto code uses... almost there! I'm using virtualenv, and if you have more than 1 project, you should too... but here, I'm going to just install it on the entire system to keep it simpler.

Soooo.... we're good to do pip install paramiko and it should work... voilá? nah, not so fast :P I ran my sample program and got ImportError: No module named 'winrandom' wonderful winrandom again...

This time the error was fixed by just copying C:\Python33\Lib\site-packages\Crypto\Random\OSRNG\winrandom.pyd into my project folder and now yeah, voilá!!!

I'm sure there's something missing on my python path or something... but I'd like to set it up just for Python 3.3... does anyone know what's the missing part? If you do, please let me know in the comments.

That's it! you should have paramiko working :) I'm not sure if it's the easiest one (I've seen a couple of wrappers, so my guess is that it isn't) or the fastest one, but I made it work on Windows!!!

You'll see that I mentioned a couple answers on SO... please go check them up and give them upvotes... it's extremely rewarding and those are guys that don't have lots of reputation, so it's triple cool that they're giving so great answers.

Good luck!!!!

A RESTful bridge to send and receive SMS using AT commands

I bought the Portech MV-372 to place free calls to both my and my wife's mobile phones. Despite some minor hiccups (like the web server not being 100% of the time available or stuff like that) it's been working fine.

One of the first things I wanted to do was having my twilio number forward text messages (SMS) to my mobile phone... but using the Portech (so that they're free). The problem with that is that the Portech uses AT Commands that are really something to deal with.

You need to open a telnet connection to the device and then issue the commands required to either verify if there are new texts or send a new one... there's no way to tell that a text arrived to the Portech other than by polling.

Oh, and I forgot... you can only have one concurrent telnet connection open... and that's not all... while there's a connection established, you can't place new calls. That's a lovely scenario to work on, right?

Goals

  1. Have a RESTful service that let me send and receive texts
  2. Have a RESTful service that lets me ask my carrier how much credit I have on my SIMs
I have recently started playing with python... so this is a perfect fit for my ODROID... connect once a minute to check if there were new texts to process and send the ones that were on the queue.

For the second goal my carrier (antel) exposes this through the SIM Applications... that means I had to dig into the STK AT Commands as well. The bright side, is that once I select the appropriate option what I get is just a regular text saying how much credit I have.

After just four days (what made me fall in love with python) I had something that worked. A bridge between the AT Commands and the beautiful REST world... I set up my logic on my windows server and have the bridge running locally on my odroid.

You can find the project on github. There are lots of things to do, but it gets the job done. Basically, when it's running behind nginx, it exposes a service for you to send texts and when a new text arrives it does a RESTful request to a url specified on config.py. In order to see which requests it does and what it expects, you should dig into the code... so it's not exactly ready for production but it has been pretty stable at home.

The setup is pretty raw right now, just an sql script for the database and the only documentation is on the code... but if there's people interested on it, I could see myself making it more user friendly ;)

If you happen to use it and have something to contribute, feel free to send pull requests.

Tuesday, January 14, 2014

Asterisk 12 on a Raspberry Pi... my experience

It's been a long trip since I started playing with Asterisk... I did a few more things than integrating Asterisk with Twilio.

After doing that, I compared flowroute with Anveo (which works great for outgoing calls, but had issues with an incoming number on Argentina... I wouldn't recommend them for that). I ended up using Anveo for most of my outgoing calls and it works just fine.

But, I wanted to be able to forward calls to my mobile phone (or my wife's) for free... ooor, as close to free as it gets. I then bought a Portech MV-372 on Ebay... yeah, that's probably the opposite to free... but once I got my hands on it, I bought 2 SIM cards... one of them can call me for free, the other can call my wife for free (as long as I put $5 on them every month).

I quickly realized that the NAS (a Synology DS112j) didn't have enough power to deal with the calls. I read that Asterisk needs a kernel with a clock interrupting at least at 1000 HZ. When I first read that, I didn't have a clue on what that meant... but then, I found this great post that explained it with an app that lets you figure out yours. The NAS had 96 HZ.

I then moved to a VPS... but that made calls originating on my home line to the Portech travel all the way to the US. That generated a delay that was totally unacceptable. I started to think that maybe there's a reason for everyone to run Asterisk servers locally... you can't beat a LAN.

Also, at that point, the friends from Asterisk released Asterisk 12... with a REST interface that really got me thinking...

My good friend Juan lent me his Raspberry Pi to try it out. I know there are some images around with Asterisk already installed, but I preferred to go ahead and cherry pick what would be installed. I followed the steps at this post and voilá... I won't say that the compilation was fast... but it got there :) and here I am, placing calls through the Raspberry.

I still perceive a small delay on the calls (way better than on a VPS though) even when I set ulaw:10 and alaw:10 as codecs... not exactly sure why that happens (I'm just getting started here), but I've already ordered an ODROID U3 to discard that it's due to the Raspberry Pi's lack of power.

Tuesday, December 17, 2013

Dynamically loading sip users from a database (and generating extensions for them)

My goal with this blog was just sharing the interesting (technological) problems I face and the way I solve them to read other suggestions and hopefully help a fellow developer... but, and that's what's awesome about life, things rarely go as you plan them and this time it was for the best. After I made a few blog posts on my twilio + asterisk integration, I received 2 offers to work on contract projects. It was a pleasure to work with both Seth and Abraham, and they made me want to continue working on this technology... so, you know... I'm available for Asterisk contract projects!

I don't want to lose sight of the original blog goal though, and one of the requirements Seth had was getting the sip users from a mysql database, where he would update the passwords whenever the users changed them. As I think that's worth sharing, here it goes...

After reading about how to do that, the first thing I found was Asterisk Realtime Architecture and that looked really promising. Despite having to use their table structure, the main issue is that (from that link)
The database peers/users are not kept in memory. These are only loaded when we have a call and then deleted, so there's no support for NAT keep-alives (qualify=) or voicemail indications for these peers.
Which is definitely not acceptable if you have mobile clients.

We built a simple cron that runs once per minute and, if there are any changes, it updates the sip.conf file, extensions.conf (b/c of his requirements, changes on the database may lead to changes in the extensions file) and, once both files are tweaked, the cron just does
 
asterisk -r -x "sip reload"
asterisk -r -x "dialplan reload"
 
This works just fine, it doesn't disconnect already registered peers and, as the cron does it only if it detects changes, it should be the same as doing it manually.

However, it feels like cheating... specially when there's Asterisk Realtime Architecture to load the peers and Asterisk RealTime Extensions to load the extensions from a db (why would they build it if you can do it easily with a cron?).

If you find issues with this approach (and feel like sharing) please add a comment.

Saturday, November 2, 2013

After twilio... Flowroute

Once I finished the integration with twilio, I started looking for VoIP providers... just to see what was out there and b/c I found out that I'm really liking this VoIP adventure.

The first site I found was flowroute. When I saw their prices (and compared them to twilio) I couldn't believe how cheap they were. They also have, for every route, the 1st interval and the sub interval. As I didn't know what they were (and at some point I thought that the rate could be for those intervals instead of per minute), I sent a support ticket. Their response was quick and I couldn't put it better than them, so here it is
The rates quoted are per-minute. To calculate the exact billed duration of a call, the length of the call is rounded up according to the billing intervals.

To illustrate, here are a few examples of how this works for calls to a destination with 10/6 billing intervals:

You make a call for 5 seconds: you will be billed for 10 seconds.
You make a call for 11 seconds: you will be billed for 16 seconds. (10 + 6)
You make a call for 20 seconds: you will be billed for 22 seconds. (10 + 6 + 6)
You make a call for 62 seconds: you will be billed for 64 seconds. (10+6+6+6+6+6+6+6+6+6)

At a rate of $0.01/minute for the above calls, the billing would work out as follows:

5 second call: $0.01 * 10 / 60 = $0.0017
11 second call: $0.01 * 16 / 60 = $0.0267
20 second call: $0.01 * 22 / 60 = $0.0037
62 second call: $0.01 * 64 / 60 = $0.0107

Another example given that call billed at $0.04 per minute.

If a call was 64 seconds, it will be rounded up to 66 seconds. The rate is then applied as follows:
66 seconds / 6 seconds = 1.1
1.1 * $0.04 = $0.044

A 45 second call would be rounded up to 48 seconds. The rate is then applied as follows:
48 seconds / 6 seconds = 0.8
8 * $0.04 = $0.024
From all the examples the support guy wrote, you can tell he realized I'm no expert on the matter... but I really liked how he did his best to make sure I'd understand. I created my account and they gave me 25 cents for me to play with.

The setup was straightforward, they have a System Configurator that generates exactly what you need to put on your sip.conf. I placed a few calls to the US, but I couldn't place calls to Uruguay b/c of the rate (trial accounts have a limit on the routes they can call to based on its cost per minute) so I did a $35 deposit (the minimum to "upgrade" it).

Call quality to Argentina (landline), Uruguay (landline + mobile) and US was awesome. Calls to Argentinian mobile phones were a little laggy, but way better than Skype (and a lot cheaper). Another thing I didn't know was possible is that they passed the Caller ID for US and the main Uruguayan mobile carrier (Ancel) but failed to do so on calls to Argentina or to the other Uruguayans carriers (Claro or Movistar).

A great plus to flowroute is that calls to US toll free numbers are free... On the down side, they don't offer much to customize (like real time notifications or different routes with different CLI results) but I think it's awesome as a backup provider... and I say backup provider b/c after discovering Anveo, I fell in love with it... but that's on my next post

Sunday, October 20, 2013

Asterisk + Twilio: Receiving calls from twilio (Part IV)

This is the fourth and last post of my Asterisk + Twilio series:

Aaaand now to the most interesting (and challenging) part of the journey... receiving calls from twilio to my Asterisk.

I have a twilio US number, and wand to forward incoming calls to my mobile phone. As I said on the first post of the Asterisk + Twilio series, that works just fine on twilio, but it's a little too expensive (38 cents per minute).

So, I wanted twilio to forward the call to Asterisk so that it calls me from my landline (which takes the cost down to about 15 cents + 0.25 cents per minute of the SIP call) taking the cost to less than a half.

I carefully read twilio's SIP reference, and set up a device on sip.conf this way

 
[myusername]
context = fromtwilio
type = user
secret = mypass
permit=107.21.222.153
permit=107.21.211.20
permit=107.21.231.147
permit=54.236.81.101
permit=54.236.96.128
permit=54.236.97.29
permit=54.236.97.135
permit=54.232.85.81
permit=54.232.85.82
permit=54.232.85.84
permit=54.232.85.85
permit=54.228.219.168
permit=54.228.233.229
permit=176.34.236.224
permit=176.34.236.247
permit=46.137.219.1
permit=46.137.219.3
permit=46.137.219.35
permit=46.137.219.135
permit=54.249.244.21
permit=54.249.244.24
permit=54.249.244.27
permit=54.249.244.28
 

Then, I had my number set up to point to a url that returned this xml

 
<Response>
  <Dial>
    <Sip username="myusername" password="mypass">sip:myext@mydomain.com</Sip>
  </Dial>
</Response>
 

As the username and password would travel here, I bought an SSL from Comodo (at $7 per year) and used twilio's signature in the message to validate the request... this way, I'd only reply to genuine requests and my replies would be encrypted by SSL. According to what I understood, twilio would send an INVITE using myusername@something as From, sending mypass as password and that would do the trick... but my Asterisk just returned

 
[Oct 17 19:22:58] NOTICE[9150]: chan_sip.c:22614 handle_request_invite: Sending fake auth rejection for device "+1XXXXXXXXXX" <sip:+1XXXXXXXXXX@sip.twilio.com>;tag=78774647_6772d868_43fb2951-f4f9-4c80-8377-9bb50e9458ae
 

and when I inspected the SIP packages (by downloading the PCap Log from twilio... you gotta love their debug tools) I saw this

 
From: "+1XXXXXXXXXX" <sip:+1XXXXXXXXXX@sip.twilio.com>;tag=78774647_6772d868_43fb2951-f4f9-4c80-8377-9bb50e9458ae
 

which definitely made it look like the username I chose wasn't being sent (at least, not in the From, which is where Asterisk expects it for devices with type=user).

Then, I checked the ip twilio contacted me from and changed the device to

 
[myusername]
context = fromtwilio
type = peer
secret = mypass
host = 107.21.222.153
 

To my surprise... it worked! so, it looked like I had to use type = peer and create a device per ip... but they're 23! and every device needs a different name, so I should know which IP twilio is going to use to choose the username matching the device... nope, that wouldn't fly. Then, I realized I could do this

 
[twiliocaller](!)
context = fromtwilio
type = peer
qualify=no
allowguest=yes

[twilioip-1](twiliocaller)
host=107.21.222.153

[twilioip-2](twiliocaller)
host=107.21.211.20

[twilioip-3](twiliocaller)
host=107.21.231.147

[twilioip-4](twiliocaller)
host=54.236.81.101

[twilioip-5](twiliocaller)
host=54.236.96.128

[twilioip-6](twiliocaller)
host=54.236.97.29

[twilioip-7](twiliocaller)
host=54.236.97.135

[twilioip-8](twiliocaller)
host=54.232.85.81

[twilioip-9](twiliocaller)
host=54.232.85.82

[twilioip-10](twiliocaller)
host=54.232.85.84

[twilioip-11](twiliocaller)
host=54.232.85.85

[twilioip-12](twiliocaller)
host=54.228.219.168

[twilioip-13](twiliocaller)
host=54.228.233.229

[twilioip-14](twiliocaller)
host=176.34.236.224

[twilioip-15](twiliocaller)
host=176.34.236.247

[twilioip-16](twiliocaller)
host=46.137.219.1

[twilioip-17](twiliocaller)
host=46.137.219.3

[twilioip-18](twiliocaller)
host=46.137.219.35

[twilioip-19](twiliocaller)
host=46.137.219.135

[twilioip-20](twiliocaller)
host=54.249.244.21

[twilioip-21](twiliocaller)
host=54.249.244.24

[twilioip-22](twiliocaller)
host=54.249.244.27

[twilioip-23](twiliocaller)
host=54.249.244.28
 

and even if doing allowguest=yes may feel insecure, you're identifying the peer by its ip... so an attacker should connect from one of those (and if the attacker had access to twilio's infrastructure... well, they could certainly make a request and get my user/pass from the original xml).

Then, after that, my xml turned into this

 
<Response>
  <Dial>
    <Sip>sip:myext@mydomain.com</Sip>
  </Dial>
</Response>
 

which also feels safer (and those U$S 7 spent on the certificate a little less worthy). And set up the extension for it on my extensions.conf

 
[fromtwilio]
exten => myext,1,Dial(SIP/099999999@atapstn)
 

to handle twilio's calls to that extension. This works like a charm... but I'm an absolute noobie on Asterisk, so maybe allowguest=yes is a vulnerability after all?

Well, and this is how I'm finishing this set of posts about Asterisk + Twilio, an experience that was extremely fun for me and I wanted to share. After this was working fine, I tweaked my logic to receive calls, so that if my Asterisk is down for whatever reason, I route the call through twilio as I used to... so the number points to an xml like this one

 
<Response>
  <Dial action="/my-phone/finished">
    <Sip>sip:myext@mydomain.com</Sip>
  </Dial>
</Response>
 

and the action that handles the call termination looks like this

 
public ActionResult MyPhoneFinished(TwilioRequestVM request)
{
  var res = new TwilioResponse();
  var validator = new Twilio.TwiML.RequestValidator();
  if (validator.IsValidRequest(System.Web.HttpContext.Current, ConfigurationManager.AppSettings["Twilio.Token"]))
  {
    if (request.CallStatus == "failed")
    {
      res.Dial(new Number("+59899999999"));
    }
  }
  return TwiML(res);
}
 
PS: I wish I had a cool number like the one in the examples
PS2: Inspired by my question and answer at stackoverflow