/* The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-) Copyright (C) 2001,2002,2003 Aymeric MOIZARD jack@atosc.org This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "fsm.h" #include "xixt.h" #ifdef OSIP_MT static struct osip_mutex *ict_fastmutex; static struct osip_mutex *ist_fastmutex; static struct osip_mutex *nict_fastmutex; static struct osip_mutex *nist_fastmutex; #endif #ifdef OSIP_RETRANSMIT_2XX #include #ifdef OSIP_MT static struct osip_mutex *ixt_fastmutex; #endif #endif static int __osip_global_init (void); static void __osip_global_free (void); static int increase_ref_count (void); static void decrease_ref_count (void); static int __osip_global_init () { /* load the fsm configuration */ __ict_load_fsm (); __ist_load_fsm (); __nict_load_fsm (); __nist_load_fsm (); /* load the parser configuration */ parser_init (); #ifdef OSIP_MT ict_fastmutex = osip_mutex_init (); ist_fastmutex = osip_mutex_init (); nict_fastmutex = osip_mutex_init (); nist_fastmutex = osip_mutex_init (); #ifdef OSIP_RETRANSMIT_2XX ixt_fastmutex = osip_mutex_init (); #endif #endif return 0; } static void __osip_global_free () { __ict_unload_fsm (); __ist_unload_fsm (); __nict_unload_fsm (); __nist_unload_fsm (); #ifdef OSIP_MT osip_mutex_destroy (ict_fastmutex); osip_mutex_destroy (ist_fastmutex); osip_mutex_destroy (nict_fastmutex); osip_mutex_destroy (nist_fastmutex); #ifdef OSIP_RETRANSMIT_2XX osip_mutex_destroy (ixt_fastmutex); #endif #endif } #ifdef OSIP_RETRANSMIT_2XX int osip_ixt_lock (osip_t * osip) { #ifdef OSIP_MT return osip_mutex_lock (ixt_fastmutex); #else return 0; #endif } int osip_ixt_unlock (osip_t * osip) { #ifdef OSIP_MT return osip_mutex_unlock (ixt_fastmutex); #else return 0; #endif } /* these are for transactions that would need retransmission not handled by state machines */ void osip_add_ixt (osip_t * osip, ixt_t * ixt) { /* ajout dans la liste de osip_t->ixt */ osip_ixt_lock (osip); osip_list_add (osip->ixt_retransmissions, (void *) ixt, 0); osip_ixt_unlock (osip); } void osip_remove_ixt (osip_t * osip, ixt_t * ixt) { int i; int found = 0; ixt_t *tmp; /* ajout dans la liste de osip_t->ixt */ osip_ixt_lock (osip); for (i = 0; !osip_list_eol (osip->ixt_retransmissions, i); i++) { tmp = (ixt_t *) osip_list_get (osip->ixt_retransmissions, i); if (tmp == ixt) { osip_list_remove (osip->ixt_retransmissions, i); found = 1; break; } } osip_ixt_unlock (osip); } void response_get_destination (osip_message_t * response, char **address, int *portnum) { osip_via_t *via; char *host = NULL; int port = 0; via = (osip_via_t *) osip_list_get (response->vias, 0); if (via) { osip_generic_param_t *maddr; osip_generic_param_t *received; osip_generic_param_t *rport; osip_via_param_get_byname (via, "maddr", &maddr); osip_via_param_get_byname (via, "received", &received); osip_via_param_get_byname (via, "rport", &rport); /* 1: user should not use the provided information (host and port) if they are using a reliable transport. Instead, they should use the already open socket attached to this transaction. */ /* 2: check maddr and multicast usage */ if (maddr != NULL) host = maddr->gvalue; /* we should check if this is a multicast address and use set the "ttl" in this case. (this must be done in the UDP message (not at the SIP layer) */ else if (received != NULL) host = received->gvalue; else host = via->host; if (rport == NULL || rport->gvalue == NULL) { if (via->port != NULL) port = osip_atoi (via->port); else port = 5060; } else port = osip_atoi (rport->gvalue); } *portnum = port; if (host != NULL) *address = osip_strdup (host); else *address = NULL; } int ixt_init (ixt_t ** ixt) { ixt_t *pixt; *ixt = pixt = (ixt_t *) osip_malloc (sizeof (ixt_t)); if (pixt==NULL) return -1; pixt->dialog = NULL; pixt->msg2xx = NULL; pixt->ack = NULL; pixt->start = time (NULL); pixt->interval = 500; pixt->counter = 7; pixt->dest = NULL; pixt->port = 5060; pixt->sock = -1; return 0; } void ixt_free (ixt_t * ixt) { osip_message_free (ixt->ack); osip_message_free (ixt->msg2xx); osip_free (ixt->dest); osip_free (ixt); } /* usefull for UAs */ void osip_start_200ok_retransmissions (osip_t * osip, osip_dialog_t * dialog, osip_message_t * msg200ok, int sock) { ixt_t *ixt; ixt_init (&ixt); ixt->dialog = dialog; osip_message_clone (msg200ok, &ixt->msg2xx); ixt->sock = sock; response_get_destination (msg200ok, &ixt->dest, &ixt->port); osip_add_ixt (osip, ixt); } void osip_start_ack_retransmissions (osip_t * osip, osip_dialog_t * dialog, osip_message_t * ack, char *dest, int port, int sock) { int i; ixt_t *ixt; i = ixt_init (&ixt); if (i != 0) return; ixt->dialog = dialog; osip_message_clone (ack, &ixt->ack); ixt->dest = osip_strdup (dest); ixt->port = port; ixt->sock = sock; osip_add_ixt (osip, ixt); } /* we stop the 200ok when receiving the corresponding ack */ void osip_stop_200ok_retransmissions (osip_t * osip, osip_message_t * ack) { int i; ixt_t *ixt; osip_ixt_lock (osip); for (i = 0; !osip_list_eol (osip->ixt_retransmissions, i); i++) { ixt = (ixt_t *) osip_list_get (osip->ixt_retransmissions, i); if (osip_dialog_match_as_uas (ixt->dialog, ack) == 0) { osip_list_remove (osip->ixt_retransmissions, i); ixt_free (ixt); break; } } osip_ixt_unlock (osip); } /* when a dialog is destroyed by the application, it is safer to remove all ixt that are related to it */ void osip_stop_retransmissions_from_dialog (osip_t * osip, osip_dialog_t * dialog) { int i; ixt_t *ixt; osip_ixt_lock (osip); for (i = 0; !osip_list_eol (osip->ixt_retransmissions, i); i++) { ixt = (ixt_t *) osip_list_get (osip->ixt_retransmissions, i); if (ixt->dialog == dialog) { osip_list_remove (osip->ixt_retransmissions, i); ixt_free (ixt); i--; } } osip_ixt_unlock (osip); } void ixt_retransmit (osip_t * osip, ixt_t * ixt, time_t current) { if ((current - ixt->start) * 1000 > ixt->interval) { ixt->interval = ixt->interval * 2; ixt->start = current; if (ixt->ack != NULL) osip->cb_send_message (NULL, ixt->ack, ixt->dest, ixt->port, ixt->sock); else if (ixt->msg2xx != NULL) osip->cb_send_message (NULL, ixt->msg2xx, ixt->dest, ixt->port, ixt->sock); ixt->counter--; } } void osip_retransmissions_execute (osip_t * osip) { int i; time_t current; ixt_t *ixt; current = time (NULL); osip_ixt_lock (osip); for (i = 0; !osip_list_eol (osip->ixt_retransmissions, i); i++) { ixt = (ixt_t *) osip_list_get (osip->ixt_retransmissions, i); ixt_retransmit (osip, ixt, current); if (ixt->counter == 0) { /* remove it */ osip_list_remove (osip->ixt_retransmissions, i); ixt_free (ixt); i--; } } osip_ixt_unlock (osip); } #endif int osip_ict_lock (osip_t * osip) { #ifdef OSIP_MT return osip_mutex_lock (ict_fastmutex); #else return 0; #endif } int osip_ict_unlock (osip_t * osip) { #ifdef OSIP_MT return osip_mutex_unlock (ict_fastmutex); #else return 0; #endif } int osip_ist_lock (osip_t * osip) { #ifdef OSIP_MT return osip_mutex_lock (ist_fastmutex); #else return 0; #endif } int osip_ist_unlock (osip_t * osip) { #ifdef OSIP_MT return osip_mutex_unlock (ist_fastmutex); #else return 0; #endif } int osip_nict_lock (osip_t * osip) { #ifdef OSIP_MT return osip_mutex_lock (nict_fastmutex); #else return 0; #endif } int osip_nict_unlock (osip_t * osip) { #ifdef OSIP_MT return osip_mutex_unlock (nict_fastmutex); #else return 0; #endif } int osip_nist_lock (osip_t * osip) { #ifdef OSIP_MT return osip_mutex_lock (nist_fastmutex); #else return 0; #endif } int osip_nist_unlock (osip_t * osip) { #ifdef OSIP_MT return osip_mutex_unlock (nist_fastmutex); #else return 0; #endif } int __osip_add_ict (osip_t * osip, osip_transaction_t * ict) { #ifdef OSIP_MT osip_mutex_lock (ict_fastmutex); #endif osip_list_add (osip->osip_ict_transactions, ict, -1); #ifdef OSIP_MT osip_mutex_unlock (ict_fastmutex); #endif return 0; } int __osip_add_ist (osip_t * osip, osip_transaction_t * ist) { #ifdef OSIP_MT osip_mutex_lock (ist_fastmutex); #endif osip_list_add (osip->osip_ist_transactions, ist, -1); #ifdef OSIP_MT osip_mutex_unlock (ist_fastmutex); #endif return 0; } int __osip_add_nict (osip_t * osip, osip_transaction_t * nict) { #ifdef OSIP_MT osip_mutex_lock (nict_fastmutex); #endif osip_list_add (osip->osip_nict_transactions, nict, -1); #ifdef OSIP_MT osip_mutex_unlock (nict_fastmutex); #endif return 0; } int __osip_add_nist (osip_t * osip, osip_transaction_t * nist) { #ifdef OSIP_MT osip_mutex_lock (nist_fastmutex); #endif osip_list_add (osip->osip_nist_transactions, nist, -1); #ifdef OSIP_MT osip_mutex_unlock (nist_fastmutex); #endif return 0; } int osip_remove_transaction (osip_t * osip, osip_transaction_t * tr) { int i = -1; if (tr == NULL) return -1; if (tr->ctx_type == ICT) i = __osip_remove_ict_transaction (osip, tr); else if (tr->ctx_type == IST) i = __osip_remove_ist_transaction (osip, tr); else if (tr->ctx_type == NICT) i = __osip_remove_nict_transaction (osip, tr); else if (tr->ctx_type == NIST) i = __osip_remove_nist_transaction (osip, tr); else return -1; return i; } int __osip_remove_ict_transaction (osip_t * osip, osip_transaction_t * ict) { int pos = 0; osip_transaction_t *tmp; #ifdef OSIP_MT osip_mutex_lock (ict_fastmutex); #endif while (!osip_list_eol (osip->osip_ict_transactions, pos)) { tmp = osip_list_get (osip->osip_ict_transactions, pos); if (tmp->transactionid == ict->transactionid) { osip_list_remove (osip->osip_ict_transactions, pos); #ifdef OSIP_MT osip_mutex_unlock (ict_fastmutex); #endif return 0; } pos++; } #ifdef OSIP_MT osip_mutex_unlock (ict_fastmutex); #endif return -1; } int __osip_remove_ist_transaction (osip_t * osip, osip_transaction_t * ist) { int pos = 0; osip_transaction_t *tmp; #ifdef OSIP_MT osip_mutex_lock (ist_fastmutex); #endif while (!osip_list_eol (osip->osip_ist_transactions, pos)) { tmp = osip_list_get (osip->osip_ist_transactions, pos); if (tmp->transactionid == ist->transactionid) { osip_list_remove (osip->osip_ist_transactions, pos); #ifdef OSIP_MT osip_mutex_unlock (ist_fastmutex); #endif return 0; } pos++; } #ifdef OSIP_MT osip_mutex_unlock (ist_fastmutex); #endif return -1; } int __osip_remove_nict_transaction (osip_t * osip, osip_transaction_t * nict) { int pos = 0; osip_transaction_t *tmp; #ifdef OSIP_MT osip_mutex_lock (nict_fastmutex); #endif while (!osip_list_eol (osip->osip_nict_transactions, pos)) { tmp = osip_list_get (osip->osip_nict_transactions, pos); if (tmp->transactionid == nict->transactionid) { osip_list_remove (osip->osip_nict_transactions, pos); #ifdef OSIP_MT osip_mutex_unlock (nict_fastmutex); #endif return 0; } pos++; } #ifdef OSIP_MT osip_mutex_unlock (nict_fastmutex); #endif return -1; } int __osip_remove_nist_transaction (osip_t * osip, osip_transaction_t * nist) { int pos = 0; osip_transaction_t *tmp; #ifdef OSIP_MT osip_mutex_lock (nist_fastmutex); #endif while (!osip_list_eol (osip->osip_nist_transactions, pos)) { tmp = osip_list_get (osip->osip_nist_transactions, pos); if (tmp->transactionid == nist->transactionid) { osip_list_remove (osip->osip_nist_transactions, pos); #ifdef OSIP_MT osip_mutex_unlock (nist_fastmutex); #endif return 0; } pos++; } #ifdef OSIP_MT osip_mutex_unlock (nist_fastmutex); #endif return -1; } #if 0 /* this method is made obsolete because it contains bugs and is also too much limited. any call to this method should be replace this way: //osip_distribute(osip, evt); osip_transaction_t *transaction = osip_find_transaction_and_add_event(osip, evt); if (i!=0) // in case it's a new request { if (evt is an ACK) evt could be an ACK for INVITE (not handled by oSIP) else if ( evt is a 200 for INVITE) evt could be a retransmission of a 200 for INVITE (not handled by oSIP) else if (evt is a new request) == not a ACK and not a response { transaction = osip_create_transaction(osip, evt); if (transaction==NULL) printf("failed to create a transaction\"); } } else { // here, the message as been taken by the stack. } */ /* finds the transaction context and add the sipevent in its fifo. */ /* USED ONLY BY THE TRANSPORT LAYER. */ /* INPUT : osip_t *osip | osip. contains the list of tr. context*/ /* INPUT : osip_event_t* sipevent | event to dispatch. */ osip_transaction_t * osip_distribute_event (osip_t * osip, osip_event_t * evt) { osip_transaction_t *transaction = NULL; int i; osip_fsm_type_t ctx_type; if (EVT_IS_INCOMINGMSG (evt)) { /* event is for ict */ if (MSG_IS_REQUEST (evt->sip)) { if (0 == strcmp (evt->sip->cseq->method, "INVITE") || 0 == strcmp (evt->sip->cseq->method, "ACK")) { #ifdef OSIP_MT osip_mutex_lock (ist_fastmutex); #endif transaction = osip_transaction_find (osip->osip_ist_transactions, evt); #ifdef OSIP_MT osip_mutex_unlock (ist_fastmutex); #endif } else { #ifdef OSIP_MT osip_mutex_lock (nist_fastmutex); #endif transaction = osip_transaction_find (osip->osip_nist_transactions, evt); #ifdef OSIP_MT osip_mutex_unlock (nist_fastmutex); #endif } } else { if (0 == strcmp (evt->sip->cseq->method, "INVITE") || 0 == strcmp (evt->sip->cseq->method, "ACK")) { #ifdef OSIP_MT osip_mutex_lock (ict_fastmutex); #endif transaction = osip_transaction_find (osip->osip_ict_transactions, evt); #ifdef OSIP_MT osip_mutex_unlock (ict_fastmutex); #endif } else { #ifdef OSIP_MT osip_mutex_lock (nict_fastmutex); #endif transaction = osip_transaction_find (osip->osip_nict_transactions, evt); #ifdef OSIP_MT osip_mutex_unlock (nict_fastmutex); #endif } } if (transaction == NULL) { if (EVT_IS_RCV_STATUS_1XX (evt) || EVT_IS_RCV_STATUS_2XX (evt) || EVT_IS_RCV_STATUS_3456XX (evt) || EVT_IS_RCV_ACK (evt)) { /* event MUST be ignored! */ /* EXCEPT FOR 2XX THAT MUST BE GIVEN TO THE CORE LAYER!!! */ /* TODO */ OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL, "transaction does not yet exist... %x callid:%s\n", evt, evt->sip->call_id->number)); osip_message_free (evt->sip); osip_free (evt); /* transaction thread will not delete it */ return NULL; } /* we create a new context for this incoming request */ if (0 == strcmp (evt->sip->cseq->method, "INVITE")) ctx_type = IST; else ctx_type = NIST; i = osip_transaction_init (&transaction, ctx_type, osip, evt->sip); if (i == -1) { osip_message_free (evt->sip); osip_free (evt); /* transaction thread will not delete it */ return NULL; } } evt->transactionid = transaction->transactionid; evt->transactionid = transaction->transactionid; osip_fifo_add (transaction->transactionff, evt); return transaction; } else { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_BUG, NULL, "wrong event type %x\n", evt)); return NULL; } } #endif int osip_find_transaction_and_add_event (osip_t * osip, osip_event_t * evt) { osip_transaction_t *transaction = __osip_find_transaction (osip, evt, 1); if (transaction == NULL) return -1; return 0; } #ifndef OSIP_MT osip_transaction_t * osip_find_transaction (osip_t * osip, osip_event_t * evt) { return __osip_find_transaction (osip, evt, 0); } #endif osip_transaction_t * __osip_find_transaction (osip_t * osip, osip_event_t * evt, int consume) { osip_transaction_t *transaction = NULL; osip_list_t *transactions = NULL; #ifdef OSIP_MT struct osip_mutex *mut = NULL; #endif if (evt == NULL || evt->sip == NULL || evt->sip->cseq == NULL) return NULL; if (EVT_IS_INCOMINGMSG (evt)) { if (MSG_IS_REQUEST (evt->sip)) { if (0 == strcmp (evt->sip->cseq->method, "INVITE") || 0 == strcmp (evt->sip->cseq->method, "ACK")) { transactions = osip->osip_ist_transactions; #ifdef OSIP_MT mut = ist_fastmutex; #endif } else { transactions = osip->osip_nist_transactions; #ifdef OSIP_MT mut = nist_fastmutex; #endif } } else { if (0 == strcmp (evt->sip->cseq->method, "INVITE") || 0 == strcmp (evt->sip->cseq->method, "ACK")) { transactions = osip->osip_ict_transactions; #ifdef OSIP_MT mut = ict_fastmutex; #endif } else { transactions = osip->osip_nict_transactions; #ifdef OSIP_MT mut = nict_fastmutex; #endif } } } else if (EVT_IS_OUTGOINGMSG (evt)) { if (MSG_IS_RESPONSE (evt->sip)) { if (0 == strcmp (evt->sip->cseq->method, "INVITE") || 0 == strcmp (evt->sip->cseq->method, "ACK")) { transactions = osip->osip_ist_transactions; #ifdef OSIP_MT mut = ist_fastmutex; #endif } else { transactions = osip->osip_nist_transactions; #ifdef OSIP_MT mut = nist_fastmutex; #endif } } else { if (0 == strcmp (evt->sip->cseq->method, "INVITE") || 0 == strcmp (evt->sip->cseq->method, "ACK")) { transactions = osip->osip_ict_transactions; #ifdef OSIP_MT mut = ict_fastmutex; #endif } else { transactions = osip->osip_nict_transactions; #ifdef OSIP_MT mut = nict_fastmutex; #endif } } } if (transactions == NULL) return NULL; /* not a message??? */ #ifdef OSIP_MT osip_mutex_lock (mut); #endif transaction = osip_transaction_find (transactions, evt); if (consume == 1) { /* we add the event before releasing the mutex!! */ if (transaction != NULL) { osip_transaction_add_event (transaction, evt); #ifdef OSIP_MT osip_mutex_unlock (mut); #endif return transaction; } } #ifdef OSIP_MT osip_mutex_unlock (mut); #endif return transaction; } osip_transaction_t * osip_create_transaction (osip_t * osip, osip_event_t * evt) { osip_transaction_t *transaction; int i; osip_fsm_type_t ctx_type; if (evt == NULL) return NULL; if (evt->sip == NULL) return NULL; /* make sure the request's method reflect the cseq value. */ if (MSG_IS_REQUEST (evt->sip)) { /* delete request where cseq method does not match the method in request-line */ if (evt->sip->cseq == NULL || evt->sip->cseq->method == NULL || evt->sip->sip_method == NULL) { return NULL; } if (0 != strcmp (evt->sip->cseq->method, evt->sip->sip_method)) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_WARNING, NULL, "core module: Discard invalid message with method!=cseq!\n")); return NULL; } } if (MSG_IS_ACK (evt->sip)) /* ACK never create transactions */ return NULL; if (EVT_IS_INCOMINGREQ (evt)) { /* we create a new context for this incoming request */ if (0 == strcmp (evt->sip->cseq->method, "INVITE")) ctx_type = IST; else ctx_type = NIST; } else if (EVT_IS_OUTGOINGREQ (evt)) { if (0 == strcmp (evt->sip->cseq->method, "INVITE")) ctx_type = ICT; else ctx_type = NICT; } else { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "Cannot build a transction for this message!\n")); return NULL; } i = osip_transaction_init (&transaction, ctx_type, osip, evt->sip); if (i == -1) { return NULL; } evt->transactionid = transaction->transactionid; return transaction; } osip_transaction_t * osip_transaction_find (osip_list_t * transactions, osip_event_t * evt) { int pos = 0; osip_transaction_t *transaction; if (EVT_IS_INCOMINGREQ (evt)) { while (!osip_list_eol (transactions, pos)) { transaction = (osip_transaction_t *) osip_list_get (transactions, pos); if (0 == __osip_transaction_matching_request_osip_to_xist_17_2_3 (transaction, evt->sip)) return transaction; pos++; } } else if (EVT_IS_INCOMINGRESP (evt)) { while (!osip_list_eol (transactions, pos)) { transaction = (osip_transaction_t *) osip_list_get (transactions, pos); if (0 == __osip_transaction_matching_response_osip_to_xict_17_1_3 (transaction, evt->sip)) return transaction; pos++; } } else /* handle OUTGOING message */ { /* THE TRANSACTION ID MUST BE SET */ while (!osip_list_eol (transactions, pos)) { transaction = (osip_transaction_t *) osip_list_get (transactions, pos); if (transaction->transactionid == evt->transactionid) return transaction; pos++; } } return NULL; } static int ref_count = 0; #ifdef OSIP_MT static struct osip_mutex *ref_mutex = NULL; #endif static int increase_ref_count (void) { #ifdef OSIP_MT if (ref_count == 0) ref_mutex = osip_mutex_init (); /* Here we should assert() that the mutex was really generated. */ osip_mutex_lock (ref_mutex); #endif if (ref_count == 0) __osip_global_init (); ref_count++; #ifdef OSIP_MT osip_mutex_unlock (ref_mutex); #endif return 0; } static void decrease_ref_count (void) { #ifdef OSIP_MT osip_mutex_lock (ref_mutex); #endif /* assert (ref_count > 0); */ ref_count--; if (ref_count == 0) { #ifdef OSIP_MT osip_mutex_unlock (ref_mutex); osip_mutex_destroy (ref_mutex); #endif __osip_global_free (); return; } #ifdef OSIP_MT osip_mutex_unlock (ref_mutex); #endif } int osip_init (osip_t ** osip) { if (increase_ref_count () != 0) return -1; *osip = (osip_t *) osip_malloc (sizeof (osip_t)); if (*osip == NULL) return -1; /* allocation failed */ memset (*osip, 0, sizeof (osip_t)); (*osip)->osip_ict_transactions = (osip_list_t *) osip_malloc (sizeof (osip_list_t)); osip_list_init ((*osip)->osip_ict_transactions); (*osip)->osip_ist_transactions = (osip_list_t *) osip_malloc (sizeof (osip_list_t)); osip_list_init ((*osip)->osip_ist_transactions); (*osip)->osip_nict_transactions = (osip_list_t *) osip_malloc (sizeof (osip_list_t)); osip_list_init ((*osip)->osip_nict_transactions); (*osip)->osip_nist_transactions = (osip_list_t *) osip_malloc (sizeof (osip_list_t)); osip_list_init ((*osip)->osip_nist_transactions); #ifdef OSIP_RETRANSMIT_2XX (*osip)->ixt_retransmissions = (osip_list_t *) osip_malloc (sizeof (osip_list_t)); osip_list_init ((*osip)->ixt_retransmissions); #else (*osip)->ixt_retransmissions = NULL; #endif return 0; } void osip_release (osip_t * osip) { osip_free (osip->osip_ict_transactions); osip_free (osip->osip_ist_transactions); osip_free (osip->osip_nict_transactions); osip_free (osip->osip_nist_transactions); #ifdef OSIP_RETRANSMIT_2XX osip_free (osip->ixt_retransmissions); #endif osip_free (osip); decrease_ref_count (); } void osip_set_application_context (osip_t * osip, void *pointer) { osip->application_context = pointer; } void * osip_get_application_context (osip_t * osip) { if (osip == NULL) return NULL; return osip->application_context; } int osip_ict_execute (osip_t * osip) { osip_transaction_t *transaction; osip_event_t *se; int more_event; int tr; tr = 0; while (!osip_list_eol (osip->osip_ict_transactions, tr)) { transaction = osip_list_get (osip->osip_ict_transactions, tr); tr++; more_event = 1; do { se = (osip_event_t *) osip_fifo_tryget (transaction->transactionff); if (se == NULL) /* no more event for this transaction */ more_event = 0; else osip_transaction_execute (transaction, se); } while (more_event == 1); } return 0; } int osip_ist_execute (osip_t * osip) { osip_transaction_t *transaction; osip_event_t *se; int more_event; int tr; tr = 0; while (!osip_list_eol (osip->osip_ist_transactions, tr)) { transaction = osip_list_get (osip->osip_ist_transactions, tr); tr++; more_event = 1; do { se = (osip_event_t *) osip_fifo_tryget (transaction->transactionff); if (se == NULL) /* no more event for this transaction */ more_event = 0; else osip_transaction_execute (transaction, se); } while (more_event == 1); } return 0; } int osip_nict_execute (osip_t * osip) { osip_transaction_t *transaction; osip_event_t *se; int more_event; int tr; tr = 0; while (!osip_list_eol (osip->osip_nict_transactions, tr)) { transaction = osip_list_get (osip->osip_nict_transactions, tr); tr++; more_event = 1; do { se = (osip_event_t *) osip_fifo_tryget (transaction->transactionff); if (se == NULL) /* no more event for this transaction */ more_event = 0; else osip_transaction_execute (transaction, se); } while (more_event == 1); } return 0; } int osip_nist_execute (osip_t * osip) { osip_transaction_t *transaction; osip_event_t *se; int more_event; int tr; tr = 0; while (!osip_list_eol (osip->osip_nist_transactions, tr)) { transaction = osip_list_get (osip->osip_nist_transactions, tr); tr++; more_event = 1; do { se = (osip_event_t *) osip_fifo_tryget (transaction->transactionff); if (se == NULL) /* no more event for this transaction */ more_event = 0; else osip_transaction_execute (transaction, se); } while (more_event == 1); } return 0; } void osip_timers_gettimeout (osip_t * osip, struct timeval *lower_tv) { struct timeval now; osip_transaction_t *tr; int pos = 0; gettimeofday (&now, NULL); lower_tv->tv_sec = now.tv_sec + 3600 * 24 * 265; /* wake up evry year :-) */ lower_tv->tv_usec = now.tv_usec; #ifdef OSIP_MT osip_mutex_lock (ict_fastmutex); #endif /* handle ict timers */ while (!osip_list_eol (osip->osip_ict_transactions, pos)) { tr = (osip_transaction_t *) osip_list_get (osip->osip_ict_transactions, pos); if (1 <= osip_fifo_size (tr->transactionff)) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL, "1 Pending event already in transaction !\n")); lower_tv->tv_sec = 0; lower_tv->tv_usec = 0; #ifdef OSIP_MT osip_mutex_unlock (ict_fastmutex); #endif return; } else { if (tr->state == ICT_CALLING) min_timercmp (lower_tv, &tr->ict_context->timer_b_start); if (tr->state == ICT_CALLING) min_timercmp (lower_tv, &tr->ict_context->timer_a_start); if (tr->state == ICT_COMPLETED) min_timercmp (lower_tv, &tr->ict_context->timer_d_start); if (timercmp (&now, lower_tv, >)) { lower_tv->tv_sec = 0; lower_tv->tv_usec = 0; #ifdef OSIP_MT osip_mutex_unlock (ict_fastmutex); #endif return; } } pos++; } #ifdef OSIP_MT osip_mutex_unlock (ict_fastmutex); #endif #ifdef OSIP_MT osip_mutex_lock (ist_fastmutex); #endif /* handle ist timers */ while (!osip_list_eol (osip->osip_ist_transactions, pos)) { tr = (osip_transaction_t *) osip_list_get (osip->osip_ist_transactions, pos); if (tr->state == IST_CONFIRMED) min_timercmp (lower_tv, &tr->ist_context->timer_i_start); if (tr->state == IST_COMPLETED) min_timercmp (lower_tv, &tr->ist_context->timer_h_start); if (tr->state == IST_COMPLETED) min_timercmp (lower_tv, &tr->ist_context->timer_g_start); if (timercmp (&now, lower_tv, >)) { lower_tv->tv_sec = 0; lower_tv->tv_usec = 0; #ifdef OSIP_MT osip_mutex_unlock (ist_fastmutex); #endif return; } pos++; } #ifdef OSIP_MT osip_mutex_unlock (ist_fastmutex); #endif #ifdef OSIP_MT osip_mutex_lock (nict_fastmutex); #endif /* handle nict timers */ while (!osip_list_eol (osip->osip_nict_transactions, pos)) { tr = (osip_transaction_t *) osip_list_get (osip->osip_nict_transactions, pos); if (tr->state == NICT_COMPLETED) min_timercmp (lower_tv, &tr->nict_context->timer_k_start); if (tr->state == NICT_PROCEEDING || tr->state == NICT_TRYING) min_timercmp (lower_tv, &tr->nict_context->timer_f_start); if (tr->state == NICT_PROCEEDING || tr->state == NICT_TRYING) min_timercmp (lower_tv, &tr->nict_context->timer_e_start); if (timercmp (&now, lower_tv, >)) { lower_tv->tv_sec = 0; lower_tv->tv_usec = 0; #ifdef OSIP_MT osip_mutex_unlock (nict_fastmutex); #endif return; } pos++; } #ifdef OSIP_MT osip_mutex_unlock (nict_fastmutex); #endif #ifdef OSIP_MT osip_mutex_lock (nist_fastmutex); #endif /* handle nist timers */ while (!osip_list_eol (osip->osip_nist_transactions, pos)) { tr = (osip_transaction_t *) osip_list_get (osip->osip_nist_transactions, pos); if (tr->state == NIST_COMPLETED) min_timercmp (lower_tv, &tr->nist_context->timer_j_start); if (timercmp (&now, lower_tv, >)) { lower_tv->tv_sec = 0; lower_tv->tv_usec = 0; #ifdef OSIP_MT osip_mutex_unlock (nist_fastmutex); #endif return; } pos++; } #ifdef OSIP_MT osip_mutex_unlock (nist_fastmutex); #endif lower_tv->tv_sec = lower_tv->tv_sec - now.tv_sec; lower_tv->tv_usec = lower_tv->tv_usec - now.tv_usec; /* just make sure the value is correct! */ if (lower_tv->tv_usec < 0) { lower_tv->tv_usec = lower_tv->tv_usec + 1000000; lower_tv->tv_sec--; } if (lower_tv->tv_sec < 0) { lower_tv->tv_sec = 0; lower_tv->tv_usec = 0; } if (lower_tv->tv_usec > 1000000) { lower_tv->tv_usec = lower_tv->tv_usec - 1000000; lower_tv->tv_sec++; } return; } void osip_timers_ict_execute (osip_t * osip) { osip_transaction_t *tr; int pos = 0; #ifdef OSIP_MT osip_mutex_lock (ict_fastmutex); #endif /* handle ict timers */ while (!osip_list_eol (osip->osip_ict_transactions, pos)) { osip_event_t *evt; tr = (osip_transaction_t *) osip_list_get (osip->osip_ict_transactions, pos); if (1 <= osip_fifo_size (tr->transactionff)) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO4, NULL, "1 Pending event already in transaction !\n")); } else { evt = __osip_ict_need_timer_b_event (tr->ict_context, tr->state, tr->transactionid); if (evt != NULL) osip_fifo_add (tr->transactionff, evt); else { evt = __osip_ict_need_timer_a_event (tr->ict_context, tr->state, tr->transactionid); if (evt != NULL) osip_fifo_add (tr->transactionff, evt); else { evt = __osip_ict_need_timer_d_event (tr->ict_context, tr->state, tr->transactionid); if (evt != NULL) osip_fifo_add (tr->transactionff, evt); } } } pos++; } #ifdef OSIP_MT osip_mutex_unlock (ict_fastmutex); #endif } void osip_timers_ist_execute (osip_t * osip) { osip_transaction_t *tr; int pos = 0; #ifdef OSIP_MT osip_mutex_lock (ist_fastmutex); #endif /* handle ist timers */ while (!osip_list_eol (osip->osip_ist_transactions, pos)) { osip_event_t *evt; tr = (osip_transaction_t *) osip_list_get (osip->osip_ist_transactions, pos); evt = __osip_ist_need_timer_i_event (tr->ist_context, tr->state, tr->transactionid); if (evt != NULL) osip_fifo_add (tr->transactionff, evt); else { evt = __osip_ist_need_timer_h_event (tr->ist_context, tr->state, tr->transactionid); if (evt != NULL) osip_fifo_add (tr->transactionff, evt); else { evt = __osip_ist_need_timer_g_event (tr->ist_context, tr->state, tr->transactionid); if (evt != NULL) osip_fifo_add (tr->transactionff, evt); } } pos++; } #ifdef OSIP_MT osip_mutex_unlock (ist_fastmutex); #endif } void osip_timers_nict_execute (osip_t * osip) { osip_transaction_t *tr; int pos = 0; #ifdef OSIP_MT osip_mutex_lock (nict_fastmutex); #endif /* handle nict timers */ while (!osip_list_eol (osip->osip_nict_transactions, pos)) { osip_event_t *evt; tr = (osip_transaction_t *) osip_list_get (osip->osip_nict_transactions, pos); evt = __osip_nict_need_timer_k_event (tr->nict_context, tr->state, tr->transactionid); if (evt != NULL) osip_fifo_add (tr->transactionff, evt); else { evt = __osip_nict_need_timer_f_event (tr->nict_context, tr->state, tr->transactionid); if (evt != NULL) osip_fifo_add (tr->transactionff, evt); else { evt = __osip_nict_need_timer_e_event (tr->nict_context, tr->state, tr->transactionid); if (evt != NULL) osip_fifo_add (tr->transactionff, evt); } } pos++; } #ifdef OSIP_MT osip_mutex_unlock (nict_fastmutex); #endif } void osip_timers_nist_execute (osip_t * osip) { osip_transaction_t *tr; int pos = 0; #ifdef OSIP_MT osip_mutex_lock (nist_fastmutex); #endif /* handle nist timers */ while (!osip_list_eol (osip->osip_nist_transactions, pos)) { osip_event_t *evt; tr = (osip_transaction_t *) osip_list_get (osip->osip_nist_transactions, pos); evt = __osip_nist_need_timer_j_event (tr->nist_context, tr->state, tr->transactionid); if (evt != NULL) osip_fifo_add (tr->transactionff, evt); pos++; } #ifdef OSIP_MT osip_mutex_unlock (nist_fastmutex); #endif } void osip_set_cb_send_message (osip_t * cf, int (*cb) (osip_transaction_t *, osip_message_t *, char *, int, int)) { cf->cb_send_message = cb; } void __osip_message_callback (int type, osip_transaction_t * tr, osip_message_t * msg) { osip_t *config = tr->config; if (type >= OSIP_MESSAGE_CALLBACK_COUNT) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_BUG, NULL, "invalid callback type %d\n", type)); return; } if (config->msg_callbacks[type] == NULL) return; config->msg_callbacks[type] (type, tr, msg); } void __osip_kill_transaction_callback (int type, osip_transaction_t * tr) { osip_t *config = tr->config; if (type >= OSIP_KILL_CALLBACK_COUNT) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_BUG, NULL, "invalid callback type %d\n", type)); return; } if (config->kill_callbacks[type] == NULL) return; config->kill_callbacks[type] (type, tr); } void __osip_transport_error_callback (int type, osip_transaction_t * tr, int error) { osip_t *config = tr->config; if (type >= OSIP_TRANSPORT_ERROR_CALLBACK_COUNT) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_BUG, NULL, "invalid callback type %d\n", type)); return; } if (config->tp_error_callbacks[type] == NULL) return; config->tp_error_callbacks[type] (type, tr, error); } int osip_set_message_callback (osip_t * config, int type, osip_message_cb_t cb) { if (type >= OSIP_MESSAGE_CALLBACK_COUNT) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "invalid callback type %d\n", type)); return -1; } config->msg_callbacks[type] = cb; return 0; } int osip_set_kill_transaction_callback (osip_t * config, int type, osip_kill_transaction_cb_t cb) { if (type >= OSIP_KILL_CALLBACK_COUNT) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "invalid callback type %d\n", type)); return -1; } config->kill_callbacks[type] = cb; return 0; } int osip_set_transport_error_callback (osip_t * config, int type, osip_transport_error_cb_t cb) { if (type >= OSIP_TRANSPORT_ERROR_CALLBACK_COUNT) { OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL, "invalid callback type %d\n", type)); return -1; } config->tp_error_callbacks[type] = cb; return 0; }