stream-deploy/ZLM/3rdpart/media-server/libsip/src/uas/sip-uas-transaction.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;
}