oSIP user Manual

A guide to implement SIP agents with the oSIP stack.

Aymeric Moizard

jacK@atosc.org

Abstract

"The Session Initiation Protocol (SIP) is an application-layer control (signaling) protocol for creating, modifying and terminating sessions with one or more participants. These sessions include Internet multimedia conferences, Internet telephone calls and multimedia distribution. Members in a session can communicate via multicast or via a mesh of unicast relations, or a combination of these."

IETF - rfc 2543

This document can be freely distributed and translated under the terms of the LDP License.


Table of Contents
1. The SIP protocol
SIP is independent of media
SIP is independent of the transort layer
SIP is extensible
SIP and end-user services
2. SIP overview
SIP syntax
SIP transactions
SIP sessions
Server behaviour
3. The oSIP Library
The goals
The development level
The parser
The state machines
The parser
A transaction manager
Who will benefit from oSIP
Available ports
4. The oSIP parser
files
SIP-URL
Definition and purpose
API for SIP-URL
API for url_param_t and url_header_t
SIP headers
Definition and purpose
Common API for all implemented header.
Specific API for "To" header
API for generic param
The internal parser
The header and message API
Sample code to create a message
5. The transaction manager
The finite state machines
Sending events to control transaction
How to use the callbacks
Notes for proxy implementation
6. Designing your own architecture
Bibliography

Chapter 1. The SIP protocol

SIP is a signaling protocol used to initiate and control multimedia sessions. It is already published by IETF (www.ietf.org) as the rfc2543.

SIP is part of the IETF effort to bring telephony on the IP network. It is about to become the standard used by the emerging IP telephony industry. As simple as the mail protocol, it may become as popular...


SIP is independent of media

The traditionnal telephony was based on one media. Now, it's over. Your phone will be able to connect to a TV, to a camera, to others phones with different qualities and different codecs. Hopefully, SIP is independent of any media used by the applications. SIP is able to negociate media used within sessions. Any multimedia application (games, distance learning application) can use SIP to set up sessions.


SIP is independent of the transort layer

SIP is not tight to any transport protocol. This aspect will minimize efforts to interoperate with new third generation networks. Wireless phones are also concerned. A SIP stack perfectly fits the signaling needs of the new cellulars' generation.


SIP is extensible

The rfc2543 defines 6 types of transaction (INVITE, BYE, CANCEL...). Those transactions are used to negociate media, set up, modify and terminate calls. Many services are already provided this way but SIP is designed for extensibility and the transactionnal model can be reused (transparently for servers) by new type of transaction to create some supplementary services. Here is a list of possible services:

MESSAGE for instant messaging
SUBSCRIBE/NOTIFY for presence management
REFER for call-transfert management


SIP and end-user services

"SIP transparently supports name mapping and redirection services, allowing the implementation of ISDN and Intelligent Network telephony subscriber services. These facilities also enable personal mobility."

rfc2543.txt (Section 1.1)

SIP servers are used to locate users and distribute location (through urls) on demand. This way, end user agents have very minimal requirements and still have access to a wide variety of services.

Many extensions are already available as draft. Your can also add your personnal phone capabilities and remain interoperable with existing servers.


Chapter 2. SIP overview

This section does not intend to fully describe the RFC. It is a fast and incomplete overview of the protocol behavior.


SIP syntax

SIP is a text protocol based on utf8 encoding. (making it more readable and easier to debug) SIP describe a syntax for SIP requests, responses and their headers. The full syntax is available at Columbia in an augmented BNF form.

The syntax is borrowed from the MAIL and HTTP syntax. 6 types of requests are defined by SIP. The basic available methods are:

INVITE
ACK
CANCEL
BYE
INFO
OPTIONS

As you can see in the BNF definition of request, SIP is not limited to this short list of methods and includes in its definition the extension-method token. Any other request can be handled by oSIP. NOTIFY and SUBSCRIBE are good examples of new possible methods. These ones are used specifically to provide presence capabilities to your SIP phone without much effort.

Example 2-1. INVITE request

INVITE requests are used to initiate and modify sessions. Here, cha from sipworld.net is calling jack from domain atosc.org. This request should be sent to the proxy server managing atosc.org, it will forward the call to jack at his real IP address.

INVITE sip:jacK@atosc.org SIP/2.0
Via: SIP/2.0/UDP home.sipworld.org
To: sip:jacK@atosc.org
From: sip:cha@sipworld.org
Call-ID: 35778645354@home.sipworld.org
CSeq: 1 INVITE
Contact: sip:cha@home.sipworld.org
Content-type: application/sdp
Content-length: 267

v=0
o=user1 53655765 2353687637 IN IP4 128.3.4.5
s=Mbone Audio
i=Discussion of Mbone Engineering Issues
e=mbone@somewhere.com
c=IN IP4 128.3.4.5
t=0 0
m=audio 3456 RTP/AVP 0
a=rtpmap:0 PCMU/8000


SIP transactions

In order to control sessions, SIP uses transactions. Transactions (INVITE, CANCEL, BYE...) usually result in a modification of a current session. Some other transactions (SUBSCRIBE, NOTIFY...) are not bound to a session. A transaction is composed of one request and its responses (many informational responses and one final response). The following headers: To, From, Call-ID and CSeq are used to identify messages within a transaction.

As SIP can use unreliable transport protocol (UDP is recommended on the IP network), SIP also defines retransmission rules for messages within a transaction.

Example 2-2. INVITE transaction

This is the most basic call flow showing the initiation of a session. Only two SIP User Agents (UAC/UAS) are involved. (retransmissions are hidden)


	          UAC1               UAS2

	  jacks    |   INVITE         |
	initiate a |----------------->|   Bob's
	  call     |                  | Phone starts
	           |       180 Ringing|   ringing
	           |<-----------------|
	           |       180 Ringing|
        	   |<-----------------|
	           |           200 OK |
	           |<-----------------|
	           |   ACK            |
	           |----------------->|

SIP sessions

Transactions are used by user agent as means to control sessions. A session is always initiated by an INVITE. SIP defines a large set of answer codes. A proxy may answer you the well known "404 User Not found" as for an HTTP error. Errors have different levels. A transaction can fail but still proposes new locations to try. Responses from class 3xx serve as redirection mechnism. 4xx, 5xx and 6xx responses are respectively reserved for client error, server error and global failure.

Example 2-3. A complete session

First, both user agents must send a REGISTER to a registrar. In the following example, the proxy also support registration.

This session is initiated with an INVITE transaction to join jack at home.net. A redirection to jack's office is made by the redirect server of home.net. UA1 understand the redirection and send a new INVITE towards the real User Agent (UA2) currently used by jack at office.atosc.org. UA2 first rings and jack accepts the call with a 200 OK response. After several minutes, jack and bob want to use their new cameras. The session is modified with an INVITE sent by jack to negociate new parameters for video. The session is finally ended by bob.

         BOB           home.net    Jack (atosc.org)
         UA1            PROXY            UA2
          | REGISTER      |               .
          |-------------->|               .
          |        200 OK |               .
          |<--------------|               .
          .               .               .
later...  .               .               .
          .               .               .
          .               |      REGISTER |
          .               |<--------------|
          .               | 200 OK        |
          .               |-------------->|
          .               .               .
later...  .               .               .
          | INVITE jack@home.net          .
          |-------------->|               .
          |302 Moved temporily            .
          |<--------------|               .
          | ACK           |               .
          |-------------->|               .
          | INVITE jack@office.atosc.org  |
          | audio                         |
          |------------------------------>|
          |                  180 Ringing  |
          |<------------------------------|
          |                  180 Ringing  |
          |<------------------------------|
          |                       200 OK  |
          |<------------------------------|
          | ACK jack@office.atosc.org     |
          |------------------------------>|
          .                               .
later     .                               .
          .                               .
          |      INVITE bob@bob.home.net  |
          |      audio + video            |
          |<------------------------------|
          |    200 OK                     |
          |------------------------------>|
          |     ACK bob@bob.home.org      |
          |<------------------------------|
          .                               .
later     .                               .
          .                               .
          | BYE jack@office.atosc.org     |
          |------------------------------>|
          |                       200 OK  |
          |<------------------------------|
          |                               |

Server behaviour

SIP defines behaviours for proxy, redirect and registrar server. For complete information, please read the RFC...

Usually, a user agent sends its requests to an outbound proxy. As users do not know the current location of their correspondent, they use for the sip url a username and a domain. The outbound proxy (where firewall capabilities can be inserted) uses DNS SRV RECORDS to find servers belonging to the requested domain. Once the server is found, the request is forwarded. This server is the inbound proxy of the correspondent. If the user is available, its application must have registered its location before, so the proxy is now able to forward the request to the real user location. On local network, other standards may be used to find user. (finger,...)


Chapter 3. The oSIP Library

Having a good knowledge of SIP is recommended for this section. If you plan to use SIP, you should read more carefully the rfc2543 from ietf.org.


The goals

The oSIP project has started in July 2000. The first official and public release (0.5.0) was published in May 2001.

The oSIP library is at first a free software project. In the context of the third generation network, more and more telecom operators will use IP technology, the favorite land of Linux. One aspect of this evolution is that the future of Linux is highly dependant on the multimedia tools that will be available. oSIP, as a SIP implementation, will allow building interoperable registrar, user-agent (software phones), and proxy thus giving more chance to Linux to be part of the next generation telephony products.

But oSIP is not only targeted towards PC applications. oSIP is enough flexible and tiny to be used on small OS with low requirements. From the 0.7.0 release, the thread support is now optionnal and the design of the application is entirely chosen by the end-developper. oSIP will now perfectly fit cellulars or any embeded systems. oSIP is known to run on the real time OS VxWorks and other ports should be simple.


The development level

The parser

oSIP now supports utf8 and the full SIP grammar. The release 0.7.0 contains a compliant SIP parser with minimal exceptions. Also, the parser has just been released (25th of July 2001) and may have some bugs. Please report them to , I'll fix them.

To get more information, see next section

The state machines

The 4 finite state machines have been tested during weeks and have also been tested with around 30 products at the 8th SIPit in Cardiff (SIP Interoperability tests). They appear to be stable.


The parser

oSIP contains a parser which is able to read and write any SIP message as described in the RFC. Currently oSIP is able to parse only a minimal set of headers such as Via, Call-ID, To, From, Contact, CSeq, Route, Record-Route, mime-version, Content-Type, and Content-Length. All other headers are stored as strings. Efforts will be done to support more headers.

By now, the SIP parser supports multiple attachments through the MIME format. (this part is new and remains untested)

Unwanted behaviors may happen sometimes. Messages could be somewhat transformed after being analysed by oSIP. oSIP cannot maintain:

the order of header
the presence of multiple headers on the same line
the presence of extra SPACEs between tokens
the presence of LWS (internal CRLF)
the presence of bracket in to,from,contact... headers


A transaction manager

oSIP presents an easy to use interface. Transactions are modelled through 4 finite state machines which is the core of the oSIP library. Each transaction uses a separate fifo filled by external modules. Events are processed on demand by user. A set of dynamic callback registred by user allow the application to be informed of each transactions's evolution.

Building an application on the top of oSIP will require to build several modules as shown in the above graphic. First, you'll have to build a module to manage the transport facilities. Thsi way, oSIP is independant from any transport layer. Then, you'll be required to build a module for timers management (a basic but far from excellent sample is provided). This module will be responsible of retransmission and will decide when the transaction context can be deleted safely. The third required module is the biggest part of the mutlimedia application. This modules will have to maintain and update the state of SIP sessions over transactions. This modules is entirely

It has been heavily tested and is already stable. Also, oSIP does not require much memory and runs fast. Since release 0.7.0, the library can be used either in multi-threaded mode or not!


Who will benefit from oSIP

oSIP offers a simple interface to add SIP capabilities in multimedia applications. Also, oSIP is not tight to any kind of SIP agents or OSs. You can build either end-user agents, stateless proxy and gateway. If you want to implement a proxy, please read "notes for proxy implementation" in the chapter called "the transaction manager".


Available ports

The library has been built with portability in mind and should be quickly usable on most posix systems. It has already been built under Solaris and the RT OS VxWorks. GNU/Linux (2.2.16 and 2.4.7) has been used for initial developments.


Chapter 4. The oSIP parser

files

./libosip-x.x.x/parser is the source directory for the SIP parser.

-losip-x.x.x is the library containing the SIP parser

#include <osip/smsg.h> is the include file describing the external SIP parser API


SIP-URL

Definition and purpose

URL are used to describe entirely a SIP entity: user, registrar, proxy have their own urls. oSIP uses the url_t type definition in the request-uri and in the folowing headers: "to", "from", "contact", "route", and "record-route".

type definition of url_t.

typedef struct _sipurl_t {
  char *scheme;
  char *username;
  char *password;
  char *host;
  char *port;
  list_t *url_params;
  list_t *url_headers;
} url_t ;

API for SIP-URL

#include <smsg.h>

url_init

Name

url_init -- Allocate and initialize the url element.

Description

int url_init(url_t **url);

on success, return 0.

url_free

Name

url_free -- free ressources contained in url

Description

void url_free(url_t ** url);

url_parse

Name

url_parse -- Parse field_value and store results in url.

Description

int url_parse(url_t * url char * field_value);

on success, return 0.

url_2char

Name

url_2char -- Allocate a string in field_value with the information stored in the url parameter.

Description

int url_2char(url_t *url char **field_value);

on success, return 0.

url_clone

Name

url_clone -- duplicate the url element in a new dest element.

Description

int url_clone(url_t *url url_t **dest);

on success, return 0.

url_uparam_add

Name

url_uparam_add -- allocate and add a new url parameter in the url element.

Description

int url_uparam_add(url_t *url char *pname char *pvalue);

on success, return 0.

url_uheader_add

Name

url_uheader_add -- allocate and add a new url header element in the url element.

Description

int url_uheader_add(url_t *url char *hname char *hvalue);

on success, return 0.

url_uparam_getbyname

Name

url_uparam_getbyname -- Find a url parameter with pname in the url element.

Description

int url_uparam_getbyname(url_t *url char *pname url_param_t **url_param);

on success, return 0.


API for url_param_t and url_header_t

url_param_t and url_header_t are data types used inside a SIP-URL. Methods are provided to allocate, free or clone ressources.

Important: The API is the same for url parameters and header parameters. You just have to replace "url_" with "header_".

Example 4-1. type definitions of url_param_t and header_param_t.

  typedef struct _url_param_t {
    char *pname;
    char *pvalue;
  } url_param_t;

  typedef struct _url_header_t {
    char *hname;
    char *hvalue;
  } url_header_t;

url_param_init

Name

url_param_init -- Allocate and initialize the url_param element.

Description

int url_param_init(url_param_t **url_param);

on success, return 0.

url_param_free

Name

url_param_free -- free ressources contained in url_param

Description

void url_param_free(url_param_t **url_param);

url_param_set

Name

url_param_set -- Set name and value in the url_param element.

Description

void url_param_set(url_param_t *url_param char *pname char *pvalue);

on success, return 0.

url_param_freelist

Name

url_param_freelist -- free ressources contained in the list url_params elements.

Description

void url_param_freelist(list_t *url_params);

url_param_add

Name

url_param_add -- add url_param in the list url_params.

Description

void url_param_add(list_t *url_params char *pname char *pvalue);

on success, return 0.

url_param_getbyname

Name

url_param_getbyname -- Find the url_param with name in the list url_params.

Description

void url_param_getbyname(list_t *url_params char *pname url_param_t **url_param);

on success, return 0.


SIP headers

Definition and purpose

The rfc2543 defines around 40 headers. SIP Messages are mainly composed of a list of headers. This first part documents the API to create, allocate, parse and print the SIP headers elements. The second one shows that headers also have a extended API only applicable for one type of header. The extended API presented below is for the "To" header. Nevertheless, the extended API for the "To" header is also valid for the "From", "Contact", "Route" and "Record-Route" headers.

Example 4-2. type definition of to_t

typedef struct _to_t {
  char *displayname;
  url_t *url;
  list_t *gen_params;
} to_t;

Common API for all implemented header.

The SIP parser can parse entirely the following headers: Via, To, From, CSeq, Call-Id, Contact, Route, Record-Route, Content-Type, Content-length, Mime-Version. The other headers are accessible as strings through a special API.

Important: This API presented below is applicable for all known headers: Simply replace to_ by one of the following list: "via_", "to_", "from_", "contact", "route_", "record_route_", "content_type_", "content_length_", "mime_version_", "cseq_", "call_id_"

As a sample, the following methods concern the API to manipulate a to_t structure.

#include <smsg.h>

to_init

Name

to_init -- Allocate and initialize the structure to

Description

int to_init(to_t ** to err_t * err);

on success, return 0.

to_free

Name

to_free -- free ressources contained in to

Description

void to_free(to_t ** to);

to_parse

Name

to_parse -- Parse field_value and store results in to.

Description

int to_parse(to_t * to char * field_value err_t * err);

on success, return 0.

to_2char

Name

to_2char -- Allocate a string in field_value with the information stored in the to parameter.

Description

int to_2char(to_t *to char **field_value);

on success, return 0.

to_clone

Name

to_clone -- duplicate the to element in a new dest element.

Description

int to_clone(to_t *to to_t **dest);

on success, return 0.


Specific API for "To" header

In addition to the common API shared by all known implemented header, there is a specific API valid only for each header.

to_setdisplayname

Name

to_setdisplayname -- set the display name in the to element.

Description

void to_setdisplayname(to_t * to char * value);

to_getdisplayname

Name

to_getdisplayname -- return the display name from the to element.

Description

char *to_getdisplayname(to_t *to);

to_seturl

Name

to_seturl -- set the url in the to element.

Description

void to_seturl(to_t *to url_t *url);

to_geturl

Name

to_geturl -- return the url from the to element.

Description

url_t *to_geturl(to_t *to);

to_addparam

Name

to_addparam -- add a param in the to element.

Description

int to_addparam(to_t * to generic_param_t *param err_t * err);

on success, return 0.

to_param_get

Name

to_param_get -- return the param at index pos from the to element.

Description

int to_param_get(to_t *to int pos generic_param_t **gp);

to_gettag

Name

to_gettag -- return the value associated with the tag parameter from the to.

Description

char * to_gettag(to_t * to);

to_settag

Name

to_settag -- set the tag parameter int the to element.

Description

void to_settag(to_t *to char *tag);

to_param_getbyname

Name

to_param_getbyname -- get the parameter with pname from the to element.

Description

int to_param_getbyname(to_t *to char *pname to_t **dest err_t * err);

on success, return 0.

to_param_add

Name

to_param_add -- add gen_param in the to element.

Description

int to_param_add(to_t *to generic_param_t *gen_param err_t * err);

on success, return 0.


API for generic param

Generic parameter are components of many headers such as To, From, Contact, Route, Record-Route, and Content-Type.

Example 4-3. The To header has a list of generic_param_t

  typedef struct _generic_param_t {
      char *pname;
      char *pvalue;
  } generic_param_t;

  /* to, from and contact headers are defined as below */ 
  typedef struct _to_t {
      char *displayname;
      url_t *url;
      list_t *gen_params;
  }

Example 4-4. A To header with the "tag" parameter

  to: "jack" <sip:atosc.org>;tag=ae56fr-dz-23

generic_param_init

Name

generic_param_init -- Allocate and initialize the structure gen_param.

Description

int generic_param_init(generic_param_t * gen_param err_t * err);

on success, return 0.

generic_param_free

Name

generic_param_free -- free ressources contained in the structure gen_param.

Description

void generic_param_free(generic_param_t *gen_param);

generic_param_set

Name

generic_param_set -- Set name and value in the gen_param element.

Description

void generic_param_set(generic_param_t *gen_param char *pname char *pvalue);

on success, return 0.

generic_param_getname

Name

generic_param_getname -- Get name from the gen_param element.

Description

char *generic_param_getname(generic_param_t *gen_param);

generic_param_setname

Name

generic_param_setname -- set name in the gen_param element.

Description

void generic_param_setname(generic_param_t *gen_param char *pname);

generic_param_getvalue

Name

generic_param_getvalue -- Get value from the gen_param element.

Description

char *generic_param_getvalue(generic_param_t *gen_param);

generic_param_setvalue

Name

generic_param_setvalue -- set value in the gen_param element.

Description

void generic_param_setvalue(generic_param_t *gen_param char *pvalue);

generic_param_parseall

Name

generic_param_parseall -- Parse params and store results in the list of gen_param.

Description

char *generic_param_parseall(list_t *gen_param char *params);

on success, return 0.

generic_param_add

Name

generic_param_add -- Parse params and store results in the list of gen_param.

Description

char *generic_param_add(list_t *gen_param generic_param_t *param);

on success, return 0.

generic_param_getbyname

Name

generic_param_getbyname -- find and return a parameter element containing name from the list gen_params.

Description

generic_param_t *generic_param_getbyname(list_t *gen_params char *name);

generic_param_freelist

Name

generic_param_freelist -- free ressources contained in the list gen_params elements.

Description

void generic_param_freelist(list_t *gen_params);


The internal parser

First, oSIP contains a parser for the SIP syntax. This is the initial requirement to use SIP.

Here is a list of methods used by the parser. They define read and write operations on the sip_t structure. Those methods are available through the osip/smsg.h header file.

Example 4-5. Declaration of the sip_t structure.

Currently this structure is not complete. All other header than From, To, Call-Id, CSeq, via, contact and content-length are stored as string in a list of generic headers. This structure will be completed while improving the parser capabilities.

typedef struct _sip_t {
  startline_t  *strtline;

  from_t    *from;
  to_t      *to;
  call_id_t *call_id;
  cseq_t    *cseq;
  list_t    *vias;
  list_t    *contacts;

  list_t    *headers;

  content_type_t   *content_type;
  content_length_t *contentlength;
  mime_version_t   *mime_version;
  list_t    *bodies;

} sip_t;

Example 4-6. Method to manage "sip_t"

The oSIP stack only parses a minimal set of headers. Nevertheless, you still have access to other headers which are available as strings. (This is also a way to support headers not defined by the initial RFC)

int   msg_parse(sip_t *dest , char *message, err_t *err);
int   msg_init(sip_t **sip, err_t *err);
void  msg_free(sip_t *sip);

/* Those methods parse some specific headers.         */
int   msg_setheader(sip_t *sip, char *hname,
		    char *hvalue, err_t *err);
int   msg_setcall_id(sip_t *sip, char *hvalue, err_t *err);
int   msg_setcontact(sip_t *sip, char *hvalue, err_t *err);
int   msg_setcseq   (sip_t  *sip, char *hvalue, err_t *err);
int   msg_setfrom(sip_t *sip, char *hvalue, err_t *err);
int   msg_setto(sip_t *sip, char *hvalue, err_t *err);
int   msg_setvia(sip_t *sip, char *hvalue, err_t *err);
int   msg_setcontent_length(sip_t *sip, char *hvalue, err_t *);
int   msg_setcontent_type(sip_t *sip, char *hvalue,err_t *err);
int   msg_setmime_version(sip_t *sip, char *hvalue, err_t *err);
#define msg_setcontent_encoding(C, S, E) msg_setcontent_length(C, S, E) 

int   url_parse      (url_t *url,  char *buf, err_t *err);


/* The following macros can be used to access other headers   */
/* This list is not complete, but a MACRO exists for each SIP */
/* header (in osip/smsg.h). */
#define setaccept(P,R)    msg_setheader((sip_t *)P,(char *)"accept",R)
...
...
...

A powerful feature of the oSIP stack is that it provides an existing architecture for transactions. If the simple design does not fit your need, you can also reuse the previous methods and create your own design. This is the only case where those methods has to be made public.

url_parse(...) is the exception. You will often need to handle sip url in the application layer!

Other helpful MACROs are defined. They are used to test characteristics of messages such as the type of a message, the method of a request and the status code of a response.

Example 4-7. List of MACROs

#define MSG_IS_RESPONSE(resp)     (resp->strtline->statuscode!=NULL)
#define MSG_IS_REQUEST(req)       (req->strtline->statuscode==NULL)

#define MSG_IS_INVITE(msg)        (0==strncmp(msg->strtline->sipmethod,"INVITE",6))
#define MSG_IS_ACK(msg)           (0==strncmp(msg->strtline->sipmethod,"ACK",6))
#define MSG_IS_BYE(msg)           (0==strncmp(msg->strtline->sipmethod,"BYE",6))
#define MSG_IS_REGISTER(msg)      (0==strncmp(msg->strtline->sipmethod,"REGISTER",6))
#define MSG_IS_CANCEL(msg)        (0==strncmp(msg->strtline->sipmethod,"CANCEL",6))
#define MSG_IS_OPTIONS(msg)       (0==strncmp(msg->strtline->sipmethod,"OPTIONS",6))
#define MSG_IS_INFO(msg)          (0==strncmp(msg->strtline->sipmethod,"INFO",6))
#define MSG_IS_PRACK(msg)         (0==strncmp(msg->strtline->sipmethod,"PRACK",6))

#define MSG_IS_STATUS_1XX(msg)    (0==strncmp(msg->strtline->statuscode,"1",1))
#define MSG_IS_STATUS_2XX(msg)    (0==strncmp(msg->strtline->statuscode,"2",1))
#define MSG_IS_STATUS_3XX(msg)    (0==strncmp(msg->strtline->statuscode,"3",1))
#define MSG_IS_STATUS_4XX(msg)    (0==strncmp(msg->strtline->statuscode,"4",1))
#define MSG_IS_STATUS_5XX(msg)    (0==strncmp(msg->strtline->statuscode,"5",1))
#define MSG_IS_STATUS_6XX(msg)    (0==strncmp(msg->strtline->statuscode,"6",1))
#define MSG_TEST_CODE(resp, code) (resp->strtline->statuscode!=NULL \
				   && code==(int)satoi(resp->strtline->statuscode))
#define MSG_IS_RESPONSEFOR(resp,requestname) \
                                  (0==strcmp(resp->cseq->method,requestname))

The header and message API

oSIP defines structures for main headers: Via, From, To, Call-Id, Content-Length and CSeq. For each structure, get and set methods are available. Other useful methods simplify allocation and de-allocation of structures.

Example 4-8. header API

int   msg_init(sip_t **sip, err_t *err);
int   msg_free(sip_t *sip, err_t *err);
int   msg_parse(sip_t *sip, char *message, err_t *err);
int   msg_2char(sip_t *sip, char **dest, err_t *err);

int   header_init(header_t **header, err_t *err);
void  header_free(header_t *header);
int   header_parse(header_t *header,char *string, err_t *err);
int   header_2char(header_t *header,char **dest, err_t *err);

int   call_id_init     (callid_t **dest, err_t *err);
void  call_id_free     (callid_t *callid);
int   call_id_parse    (callid_t *callid, char *hvalue, err_t *err);
int   call_id_2char    (callid_t *callid, char **dest, err_t *err);
int   call_id_getnumber(callid_t *callid, char** dest);
int   call_id_gethost  (callid_t *callid, char** dest);
void  call_id_setnumber(callid_t *callid, char *number);
void  call_id_sethost  (callid_t *callid, char *host);

void  cseq_init     (cseq_t **dest, err_t *err);
void  cseq_free     (cseq_t *cseq);
char* cseq_2char    (cseq_t *cseq, char **dest, err_t *err);
int   cseq_parse    (cseq_t *cseq, char *hvalue, err_t *err);
char* cseq_getnumber(cseq_t *cseq, char **dest);
char* cseq_getmethod(cseq_t *cseq, char **dest);
void  cseq_setnumber(cseq_t *cseq, char *number);
void  cseq_setmethod(cseq_t *cseq, char *method);

int   from_init     (from_t **dest, err_t *err);
void  from_free     (from_t *from);
int   from_parse    (from_t *from, char *string, err_t *err);
int   from_2char    (from_t *from, char **dest, err_t *err);
int   from_compare  (from_t *from1, from_t *from2);

int   to_init(to_t **dest, err_t *err);
void  to_free(to_t *to);
int   to_parse(to_t *to, char *string, err_t *err);
int   to_2char(to_t *to, char **dest, err_t *err);

int   via_init(via_t **dest, err_t *err);
void  via_free(via_t *via);
int   via_parse(via_t *via, char *string, err_t *err);
int   via_2char(via_t *via, char **dest, err_t *err);

int   content_length_init (content_length_t **dest, err_t *err);
void  content_length_free (content_length_t *content_length);
int   content_length_parse(content_length_t *content_length,
			   char *string, err_t *err);
char* content_length_2char(content_length_t *content_length,
                           char **dest, err_t *err);

int   contact_init (contact_t **dest, err_t *err);
void  contact_free(contact_t *contact);
int   contact_parse(contact_t *contact, char *string, err_t *err);
char* contact_2char(contact_t *contact, err_t *err);

int   body_init(body_t *body, err_t *err);
void  body_free(body_t *body);
int   body_parse(body_t *body, char *string, err_t *err);
int   body_parse_mime(body_t *body, char *string, err_t *err);
char* body_2char(body_t *body, err_t *err);

/* this is a default method to create generic response      */
/* You SHOULD NOT use it. Creating your own is recommended  */
int   msg_makereply(int status, char *reason, sip_t *request,
                    sip_t *dest, char *body, char *content_type,
                    int options, err_t *err);

The parser may sometimes be too much tolerant. It will accept silently most of your mistakes. For interoperability needs, you should fill correctly each field.

To improve stack performance, you can configure the parser at link time by choosing which headers must ALWAYS be completely decoded. (It could also be possible at run time with minimal work!). You may find this useful in terms of performance when implementing proxies.

An more complete API reference for messages and headers processing is available in the ./doc directory of the distribution.


Sample code to create a message

Here is the description of a basic INVITE. Like for any other messages, it must contain a set of mandatory headers: To, From, Call-ID and CSeq. INVITE request also includes a body describing the caller's media description.

Example 4-9. Example for INVITE

sip_t *
msg_create_invite()
{
 /* allocation of the structure */
 sip_t  *msg;
 url_t  *uri;
 err_t  err;
 msg_init(&msg,&err);

 /* make a request line. */
 uri_init(&uri,&err);
 url_setscheme(url,sstrdup("sip"));
 url_setuserinfo(url,sstrdup("jack"));
 url_sethost(url,sstrdup("atosc.org"));

 sip_setmethod(msg,sstrdup("INVITE"));
 sip_setversion(msg,sstrdup("2.0"));
 sip_seturi(msg,uri);

 /* set main headers */
 {
  url_t    *url;
  to_t     *to;

  url_init(&url, &err);
  url_setscheme(url,sstrdup("sip"));
  url_setuserinfo(url,sstrdup("jack"));
  url_sethost(url,sstrdup("atosc.org"));
  url_setport(url,sstrdup("5060"));
  url_seturlparam(url,sstrdup("transport"),sstrdup("TCP"));
  url_seturlparam(url,sstrdup("ttl"),sstrdup("100"));
  url_seturlparam(url,sstrdup("maddr"),sstrdup("here.atosc.org"));
  url_setheader(url,sstrdup("subject"),sstrdup("oSIP stack"));

  to_init(&to, &err);
  to_seturl(to,url);
  to_setdisplayname(to,sstrdup("jack the stack"));
  to_settoparam(to,sstrdup("tag"),sstrdup("a48a"));

  sip_setto(msg, to);
 }

  /* the same API is available for the from_t structure */
 {
  from_t   *from;
  ...
  ...
  sip_setfrom(msg, from);
 }

 {
  via_t    *via;
  via_init(&via, &err);

  via_setprotocolname(via,sstrdup("SIP")); /* this is a default value */
  via_setsentprotocol(via,sstrdup("UDP")); /* this is a default value */
  via_setsentbyhost(via,sstrdup("137.137.137.137"));
  via_setsentbyport(via,sstrdup("5060"));  /* this is a default value */
  via_sethidden(via);                      /* default is not hidden   */
  via_setparam(via,sstrdup("branch"),sstrdup("a7c6a8dlze.1"));

  sip_setvia(msg, via);
 }

 {
  cseq_t   *cseq;
  cseq_init(&cseq, &err);
  ...

  sip_setcseq(msg, cseq);
 }

 {
  callid_t *callid;
  callid_init(&callid, &err);

  callid_settoken1(callid,sstrdup("f81d4"));
  callid_settoken2(callid,sstrdup("foo.atosc.org"));

  sip_setcallid(msg, callid);
 }

 /* this API can also be used, but it is much more time consuming! */
 /* msg_setto(msg,"sip:cha@anydomain.org", &err); */
 /* msg_setfrom(msg,"sip:jacK@atosc.org", &err); */
 /* msg_setcall_id(msg,"132485@foo.atosc.org", &err); */
 /* msg_setcseq(msg,"1 INVITE", &err); */
 /* msg_setvia(msg,"SIP/2.0/UDP 202.202.202.202:5080", &err); */

 msg_setcontact(msg,"sip:jacK@office.atosc.org", &err);

 /* Let's add some headers */
 msg_setheader(msg,sstrdup("SuBjecT"),sstrdup("Need support for oSIP!"), &err);

 /* add a body */
 sip_setbody(msg,"v=0\\o=user1 53655765 2353687637 IN IP4 128.3.4.5\r\ns=Mbone Audio\r\ni=Discussion of Mbone Engineering Issues\r\ne=mbone@somewhere.com\r\nc=IN IP4 128.3.4.5\r\nt=0 0\r\nm=audio 3456 RTP/AVP 0\r\na=rtpmap:0 PCMU/8000\r\n\r\n", &err);

 /* get the message as a string */
 {
  char *astring;
  astring = sip_2char(msg, &err);
  sfree(astring);
 }

 return msg;
 /* This is how you must free all memory */
 sip_free(sip);
 sfree(sip);
}

An API reference for headers is available in the ./doc directory of the distribution.


Chapter 5. The transaction manager

The finite state machines

oSIP contains 4 finite state machines. SIP defines 2 kinds of transactions. INVITE and other than INVITE transactions differs slightly. INVITE transactions must be followed by an ACK. Also, different rules apply for retransmission mechanism.

A finite state machine is an ideal way to implement the transaction layer. Threads are waiting for events. Events come from the user layer, transport layer or the timer management facility. The stack always provides information about the state of transaction to the application layer. This is done through a set of callbacks.

Example 5-1. How to use the finite state machine

Each user layer has access to its list of pending transactions (through a set of methods). Each context is stored in a transaction_t structure. The transaction_t structure has a attribute which is the FIFO address of the transaction.

/* method to send events */
transaction_t *trn_findid(list_t *transactions, int transactionid);
void fifo_add(fifo_t *fifo, void *element);

                                                    Transaction
          User Layer                                  contexts
        +------------+                             +-----------+
        |            |    events          +--------|           |
        |            |-----+------------->| FIFO 1 |     1     |
        +------------+     |              +--------|           |
                           |                       +-----------+
                           |
                           |                       +-----------+
                           |              +--------|           |
                           +------------->| FIFO N |     N     |
                                          +--------|           |
                                                   +-----------+

Sending events to control transaction

Here is a sample to initiate a transaction. The initial event sent to the FIFO contains the first INVITE request. The creation of the message is not shown here.

int
create_session()
{
  sip_t         *invite;
  transaction_t *transaction;

  /* You must create your own SIP message. */
  msg_init (&invite, &err);
  your_own_method_to_setup_messages(invite);

  /* When it is the first invite, allocate    */
  /* and start a new transaction (prepare the
  /* context and start a thread.)             */
  trn_init(&transaction,
	   myconfig,
	   invite->to,
	   invite->from,
	   invite->callid,
	   invite->cseq,
	   &err);

  /* The thread is now waiting on its fifo  */
  /* The following method allocate an event */
  /* and send it to the transaction.        */
  evt_sendmsg(transaction,invite);
}

int
evt_sendmsg(transaction_t *transaction,sip_t *msg)
{
    sipevent_t *sipevent;
    
    sipevent = evt_new_fromuser(msg);
    sipevent->transactionid =  transaction->transactionid;

    uaapp_annouceoutrequest(sipevent->sip,sipevent->transactionid);
    fifo_add(transaction->transactionff,sipevent);
}

How to use the callbacks

Caution

THIS IS NOT ALREADY TRUE IN RELEASE 0.6.0. THIS IS THE WAY I PLAN TO USE THE CALLBACKS IN THE NEXT RELEASE

Here is the set of callback that must be registred by user. Each user layer (described by one osip_t structure) can have its own callback methods. When user do not want to be aware of some kinds of events (retransmission...), he does not have to register his callback.

Example 5-2. Your application

At the beginning of your program and before you start using the SIP stack, you must build your own osip_t configuration. The osip_t structure is described below:

typedef struct _osip_t {

  /* this is the (udp) port where sipd is waiting */
  int udp_port;
  int tcp_port;

  list_t *transactions;
  /* One layer can handle several transactions */

  fifo_t *uas_timerff;
  pthread_t *uas_timerthread;
  fifo_t *uac_timerff;
  pthread_t *uac_timerthread;

  /* CAUTION: THIS IS NOT YET AVAILABLE IN RELEASE 0.6.0 */
  int  (*cb_connection_refused)(sipevent_t*, transaction_t *);
  int  (*cb_network_error)(sipevent_t*, transaction_t *);

  int  (*cb_inc_sndresp_retransmission(sipevent_t*, transaction_t *);
  int  (*cb_inc_rcvreq_retransmission(sipevent_t*, transaction_t *);
  int  (*cb_incoming_invite)(sipevent_t*, transaction_t *);
  int  (*cb_inc_ack)(sipevent_t*, transaction_t *);
  int  (*cb_inc_bye)(sipevent_t*, transaction_t *);
  int  (*cb_inc_cancel)(sipevent_t*, transaction_t *);
  int  (*cb_inc_info)(sipevent_t*, transaction_t *);
  int  (*cb_inc_options)(sipevent_t*, transaction_t *);
  int  (*cb_inc_register)(sipevent_t*, transaction_t *);
  int  (*cb_inc_prack)(sipevent_t*, transaction_t *);
  int  (*cb_inc_unknownreq)(sipevent_t*, transaction_t *);
  int  (*cb_inc_1xx)(sipevent_t*, transaction_t *);
  int  (*cb_inc_2xx)(sipevent_t*, transaction_t *);
  int  (*cb_inc_3xx)(sipevent_t*, transaction_t *);
  int  (*cb_inc_4xx)(sipevent_t*, transaction_t *);
  int  (*cb_inc_5xx)(sipevent_t*, transaction_t *);
  int  (*cb_inc_6xx)(sipevent_t*, transaction_t *);

  int  (*cb_outg_sndreq_retransmission)(transaction_t *);
  int  (*cb_outg_rcvresp_retransmission)(transaction_t *);
  int  (*cb_outg_invite)(sipevent_t*, transaction_t *);
  int  (*cb_outg_ack)(sipevent_t*, transaction_t *);
  int  (*cb_outg_bye)(sipevent_t*, transaction_t *);
  int  (*cb_outg_cancel)(sipevent_t*, transaction_t *);
  int  (*cb_outg_info)(sipevent_t*, transaction_t *);
  int  (*cb_outg_options)(sipevent_t*, transaction_t *);
  int  (*cb_outg_register)(sipevent_t*, transaction_t *);
  int  (*cb_outg_prack)(sipevent_t*, transaction_t *);
  int  (*cb_outg_unknownreq)(sipevent_t*, transaction_t *);
  int  (*cb_outg_1xx)(sipevent_t*, transaction_t *);
  int  (*cb_outg_2xx)(sipevent_t*, transaction_t *);
  int  (*cb_outg_3xx)(sipevent_t*, transaction_t *);
  int  (*cb_outg_4xx)(sipevent_t*, transaction_t *);
  int  (*cb_outg_5xx)(sipevent_t*, transaction_t *);
  int  (*cb_outg_6xx)(sipevent_t*, transaction_t *);

} osip_t;

An API is provided to register your callbacks. Note that most callbacks are mandatory and thus must be registred before using the stack.

Example 5-3. Callback registration API

osip_t *osip;
osip_init(&osip, &err);

/************/
/* UAS part */
/************/

/* register callbacks for new incoming requests */
/* Those callbacks are highly needed for UAS    */
osip_on_inc_invite    (osip, app_inc_invite);
osip_on_inc_ack       (osip, app_inc_ack);
osip_on_inc_bye       (osip, app_inc_bye
osip_on_inc_cancel    (osip, app_inc_cancel);
osip_on_inc_info      (osip, app_inc_info);
osip_on_inc_options   (osip, app_inc_options);
osip_on_inc_register  (osip, app_inc_register);
osip_on_inc_unknown   (osip, app_inc_unknown);

/* register callbacks for new outgoing response  */
/* Those callbacks may be useless in many cases. */
osip_on_outg_1xx       (osip, app_outg_1xx);
osip_on_outg_2xx       (osip, app_outg_2xx);
osip_on_outg_3xx       (osip, app_outg_3xx);
osip_on_outg_4xx       (osip, app_outg_4xx);
osip_on_outg_5xx       (osip, app_outg_5xx);
osip_on_outg_6xx       (osip, app_outg_6xx);

/* register callbacks to be aware of retransmissions */
/* Those callbacks may be useless in many cases.     */
osip_on_sndresp_retransmission(osip, app_sndresp_retransmission);
osip_on_rcvreq_retransmission (osip, app_rcvreq_retransmission);

/* register callback to be aware of network errors */
osip_on_connection_refused(osip, app_network_error);
osip_on_network_error(osip,app_network_error);

/************/
/* UAC part */
/************/
/* register callbacks for new incoming response */
/* those callbacks are mandatory.               */
osip_on_inc_1xx       (osip, app_inc_1xx);
osip_on_inc_2xx       (osip, app_inc_2xx);
osip_on_inc_3xx       (osip, app_inc_3xx);
osip_on_inc_4xx       (osip, app_inc_4xx);
osip_on_inc_5xx       (osip, app_inc_5xx);
osip_on_inc_6xx       (osip, app_inc_6xx);


/* register callbacks for new outgoing request  */
/* Those callbacks may be useless in many cases */
osip_on_outg_invite   (osip, app_outg_invite);
osip_on_outg_ack      (osip, app_outg_ack);
osip_on_outg_bye      (osip, app_outg_bye);
osip_on_outg_cancel   (osip, app_outg_cancel);
osip_on_outg_info     (osip, app_outg_info);
osip_on_outg_options  (osip, app_outg_options);
osip_on_outg_register (osip, app_outg_register);
osip_on_outg_unknown  (osip, app_outg_unknown);

/* register callbacks to be aware of retransmissions */
/* Those callbacks may be useless in many cases.     */
osip_on_rcvresp_retransmission(osip, app_rcvresp_retransmission);
osip_on_sndreq_retransmission (osip, app_sndreq_retransmission);

Example 5-4. Relations between interface and SIP behavior

The application controls the behavior of transactions by sending events to a FIFO (an event generally contains a SIP message). In the following example, the user has to allocate two SIP messages (INVITE and ACK) and transmits them to the stack. All responses are filtered by the finite state machines and the stack will start the appropriate actions. If the stack does not require any additional information to handle the event, the task is executed automatically and the application is informed through a callback.

         *add_event(INVITE)*   |           |
              ------------>    |           |  INVITE
                               |   STACK   |------------>
                               | internals |  INVITE retransmission
osip->cb_rcvresp_retransmission(e)         |------------>
              <------------    |           |
                               |           |  100 Trying
osip->cb_inc_1xx(evt)          |           |<------------
              <------------    |           |
                               |           |  180 Ringing
osip->cb_inc_1xx(evt)          |           |<------------
              <------------    |           |
                               |           |  200 OK
osip->cb_inc_2xx(evt)          |           |<------------
              <------------    |           |  200 OK retransmission
                               |           |<------------
     add_event(fifo,evt)       |           |
              ------------>    |           |  ACK
                               |           |------------>
                               |           |  200 OK
osip->cb_rcvresp_retransmission(e)         |<------------
              <------------    |           |
                               |           |  ACK
                               |           |------------>

osip->cb_*(sipevent_t *) are methods implemented by the application layer. They are used by the stack to announce states of transactions.


Notes for proxy implementation

The transaction layer provided for oSIP is usable by user agents, registrar, redirect server and conference server. A state machine for statefull proxy will be added in the future. The existing state machines are not easily usable for proxy implementation.

For stateless proxy, a state machine is very simple (No state is needed!). Nevertheless, A state machine will be implemented. its only role will be to cache results for a short time.


Chapter 6. Designing your own architecture

The stack currently imposes a multi-threaded architecture. This limits the possible number of transaction to the number of threads available. This design can sometimes be very useful. -If only one thread were used for all transactions, sometimes, it could be in a blocked state (IP address resolution) and the other transactions would be in a blocked state also.

Hopefully, the design may be changed to better fit your needs with a minimal work.

Example 6-1. The initial design

The file "transactionnal_design.c" contains a method called "sip_start_design1(int udpport, int tcpport, osip_t *config)". This methods starts the following list of threads:

a timer for UAC contexts
a timer for UAS contexts
a UDP listener for incoming SIP messages
a TCP listener for incoming SIP messages
N thread: one for each pending TRANSACTION.

When a message for a new INCOMING transaction is coming, the UDP/TCP thread launches a new thread dedicated to the new transaction. This new thread has a FIFO where all future events will be sent.

What is interesting is that every threads sharing the same memory space can add events in this FIFO. The timer threads will add TIMEOUT and KILL_TRANSACTION events. The UDP threads (or any other transport layer) will add RCV_* events. The user can control each transactions with the same facility. He can send SND_* events (containing outgoing requests) as shown in the previous sections.

Example 6-2. Multi-Threaded design provided by oSIP

This is the first available architecture you can use with oSIP

          User Layer
        +------------+
        |            |
        +------------+
            |        msg to send
            +--------------------+        Transaction
            Timers               |           layer
        +------------+           |
        |            |           |        +----------+
        +------------+           +------->| TRANSAC. |
            |        timeout evt |        |    1     |
            +--------------------+        +----------+
       Transport Layer           |
        +------------+           |        +----------+
        |            |           +------->| TRANSAC. |
        +------------+           |        |    2     |
            |      incoming msg  |        +----------+
            +--------------------+
        Your own Layer           |
        +------------+           |
        |            |           |
        +------------+           |
            |      incoming msg  |
            +--------------------+

Bibliography

SIP, Session Initiation Protocole - rfc2543.txt, ietf.

SDP, Session Description Protocole - rfc2543.txt, ietf.