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
A parser
A transaction manager
Who will benefit from oSIP
Available ports
4. Using the oSIP parser
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 medias 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. This part defines how to handle transactions, write message, negociate medias, set up, modify and terminate calls. Many services are provided, but SIP is design for extensibility and the transactionnal model can be reused (transparently for servers) to create some supplementary services. Many such drafts are in progress.


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 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.

Example 2-1. Description of SIP message in ABNF:

Here is the description of SIP messages and SIP request. It is far from complete and only shows request and response definitions.

        message-header  =  ( general-header
                           | request-header
                           | response-header
                           | entity-header )
        Request  =  Request-Line       ;  Section 4.1
                    *( general-header
                    | request-header
                    | entity-header )
                    CRLF
                    [ message-body ]   ;  Section 8
        Request-Line  =  Method SP Request-URI SP SIP-Version CRLF
        Request-URI   =  SIP-URL | absoluteURI
        SIP-Version   =  "SIP/2.0"
        Method            =  "INVITE" | "ACK" | "OPTIONS" | "BYE"
                             | "CANCEL" | "REGISTER" | extension-method
        extension-method  =  token

The syntax is borrowed from the MAIL and HTTP syntax. 6 types of requests are defined by SIP. The 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-2. 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-3. 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-4. 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.

SIP defines several levels of abstraction. oSIP hides the SIP syntax, hides the networking tasks under UDP (TCP is nearly available) and also hides the management of transactions. oSIP makes applications easier to build. As the oSIP stack is growing, you will be provided more and more facilities to handle sessions. A complete module to manage sessions might be provided in a near future.


A 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 Call-ID, To, From, Contact, CSeq, Content-Type, and Content-Length. All other headers are stored as string. Efforts will be done to support more headers.

Caution

Development level:

oSIP now supports utf8 and the full SIP grammar. The release 0.6.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.

By now, the SIP parser supports multiple attachments through the MIME format.


A transaction manager

Caution

THIS IS NOT ALREADY TRUE IN RELEASE 0.6.0

oSIP presents an easy to use interface. Transactions are modelled through finite state machines. This is the core of the oSIP library. It has been heavily tested and is already stable. Also, oSIP does not require much memory and runs fast. The current design limits the number of possible transactions to the number of threads available on the host. This design is profitable for SIP agent with few connections. Servers may need to use a fixed number of threads. New architecture without threads will also be available in the future for systems that don't support threads!

Example 3-1. 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.


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. 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 successfully 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) has been used for initial developments.


Chapter 4. Using the oSIP parser

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


The internal parser

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-1. 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-2. 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-3. 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-4. 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-5. 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);

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.