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

Copyright (c) 2001 Aymeric MOIZARD. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".


Table of Contents
1. The SIP protocol
SIP is independent of media
SIP is independent of the transport layer
SIP is extensible
SIP and end-user services
2. SIP overview
SIP syntax
SIP transactions
SIP sessions
Server behavior
3. The oSIP Library
The goals
The development level
The parser
The state machines
The parser
The 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
SIP messages
definition and purpose
API for SIP message
MACROs
The parser HOW-TO.
Initialise the parser
Allocate a structure
create a URL and a Request-URI.
Add headers in a message
make a string from a structure
free resource in a structure
How to improve the parser performance
5. The oSIP state machines
files
Transaction and events
Definition and purpose of transaction
Definition and purpose of sipevent
API
The callbacks.
Useful MACROs.
The finite state machines HOW-TO
Initialise the osip stack
Allocate and initialize an osip_t element
Send events to control transaction
Notes for proxy implementation
Build 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 traditional 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 negotiate media used within sessions. Any multimedia application (games, distance learning application) can use SIP to set up sessions.


SIP is independent of the transport 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 negotiate media, set up, modify and terminate calls. Many services are already provided this way but SIP is designed for extensibility and the transactional 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-transfer 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 personal 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 syntax and behavior.


SIP syntax

SIP is a text protocol based on utf8 encoding. (making it more readable and easier to debug) SIP describes a syntax for SIP requests, URL, 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 (see rfc2543.txt section 3), 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 at 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 mechanism. 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 understands the redirection and sends 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 negotiate 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 temporarily            .
          |<--------------|               .
          | 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 behavior

SIP defines behaviors 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 dependent 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 optional and the design of the application is entirely chosen by the end-developer. 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 new 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 analyzed by oSIP. oSIP cannot maintain reliability: (Nevertheless, oSIP remains fully SIP compliant)

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


The transaction manager

oSIP presents an easy to use interface. Transactions are modeled 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 registered by user allow the application to be informed of each transaction'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. This way, oSIP is independent from any transport layer. Then, you'll be required to build a module for timers management (a basic and 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 multimedia application. This modules will have to maintain and update the state of SIP sessions over transactions.

The transaction manager 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, HP unix 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.

-losipparser-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 following 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 resources 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 resources.

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 resources 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 resources 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_"

If you are interested in developing new headers, you just have to re-use the API proposed below. Contributions are welcomed...

As an example, 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);

on success, return 0.

to_free

Name

to_free -- free resources 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);

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. The following methods concern the "To" 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_param_add

Name

to_param_add -- add a gen_param in the to element.

Description

int to_param_add(to_t *to char *name char *value);

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

void to_gettag(to_t *to generic_param_t **dest);

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 generic_param_t **dest);

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);

on success, return 0.

generic_param_free

Name

generic_param_free -- free resources 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);

generic_param_getname

Name

generic_param_getname -- Get name from the gen_param element.

Description

char *generic_param_getname(generic_param_t *gen_param);

on error, return NULL.

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);

on error, return NULL.

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_add

Name

generic_param_add -- Allocate a new generic param with name and value and add the element in the list gen_params.

Description

int generic_param_add(list_t *gen_params char *name char *value);

on success, return 0.

generic_param_getbyname

Name

generic_param_getbyname -- find element containing name from the list gen_params and return it in gen_param .

Description

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

on success, return 0.

generic_param_freelist

Name

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

Description

void generic_param_freelist(list_t *gen_params);


SIP messages

definition and purpose

The messages are based at this level on three parts. The first line is either the request-uri for requests or the status code for responses. This first line corresponds to the first attribute of oSIP: startline_t. The second valuable part of a SIP message is a list of headers. The last attribute (list of body) is a possible list of attachments found in the message.

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

Example 4-5. type definition for sip_t.

typedef struct _sip_t {
  /* Start-Line definition */
  char *sipversion;
  /* req */
  osip_uri_t *rquri;
  char *sipmethod;
  /* resp */
  char *statuscode;
  char *reasonphrase;
  /* End of Start-Line definition */

  /* for all header fully implemented by oSIP */
  from_t           *from;
  to_t             *to;
  call_id_t        *call_id;
  cseq_t           *cseq;
  list_t           *vias;
  list_t           *contacts;
  list_t           *record_routes;
  list_t           *routes;
  content_type_t   *content_type;
  content_length_t *contentlength;
  mime_version_t   *mime_version;

  /* for all other headers */
  list_t           *headers;

  /* for all attachments */
  list_t           *bodies;

} sip_t;

API for SIP message

#include <smsg.h>

msg_init

Name

msg_init -- Allocate and initialize the structure msg

Description

int msg_init(sip_t **msg);

on success, return 0.

osip_message_free

Name

osip_message_free -- free resources contained in msg

Description

void osip_message_free(sip_t **msg);

msg_parse

Name

msg_parse -- Parse field_value and store results in msg.

Description

int msg_parse(sip_t *msg char *field_value);

on success, return 0.

msg_2char

Name

msg_2char -- Allocate a string in field_value with the information stored in the msg parameter.

Description

int msg_2char(sip_t *msg char **field_value);

on success, return 0.

msg_clone

Name

msg_clone -- duplicate the msg element in a new dest element.

Description

int msg_clone(sip_t *msg sip_t **dest);

on success, return 0.

msg_setheader

Name

msg_setheader -- Parse hvalue, allocate and add a new header in sip element.

Description

int msg_setheader(sip_t *sip char *hname char *hvalue);

on success, return 0.

msg_getheader

Name

msg_getheader -- set in dest the header at index pos from the sip element.

Description

int msg_getheader(sip_t *sip int pos header_t **header);

on success, return 0.

msg_header_getbyname

Name

msg_header_getbyname -- find the first header (after index pos) with hname from the list of header in the sip element.

Description

int msg_header_getbyname(char *hname sip_t *sip int pos header_t **dest);

on success, return 0.

msg_setcall_id

Name

msg_setcall_id -- Parse hvalue, allocate and set the call_id attribute of the sip element.

Description

int msg_setcall_id(sip_t *sip char *hvalue);

on success, return 0.

msg_getcall_id

Name

msg_getcall_id -- return the call_id attribute from the sip element.

Description

call_id_t *msg_getcall_id(sip_t *sip);

on success, return 0.

msg_setcseq

Name

msg_setcseq -- Parse hvalue, allocate and set the cseq attribute of the sip element.

Description

int msg_setcseq(sip_t *sip char *hvalue);

on success, return 0.

msg_getcseq

Name

msg_getcseq -- return the cseq attribute from the sip element.

Description

cseq_t *msg_getcseq(sip_t *sip);

on success, return 0.

msg_setcontact

Name

msg_setcontact -- Parse hvalue, allocate and add a new contact attribute in the sip element.

Description

int msg_setcontact(sip_t *sip char *hvalue);

on success, return 0.

msg_getcontact

Name

msg_getcontact -- set in dest the contact at index pos from the sip element.

Description

int msg_getcontact(sip_t *sip int pos contact_t **contact);

on success, return 0.

msg_setfrom

Name

msg_setfrom -- Parse hvalue, allocate and set the from attribute of the sip element.

Description

int msg_setfrom(sip_t *sip char *hvalue);

on success, return 0.

msg_getfrom

Name

msg_getfrom -- return the from attribute from the sip element.

Description

from_t *msg_getfrom(sip_t *sip);

on success, return 0.

msg_setto

Name

msg_setto -- Parse hvalue, allocate and set the to attribute of the sip element.

Description

int msg_setto(sip_t *sip char *hvalue);

on success, return 0.

msg_getto

Name

msg_getto -- return the to attribute from the sip element.

Description

to_t *msg_getto(sip_t *sip);

on success, return 0.

msg_setvia

Name

msg_setvia -- Parse hvalue, allocate and add a new via in sip element.

Description

int msg_setvia(sip_t *sip char *hvalue);

on success, return 0.

msg_getvia

Name

msg_getvia -- set in dest the via at index pos from the sip element.

Description

int msg_getvia(sip_t *sip int pos via_t **via);

on success, return 0.

msg_setrecord_route

Name

msg_setrecord_route -- Parse hvalue, allocate and add a new record_route in the sip element.

Description

int msg_setrecord_route(sip_t *sip char *hvalue);

on success, return 0.

msg_getrecord_route

Name

msg_getrecord_route -- set in dest the record_route at index pos from the sip element.

Description

int msg_getrecord_route(sip_t *sip int pos record_route_t **record_route);

on success, return 0.

msg_setroute

Name

msg_setroute -- Parse hvalue, allocate and add a new route in the sip element.

Description

int msg_setroute(sip_t *sip char *hvalue);

on success, return 0.

msg_getroute

Name

msg_getroute -- set in dest the route at index pos from the sip element.

Description

int msg_getroute(sip_t *sip int pos route_t **route);

on success, return 0.

msg_setcontent_length

Name

msg_setcontent_length -- Parse hvalue, allocate and set the content_length attribute of the sip element.

Description

int msg_setcontent_length(sip_t *sip char *hvalue);

on success, return 0.

msg_getcontent_length

Name

msg_getcontent_length -- return the content_length attribute from the sip element.

Description

content_length_t * msg_getcontent_length(sip_t *sip);

msg_setcontent_type

Name

msg_setcontent_type -- Parse hvalue, allocate and set the content_type attribute of the sip element.

Description

int msg_setcontent_type(sip_t *sip char *hvalue);

on success, return 0.

msg_getcontent_type

Name

msg_getcontent_type -- return the content_type attribute from the sip element.

Description

content_type_t * msg_getcontent_type(sip_t *sip);

msg_setmime_version

Name

msg_setmime_version -- Parse hvalue, allocate and set the mime_version attribute of the sip element.

Description

int msg_setmime_version(sip_t *sip char *hvalue);

on success, return 0.

msg_getmime_version

Name

msg_getmime_version -- return the mime_version attribute from the sip element.

Description

mime_version_t * msg_getmime_version(sip_t *sip);


MACROs

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-6. List of MACROs

/* a few MACROS to ease readability of code */
#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 parser HOW-TO.

Building a message is not a complex task and does not require to be a C expert. Nevertheless, it requires you to be a SIP expert! The document describing SIP (rfc2543.txt) provides all you need to build correct requests and response. You will have to know which header MUST be included ("To", "From", "Call-ID", "Cseq" are the most common...), which header MUST NOT be included, and which header MAY appear in the message.


Initialise the parser

The oSIP parser need to be initialised at run time. This is done by first calling the following method before using the oSIP parser. (This method MUST only be called once.)

int parser_init();


Allocate a structure

oSIP has a standard approach to request allocation of structures. For each SIP related one, (like sip_t, or from_t) a method "xxxx_init()" exists and MUST be used to request allocation. Using this method with the correspondent "xxxx_free()" allows a competitive memory management thus avoiding as many memory leaks as possible.

Example 4-7. allocation of sip_t and url_t.

 sip_t  *msg;
 msg_init(&msg);

 osip_message_free(msg);
 free(msg);

 url_t *url;
 url_init(&url);

 url_free(url);
 free(url);

create a URL and a Request-URI.

The following code is used to build a request line for an INVITE message. This line will be: "INVITE sip:jack@atosc.org SIP/2.0".

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

 msg_setmethod(msg,sstrdup("INVITE"));
 msg_seturi(msg,uri);
 msg_setversion(msg,sstrdup("2.0"));

Add headers in a message

This example shows the code required to allocate headers and add each of them in the sip_t structure. You should take a look at the RFC when building messages.

For INVITE requests,the following headers are mandatory:

Via
CSeq
Call-Id
To
From
Contact
Content-length
Content-Type (if a body is attached)

Example 4-8. Add header

 {
  url_t    *url;
  to_t     *to;
  url_init(&url);
  url_setusername(url,sstrdup("jack"));
  url_sethost(url,sstrdup("atosc.org"));

  to_init(&to);
  to_seturl(to,url);
  to_setdisplayname(to,sstrdup("jack..."));

  msg_setto(msg, to);
 }

  /* the same API is available for the from_t structure */
 {
  from_t   *from;
  /* allocate a url_t */
  url_init(&url);
  url_setusername(url,sstrdup("cha"));
  url_sethost(url,sstrdup("anywhere.org"));

  /* allocate a from_t */
  from_init(&from);
  from_seturl(from,url);
  from_setdisplayname(from,sstrdup("My love"));
  from_set_tag(from,sstrdup("a48a"));

  msg_setfrom(msg, from);
 }

 {
  via_t    *via;
  via_init(&via);

  via_setversion(via,sstrdup("2.0"));
  via_setprotocol(via,sstrdup("UDP"));
  via_sethost(via,sstrdup("137.137.137.137"));
  via_set_branch(via,sstrdup("branch"),sstrdup("a7c6a8dlze.1"));

  msg_setvia(msg, via);
 }

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

  msg_setcseq(msg, cseq);
 }

 {
  callid_t *callid;
  callid_init(&callid);

  callid_setnumber(callid,sstrdup("f81d4"));
  callid_sethost(callid,sstrdup("foo.atosc.org"));

  msg_setcallid(msg, callid);
 }

 /* this API can also be used, but it is much more time consuming! */
 msg_setcontact(msg,"sip:jacK@office.atosc.org");

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

 /* add a body */
 msg_setbody(msg,"v=0\r\no=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");

make a string from a structure

For sip_t like for the structure used for the headers (to_t, from_t, call_id_t...), a method exist to retrieve printed version of the element.

Example 4-9. Method xxxx_2char

  char *astring;
  astring = sip_2char(msg);
  free(astring);

free resource in a structure

Example 4-10. Method xxxx_free

This is how you must free all memory.

 sip_free(msg);
 free(msg);

The parser may sometimes be too much tolerant. It will accept silently some of your mistakes. As an example, the parser should accept a display name (for a "To" header) with a comma inside. The developer MUST enclose the string between quotes. To keep the application interoperable, please always keep an eye on the rfc and control every characters you put in SIP message.


How to improve the parser performance

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.


Chapter 5. The oSIP state machines

files

./libosip-x.x.x/fsm is the source directory for the SIP finite state machines.

-losipfsm-x.x.x is the library containing the SIP finite state machines

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


Transaction and events

Definition and purpose of transaction

Transactions are used to store the context of one "SIP transactions" as defined in the rfc2543. oSIP uses the transaction_t internally so you probably don't need to understand each attribute.

Example 5-1. oSIP model for transactions

Managing a transaction consists in building modules that can send events to control the transaction

       User module
        +------------+
        |            |
        +------------+
            |        msg to send
            +--------------------+        Transaction
       Timer module              |         contexts
        +------------+           |
        |            |           |        +----------+
        +------------+           +------->| TRANSAC. |
            |        timeout evt |        |    1     |
            +--------------------+        +----------+
       Transport module          |
        +------------+           |        +----------+
        |            |           +------->| TRANSAC. |
        +------------+           |        |    2     |
            |      incoming msg  |        +----------+
            +--------------------+

The attribute your_instance is not used by oSIP. You can use it to store a pointer on your own context.

You can use the transactionid as a unique id for the transaction.

transactionff is a FIFO used to receive all events (sipevent_t) which concern this transaction.

The proxy attribute give the host and port to be used for requests. (all requests will be forced to go to the proxy whatever the request-uri contains.

Example 5-2. type definition of transaction_t.

typedef struct _transaction_t {
  void *your_instance;       /* add whatever you want here.       */
  int transactionid;         /* simple id used to identify the tr.*/
  fifo_t *transactionff;     /* events must be added in this FIFO */
  url_t *proxy;              /* url used to send requests        */    

  from_t    *from;           /* CALL-LEG definition */
  to_t      *to;
  call_id_t *callid;
  cseq_t    *cseq;
  sip_t *lastrequest;        /* last request received or sent     */
  sip_t *lastresponse;       /* last response received or sent    */

  state_t state;             /* state of transaction              */
  statemachine_t *statemachine; /* state-machine of transaction   */

  time_t birth_time;         /* birth_date of transaction         */
  time_t completed_time;     /* end   date of transaction         */
  int retransmissioncounter; /* facilities for internal timer     */

  void *config;              /* transaction is managed by config */
                             /* of type osip_t */
} transaction_t;

Definition and purpose of sipevent

sipevents objects are used to control the context of one "SIP transaction" element. There is 3 types of SIP events for the finite state machines:

incoming SIP messages. (requests and responses)
outgoing SIP messages. (requests and responses)
timers events. (retransmissions and context removal)

The attribute type always contains the type of the event.

transactionid is now for internal use.

When the event is a SIP message, sip is the parsed message.

Example 5-3. type definition of sipevent_t.

typedef struct _sipevent_t {
  type_t type;
  int transactionid;
  sip_t *sip;
} sipevent_t;

API

#include <osip.h>

osip_init

Name

osip_init -- Allocate and initialize the osip element.

Description

int osip_init(osip_t **osip);

on success, return 0.

osip_free

Name

osip_free -- free resources contained in osip

Description

void osip_free(osip_t *osip);

osip_init_proxy

Name

osip_init_proxy -- set the default proxy to be used for all transactions related to osip.

Description

void osip_init_proxy(osip_t *osip url_t *url);

osip_execute

Name

osip_execute -- consumes all SIP events from the list of transactions contained in osip. (This method is only designed for NON-MULTI THREADED application.)

Description

int osip_execute(osip_t *osip);

osip_distribute_event

Name

osip_distribute_event -- distribute a SIP event (message) in the right FIFO of osip This method has to be used for incoming message.

Description

transaction_t *osip_distribute_event(osip_t *osip sipevent_t *se);

osip_parse

Name

osip_parse -- parse buf and create an SIP incoming message event.

Description

sipevent_t *osip_parse(char *buf);

osip_new_event

Name

osip_new_event -- create a new SIP event with attributes type and transactionid. (This method is used by the timer module)

Description

sipevent_t *osip_new_event(type_t type int transactionid);

osip_new_incoming_event

Name

osip_new_incoming_event -- create a new SIP event for an incoming SIP message.

Description

sipevent_t *osip_new_incoming_event(sip_t *sip);

osip_new_outgoing_event

Name

osip_new_outgoing_event -- create a new SIP event for an outgoing SIP message.

Description

sipevent_t *osip_new_outgoing_event(sip_t *sip);

osip_find_asincomingmessage

Name

osip_find_asincomingmessage -- find (or create) the transaction related to the SIP incoming event.

Description

transaction_t *osip_find_asincomingmessage(osip_t *osip sipevent_t *sipevent);

osip_find_asoutgoingmessage

Name

osip_find_asoutgoingmessage -- find the transaction related to the SIP outgoing event.

Description

transaction_t *osip_find_asoutgoingmessage(osip_t *osip sipevent_t *sipevent);

osip_find_byid

Name

osip_find_byid -- find the transaction with transactionid attribute.

Description

transaction_t *osip_find_byid(osip_t *osip int transactionid);

osip_remove_byid

Name

osip_remove_byid -- remove the transaction with transactionid attribute.

Description

int osip_remove_byid(osip_t *osip int transactionid);

transaction_init

Name

transaction_init -- Allocate and initialize the transaction element.

Description

int transaction_init(transaction_t **transaction);

transaction_free

Name

transaction_free -- free resources contained in transaction

Description

void transaction_free(transaction_t *transaction);

transaction_execute

Name

transaction_execute -- consume sipevent in the context of transaction.

Description

int transaction_execute(transaction_t *transaction);


The callbacks.

oSIP uses a set of callbacks to announce the evolution of SIP transactions. For example, when receiving an INVITE request, the method registered by "osip_setcb_rcvinvite(...)" will be called by the stack and used by the developer to create a provisional response and start actions to build a final one.

The following methods allow you to register, on initialization of the osip stack, the methods that will be used to handle transactions. Some of those methods are mandatory (announcements of requests, and final responses) and some are optional (announcements of provisional responses and retransmissions).

void osip_setcb_rcvinvite  (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvack     (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvbye     (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvcancel  (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvinfo    (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvoptions (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvregister(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvprack   (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcvunkrequest(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));

void osip_setcb_sndinvite  (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndack     (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndbye     (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndcancel  (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndinfo    (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndoptions (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndregister(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndprack   (osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_sndunkrequest(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));

void osip_setcb_rcv1xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcv2xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcv3xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcv4xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcv5xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_rcv6xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));

void osip_setcb_snd1xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_snd2xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_snd3xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_snd4xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_snd5xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));
void osip_setcb_snd6xx(osip_t *cf,void (*cb)(sipevent_t*,transaction_t*));

void osip_setcb_rcvresp_retransmission(osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_sndreq_retransmission (osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_sndresp_retransmission(osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_rcvreq_retransmission (osip_t *cf,void (*cb)(transaction_t*));

void osip_setcb_killtransaction   (osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_endoftransaction  (osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_connection_refused(osip_t *cf,void (*cb)(transaction_t*));
void osip_setcb_network_error     (osip_t *cf,void (*cb)(transaction_t*));

Useful MACROs.

You can also use the following list of MACROs. It is recommended to use them if you want your application to keep full compatibility over oSIP releases.

/* FOR INCOMING TRANSACTION */
#define EVT_IS_RCV_INVITE(event)       (event->type==RCV_REQINVITE)
#define EVT_IS_RCV_ACK(event)          (event->type==RCV_REQACK)
#define EVT_IS_RCV_REQUEST(event)      (event->type==RCV_REQUEST)
#define EVT_IS_RCV_STATUS_1XX(event)   (event->type==RCV_STATUS_1XX)
#define EVT_IS_RCV_STATUS_23456XX(event)   (event->type==RCV_STATUS_23456XX)

/* FOR OUTGOING TRANSACTION */
#define EVT_IS_SND_INVITE(event)       (event->type==SND_REQINVITE)
#define EVT_IS_SND_ACK(event)          (event->type==SND_REQACK)
#define EVT_IS_SND_REQUEST(event)      (event->type==SND_REQUEST)
#define EVT_IS_SND_STATUS_1XX(event)   (event->type==SND_STATUS_1XX)
#define EVT_IS_SND_STATUS_23456XX(event)   (event->type==SND_STATUS_23456XX)

#define EVT_IS_INCOMINGMSG(event)      (event->type>=RCV_REQINVITE \
                	               &&event->type<=RCV_STATUS_23456XX)
#define EVT_IS_INCOMINGREQ(event)      (EVT_IS_RCV_INVITE(event) \
                                       ||EVT_IS_RCV_ACK(event) \
                                       ||EVT_IS_RCV_REQUEST(event))
#define EVT_IS_INCOMINGRESP(event)     (EVT_IS_RCV_STATUS_1XX(event) \
                                       ||EVT_IS_RCV_STATUS_23456XX(event))
#define EVT_IS_OUTGOINGMSG(event)      (event->type>=SND_REQINVITE \
                	               &&event->type<=SND_STATUS_23456XX)
#define EVT_IS_OUTGOINGREQ(event)      (EVT_IS_SND_INVITE(event) \
                                       ||EVT_IS_SND_ACK(event) \
                                       ||EVT_IS_SND_REQUEST(event))
#define EVT_IS_OUTGOINGRESP(event)     (EVT_IS_SND_STATUS_1XX(event) \
                                       ||EVT_IS_SND_STATUS_23456XX(event))
#define EVT_IS_MSG(event)              (event->type>=RCV_REQINVITE \
                	               &&event->type<=SND_STATUS_23456XX)
#define EVT_IS_KILL_TRANSACTION(event) (event->type==KILL_TRANSACTION)
#define EVT_IS_UNKNOWN_EVT(event)      (event->type==UNKNOWN_EVT)
#define EVT_IS_TIMEOUT(event)          (event->type==TIMEOUT)

The finite state machines HOW-TO

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 which is part of the state machine for INVITE. 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 the transaction to the application layer. This is done through the set of dynamic callbacks.

Example 5-4. SIP events are distributed to fifo.

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


Initialise the osip stack

The oSIP stack needs to be initialised at run time. This is done by first calling the following method before using the oSIP stack. (This method MUST only be called once). Calling this method also initialise the parser.

int osip_global_init();


Allocate and initialize an osip_t element

The osip_t structure is a container for a list of transactions. This element represents an instance of a SIP agent. osip_t can be used for UAS/UAC, registrar and redirect server.

The following code is used to build a complete osip_t element. First, you have to register a set of mandatory and optional callbacks. You can also configure the stack to use a proxy for all outgoing requests.

  osip_t  *osip;

  if (-1==osip_global_init())
    return -1; /* mutex is not initialized properly */

  osip_init(&osip);
  osip_init_proxy(osip,url_of_proxy);

  osip_setcb_rcvresp_retransmission(cf,&cb_rcvresp_retransmission);
  osip_setcb_sndreq_retransmission(cf,&cb_sndreq_retransmission);
  osip_setcb_sndresp_retransmission(cf,&cb_sndresp_retransmission);
  osip_setcb_rcvreq_retransmission(cf,&cb_rcvreq_retransmission);
  
  osip_setcb_killtransaction(cf,&cb_killtransaction);
  osip_setcb_endoftransaction(cf,&cb_endoftransaction);
  
  osip_setcb_connection_refused(cf,&cb_connection_refused);
  osip_setcb_network_error(cf,&cb_network_error);
  
  osip_setcb_rcv1xx(cf,&cb_rcv1xx);
  osip_setcb_rcv2xx(cf,&cb_rcv2xx);
  osip_setcb_rcv3xx(cf,&cb_rcv3xx);
  osip_setcb_rcv4xx(cf,&cb_rcv4xx);
  osip_setcb_rcv5xx(cf,&cb_rcv5xx);
  osip_setcb_rcv6xx(cf,&cb_rcv6xx);
  
  osip_setcb_sndinvite  (cf,&cb_sndinvite);
  osip_setcb_sndack     (cf,&cb_sndack);
  osip_setcb_sndbye     (cf,&cb_sndbye);
  osip_setcb_sndcancel  (cf,&cb_sndcancel);
  osip_setcb_sndinfo    (cf,&cb_sndinfo);
  osip_setcb_sndoptions (cf,&cb_sndoptions);
  osip_setcb_sndregister(cf,&cb_sndregister);
  osip_setcb_sndprack   (cf,&cb_sndprack);
  osip_setcb_sndunkrequest(cf,&cb_sndunkrequest);

  osip_setcb_snd1xx(cf,&cb_snd1xx);
  osip_setcb_snd2xx(cf,&cb_snd2xx);
  osip_setcb_snd3xx(cf,&cb_snd3xx);
  osip_setcb_snd4xx(cf,&cb_snd4xx);
  osip_setcb_snd5xx(cf,&cb_snd5xx);
  osip_setcb_snd6xx(cf,&cb_snd6xx);

  osip_setcb_rcvinvite  (cf,&cb_rcvinvite);
  osip_setcb_rcvack     (cf,&cb_rcvack);
  osip_setcb_rcvbye     (cf,&cb_rcvbye);
  osip_setcb_rcvcancel  (cf,&cb_rcvcancel);
  osip_setcb_rcvinfo    (cf,&cb_rcvinfo);
  osip_setcb_rcvoptions (cf,&cb_rcvoptions);
  osip_setcb_rcvregister(cf,&cb_rcvregister);
  osip_setcb_rcvprack   (cf,&cb_rcvprack);
  osip_setcb_rcvunkrequest(cf,&cb_rcvunkrequest);

Send 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(osip_t *osip)
{
  sip_t         *invite;
  transaction_t *transaction;

  /* You must create your own SIP message. */
  msg_init (&invite);
  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,
	   osip,
	   invite->to,
	   invite->from,
	   invite->callid,
	   invite->cseq);

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

int
osip_sendmsg(transaction_t *transaction,sip_t *msg)
{
    sipevent_t *sipevent;

    sipevent = osip_new_outgoing_sipmessage(msg);
    sipevent->transactionid =  transaction->transactionid;

/* depending on your application model, you can choose */
/* either to consume the event are add it in the fifo  */
/* and delay the operations */

#ifdef OSIP_MT
    fifo_add(transaction->transactionff,sipevent);
#else
    transaction_execute(transaction,sipevent);
#endif

    return 0;
}

Notes for proxy implementation

The transaction layer provided for oSIP is usable by user agents, registrar, redirect server and some type of 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.


Build your own architecture

Since release 0.7.0, the stack can be used without semaphore and thread support. The design is completely up to you. You have to choose a design that fit the type of application. Here are the different aspect that you need to take care of:

choose which transport protocol you want (UDP is recommended)
use one socket for all transactions or one per each.
choose a multi thread design or not.
choose how to start timers.

By now, UDP is recommended for SIP for servers as well as other user agents. When implementing a server, you may face many parallel transactions. If you use one connection per each transaction, the application may run quickly out of resources. (Also, you can't always guess if other applications on the same computer require many sockets too). For servers, you may also take care when using a multi threaded architecture with one thread per transactions. For a highly used server, you may quickly run out of thread resources. (Specifically, the standard kernel from debian support a low number of threads).

If your application wants to access a database and make IP address resolution, this could leave your application in a blocked state for a while. In this case, a multi threaded application could be very useful and avoid some issues. It worth to say that performance of your application will heavily rely on how you will deal with those issue.


Bibliography

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

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