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."
This document can be freely distributed and translated under the terms of the LDP License.
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...
The traditionnal telephony was based on one media. Now, it's over. Your phone will be able to connect to a TV, to a camera, to others phones with different qualities and different codecs. Hopefully, SIP is independent of any media used by the applications. SIP is able to negociate medias used within sessions. Any multimedia application (games, distance learning application) can use SIP to set up sessions.
SIP is 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 rfc2543 defines 6 types of transaction. This part defines how to handle transactions, write message, negociate medias, set up, modify and terminate calls. Many services are provided, but SIP is design for extensibility and the transactionnal model can be reused (transparently for servers) to create some supplementary services. Many such drafts are in progress.
"SIP transparently supports name mapping and redirection services, allowing the implementation of ISDN and Intelligent Network telephony subscriber services. These facilities also enable personal mobility."
rfc2543.txt (Section 1.1)
SIP servers are used to locate users and distribute location on demand. This way, end user agents have very minimal requirements and still have access to a wide variety of services.
Many extensions are already available as draft. Your can also add your personnal phone capabilities and remain interoperable with existing servers.
This section does not intend to fully describe the RFC. It is a fast and incomplete overview of the protocol behavior.
SIP is a text protocol based on utf8 encoding. (making it more readable and easier to debug) SIP describe a syntax for SIP requests, responses and their headers. The full syntax is available at Columbia in an augmented BNF form.
Example 2-1. Description of SIP message in ABNF:
Here is the description of SIP messages and SIP request. It is far from complete and only shows request and response definitions.
message-header = ( general-header | request-header | response-header | entity-header ) Request = Request-Line ; Section 4.1 *( general-header | request-header | entity-header ) CRLF [ message-body ] ; Section 8 Request-Line = Method SP Request-URI SP SIP-Version CRLF Request-URI = SIP-URL | absoluteURI SIP-Version = "SIP/2.0" Method = "INVITE" | "ACK" | "OPTIONS" | "BYE" | "CANCEL" | "REGISTER" | extension-method extension-method = token
The syntax is borrowed from the MAIL and HTTP syntax. 6 types of requests are defined by SIP. The available methods are:
INVITE |
ACK |
CANCEL |
BYE |
INFO |
OPTIONS |
As you can see in the BNF definition of request, SIP is not limited to this short list of methods and includes in its definition the extension-method token. Any other request can be handled by oSIP. NOTIFY and SUBSCRIBE are good examples of new possible methods. These ones are used specifically to provide presence capabilities to your SIP phone without much effort.
Example 2-2. INVITE request
INVITE requests are used to initiate and modify sessions. Here, cha from sipworld.net is calling jack from domain atosc.org. This request should be sent to the proxy server managing atosc.org, it will forward the call to jack at his real IP address.
INVITE sip:jacK@atosc.org SIP/2.0 Via: SIP/2.0/UDP home.sipworld.org To: sip:jacK@atosc.org From: sip:cha@sipworld.org Call-ID: 35778645354@home.sipworld.org CSeq: 1 INVITE Contact: sip:cha@home.sipworld.org Content-type: application/sdp Content-length: 267 v=0 o=user1 53655765 2353687637 IN IP4 128.3.4.5 s=Mbone Audio i=Discussion of Mbone Engineering Issues e=mbone@somewhere.com c=IN IP4 128.3.4.5 t=0 0 m=audio 3456 RTP/AVP 0 a=rtpmap:0 PCMU/8000
In order to control sessions, SIP uses transactions. Transactions (INVITE, CANCEL, BYE...) usually result in a modification of a current session. Some other transactions (SUBSCRIBE, NOTIFY...) are not bound to a session. A transaction is composed of one request and its responses (many informational responses and one final response). The following headers: To, From, Call-ID and CSeq are used to identify messages within a transaction.
As SIP can use unreliable transport protocol (UDP is recommended on the IP network), SIP also defines retransmission rules for messages within a transaction.
Example 2-3. INVITE transaction
This is the most basic call flow showing the initiation of a session. Only two SIP User Agents (UAC/UAS) are involved. (retransmissions are hidden)
UAC1 UAS2 jacks | INVITE | initiate a |----------------->| Bob's call | | Phone starts | 180 Ringing| ringing |<-----------------| | 180 Ringing| |<-----------------| | 200 OK | |<-----------------| | ACK | |----------------->|
Transactions are used by user agent as means to control sessions. A session is always initiated by an INVITE. SIP defines a large set of answer codes. A proxy may answer you the well known "404 User Not found" as for an HTTP error. Errors have different levels. A transaction can fail but still proposes new locations to try. Responses from class 3xx serve as redirection mechnism. 4xx, 5xx and 6xx responses are respectively reserved for client error, server error and global failure.
Example 2-4. A complete session
First, both user agents must send a REGISTER to a registrar. In the following example, the proxy also support registration.
This session is initiated with an INVITE transaction to join jack at home.net. A redirection to jack's office is made by the redirect server of home.net. UA1 understand the redirection and send a new INVITE towards the real User Agent (UA2) currently used by jack at office.atosc.org. UA2 first rings and jack accepts the call with a 200 OK response. After several minutes, jack and bob want to use their new cameras. The session is modified with an INVITE sent by jack to negociate new parameters for video. The session is finally ended by bob.
BOB home.net Jack (atosc.org) UA1 PROXY UA2 | REGISTER | . |-------------->| . | 200 OK | . |<--------------| . . . . later... . . . . . . . | REGISTER | . |<--------------| . | 200 OK | . |-------------->| . . . later... . . . | INVITE jack@home.net . |-------------->| . |302 Moved temporily . |<--------------| . | ACK | . |-------------->| . | INVITE jack@office.atosc.org | | audio | |------------------------------>| | 180 Ringing | |<------------------------------| | 180 Ringing | |<------------------------------| | 200 OK | |<------------------------------| | ACK jack@office.atosc.org | |------------------------------>| . . later . . . . | INVITE bob@bob.home.net | | audio + video | |<------------------------------| | 200 OK | |------------------------------>| | ACK bob@bob.home.org | |<------------------------------| . . later . . . . | BYE jack@office.atosc.org | |------------------------------>| | 200 OK | |<------------------------------| | |
SIP defines behaviours for proxy, redirect and registrar server. For complete information, please read the RFC...
Usually, a user agent sends its requests to an outbound proxy. As users do not know the current location of their correspondent, they use for the sip url a username and a domain. The outbound proxy (where firewall capabilities can be inserted) uses DNS SRV RECORDS to find servers belonging to the requested domain. Once the server is found, the request is forwarded. This server is the inbound proxy of the correspondent. If the user is available, its application must have registered its location before, so the proxy is now able to forward the request to the real user location. On local network, other standards may be used to find user. (finger,...)
Having a good knowledge of SIP is recommended for this section. If you plan to use SIP, you should read more carefully the rfc2543 from ietf.org.
SIP defines several levels of abstraction. oSIP hides the SIP syntax, hides the networking tasks under UDP (TCP is nearly available) and also hides the management of transactions. oSIP makes applications easier to build. As the oSIP stack is growing, you will be provided more and more facilities to handle sessions. A complete module to manage sessions might be provided in a near future.
oSIP contains a parser which is able to read and write any SIP message as described in the RFC. Currently oSIP is able to parse only a minimal set of headers such as Call-ID, To, From, Contact, CSeq, Content-Type, and Content-Length. All other headers are stored as string. Efforts will be done to support more headers.
Caution |
Development level: oSIP now supports utf8 and the full SIP grammar. The release 0.6.0 contains a compliant SIP parser with minimal exceptions. Also, the parser has just been released (25th of July 2001) and may have some bugs. Please report them to <osip@atosc.org>, I'll fix them. |
By now, the SIP parser supports multiple attachments through the MIME format.
Caution |
THIS IS NOT ALREADY TRUE IN RELEASE 0.6.0 |
oSIP presents an easy to use interface. Transactions are modelled through finite state machines. This is the core of the oSIP library. It has been heavily tested and is already stable. Also, oSIP does not require much memory and runs fast. The current design limits the number of possible transactions to the number of threads available on the host. This design is profitable for SIP agent with few connections. Servers may need to use a fixed number of threads. New architecture without threads will also be available in the future for systems that don't support threads!
Example 3-1. Relations between interface and SIP behavior
The application controls the behavior of transactions by sending events to a FIFO (an event generally contains a SIP message). In the following example, the user has to allocate two SIP messages (INVITE and ACK) and transmits them to the stack. All responses are filtered by the finite state machines and the stack will start the appropriate actions. If the stack does not require any additional information to handle the event, the task is executed automatically and the application is informed through a callback.
*add_event(INVITE)* | | ------------> | | INVITE | STACK |------------> | internals | INVITE retransmission osip->cb_rcvresp_retransmission(e) |------------> <------------ | | | | 100 Trying osip->cb_inc_1xx(evt) | |<------------ <------------ | | | | 180 Ringing osip->cb_inc_1xx(evt) | |<------------ <------------ | | | | 200 OK osip->cb_inc_2xx(evt) | |<------------ <------------ | | 200 OK retransmission | |<------------ add_event(fifo,evt) | | ------------> | | ACK | |------------> | | 200 OK osip->cb_rcvresp_retransmission(e) |<------------ <------------ | | | | ACK | |------------>
osip->cb_*(sipevent_t *) are methods implemented by the application layer. They are used by the stack to announce states of transactions.
oSIP offers a simple interface to add SIP capabilities in multimedia applications. Also, oSIP is not tight to any kind of SIP agents. You can build either end-user agents, stateless proxy and gateway. If you want to implement a proxy, please read "notes for proxy implementation" in the chapter called "the transaction manager".
The library has successfully been built with portability in mind and should be quickly usable on most posix systems. It has already been built under Solaris and the RT OS VxWorks. GNU/Linux (2.2.16) has been used for initial developments.
First, oSIP contains a parser for the SIP syntax. This is the initial requirement to use SIP.
Here is a list of methods used by the parser. They define read and write operations on the sip_t structure. Those methods are available through the osip/smsg.h header file.
Example 4-1. Declaration of the sip_t structure.
Currently this structure is not complete. All other header than From, To, Call-Id, CSeq, via, contact and content-length are stored as string in a list of generic headers. This structure will be completed while improving the parser capabilities.
typedef struct _sip_t { startline_t *strtline; from_t *from; to_t *to; call_id_t *call_id; cseq_t *cseq; list_t *vias; list_t *contacts; list_t *headers; content_type_t *content_type; content_length_t *contentlength; mime_version_t *mime_version; list_t *bodies; } sip_t;
Example 4-2. Method to manage "sip_t"
The oSIP stack only parses a minimal set of headers. Nevertheless, you still have access to other headers which are available as strings. (This is also a way to support headers not defined by the initial RFC)
int msg_parse(sip_t *dest , char *message, err_t *err); int msg_init(sip_t **sip, err_t *err); void msg_free(sip_t *sip); /* Those methods parse some specific headers. */ int msg_setheader(sip_t *sip, char *hname, char *hvalue, err_t *err); int msg_setcall_id(sip_t *sip, char *hvalue, err_t *err); int msg_setcontact(sip_t *sip, char *hvalue, err_t *err); int msg_setcseq (sip_t *sip, char *hvalue, err_t *err); int msg_setfrom(sip_t *sip, char *hvalue, err_t *err); int msg_setto(sip_t *sip, char *hvalue, err_t *err); int msg_setvia(sip_t *sip, char *hvalue, err_t *err); int msg_setcontent_length(sip_t *sip, char *hvalue, err_t *); int msg_setcontent_type(sip_t *sip, char *hvalue,err_t *err); int msg_setmime_version(sip_t *sip, char *hvalue, err_t *err); #define msg_setcontent_encoding(C, S, E) msg_setcontent_length(C, S, E) int url_parse (url_t *url, char *buf, err_t *err); /* The following macros can be used to access other headers */ /* This list is not complete, but a MACRO exists for each SIP */ /* header (in osip/smsg.h). */ #define setaccept(P,R) msg_setheader((sip_t *)P,(char *)"accept",R) ... ... ...
A powerful feature of the oSIP stack is that it provides an existing architecture for transactions. If the simple design does not fit your need, you can also reuse the previous methods and create your own design. This is the only case where those methods has to be made public.
url_parse(...) is the exception. You will often need to handle sip url in the application layer!
Other helpful MACROs are defined. They are used to test characteristics of messages such as the type of a message, the method of a request and the status code of a response.
Example 4-3. List of MACROs
#define MSG_IS_RESPONSE(resp) (resp->strtline->statuscode!=NULL) #define MSG_IS_REQUEST(req) (req->strtline->statuscode==NULL) #define MSG_IS_INVITE(msg) (0==strncmp(msg->strtline->sipmethod,"INVITE",6)) #define MSG_IS_ACK(msg) (0==strncmp(msg->strtline->sipmethod,"ACK",6)) #define MSG_IS_BYE(msg) (0==strncmp(msg->strtline->sipmethod,"BYE",6)) #define MSG_IS_REGISTER(msg) (0==strncmp(msg->strtline->sipmethod,"REGISTER",6)) #define MSG_IS_CANCEL(msg) (0==strncmp(msg->strtline->sipmethod,"CANCEL",6)) #define MSG_IS_OPTIONS(msg) (0==strncmp(msg->strtline->sipmethod,"OPTIONS",6)) #define MSG_IS_INFO(msg) (0==strncmp(msg->strtline->sipmethod,"INFO",6)) #define MSG_IS_PRACK(msg) (0==strncmp(msg->strtline->sipmethod,"PRACK",6)) #define MSG_IS_STATUS_1XX(msg) (0==strncmp(msg->strtline->statuscode,"1",1)) #define MSG_IS_STATUS_2XX(msg) (0==strncmp(msg->strtline->statuscode,"2",1)) #define MSG_IS_STATUS_3XX(msg) (0==strncmp(msg->strtline->statuscode,"3",1)) #define MSG_IS_STATUS_4XX(msg) (0==strncmp(msg->strtline->statuscode,"4",1)) #define MSG_IS_STATUS_5XX(msg) (0==strncmp(msg->strtline->statuscode,"5",1)) #define MSG_IS_STATUS_6XX(msg) (0==strncmp(msg->strtline->statuscode,"6",1)) #define MSG_TEST_CODE(resp, code) (resp->strtline->statuscode!=NULL \ && code==(int)satoi(resp->strtline->statuscode)) #define MSG_IS_RESPONSEFOR(resp,requestname) \ (0==strcmp(resp->cseq->method,requestname))
oSIP defines structures for main headers: Via, From, To, Call-Id, Content-Length and CSeq. For each structure, get and set methods are available. Other useful methods simplify allocation and de-allocation of structures.
Example 4-4. header API
int msg_init(sip_t **sip, err_t *err); int msg_free(sip_t *sip, err_t *err); int msg_parse(sip_t *sip, char *message, err_t *err); int msg_2char(sip_t *sip, char **dest, err_t *err); int header_init(header_t **header, err_t *err); void header_free(header_t *header); int header_parse(header_t *header,char *string, err_t *err); int header_2char(header_t *header,char **dest, err_t *err); int call_id_init (callid_t **dest, err_t *err); void call_id_free (callid_t *callid); int call_id_parse (callid_t *callid, char *hvalue, err_t *err); int call_id_2char (callid_t *callid, char **dest, err_t *err); int call_id_getnumber(callid_t *callid, char** dest); int call_id_gethost (callid_t *callid, char** dest); void call_id_setnumber(callid_t *callid, char *number); void call_id_sethost (callid_t *callid, char *host); void cseq_init (cseq_t **dest, err_t *err); void cseq_free (cseq_t *cseq); char* cseq_2char (cseq_t *cseq, char **dest, err_t *err); int cseq_parse (cseq_t *cseq, char *hvalue, err_t *err); char* cseq_getnumber(cseq_t *cseq, char **dest); char* cseq_getmethod(cseq_t *cseq, char **dest); void cseq_setnumber(cseq_t *cseq, char *number); void cseq_setmethod(cseq_t *cseq, char *method); int from_init (from_t **dest, err_t *err); void from_free (from_t *from); int from_parse (from_t *from, char *string, err_t *err); int from_2char (from_t *from, char **dest, err_t *err); int from_compare (from_t *from1, from_t *from2); int to_init(to_t **dest, err_t *err); void to_free(to_t *to); int to_parse(to_t *to, char *string, err_t *err); int to_2char(to_t *to, char **dest, err_t *err); int via_init(via_t **dest, err_t *err); void via_free(via_t *via); int via_parse(via_t *via, char *string, err_t *err); int via_2char(via_t *via, char **dest, err_t *err); int content_length_init (content_length_t **dest, err_t *err); void content_length_free (content_length_t *content_length); int content_length_parse(content_length_t *content_length, char *string, err_t *err); char* content_length_2char(content_length_t *content_length, char **dest, err_t *err); int contact_init (contact_t **dest, err_t *err); void contact_free(contact_t *contact); int contact_parse(contact_t *contact, char *string, err_t *err); char* contact_2char(contact_t *contact, err_t *err); int body_init(body_t *body, err_t *err); void body_free(body_t *body); int body_parse(body_t *body, char *string, err_t *err); int body_parse_mime(body_t *body, char *string, err_t *err); char* body_2char(body_t *body, err_t *err); /* this is a default method to create generic response */ /* You SHOULD NOT use it. Creating your own is recommended */ int msg_makereply(int status, char *reason, sip_t *request, sip_t *dest, char *body, char *content_type, int options, err_t *err);
The parser may sometimes be too much tolerant. It will accept silently most of your mistakes. For interoperability needs, you should fill correctly each field.
To improve stack performance, you can configure the parser at link time by choosing which headers must ALWAYS be completely decoded. (It could also be possible at run time with minimal work!). You may find this useful in terms of performance when implementing proxies.
An more complete API reference for messages and headers processing is available in the ./doc directory of the distribution.
Here is the description of a basic INVITE. Like for any other messages, it must contain a set of mandatory headers: To, From, Call-ID and CSeq. INVITE request also includes a body describing the caller's media description.
Example 4-5. Example for INVITE
sip_t * msg_create_invite() { /* allocation of the structure */ sip_t *msg; url_t *uri; err_t err; msg_init(&msg,&err); /* make a request line. */ uri_init(&uri,&err); url_setscheme(url,sstrdup("sip")); url_setuserinfo(url,sstrdup("jack")); url_sethost(url,sstrdup("atosc.org")); sip_setmethod(msg,sstrdup("INVITE")); sip_setversion(msg,sstrdup("2.0")); sip_seturi(msg,uri); /* set main headers */ { url_t *url; to_t *to; url_init(&url, &err); url_setscheme(url,sstrdup("sip")); url_setuserinfo(url,sstrdup("jack")); url_sethost(url,sstrdup("atosc.org")); url_setport(url,sstrdup("5060")); url_seturlparam(url,sstrdup("transport"),sstrdup("TCP")); url_seturlparam(url,sstrdup("ttl"),sstrdup("100")); url_seturlparam(url,sstrdup("maddr"),sstrdup("here.atosc.org")); url_setheader(url,sstrdup("subject"),sstrdup("oSIP stack")); to_init(&to, &err); to_seturl(to,url); to_setdisplayname(to,sstrdup("jack the stack")); to_settoparam(to,sstrdup("tag"),sstrdup("a48a")); sip_setto(msg, to); } /* the same API is available for the from_t structure */ { from_t *from; ... ... sip_setfrom(msg, from); } { via_t *via; via_init(&via, &err); via_setprotocolname(via,sstrdup("SIP")); /* this is a default value */ via_setsentprotocol(via,sstrdup("UDP")); /* this is a default value */ via_setsentbyhost(via,sstrdup("137.137.137.137")); via_setsentbyport(via,sstrdup("5060")); /* this is a default value */ via_sethidden(via); /* default is not hidden */ via_setparam(via,sstrdup("branch"),sstrdup("a7c6a8dlze.1")); sip_setvia(msg, via); } { cseq_t *cseq; cseq_init(&cseq, &err); ... sip_setcseq(msg, cseq); } { callid_t *callid; callid_init(&callid, &err); callid_settoken1(callid,sstrdup("f81d4")); callid_settoken2(callid,sstrdup("foo.atosc.org")); sip_setcallid(msg, callid); } /* this API can also be used, but it is much more time consuming! */ /* msg_setto(msg,"sip:cha@anydomain.org", &err); */ /* msg_setfrom(msg,"sip:jacK@atosc.org", &err); */ /* msg_setcall_id(msg,"132485@foo.atosc.org", &err); */ /* msg_setcseq(msg,"1 INVITE", &err); */ /* msg_setvia(msg,"SIP/2.0/UDP 202.202.202.202:5080", &err); */ msg_setcontact(msg,"sip:jacK@office.atosc.org", &err); /* Let's add some headers */ msg_setheader(msg,sstrdup("SuBjecT"),sstrdup("Need support for oSIP!"), &err); /* add a body */ sip_setbody(msg,"v=0\\o=user1 53655765 2353687637 IN IP4 128.3.4.5\r\ns=Mbone Audio\r\ni=Discussion of Mbone Engineering Issues\r\ne=mbone@somewhere.com\r\nc=IN IP4 128.3.4.5\r\nt=0 0\r\nm=audio 3456 RTP/AVP 0\r\na=rtpmap:0 PCMU/8000\r\n\r\n", &err); /* get the message as a string */ { char *astring; astring = sip_2char(msg, &err); sfree(astring); } return msg; /* This is how you must free all memory */ sip_free(sip); sfree(sip); }
An API reference for headers is available in the ./doc directory of the distribution.
oSIP contains 4 finite state machines. SIP defines 2 kinds of transactions. INVITE and other than INVITE transactions differs slightly. INVITE transactions must be followed by an ACK. Also, different rules apply for retransmission mechanism.
A finite state machine is an ideal way to implement the transaction layer. Threads are waiting for events. Events come from the user layer, transport layer or the timer management facility. The stack always provides information about the state of transaction to the application layer. This is done through a set of callbacks.
Example 5-1. How to use the finite state machine
Each user layer has access to its list of pending transactions (through a set of methods). Each context is stored in a transaction_t structure. The transaction_t structure has a attribute which is the FIFO address of the transaction.
/* method to send events */ transaction_t *trn_findid(list_t *transactions, int transactionid); void fifo_add(fifo_t *fifo, void *element); Transaction User Layer contexts +------------+ +-----------+ | | events +--------| | | |-----+------------->| FIFO 1 | 1 | +------------+ | +--------| | | +-----------+ | | +-----------+ | +--------| | +------------->| FIFO N | N | +--------| | +-----------+
Here is a sample to initiate a transaction. The initial event sent to the FIFO contains the first INVITE request. The creation of the message is not shown here.
int create_session() { sip_t *invite; transaction_t *transaction; /* You must create your own SIP message. */ msg_init (&invite, &err); your_own_method_to_setup_messages(invite); /* When it is the first invite, allocate */ /* and start a new transaction (prepare the /* context and start a thread.) */ trn_init(&transaction, myconfig, invite->to, invite->from, invite->callid, invite->cseq, &err); /* The thread is now waiting on its fifo */ /* The following method allocate an event */ /* and send it to the transaction. */ evt_sendmsg(transaction,invite); } int evt_sendmsg(transaction_t *transaction,sip_t *msg) { sipevent_t *sipevent; sipevent = evt_new_fromuser(msg); sipevent->transactionid = transaction->transactionid; uaapp_annouceoutrequest(sipevent->sip,sipevent->transactionid); fifo_add(transaction->transactionff,sipevent); }
Caution |
THIS IS NOT ALREADY TRUE IN RELEASE 0.6.0. THIS IS THE WAY I PLAN TO USE THE CALLBACKS IN THE NEXT RELEASE |
Here is the set of callback that must be registred by user. Each user layer (described by one osip_t structure) can have its own callback methods. When user do not want to be aware of some kinds of events (retransmission...), he does not have to register his callback.
Example 5-2. Your application
At the beginning of your program and before you start using the SIP stack, you must build your own osip_t configuration. The osip_t structure is described below:
typedef struct _osip_t { /* this is the (udp) port where sipd is waiting */ int udp_port; int tcp_port; list_t *transactions; /* One layer can handle several transactions */ fifo_t *uas_timerff; pthread_t *uas_timerthread; fifo_t *uac_timerff; pthread_t *uac_timerthread; /* CAUTION: THIS IS NOT YET AVAILABLE IN RELEASE 0.6.0 */ int (*cb_connection_refused)(sipevent_t*, transaction_t *); int (*cb_network_error)(sipevent_t*, transaction_t *); int (*cb_inc_sndresp_retransmission(sipevent_t*, transaction_t *); int (*cb_inc_rcvreq_retransmission(sipevent_t*, transaction_t *); int (*cb_incoming_invite)(sipevent_t*, transaction_t *); int (*cb_inc_ack)(sipevent_t*, transaction_t *); int (*cb_inc_bye)(sipevent_t*, transaction_t *); int (*cb_inc_cancel)(sipevent_t*, transaction_t *); int (*cb_inc_info)(sipevent_t*, transaction_t *); int (*cb_inc_options)(sipevent_t*, transaction_t *); int (*cb_inc_register)(sipevent_t*, transaction_t *); int (*cb_inc_prack)(sipevent_t*, transaction_t *); int (*cb_inc_unknownreq)(sipevent_t*, transaction_t *); int (*cb_inc_1xx)(sipevent_t*, transaction_t *); int (*cb_inc_2xx)(sipevent_t*, transaction_t *); int (*cb_inc_3xx)(sipevent_t*, transaction_t *); int (*cb_inc_4xx)(sipevent_t*, transaction_t *); int (*cb_inc_5xx)(sipevent_t*, transaction_t *); int (*cb_inc_6xx)(sipevent_t*, transaction_t *); int (*cb_outg_sndreq_retransmission)(transaction_t *); int (*cb_outg_rcvresp_retransmission)(transaction_t *); int (*cb_outg_invite)(sipevent_t*, transaction_t *); int (*cb_outg_ack)(sipevent_t*, transaction_t *); int (*cb_outg_bye)(sipevent_t*, transaction_t *); int (*cb_outg_cancel)(sipevent_t*, transaction_t *); int (*cb_outg_info)(sipevent_t*, transaction_t *); int (*cb_outg_options)(sipevent_t*, transaction_t *); int (*cb_outg_register)(sipevent_t*, transaction_t *); int (*cb_outg_prack)(sipevent_t*, transaction_t *); int (*cb_outg_unknownreq)(sipevent_t*, transaction_t *); int (*cb_outg_1xx)(sipevent_t*, transaction_t *); int (*cb_outg_2xx)(sipevent_t*, transaction_t *); int (*cb_outg_3xx)(sipevent_t*, transaction_t *); int (*cb_outg_4xx)(sipevent_t*, transaction_t *); int (*cb_outg_5xx)(sipevent_t*, transaction_t *); int (*cb_outg_6xx)(sipevent_t*, transaction_t *); } osip_t;
An API is provided to register your callbacks. Note that most callbacks are mandatory and thus must be registred before using the stack.
Example 5-3. Callback registration API
osip_t *osip; osip_init(&osip, &err); /************/ /* UAS part */ /************/ /* register callbacks for new incoming requests */ /* Those callbacks are highly needed for UAS */ osip_on_inc_invite (osip, app_inc_invite); osip_on_inc_ack (osip, app_inc_ack); osip_on_inc_bye (osip, app_inc_bye osip_on_inc_cancel (osip, app_inc_cancel); osip_on_inc_info (osip, app_inc_info); osip_on_inc_options (osip, app_inc_options); osip_on_inc_register (osip, app_inc_register); osip_on_inc_unknown (osip, app_inc_unknown); /* register callbacks for new outgoing response */ /* Those callbacks may be useless in many cases. */ osip_on_outg_1xx (osip, app_outg_1xx); osip_on_outg_2xx (osip, app_outg_2xx); osip_on_outg_3xx (osip, app_outg_3xx); osip_on_outg_4xx (osip, app_outg_4xx); osip_on_outg_5xx (osip, app_outg_5xx); osip_on_outg_6xx (osip, app_outg_6xx); /* register callbacks to be aware of retransmissions */ /* Those callbacks may be useless in many cases. */ osip_on_sndresp_retransmission(osip, app_sndresp_retransmission); osip_on_rcvreq_retransmission (osip, app_rcvreq_retransmission); /* register callback to be aware of network errors */ osip_on_connection_refused(osip, app_network_error); osip_on_network_error(osip,app_network_error); /************/ /* UAC part */ /************/ /* register callbacks for new incoming response */ /* those callbacks are mandatory. */ osip_on_inc_1xx (osip, app_inc_1xx); osip_on_inc_2xx (osip, app_inc_2xx); osip_on_inc_3xx (osip, app_inc_3xx); osip_on_inc_4xx (osip, app_inc_4xx); osip_on_inc_5xx (osip, app_inc_5xx); osip_on_inc_6xx (osip, app_inc_6xx); /* register callbacks for new outgoing request */ /* Those callbacks may be useless in many cases */ osip_on_outg_invite (osip, app_outg_invite); osip_on_outg_ack (osip, app_outg_ack); osip_on_outg_bye (osip, app_outg_bye); osip_on_outg_cancel (osip, app_outg_cancel); osip_on_outg_info (osip, app_outg_info); osip_on_outg_options (osip, app_outg_options); osip_on_outg_register (osip, app_outg_register); osip_on_outg_unknown (osip, app_outg_unknown); /* register callbacks to be aware of retransmissions */ /* Those callbacks may be useless in many cases. */ osip_on_rcvresp_retransmission(osip, app_rcvresp_retransmission); osip_on_sndreq_retransmission (osip, app_sndreq_retransmission);
The transaction layer provided for oSIP is usable by user agents, registrar, redirect server and conference server. A state machine for statefull proxy will be added in the future. The existing state machines are not easily usable for proxy implementation.
For stateless proxy, a state machine is very simple (No state is needed!). Nevertheless, A state machine will be implemented. its only role will be to cache results for a short time.
The stack currently imposes a multi-threaded architecture. This limits the possible number of transaction to the number of threads available. This design can sometimes be very useful. -If only one thread were used for all transactions, sometimes, it could be in a blocked state (IP address resolution) and the other transactions would be in a blocked state also.
Hopefully, the design may be changed to better fit your needs with a minimal work.
Example 6-1. The initial design
The file "transactionnal_design.c" contains a method called "sip_start_design1(int udpport, int tcpport, osip_t *config)". This methods starts the following list of threads:
a timer for UAC contexts |
a timer for UAS contexts |
a UDP listener for incoming SIP messages |
a TCP listener for incoming SIP messages |
N thread: one for each pending TRANSACTION. |
When a message for a new INCOMING transaction is coming, the UDP/TCP thread launches a new thread dedicated to the new transaction. This new thread has a FIFO where all future events will be sent.
What is interesting is that every threads sharing the same memory space can add events in this FIFO. The timer threads will add TIMEOUT and KILL_TRANSACTION events. The UDP threads (or any other transport layer) will add RCV_* events. The user can control each transactions with the same facility. He can send SND_* events (containing outgoing requests) as shown in the previous sections.
Example 6-2. Multi-Threaded design provided by oSIP
This is the first available architecture you can use with oSIP
User Layer +------------+ | | +------------+ | msg to send +--------------------+ Transaction Timers | layer +------------+ | | | | +----------+ +------------+ +------->| TRANSAC. | | timeout evt | | 1 | +--------------------+ +----------+ Transport Layer | +------------+ | +----------+ | | +------->| TRANSAC. | +------------+ | | 2 | | incoming msg | +----------+ +--------------------+ Your own Layer | +------------+ | | | | +------------+ | | incoming msg | +--------------------+