Posts Tagged ‘Asterisk’

[quicky] Asterisk and incoming cli manipulation

Thursday, January 15th, 2009

It is very common in professional VoIP that service providers put through CLIs transparently. If you are just a traffic routing party you usually don’t care about it, but for premium services however it can cause major issues. For example you have an application that matches the calling party’s CLI to an entry in a database. If you have built that application yourself it’s very easy to do some internal mangling, however some people buy products which are not suited to do the job properly.

So this article is about manipulating the calling party’s CLI, rather than the called number. Let’s take a look at the following call example. Person A (00123456789) calls service B(1223235325). Service B has a number in E.164 format, however person A does not. In this example person A is the calling party. However the service requires the calling number to be in E.164 format.

So what would we do in asterisk to fix that? Well, let’s say for example also the B-number has to zeros at the beginning of the number:

001223235325

In the asterisk configuration we could have the following example dial plan to strip the two zeros:

[example]
; match incoming 00xxxxxxx numbers and reformat
exten => 00X.,1,Goto(${EXTEN:2}, 1)
 
; normal dialplan
exten => _X.,1,Answer()
[...]

By using the ‘:2′ on the ‘EXTEN’ variable we get rid of the first ‘2′ digits, this is a substring operation.

So that was easy! However, when doing it for the incoming CLI it’s a totally different story. The incoming CLI is exposed by the CALLERID function. There is some regular expression support in Asterisk, so we can get going. Unfortunately it only supports matching. So no support for getting matched groups, which would be a lot easier. However after some twiddling I came up with the following:

[example]
; print cli as it came in
exten => _X.,1,Verbose(${CALLERID(num)})
 
; test if it matches 00XXX
exten => _X.,n,Set(STRIPCLI=${REGEX("^[0]\{2\}[1-9][0-9]+$" ${CALLERID(num)})})
 
; Depending if it is set to 0 or 1 goto continue or continue and format the cli
exten => _X.,n,GotoIf($["${STRIPCLI}" = "0"]?continue)
exten => _X.,n,Verbose(Formatting outbound CLI)
 
; format the CLI
exten => _X.,n,Set(CALLERID(num)=${CALLERID(num):2)})
 
; continue normal operation
exten => _X.,n(continue),Verbose(${CALLERID(num)})
exten => _X.,n,Dial(SIP/SOMESERVER/${EXTEN})
[..]

Let’s go back to our example now and how the dial plan handles it:

Verbose(00123456789)
Match on regular expression and put it a variable
if(STRIPCLI == false)
  continue normal dialplan
else
  format cli
 
normal dialplan:
 
Verbose(123456789)
Forward call to some service

As you can see the ‘00′ has been stripped of and the A number is now in E.164 format. I hope this will become useful for someone else as well :-)

[Quicky] Shorewall and opening port ranges

Saturday, December 6th, 2008

Imagine you want to open up a port range to your host ($FW) in your shorewall’s ‘rules’ file. So let’s say we run an Asterisk machine and we want to open RTP ports 10000 to 20000 to your machine, you would have to do something like:

ACCEPT  net  $FW  udp  10000:20000

For more reference on Asterisk’s firewall rules, click here.

[Hibernate+postgres] Composite keys and @GeneratedValue problems

Friday, September 12th, 2008

Still working with hibernate for use with an Asterisk machine and a Postgres database server. This time I am try to get extensions.conf in the database. There is a predefined model for this for use with postgres+asterisk:

CREATE TABLE extensions (
  id serial,
  context varchar(40) NOT NULL DEFAULT '',
  exten varchar(40) NOT NULL DEFAULT '',
  priority int4 NOT NULL DEFAULT 0,
  app varchar(40) NOT NULL DEFAULT '',
  appdata varchar(256) NOT NULL DEFAULT '',
  PRIMARY KEY  (context,exten,priority),
  UNIQUE (id)
);

Now I wanted a POJO with a composite key(context, exten, priority) for this. So I started coding:

@Entity
@Table(name = "asterisk_extensions")
public class Extension implements Serializable {
 
    @GeneratedValue(strategy = GenerationType.TABLE)
    @NotNull
    @Column(name = "id")
    private int id;
 
    @Id
    @NotNull
    @Column(name = "context", length = 40)
    private String context;
 
    @Id
    @NotNull
    @Column(name = "exten", length = 40)
    private String extension;
 
    @Id
    @NotNull
    @Column(name = "priority")
    private int priority;
 
    @NotNull
    @Column(name = "app", length = 40)
    private String application;
 
    @NotNull
    @Column(name = "appdata", length = 256)
    private String applicationData;
 
    // getters and setters
    [...]
}

This won’t do the trick. I can’t specify three @Id annotations for this POJO. Also the @GeneratedValue doesn’t work since it’s not annotated as @Id but I will come to that later on.

So to get a composite we need something else. We will need the @IdClass annotation for the calls to specify a class to use as a reference by Hibernate for handling the composite primary key. That class needs to be @Embeddable. And also have all the fields available for the composite key. Furthermore it should implement a hashCode() and equals() method.

Let’s put all of this together:

Extension.java:

@Entity
@Table(name = "asterisk_extensions")
@IdClass(ExtensionPK.class)
public class Extension implements Serializable {
 
    @GeneratedValue(strategy = GenerationType.TABLE)
    @NotNull
    @Column(name = "id")
    private int id;
 
    @Id
    @NotNull
    @Column(name = "context", length = 40)
    private String context;
 
    @Id
    @NotNull
    @Column(name = "exten", length = 40)
    private String extension;
 
    @Id
    @NotNull
    @Column(name = "priority")
    private int priority;
 
    @NotNull
    @Column(name = "app", length = 40)
    private String application;
 
    @NotNull
    @Column(name = "appdata", length = 256)
    private String applicationData;
 
    // getters and setters
    [...]
}

ExtensionPK.java

@Embeddable
public class ExtensionPK implements Serializable {
 
    @NotNull()
    @Column(name = "context", length = 40)
    private String context;
 
    @NotNull
    @Column(name = "exten", length = 40)
    private String extension;
 
    @NotNull
    @Column(name = "priority")
    private int priority;
 
    @Override
    public boolean equals(Object object) {
 
        if (object instanceof ExtensionPK) {
 
            ExtensionPK extensionPK = (ExtensionPK)object;
 
            return (this.context.equals(extensionPK.context) && this.extension.equals(extensionPK.extension) && this.priority == extensionPK.priority);
        }
 
        return false;
    }
 
    @Override
    public int hashCode() {
 
        String hash = this.context + this.extension + this.priority;
 
        // let String do the job for us.
        return hash.hashCode();
    }
 
    // getters and setters
    [...]
 
}

This will solve our first problem regarding the composite key. That wasn’t too difficult, now was it?

Ok, let’s move on to our second problem. Since the ‘id’ field is not @Id annotated, somehow Hibernate will not create a sequence in the database for auto incrementing the value. So since it’s an application using only postgres anyway I see no problem in getting our friend ‘columnDefinition’ back again:

@NotNull
@Column(name = "id", columnDefinition = "bigserial")
private int id;

Which will do the trick!

Note: As a reminder, I let hibernate create my database schema. Important not to forget to recreate the schema!

[Hibernate] Default column field

Friday, September 5th, 2008

It has been a while since I last posted here. Meanwhile I learned lots of stuff at Lunatech Research.

Anyway, let’s talk about some of the more technical stuff. As a hobby project I’m writing a Java application that is able to interface with an Asterisk instance. I do this by mapping Asterisk to a database using unixODBC. Via the database I can control Asterisk. Asterisk does require specific column properties on the database tables.

For the Java application part I use Hibernate and PostgreSQL. I use the annotation support in hibernate to define my POJO’s. Also I am letting Hibernate create my database schema. Asterisk however, needs some special column properties which are database specific. So using Hibernate to create the schema also caused a bit of a problem for me, because there is no annotation for setting the ‘DEFAULT NOW()’ property for a column. This is just one example of course.

Here’s the SQL statement for use with asterisk+postgres as found here:

CREATE TABLE cdr (
  calldate timestamp WITH time zone NOT NULL DEFAULT now(),
  clid varchar(80) NOT NULL DEFAULT '', 
  src varchar(80) NOT NULL DEFAULT '', 
  dst varchar(80) NOT NULL DEFAULT '', 
  dcontext varchar(80) NOT NULL DEFAULT '', 
  channel varchar(80) NOT NULL DEFAULT '', 
  dstchannel varchar(80) NOT NULL DEFAULT '', 
  lastapp varchar(80) NOT NULL DEFAULT '', 
  lastdata varchar(80) NOT NULL DEFAULT '', 
  duration bigint NOT NULL DEFAULT '0', 
  billsec bigint NOT NULL DEFAULT '0', 
  disposition varchar(45) NOT NULL DEFAULT '', 
  amaflags bigint NOT NULL DEFAULT '0', 
  accountcode varchar(20) NOT NULL DEFAULT '', 
  uniqueid varchar(32) NOT NULL DEFAULT '', 
  userfield varchar(255) NOT NULL DEFAULT '' 
);

This became a bit of an annoyance for me. I do understand their point that it’s database specific. I was more upset with the fact that it wasn’t obvious which annotation I had to use to tackle this problem.

Finally after reading this page thoroughly enough I concluded I had to use the ‘columnDefinition’ in the ‘@Column’ annotation.

Here is an example for the ‘calldate’ field:

@Entity
@Table(name = "asterisk_cdr")
public class CallDetailRecord implements Serializable {
 
    /** The time the call was placed */
    @Column(name = "calldate", columnDefinition = "timestamp with time zone NOT NULL default now()")
    @Temporal(TemporalType.TIMESTAMP)
    private Date callDate;
 
    [...]
}

So in the end it’s not all that bad since I’m using only one database engine for my application anyway. So, no need to switch later on, meaning that for me the ‘columnDefinition’ is something I will be using a lot more in this application.

[Asterisk] Playing audio streams

Wednesday, July 9th, 2008

You might be in a situation where you want to put a user on hold, but you don’t want to use the standard MusicOnHold with a fixed amount of music. A solution could be to play a webstream so you’ll have random music most of the time. In asterisk ( >= 1.2) this is very simple to do. You will need to install mpg123 first, because I will only discuss playing mp3 streams, you could instead use mplayer or others, but I will discuss the use of mpg123 now, so if you’re running debian:

apt-get install mpg123

Now you’ll need to edit your /etc/asterisk/musiconhold.conf or where ever you’ve put it. I recommend using an empty file. The following example shows a default class for music on hold, it’s left default like the default asterisk config.

[default]
mode=quietmp3
directory=/usr/share/asterisk/mohmp3
 
[aardschok]
mode=custom
application=/usr/bin/mpg123 -q -r 8000 -f 8192 -s --mono http://81.173.3.25:80
 
[classx]
mode=custom
application=/usr/bin/mpg123 -q -r 8000 -f 8192 -s --mono http://81.173.3.24:80
 
[kink]
mode=custom
application=/usr/bin/mpg123 -q -r 8000 -f 8192 -s --mono http://81.173.3.20:80/

I defined three other classes, namely aardschok, classx and kink. These are three streams from radio stations in the Netherlands and are used as an example. Also see that we are resampling the stream to 8KHz so that asterisk will understand the sound.

Now in your extensions.conf (example):

[radio-context]
exten => _X.,1,MusicOnHold(aardschok)

Once this extension get’s called, the calling party will hear the audio stream! You could also create a menu to allow users to say which channel they want to listen to:

[radio-context]
exten => _X.,1,Background(/sounds/choices) ; A prompt for telling the menu entries
exten => _X.,n,Wait(5) ; Read choice
exten => _X.,n,MusicOnHold(kink) ; On timeout just use default channel
exten => _X.,n,Hangup()
 
; Let's define some extensions for our radio channels
exten => 1,1,MusicOnHold(aardschok)
exten => 1,n,Hangup()
 
exten => 2,1,MusicOnHold(classx)
exten => 2,n,Hangup()
 
exten => 3,1,MusicOnHold(kink)
exten => 3,n,Hangup()

Actually it’s quite simple, more details and examples on getting non-mp3 streams to work, you can find on:

voip-info.org

I really recommend reading that page, because there are some things that you will need to know. This post is only to show you how easy it can be.