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."
Copyright (c) 2001,2002,2003,2004,2005 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".
SIP is a signaling protocol used to initiate and control multimedia sessions. It is already published by IETF (www.ietf.org) as the rfc3261.
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...
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 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.
The rfc3261 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 transparently supports name mapping and redirection services, allowing the implementation of ISDN and Intelligent Network telephony subscriber services. These facilities also enable personal mobility."
rfc3261.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.
This section does not intend to fully describe the RFC. It is a fast and incomplete overview of the protocol syntax and behavior.
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 in the rfc3261 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 rfc3261.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;branch=z9hG4bK776asdhds Max-Forwards: 70 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
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.
Since the rfc3261, The "branch" parameter from the "Via" header provides a unique string to match requests and responses within a transactions.
As SIP often use unreliable transport protocol (UDP is recommended but TCP or TLS can be prefered), 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 |----------------->| call | | | 100 Trying | |<-----------------| Bob's | 180 Ringing| Phone starts |<-----------------| ringing | | | | | 200 OK | |<-----------------| | ACK | |----------------->|
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 are used to redirect calls. 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 (office.home.net) UA1 PROXY UA2 | REGISTER | . |-------------->| . | 200 OK | . |<--------------| . . . . later... . . . . . . . | REGISTER | . |<--------------| . | 200 OK | . |-------------->| . . . later... . . . | INVITE jack@home.net . |-------------->| . |302 Moved temporarily . |<--------------| . | ACK | . |-------------->| . | INVITE jack@office.home.net | | audio | |------------------------------>| | 100 Trying | |<------------------------------| | 180 Ringing | |<------------------------------| | 200 OK | |<------------------------------| | ACK jack@office.home.net | |------------------------------>| . . later . . . . | INVITE bob@bob.home.net | | audio + video | |<------------------------------| | 200 OK | |------------------------------>| | ACK bob@bob.home.org | |<------------------------------| . . later . . . . | BYE jack@office.home.net | |------------------------------>| | 200 OK | |<------------------------------| | |
SIP defines behaviors for proxy 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,...)
Having a good knowledge of SIP is recommended for this section. If you plan to use SIP, you should read more carefully the rfc3261 from ietf.org.
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.
Around 2002, the API has been totally refined and osip2 was first release. The code and API is now much more readable and heavy work has been previously done to update from rfc3543 to rfc3261.
oSIP should be provide a fully compliant SIP parser. If you ever see something missing or not correct, please report bugs to <osip@atosc.org>, I'll fix them.
To get more information, see next sectionThe 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.
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 so that you can access it with the API for generic headers.
By now, the SIP parser supports multiple attachments through the MIME format. (this part has not been tested a lot, reports bugs)
oSIP may alter Incoming SIP messages when messages contains: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 |
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. First, you'll have to build a module to manage the transport protocole (send and rcv methods). This way, oSIP is independent from any transport layer. Then, you'll be required to build a module for transaction management where a few methods of osip must be regularly called so that osip can run the finite state machines events. 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!
oSIP offers a low level interface to build any SIP applications (proxy, Endpoint, Server or gateway). Also, oSIP is not tight to any kind of OSs. If you want to implement a proxy, please read "notes for proxy implementation" in the chapter called "the transaction manager".
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.
./libosip-x.x.x/src/osipparser2
-losipparser2
#include <osipparser2/osip_parser.h>
URL are used to describe entirely a SIP entity: user, registrar, proxy have their own urls. oSIP uses the osip_uri_t type definition in the request-uri and in the following headers: "to", "from", "contact", "route", and "record-route".
type definition of osip_uri_t.
typedef struct osip_uri { char *scheme; char *username; char *password; char *host; char *port; list_t *url_params; list_t *url_headers; } osip_uri_t ;
#include <osipparser2/osip_uri.h>
osip_uri_param_t and osip_uri_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 "_param_" with "_header_".
Example 4-1. type definitions of osip_uri_param_t and header_param_t.
typedef struct osip_uri_param { char *pname; char *pvalue; } osip_uri_param_t; typedef osip_uri_param_t osip_uri_header_t;
void osip_uri_param_set
(osip_uri_param_t *osip_uri_param,
char *pname,
char *pvalue);
on success, return 0.
void osip_uri_param_add
(list_t *osip_uri_params,
char *pname,
char *pvalue);
on success, return 0.
The rfc3261 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 osip_to_t { char *displayname; osip_uri_t *url; list_t *gen_params; } osip_to_t;
The SIP parser can parse entirely a few headers such as: Via, To, From, CSeq, Call-Id, Contact, Route, Record-Route, Content-Type, Content-length, Mime-Version (see API for a complete list of headers API). The other headers are accessible as strings through a special API.
Important: This API presented below is applicable for all known headers: Simply replace "osip_to_" by one of the following list: "osip_via_", "osip_from_", "osip_contact", "osip_route_", "osip_record_route_", "osip_content_type_", "osip_content_length_", "osip_mime_version_", "osip_cseq_", "osip_call_id_" (see API for a complete list of headers API)
As an example, the following methods concern the API to manipulate a osip_to_t structure.
#include <osipparser2/osip_parser.h>
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.
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 _osip_to_t { char *displayname; osip_uri_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
int generic_param_get_byname
(list_t *gen_params
char *name
generic_param_t **gen_param);
on success, return 0.
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 osip_message { char *sip_version; /**< SIP version (SIP request only) */ osip_uri_t *req_uri; /**< Request-Uri (SIP request only) */ char *sip_method; /**< METHOD (SIP request only) */ int status_code; /**< Status Code (SIP answer only) */ char *reason_phrase; /**< Reason Phrase (SIP answer only) */ osip_list_t *accepts; /**< Accept headers */ osip_list_t *accept_encodings; /**< Accept-Encoding headers */ osip_list_t *accept_languages; /**< Accept-Language headers */ osip_list_t *alert_infos; /**< Alert-Info headers */ osip_list_t *allows; /**< Allows headers */ osip_list_t *authentication_infos;/**< authentication_info headers */ osip_list_t *authorizations; /**< Authorizations headers */ osip_call_id_t *call_id; /**< Call-ID header */ osip_list_t *call_infos; /**< Call-Infos header */ osip_list_t *contacts; /**< Contacts headers */ osip_list_t *content_dispositions;/**< Content-Dispositions headers */ osip_list_t *content_encodings; /**< Content-Encodings headers */ osip_content_length_t *content_length; /**< Content-Length header */ osip_content_type_t *content_type; /**< Content-Type header */ osip_cseq_t *cseq; /**< CSeq header */ osip_list_t *error_infos; /**< Error-Info headers */ osip_from_t *from; /**< From header */ osip_mime_version_t *mime_version;/**< Mime-Version header */ osip_list_t *proxy_authenticates; /**< Proxy-Authenticate headers */ osip_list_t *proxy_authentication_infos; /**< P-Authentication-Info headers */ osip_list_t *proxy_authorizations;/**< Proxy-authorization headers */ osip_list_t *record_routes; /**< Record-Route headers */ osip_list_t *routes; /**< Route headers */ osip_to_t *to; /**< To header */ osip_list_t *vias; /**< Vias headers */ osip_list_t *www_authenticates; /**< WWW-Authenticate headers */ osip_list_t *headers; /**< Other headers */ osip_list_t *bodies; /**< List of attachements */ /* 1: structure and buffer "message" are identical. 2: buffer "message" is not up to date with the structure info (call osip_message_to_str to update it). */ int message_property; /**@internal */ char *message; /**@internal */ size_t message_length; /**@internal */ void *application_data; /**can be used by upper layer*/ } osip_message_t;
#include <osipparser2/osip_parser.h>
int osip_message_set_header
(osip_message_t *sip,
char *hname,
char *hvalue);
on success, return 0.
int osip_message_get_header
(osip_message_t *sip,
int pos,
header_t **header);
on success, return 0.
int osip_message_header_get_byname
(char *hname,
osip_message_t *sip,
int pos,
header_t **dest);
on success, return 0.
int osip_message_get_contact
(osip_message_t *sip,
int pos,
contact_t **contact);
on success, return 0.
int osip_message_get_via
(osip_message_t *sip,
int pos,
osip_via_t **via);
on success, return 0.
int osip_message_set_record_route
(osip_message_t *sip,
char *hvalue);
on success, return 0.
int osip_message_get_record_route
(osip_message_t *sip,
int pos,
record_route_t **record_route);
on success, return 0.
int osip_message_get_route
(osip_message_t *sip,
int pos,
route_t **route);
on success, return 0.
int osip_message_set_content_length
(osip_message_t *sip,
char *hvalue);
on success, return 0.
int osip_message_set_content_type
(osip_message_t *sip,
char *hvalue);
on success, return 0.
int osip_message_set_mime_version
(osip_message_t *sip,
char *hvalue);
on success, return 0.
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 */ MSG_IS_RESPONSE(resp) MSG_IS_REQUEST(req) MSG_IS_INVITE(msg) MSG_IS_ACK(msg) MSG_IS_BYE(msg) MSG_IS_REGISTER(msg) MSG_IS_CANCEL(msg) MSG_IS_OPTIONS(msg) MSG_IS_INFO(msg) MSG_IS_PRACK(msg) MSG_IS_STATUS_1XX(msg) MSG_IS_STATUS_2XX(msg) MSG_IS_STATUS_3XX(msg) MSG_IS_STATUS_4XX(msg) MSG_IS_STATUS_5XX(msg) MSG_IS_STATUS_6XX(msg) MSG_TEST_CODE(resp, code) MSG_IS_RESPONSEFOR(resp,requestname)
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 (rfc3261.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.
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();
oSIP has a standard approach to request allocation of structures. For each SIP related one, (like osip_message_t, or osip_from_t) a method "osip_xxxx_init()" exists and MUST be used to request allocation. It must be used along with "osip_xxxx_free()".
Example 4-7. allocation of osip_message_t and osip_uri_t.
osip_message_t *msg; osip_message_init(&msg); osip_message_free(msg); osip_uri_t *url; osip_uri_init(&url); osip_uri_free(url);
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. */ osip_uri_t *uri; osip_uri_init(&uri); osip_uri_set_scheme(url,osip_strdup("sip")); osip_uri_set_username(url,osip_strdup("jack")); osip_uri_set_host(url,osip_strdup("atosc.org")); osip_message_set_method(msg,osip_strdup("INVITE")); osip_message_set_uri(msg,uri); osip_message_set_version(msg,osip_strdup("2.0"));
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 (mandatory for TCP, recommended for ALL) |
Content-Type (if a body is attached) |
Example 4-8. Add header
{ osip_uri_t *url; osip_to_t *to; osip_uri_init(&url); osip_uri_set_username(url,osip_strdup("jack")); osip_uri_set_host(url,osip_strdup("atosc.org")); osip_to_init(&to); osip_to_set_url(to,url); osip_to_set_displayname(to,osip_strdup("Aymeric MOIZARD")); osip_message_set_to(msg, to); } /* the same API is available for the osip_from_t structure */ { osip_from_t *from; /* allocate a osip_uri_t */ osip_uri_init(&url); osip_uri_set_username(url,osip_strdup("cha")); osip_uri_set_host(url,osip_strdup("anywhere.org")); /* allocate a osip_from_t */ osip_from_init(&from); osip_from_set_url(from,url); osip_from_set_displayname(from,osip_strdup("My love")); osip_from_set_tag(from,osip_strdup("a48a")); osip_message_set_from(msg, from); } { osip_via_t *via; osip_via_init(&via); osip_via_set_version(via,osip_strdup("2.0")); osip_via_set_protocol(via,osip_strdup("UDP")); osip_via_set_host(via,osip_strdup("137.137.137.137")); osip_via_set_branch(via,osip_strdup("branch"),osip_strdup("z9hG4bK776asdhds.1")); osip_message_set_via(msg, via); } { osip_cseq_t *cseq; cseq_init(&cseq); ... osip_message_set_cseq(msg, cseq); } { osip_call_id_t *callid; osip_call_id_init(&callid); osip_callid_set_number(callid,osip_strdup("f81d4")); osip_callid_set_host(callid,osip_strdup("foo.atosc.org")); osip_message_set_callid(msg, callid); } /* this API can also be used, but it is much more time consuming! */ osip_message_set_contact(msg,"sip:jacK@office.atosc.org"); /* Let's add some headers */ osip_message_set_header(msg,osip_strdup("SuBjecT"),osip_strdup("Need support for oSIP!")); /* add a body */ osip_message_set_body(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");
For osip_message_t like for the structure used for the headers (osip_to_t, osip_from_t, osip_call_id_t...), a method exist to retrieve printed version of the element.
Example 4-9. Method xxxx_to_str
char *astring = NULL; int length; i = osip_message_to_str(msg, &astring, &length); if (i!=0) return -1; osip_free(astring);
Example 4-10. Method xxxx_free
This is how you must free all memory.
osip_messagefree(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.
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.
./libosip-x.x.x/src/osip2 is the source directory for the SIP finite state machines.
-losip2 is the library containing the SIP finite state machines
#include <osip2/osip.h> is the include file describing the external SIP parser API
Transactions are used to store the context of one "SIP transactions" as defined in the rfc3261. oSIP uses the osip_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 (osip_event_t) which concern this transaction.
Example 5-2. type definition of osip_transaction_t.
typedef struct osip__transaction { void *your_instance; /**< User Defined Pointer. */ int transactionid; /**< Internal Transaction Identifier. */ osip_fifo_t *transactionff; /**< events must be added in this fifo */ osip_via_t *topvia; /**< CALL-LEG definition (Top Via) */ osip_from_t *from; /**< CALL-LEG definition (From) */ osip_to_t *to; /**< CALL-LEG definition (To) */ osip_call_id_t *callid; /**< CALL-LEG definition (Call-ID) */ osip_cseq_t *cseq; /**< CALL-LEG definition (CSeq) */ osip_message_t *orig_request; /**< Initial request */ osip_message_t *last_response; /**< Last response */ osip_message_t *ack; /**< ack request sent */ state_t state; /**< Current state of the transaction */ time_t birth_time; /**< birth date of transaction */ time_t completed_time; /**< end date of transaction */ int in_socket; /**< Optional socket for incoming message */ int out_socket; /**< Optional place for outgoing message */ void *config; /**@internal transaction is managed by osip_t */ osip_fsm_type_t ctx_type; /**< Type of the transaction */ osip_ict_t *ict_context; /**@internal */ osip_ist_t *ist_context; /**@internal */ osip_nict_t *nict_context; /**@internal */ osip_nist_t *nist_context; /**@internal */ } osip_transaction_t;
osip_events 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 osip_event_t.
typedef struct osip_event { type_t type; int transactionid; osip_message_t *sip; } osip_event_t;
#include <osip2/osip.h>
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).
typedef void (*osip_message_cb_t) (int type, osip_transaction_t *, osip_message_t *); OSIP_ICT_INVITE_SENT = 0, /**< INVITE MESSAGE SENT */ OSIP_ICT_INVITE_SENT_AGAIN, /**< INVITE MESSAGE RETRANSMITTED */ OSIP_ICT_ACK_SENT, /**< ACK MESSAGE SENT */ OSIP_ICT_ACK_SENT_AGAIN, /**< ACK MESSAGE RETRANSMITTED */ OSIP_ICT_STATUS_1XX_RECEIVED, /**< 1XX FOR INVITE RECEIVED */ OSIP_ICT_STATUS_2XX_RECEIVED, /**< 2XX FOR INVITE RECEIVED */ OSIP_ICT_STATUS_2XX_RECEIVED_AGAIN, /**< 2XX FOR INVITE RECEIVED AGAIN */ OSIP_ICT_STATUS_3XX_RECEIVED, /**< 3XX FOR INVITE RECEIVED */ OSIP_ICT_STATUS_4XX_RECEIVED, /**< 4XX FOR INVITE RECEIVED */ OSIP_ICT_STATUS_5XX_RECEIVED, /**< 5XX FOR INVITE RECEIVED */ OSIP_ICT_STATUS_6XX_RECEIVED, /**< 6XX FOR INVITE RECEIVED */ OSIP_ICT_STATUS_3456XX_RECEIVED_AGAIN, /**< RESPONSE RECEIVED AGAIN */ OSIP_IST_INVITE_RECEIVED, /**< INVITE MESSAGE RECEIVED */ OSIP_IST_INVITE_RECEIVED_AGAIN, /**< INVITE MESSAGE RECEIVED AGAN */ OSIP_IST_ACK_RECEIVED, /**< ACK MESSAGE RECEIVED */ OSIP_IST_ACK_RECEIVED_AGAIN, /**< ACK MESSAGE RECEIVED AGAIN */ OSIP_IST_STATUS_1XX_SENT, /**< 1XX FOR INVITE SENT */ OSIP_IST_STATUS_2XX_SENT, /**< 2XX FOR INVITE SENT */ OSIP_IST_STATUS_2XX_SENT_AGAIN, /**< 2XX FOR INVITE RETRANSMITTED */ OSIP_IST_STATUS_3XX_SENT, /**< 3XX FOR INVITE SENT */ OSIP_IST_STATUS_4XX_SENT, /**< 4XX FOR INVITE SENT */ OSIP_IST_STATUS_5XX_SENT, /**< 5XX FOR INVITE SENT */ OSIP_IST_STATUS_6XX_SENT, /**< 6XX FOR INVITE SENT */ OSIP_IST_STATUS_3456XX_SENT_AGAIN, /**< RESPONSE RETRANSMITTED */ OSIP_NICT_REGISTER_SENT, /**< REGISTER MESSAGE SENT */ OSIP_NICT_BYE_SENT, /**< BYE MESSAGE SENT */ OSIP_NICT_OPTIONS_SENT, /**< OPTIONS MESSAGE SENT */ OSIP_NICT_INFO_SENT, /**< INFO MESSAGE SENT */ OSIP_NICT_CANCEL_SENT, /**< CANCEL MESSAGE SENT */ OSIP_NICT_NOTIFY_SENT, /**< NOTIFY MESSAGE SENT */ OSIP_NICT_SUBSCRIBE_SENT, /**< SUBSCRIBE MESSAGE SENT */ OSIP_NICT_UNKNOWN_REQUEST_SENT, /**< UNKNOWN REQUEST MESSAGE SENT */ OSIP_NICT_REQUEST_SENT_AGAIN, /**< REQUEST MESSAGE RETRANMITTED */ OSIP_NICT_STATUS_1XX_RECEIVED, /**< 1XX FOR MESSAGE RECEIVED */ OSIP_NICT_STATUS_2XX_RECEIVED, /**< 2XX FOR MESSAGE RECEIVED */ OSIP_NICT_STATUS_2XX_RECEIVED_AGAIN, /**< 2XX FOR MESSAGE RECEIVED AGAIN */ OSIP_NICT_STATUS_3XX_RECEIVED, /**< 3XX FOR MESSAGE RECEIVED */ OSIP_NICT_STATUS_4XX_RECEIVED, /**< 4XX FOR MESSAGE RECEIVED */ OSIP_NICT_STATUS_5XX_RECEIVED, /**< 5XX FOR MESSAGE RECEIVED */ OSIP_NICT_STATUS_6XX_RECEIVED, /**< 6XX FOR MESSAGE RECEIVED */ OSIP_NICT_STATUS_3456XX_RECEIVED_AGAIN, /**< RESPONSE RECEIVED AGAIN */ OSIP_NIST_REGISTER_RECEIVED, /**< REGISTER RECEIVED */ OSIP_NIST_BYE_RECEIVED, /**< BYE RECEIVED */ OSIP_NIST_OPTIONS_RECEIVED, /**< OPTIONS RECEIVED */ OSIP_NIST_INFO_RECEIVED, /**< INFO RECEIVED */ OSIP_NIST_CANCEL_RECEIVED, /**< CANCEL RECEIVED */ OSIP_NIST_NOTIFY_RECEIVED, /**< NOTIFY RECEIVED */ OSIP_NIST_SUBSCRIBE_RECEIVED, /**< SUBSCRIBE RECEIVED */ OSIP_NIST_UNKNOWN_REQUEST_RECEIVED, /**< UNKNWON REQUEST RECEIVED */ OSIP_NIST_REQUEST_RECEIVED_AGAIN, /**< UNKNWON REQUEST RECEIVED AGAIN */ OSIP_NIST_STATUS_1XX_SENT, /**< 1XX FOR MESSAGE SENT */ OSIP_NIST_STATUS_2XX_SENT, /**< 2XX FOR MESSAGE SENT */ OSIP_NIST_STATUS_2XX_SENT_AGAIN, /**< 2XX FOR MESSAGE RETRANSMITTED */ OSIP_NIST_STATUS_3XX_SENT, /**< 3XX FOR MESSAGE SENT */ OSIP_NIST_STATUS_4XX_SENT, /**< 4XX FOR MESSAGE SENT */ OSIP_NIST_STATUS_5XX_SENT, /**< 5XX FOR MESSAGE SENT */ OSIP_NIST_STATUS_6XX_SENT, /**< 6XX FOR MESSAGE SENT */ OSIP_NIST_STATUS_3456XX_SENT_AGAIN, /**< RESPONSE RETRANSMITTED */ int osip_set_message_callback (osip_t *osip, int type, osip_message_cb_t cb); typedef void (*osip_kill_transaction_cb_t) (int type, osip_transaction_t *); OSIP_ICT_KILL_TRANSACTION, /**< end of Client INVITE transaction */ OSIP_IST_KILL_TRANSACTION, /**< end of Server INVITE transaction */ OSIP_NICT_KILL_TRANSACTION, /**< end of Client Non-INVITE transaction */ OSIP_NIST_KILL_TRANSACTION, /**< end of Server Non-INVITE transaction */ int osip_set_kill_transaction_callback (osip_t *osip, int type, osip_kill_transaction_cb_t cb); typedef void (*osip_transport_error_cb_t) (int type, osip_transaction_t *, int error); OSIP_ICT_TRANSPORT_ERROR, /**< transport error for ICT */ OSIP_IST_TRANSPORT_ERROR, /**< transport error for IST */ OSIP_NICT_TRANSPORT_ERROR, /**< transport error for NICT */ OSIP_NIST_TRANSPORT_ERROR, /**< transport error for NIST */ int osip_set_transport_error_callback (osip_t *osip, int type, osip_transport_error_cb_t cb);
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) #define EVT_IS_RCV_ACK(event) #define EVT_IS_RCV_REQUEST(event) #define EVT_IS_RCV_STATUS_1XX(event) #define EVT_IS_RCV_STATUS_23456XX(event) /* FOR OUTGOING TRANSACTION */ #define EVT_IS_SND_INVITE(event) #define EVT_IS_SND_ACK(event) #define EVT_IS_SND_REQUEST(event) #define EVT_IS_SND_STATUS_1XX(event) #define EVT_IS_SND_STATUS_23456XX(event) >
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 not always part of the state machine for INVITE (only the ACK for final answer are above 299 are handled by the invite transactions: watch out the rfc3261). 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 | +--------| | +-----------+
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();
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_init(&osip)) return -1; /* mutex is not initialized properly */ osip_set_kill_transaction_callback(osip ,OSIP_ICT_KILL_TRANSACTION, &cb_ict_kill_transaction); osip_set_kill_transaction_callback(osip ,OSIP_NIST_KILL_TRANSACTION, &cb_ist_kill_transaction); osip_set_kill_transaction_callback(osip ,OSIP_NICT_KILL_TRANSACTION, &cb_nict_kill_transaction); osip_set_kill_transaction_callback(osip ,OSIP_NIST_KILL_TRANSACTION, &cb_nist_kill_transaction); osip_set_message_callback(osip ,OSIP_ICT_STATUS_2XX_RECEIVED_AGAIN, &cb_rcvresp_retransmission); osip_set_message_callback(osip ,OSIP_ICT_STATUS_3456XX_RECEIVED_AGAIN, &cb_rcvresp_retransmission); osip_set_message_callback(osip ,OSIP_ICT_INVITE_SENT_AGAIN, &cb_sndreq_retransmission); osip_set_message_callback(osip ,OSIP_IST_STATUS_2XX_SENT_AGAIN, &cb_sndresp_retransmission); osip_set_message_callback(osip ,OSIP_IST_STATUS_3456XX_SENT_AGAIN, &cb_sndresp_retransmission); osip_set_message_callback(osip ,OSIP_IST_INVITE_RECEIVED_AGAIN, &cb_rcvreq_retransmission); osip_set_message_callback(osip ,OSIP_NICT_STATUS_2XX_RECEIVED_AGAIN, &cb_rcvresp_retransmission); osip_set_message_callback(osip ,OSIP_NICT_STATUS_3456XX_RECEIVED_AGAIN, &cb_rcvresp_retransmission); osip_set_message_callback(osip ,OSIP_NICT_REQUEST_SENT_AGAIN, &cb_sndreq_retransmission); osip_set_message_callback(osip ,OSIP_NIST_STATUS_2XX_SENT_AGAIN, &cb_sndresp_retransmission); osip_set_message_callback(osip ,OSIP_NIST_STATUS_3456XX_SENT_AGAIN, &cb_sndresp_retransmission); osip_set_message_callback(osip ,OSIP_NIST_REQUEST_RECEIVED_AGAIN, &cb_rcvreq_retransmission); osip_set_message_callback(osip ,OSIP_ICT_INVITE_SENT, &cb_sndinvite); osip_set_message_callback(osip ,OSIP_ICT_ACK_SENT, &cb_sndack); osip_set_message_callback(osip ,OSIP_NICT_REGISTER_SENT, &cb_sndregister); osip_set_message_callback(osip ,OSIP_NICT_BYE_SENT, &cb_sndbye); osip_set_message_callback(osip ,OSIP_NICT_CANCEL_SENT, &cb_sndcancel); osip_set_message_callback(osip ,OSIP_NICT_INFO_SENT, &cb_sndinfo); osip_set_message_callback(osip ,OSIP_NICT_OPTIONS_SENT, &cb_sndoptions); osip_set_message_callback(osip ,OSIP_NICT_SUBSCRIBE_SENT, &cb_sndsubscribe); osip_set_message_callback(osip ,OSIP_NICT_NOTIFY_SENT, &cb_sndnotify); /* osip_set_cb_nict_sndprack (osip,&cb_sndprack); */ osip_set_message_callback(osip ,OSIP_NICT_UNKNOWN_REQUEST_SENT, &cb_sndunkrequest); osip_set_message_callback(osip ,OSIP_ICT_STATUS_1XX_RECEIVED, &cb_rcv1xx); osip_set_message_callback(osip ,OSIP_ICT_STATUS_2XX_RECEIVED, &cb_rcv2xx); osip_set_message_callback(osip ,OSIP_ICT_STATUS_3XX_RECEIVED, &cb_rcv3xx); osip_set_message_callback(osip ,OSIP_ICT_STATUS_4XX_RECEIVED, &cb_rcv4xx); osip_set_message_callback(osip ,OSIP_ICT_STATUS_5XX_RECEIVED, &cb_rcv5xx); osip_set_message_callback(osip ,OSIP_ICT_STATUS_6XX_RECEIVED, &cb_rcv6xx); osip_set_message_callback(osip ,OSIP_IST_STATUS_1XX_SENT, &cb_snd1xx); osip_set_message_callback(osip ,OSIP_IST_STATUS_2XX_SENT, &cb_snd2xx); osip_set_message_callback(osip ,OSIP_IST_STATUS_3XX_SENT, &cb_snd3xx); osip_set_message_callback(osip ,OSIP_IST_STATUS_4XX_SENT, &cb_snd4xx); osip_set_message_callback(osip ,OSIP_IST_STATUS_5XX_SENT, &cb_snd5xx); osip_set_message_callback(osip ,OSIP_IST_STATUS_6XX_SENT, &cb_snd6xx); osip_set_message_callback(osip ,OSIP_NICT_STATUS_1XX_RECEIVED, &cb_rcv1xx); osip_set_message_callback(osip ,OSIP_NICT_STATUS_2XX_RECEIVED, &cb_rcv2xx); osip_set_message_callback(osip ,OSIP_NICT_STATUS_3XX_RECEIVED, &cb_rcv3xx); osip_set_message_callback(osip ,OSIP_NICT_STATUS_4XX_RECEIVED, &cb_rcv4xx); osip_set_message_callback(osip ,OSIP_NICT_STATUS_5XX_RECEIVED, &cb_rcv5xx); osip_set_message_callback(osip ,OSIP_NICT_STATUS_6XX_RECEIVED, &cb_rcv6xx); osip_set_message_callback(osip ,OSIP_NIST_STATUS_1XX_SENT, &cb_snd1xx); osip_set_message_callback(osip ,OSIP_NIST_STATUS_2XX_SENT, &cb_snd2xx); osip_set_message_callback(osip ,OSIP_NIST_STATUS_3XX_SENT, &cb_snd3xx); osip_set_message_callback(osip ,OSIP_NIST_STATUS_4XX_SENT, &cb_snd4xx); osip_set_message_callback(osip ,OSIP_NIST_STATUS_5XX_SENT, &cb_snd5xx); osip_set_message_callback(osip ,OSIP_NIST_STATUS_6XX_SENT, &cb_snd6xx); osip_set_message_callback(osip ,OSIP_IST_INVITE_RECEIVED, &cb_rcvinvite); osip_set_message_callback(osip ,OSIP_IST_ACK_RECEIVED, &cb_rcvack); osip_set_message_callback(osip ,OSIP_IST_ACK_RECEIVED_AGAIN, &cb_rcvack2); osip_set_message_callback(osip ,OSIP_NIST_REGISTER_RECEIVED, &cb_rcvregister); osip_set_message_callback(osip ,OSIP_NIST_BYE_RECEIVED, &cb_rcvbye); osip_set_message_callback(osip ,OSIP_NIST_CANCEL_RECEIVED, &cb_rcvcancel); osip_set_message_callback(osip ,OSIP_NIST_INFO_RECEIVED, &cb_rcvinfo); osip_set_message_callback(osip ,OSIP_NIST_OPTIONS_RECEIVED, &cb_rcvoptions); osip_set_message_callback(osip ,OSIP_NIST_SUBSCRIBE_RECEIVED, &cb_rcvsubscribe); osip_set_message_callback(osip ,OSIP_NIST_NOTIFY_RECEIVED, &cb_rcvnotify); osip_set_message_callback(osip ,OSIP_NIST_UNKNOWN_REQUEST_RECEIVED, &cb_rcvunkrequest);
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) { osip_message_t *invite; osip_transaction_t *transaction; /* You must create your own SIP message. */ osip_message_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.) */ osip_transaction_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(osip_transaction_t *transaction,osip_message_t *msg) { osip_event_t *evt; evt = osip_new_outgoing_sipmessage(msg); /* depending on your application model, you can choose */ /* either to consume the event are add it in the fifo */ /* and delay the operations */ osip_fifo_add(transaction->transactionff,evt); osip_transaction_execute(transaction,evt); return 0; }
The transaction layer provided for oSIP is usable by user agents, registrar, redirect server and some type of conference server.
For stateless proxy, no state machine is required.
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:
Read all the rfc about SIP!. |
implement the transport layer (UDP, TCP, TLS?) |
choose a multi thread design or not. |
choose how to start timers. |
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.