262 lines
7.5 KiB
C++
262 lines
7.5 KiB
C++
#include "sip-uas-transaction.h"
|
|
#include "sip-transport.h"
|
|
#include "sip-internal.h"
|
|
|
|
int sip_uas_link_transaction(struct sip_agent_t* sip, struct sip_uas_transaction_t* t);
|
|
int sip_uas_unlink_transaction(struct sip_agent_t* sip, struct sip_uas_transaction_t* t);
|
|
|
|
struct sip_uas_transaction_t* sip_uas_transaction_create(struct sip_agent_t* sip, const struct sip_message_t* req, const struct sip_dialog_t* dialog, void* param)
|
|
{
|
|
struct sip_uas_transaction_t* t;
|
|
t = (struct sip_uas_transaction_t*)calloc(1, sizeof(*t));
|
|
if (NULL == t) return NULL;
|
|
|
|
t->reply = sip_message_create(SIP_MESSAGE_REPLY);
|
|
if (0 != sip_message_init3(t->reply, req, dialog))
|
|
{
|
|
sip_message_destroy(t->reply);
|
|
free(t);
|
|
return NULL;
|
|
}
|
|
|
|
t->ref = 1; // for agent uac link, don't destory it
|
|
t->agent = sip;
|
|
t->initparam = param;
|
|
LIST_INIT_HEAD(&t->link);
|
|
locker_create(&t->locker);
|
|
t->status = SIP_UAS_TRANSACTION_INIT;
|
|
|
|
// 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;
|
|
|
|
// Life cycle: from create -> destroy
|
|
sip_uas_link_transaction(sip, t);
|
|
sip_uas_transaction_timeout(t, TIMER_H); // trying timeout
|
|
atomic_increment32(&s_gc.uas);
|
|
return t;
|
|
}
|
|
|
|
int sip_uas_transaction_release(struct sip_uas_transaction_t* t)
|
|
{
|
|
assert(t->ref > 0);
|
|
if (0 != atomic_decrement32(&t->ref))
|
|
return 0;
|
|
|
|
assert(0 == t->ref);
|
|
assert(NULL == t->timerg);
|
|
assert(NULL == t->timerh);
|
|
assert(NULL == t->timerij);
|
|
assert(t->link.next == t->link.prev);// unlink on termernate
|
|
|
|
// MUST: destroy t->reply after sip_uas_del_transaction
|
|
//sip_message_destroy((struct sip_message_t*)t->req);
|
|
|
|
if(t->ondestroy)
|
|
{
|
|
t->ondestroy(t->ondestroyparam);
|
|
t->ondestroy = NULL;
|
|
}
|
|
|
|
// MUST unlink before reply destroy
|
|
//sip_uas_unlink_transaction(t->agent, t);
|
|
|
|
if (t->reply)
|
|
{
|
|
sip_message_destroy(t->reply);
|
|
t->reply = NULL;
|
|
}
|
|
|
|
if(t->dialog)
|
|
{
|
|
sip_dialog_release(t->dialog);
|
|
t->dialog = NULL;
|
|
}
|
|
|
|
locker_destroy(&t->locker);
|
|
free(t);
|
|
atomic_decrement32(&s_gc.uas);
|
|
return 0;
|
|
}
|
|
|
|
int sip_uas_transaction_addref(struct sip_uas_transaction_t* t)
|
|
{
|
|
int r;
|
|
r = atomic_increment32(&t->ref);
|
|
assert(r > 1);
|
|
return r;
|
|
}
|
|
|
|
//int sip_uas_transaction_destroy(struct sip_uas_transaction_t* t)
|
|
//{
|
|
// // unlink from uas
|
|
// sip_uas_del_transaction(t->uas, t);
|
|
//
|
|
// return sip_uas_transaction_release(t);
|
|
//}
|
|
|
|
int sip_uas_transaction_handler(struct sip_uas_transaction_t* t, const struct sip_message_t* req, void* param)
|
|
{
|
|
//assert(t->param == t->initparam);
|
|
if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_CANCEL))
|
|
{
|
|
return sip_uas_oncancel(t, req, param);
|
|
}
|
|
else if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_BYE))
|
|
{
|
|
return sip_uas_onbye(t, req, param);
|
|
}
|
|
else if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_PRACK))
|
|
{
|
|
return sip_uas_onprack(t, req, param);
|
|
}
|
|
else if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_UPDATE))
|
|
{
|
|
return sip_uas_onupdate(t, req, param);
|
|
}
|
|
else if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_INFO))
|
|
{
|
|
return sip_uas_oninfo(t, req, param);
|
|
}
|
|
else if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_REGISTER))
|
|
{
|
|
return sip_uas_onregister(t, req, param);
|
|
}
|
|
else if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_OPTIONS))
|
|
{
|
|
return sip_uas_onoptions(t, req, param);
|
|
}
|
|
else if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_SUBSCRIBE))
|
|
{
|
|
return sip_uas_onsubscribe(t, req, param);
|
|
}
|
|
else if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_NOTIFY))
|
|
{
|
|
return sip_uas_onnotify(t, req, param);
|
|
}
|
|
else if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_PUBLISH))
|
|
{
|
|
return sip_uas_onpublish(t, req, param);
|
|
}
|
|
else if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_REFER))
|
|
{
|
|
return sip_uas_onrefer(t, req, param);
|
|
}
|
|
else if (0 == cstrcasecmp(&req->u.c.method, SIP_METHOD_MESSAGE))
|
|
{
|
|
return t->handler->onmessage ? t->handler->onmessage(param, req, t, req->payload, req->size) : 0;
|
|
}
|
|
else
|
|
{
|
|
// 8.2.1 Method Inspection (p46)
|
|
return sip_uas_reply(t, 405/*Method Not Allowed*/, NULL, 0, param);
|
|
}
|
|
}
|
|
|
|
int sip_uas_transaction_dosend(struct sip_uas_transaction_t* t, void* param)
|
|
{
|
|
const struct sip_via_t *via;
|
|
|
|
assert(t->size > 0);
|
|
|
|
// 18.2.2 Sending Responses (p146)
|
|
// The server transport uses the value of the top Via header field in
|
|
// order to determine where to send a response.
|
|
|
|
// If the host portion of the "sent-by" parameter
|
|
// contains a domain name, or if it contains an IP address that differs
|
|
// from the packet source address, the server MUST add a "received"
|
|
// parameter to that Via header field value.
|
|
// Via: SIP/2.0/UDP bobspc.biloxi.com:5060;received=192.0.2.4
|
|
|
|
via = sip_vias_get(&t->reply->vias, 0);
|
|
if (!via) return -1; // invalid via
|
|
|
|
return t->handler->send(param, &via->protocol, &via->host, &via->received, via->rport, t->data, t->size);
|
|
}
|
|
|
|
int sip_uas_transaction_terminated(struct sip_uas_transaction_t* t)
|
|
{
|
|
t->status = SIP_UAS_TRANSACTION_TERMINATED;
|
|
|
|
sip_uas_stop_timer(t->agent, t, &t->timerh);
|
|
sip_uas_stop_timer(t->agent, t, &t->timerg);
|
|
sip_uas_stop_timer(t->agent, t, &t->timerij);
|
|
|
|
sip_uas_unlink_transaction(t->agent, t);
|
|
return 0;
|
|
}
|
|
|
|
void sip_uas_transaction_ontimeout(void* usrptr)
|
|
{
|
|
char ptr[256];
|
|
struct cstring_t id;
|
|
struct sip_uas_transaction_t* t;
|
|
t = (struct sip_uas_transaction_t*)usrptr;
|
|
locker_lock(&t->locker);
|
|
sip_uas_stop_timer(t->agent, t, &t->timerh); // hijack free timer only, don't release transaction
|
|
|
|
if (t->status < SIP_UAS_TRANSACTION_CONFIRMED)
|
|
{
|
|
sip_uas_transaction_terminated(t);
|
|
|
|
// TODO:
|
|
// If a UAS generates a 2xx response and never receives an ACK, it
|
|
// SHOULD generate a BYE to terminate the dialog.
|
|
|
|
// 8.1.3.1 Transaction Layer Errors (p42)
|
|
if (t->dialog)
|
|
{
|
|
sip_dialog_id(&id, t->dialog, ptr, sizeof(ptr));
|
|
t->handler->onack(t->initparam, NULL, t, (t->dialog && t->dialog->state == DIALOG_CONFIRMED) ? t->dialog : NULL, (t->dialog && t->dialog->state == DIALOG_CONFIRMED) ? &id : NULL, 408/*Invite Timeout*/, NULL, 0);
|
|
}
|
|
}
|
|
|
|
locker_unlock(&t->locker);
|
|
sip_uas_transaction_release(t);
|
|
}
|
|
|
|
static void sip_uas_transaction_onterminated(void* usrptr)
|
|
{
|
|
struct sip_uas_transaction_t* t;
|
|
t = (struct sip_uas_transaction_t*)usrptr;
|
|
|
|
locker_lock(&t->locker);
|
|
sip_uas_stop_timer(t->agent, t, &t->timerij); // hijack free timer only, don't release transaction
|
|
if(SIP_UAS_TRANSACTION_TERMINATED != t->status)
|
|
sip_uas_transaction_terminated(t);
|
|
locker_unlock(&t->locker);
|
|
sip_uas_transaction_release(t);
|
|
}
|
|
|
|
// trying + proceeding timeout
|
|
int sip_uas_transaction_timeout(struct sip_uas_transaction_t* t, int timeout)
|
|
{
|
|
// try stop timer H
|
|
assert(t->status <= SIP_UAS_TRANSACTION_CONFIRMED);
|
|
sip_uas_stop_timer(t->agent, t, &t->timerh);
|
|
|
|
// restart timer H
|
|
assert(NULL == t->timerh);
|
|
t->timerh = sip_uas_start_timer(t->agent, t, timeout, sip_uas_transaction_ontimeout);
|
|
assert(t->timerh);
|
|
return 0;
|
|
}
|
|
|
|
// wait for network cache data
|
|
int sip_uas_transaction_timewait(struct sip_uas_transaction_t* t, int timeout)
|
|
{
|
|
if (SIP_UAS_TRANSACTION_TERMINATED == t->status)
|
|
return 0;
|
|
|
|
sip_uas_stop_timer(t->agent, t, &t->timerh);
|
|
sip_uas_stop_timer(t->agent, t, &t->timerg);
|
|
|
|
assert(NULL == t->timerij);
|
|
t->timerij = sip_uas_start_timer(t->agent, t, timeout, sip_uas_transaction_onterminated);
|
|
return t->timerij ? 0 : -1;
|
|
}
|