SIP stack libre: creating simple OPTIONS SIP request

Libre is a powerful and simple library than implements SIP and many other real-time communication protocol. Full list of features and protocols can be found on its web-page: http://creytiv.com/re.html.

There are several open source SIP stacks on C available:
I have tried do some simple SIP UA with four of them: libre, sofia-sip, pjsip and libeXosip2. These scripts can be found at my github project.

Some of them are used with big projects. For example, PJSIP is now a part of Asterisk project (starting from version 11) and Sofia-sip is a core SIP library of FreeSWITCH project.

Asterisk team has made some good research on SIP Stacks when they were chosing one for their project. It can be found here: https://wiki.asterisk.org/wiki/display/AST/SIP+Stack+Research

I do like sofia-sip, but, unfortunately, it was not updated for very long time (since 2011). 

Libre looks like relatively new to other project, yet features list is impressive and code looks very neat. However, the documentation is not quite good, as for me. Some functionality can be learned from demo project. Also, there is stand-alone project "baresip" which is based on libre.

Still, I did not find any core description or step-by-step tutorial. So here I explain how to build a simple application that will be sending OPTIONS packet to destination.

Libre application starts with function: libre_init(). Most of libre functions return error code as integer. So you can program in really defensive style and check every return code before continue, such avoiding nasty surprises. Also, library provides some debugging functions:
  • sys_coredump_set(bool)
  • tmr_debug()
  • mem_debug()
The first "sys_coredump_set" should be used in the very beginning and two others right before exiting your application.

In libre demo script, next step is to configure DNS client. I skip it here to make this example shorter.

Next is to allocate SIP stack instance (explained here).  Basically, it initiates "struct sip" structure. First parameter is the structure itself. Next is DNS client. I do not want to use DNS client, so it is NULL here. Next three parameters are client transaction hash tables, integers power of 2. I am not sure what exactly it means so I keep the same value as in demo. Following parameter is a string, that identifies this application, which will be used as a value in the User-agent header of a SIP packets. Then, a pointer to a "callback" function, which will be called on SIP stack instance destroy. The last parameter is a pointer to anything you wish to pass to the exit handler (callback function).

err = sip_alloc(&sip, NULL, 32, 32, 32, "MyRe test", sip_exit_handler, NULL);

When SIP stack is ready, it is time configure application's transport. What local IP address application will use, port and transport protocol: TCP, UDP one or more. The following example will use local IPv4 interface and random UDP port.

struct sa laddr;
net_default_source_addr_get(AF_INET, &laddr);
sa_set_port(&laddr, 0);
sip_transp_add(sip, SIP_TRANSP_UDP, &laddr);

Next step would be to set SIP listening socket for incoming connections using function "sipsess_listen". Where first parameter is a structure "sipsess_sock".  This parameter is passed as a pointer to the structure and will be allocated (no need to allocate before calling the function). Next parameter is SIP stack which just has been initiated (see above). Next parameters are hash table size, callback function and a pointer to any data you want to pass to callback function.

struct sipsess_sock *sess_sock;sipsess_listen(&sess_sock, sip, 32, connect_handler, NULL);

In demo script from libre, next steps are to setup media session (SDP) and start session with function "sipsess_connect". This is what to be done to make calls: send INVITE and negotiate SDP. In my case, I want simply send one OPTIONS packet, read response and exit. So function "sipsess_connect" is not good form me.

I have spent some time to figure out how to do what I want. I found a family of sip request functions. I have chosen function "sip_drequestf" to do the job. This function is using SIP dialog instance. So, before call SIP request, a dialog need to be created:

struct sip_dialog *dlg = NULL;
err = sip_dialog_alloc(&dlg, "sip:example.com:5060", // destination URI
                              "sip:alice@example.com", // To URI
                              "Alice User",    // from_name
                              "sip:alice@example.com", // From URI
                              0,               // route vector
                              0 );             // route count

Function "sip_dialog_alloc" will allocate SIP dialog instance that we now can use in request function. Function "sip_drequestf" is using "sip" and "dlg" instances that were initiated above.  Third parameter is a boolean flag to indicate if request should be stateful or stateless. Using stateful state makes it easier to handle responses in callback functions. The sixth parameter is to set CSeq header with packet sequence number. If this parameter is 0, then libre will generate this number automatically. Next parameter is "NULL", because I do not expect any authentication.
Then, there are two callback functions pointers: for send route and response route. Next parameter is a pointer to a memory with anything we want to pass to callback functions. After that, there can be given arbitrary number of string parameters that represent SIP headers with values which will be inserted in packet. Last parameter should be CRLF ("\r\n") and will be appended to SIP packet.

static struct sip_request *sipreq;
err = sip_drequestf(&sipreq, sip, true, "OPTIONS", dlg,
                      0,                // CSeq. When 0, CSec will be generated
                      NULL,             // SIP authentication state
                      send_handler,     // Send handler
                      resp_handler,     // Response handler
                      "Passing arg",    // Arg for callback func
                      "\r\n");

The callback functions are demonstrated in the source code of my application and the link to it can be found in the end of the blog.

If we compile and run the application now, it will send the OPTIONS packet to destination URI (see sip_dialog_alloc). But probably, no callback function will work, because application stops right after calling "sip_drequestf". To make it run as it should, we need to add the main libre loop function:

re_main(signal_handler);

Now, everything will work as expected.

The working example can be found here.
Another good example of simple application that initialize SIP session (call) can be found here (sip_ua): http://creytiv.com/pub/redemo-0.4.1.tar.gz

Comments

Popular posts from this blog

Asterisk Queues Realtime Dashboard with amiws and Vue

YAML documents parsing with libyaml in C