#include "sip-uac-transaction.h" #include "sip-transport.h" #include "sip-internal.h" #include "uri-parse.h" #include "cpm/param.h" int sip_uac_link_transaction(struct sip_agent_t* sip, struct sip_uac_transaction_t* t); int sip_uac_unlink_transaction(struct sip_agent_t* sip, struct sip_uac_transaction_t* t); struct sip_uac_transaction_t* sip_uac_transaction_create(struct sip_agent_t* sip, struct sip_message_t* req) { struct sip_uac_transaction_t* t; t = (struct sip_uac_transaction_t*)calloc(1, sizeof(*t)); if (NULL == t) return NULL; t->ref = 1; t->req = req; t->agent = sip; LIST_INIT_HEAD(&t->link); locker_create(&t->locker); t->status = SIP_UAC_TRANSACTION_CALLING; // 17.1.1.1 Overview of INVITE Transaction (p125) // For unreliable transports (such as UDP), the client transaction retransmits // requests at an interval that starts at T1 seconds and doubles after every retransmission. // 17.1.2.1 Formal Description (p130) // For unreliable transports, requests are retransmitted at an interval which starts at T1 and doubles until it hits T2. t->t2 = sip_message_isinvite(req) ? (64 * T1) : T2; atomic_increment32(&s_gc.uac); return t; } int sip_uac_transaction_release(struct sip_uac_transaction_t* t) { assert(!t || t->ref > 0); if (!t || 0 != atomic_decrement32(&t->ref)) return 0; assert(0 == t->ref); assert(NULL == t->timera); assert(NULL == t->timerb); assert(NULL == t->timerd); assert(t->link.next == t->link.prev) ;// unlink on termernate if (t->ondestroy) { t->ondestroy(t->ondestroyparam); } sip_dialog_release(t->dialog); assert(NULL == t->onhandle); sip_message_destroy(t->req); locker_destroy(&t->locker); free(t); atomic_decrement32(&s_gc.uac); return 0; } int sip_uac_transaction_addref(struct sip_uac_transaction_t* t) { int r; r = atomic_increment32(&t->ref); assert(r > 1); return r; } //static int sip_uac_transaction_destroy(struct sip_uac_transaction_t* t) //{ // // unlink from uac // sip_uac_del_transaction(t->uac, t); // // return sip_uac_transaction_release(t); //} static int sip_uac_transaction_dosend(struct sip_uac_transaction_t* t) { return t->transport.send(t->transportptr, t->data, t->size); } static void sip_uac_transaction_onretransmission(void* usrptr) { int r, timeout; struct sip_uac_transaction_t* t; t = (struct sip_uac_transaction_t*)usrptr; locker_lock(&t->locker); sip_uac_stop_timer(t->agent, t, &t->timera); // hijack free timer only, don't release transaction if (SIP_UAC_TRANSACTION_CALLING == t->status || (t->status <= SIP_UAC_TRANSACTION_PROCEEDING && !sip_message_isinvite(t->req))) { r = sip_uac_transaction_dosend(t); if (0 != r) { // ignore retransmission error //// 8.1.3.1 Transaction Layer Errors (p42) //if (t->oninvite) // r = t->oninvite(t->param, t, NULL, 503/*Service Unavailable*/); //else // r = t->onreply(t->param, t, 503/*Service Unavailable*/); } timeout = T1 * (1 << t->retries++); t->timera = sip_uac_start_timer(t->agent, t, MIN(t->t2, MAX(T1, timeout)), sip_uac_transaction_onretransmission); } locker_unlock(&t->locker); sip_uac_transaction_release(t); } static int sip_uac_transaction_terminate(struct sip_uac_transaction_t* t) { t->status = SIP_UAC_TRANSACTION_TERMINATED; sip_uac_stop_timer(t->agent, t, &t->timera); sip_uac_stop_timer(t->agent, t, &t->timerb); sip_uac_stop_timer(t->agent, t, &t->timerd); sip_uac_unlink_transaction(t->agent, t); return 0; } static void sip_uac_transaction_ontimeout(void* usrptr) { struct sip_uac_transaction_t* t; t = (struct sip_uac_transaction_t*)usrptr; locker_lock(&t->locker); sip_uac_stop_timer(t->agent, t, &t->timerb); // hijack free timer only, don't release transaction //if (SIP_UAC_TRANSACTION_CALLING == t->status || (t->status <= SIP_UAC_TRANSACTION_PROCEEDING && !sip_message_isinvite(&t->req))) if (t->status <= SIP_UAC_TRANSACTION_PROCEEDING) { // MUST: invite should reset timer b per 1xx response sip_uac_transaction_terminate(t); // 8.1.3.1 Transaction Layer Errors (p42) if (t->oninvite) t->oninvite(t->param, NULL, t, NULL, NULL, 408/*Request Timeout*/); else if (t->onsubscribe) t->onsubscribe(t->param, NULL, t, NULL, NULL, 408/*Request Timeout*/); else t->onreply(t->param, NULL, t, 408/*Request Timeout*/); // ignore return value, nothing to do // post-handle if (t->onhandle) { t->onhandle(t, 408); t->onhandle = NULL; } } locker_unlock(&t->locker); sip_uac_transaction_release(t); } static void sip_uac_transaction_onterminate(void* usrptr) { struct sip_uac_transaction_t* t; t = (struct sip_uac_transaction_t*)usrptr; locker_lock(&t->locker); if (SIP_UAC_TRANSACTION_TERMINATED != t->status) sip_uac_transaction_terminate(t); locker_unlock(&t->locker); sip_uac_transaction_release(t); } int sip_uac_transaction_send(struct sip_uac_transaction_t* t) { // link to transactions // unlink on tranaction terminate sip_uac_link_transaction(t->agent, t); t->retries = 1; // reset retry times if(!t->reliable) // UDP t->timera = sip_uac_start_timer(t->agent, t, TIMER_A, sip_uac_transaction_onretransmission); sip_uac_transaction_timeout(t, TIMER_B); assert(t->timerb && (t->reliable || t->timera)); // TODO: return 503/*Service Unavailable*/ return sip_uac_transaction_dosend(t); } // calling/trying + proceeding timeout int sip_uac_transaction_timeout(struct sip_uac_transaction_t* t, int timeout) { // try stop timer B assert(t->status <= SIP_UAC_TRANSACTION_PROCEEDING); sip_uac_stop_timer(t->agent, t, &t->timerb); // restart timer B assert(NULL == t->timerb); t->timerb = sip_uac_start_timer(t->agent, t, timeout, sip_uac_transaction_ontimeout); assert(t->timerb); return 0; } // wait for network cache data int sip_uac_transaction_timewait(struct sip_uac_transaction_t* t, int timeout) { if (SIP_UAC_TRANSACTION_TERMINATED == t->status) return 0; // try stop timer B assert(t->status > SIP_UAC_TRANSACTION_PROCEEDING); sip_uac_stop_timer(t->agent, t, &t->timerb); assert(NULL == t->timerd); t->timerd = sip_uac_start_timer(t->agent, t, timeout, sip_uac_transaction_onterminate); assert(t->timerd); return 0; }