blob: 13a875c2ee13183695a9cf06232f6354d14728d7 [file] [log] [blame]
/**
* @file strans.c SIP Server Transaction
*
* Copyright (C) 2010 Creytiv.com
*/
#include <re_types.h>
#include <re_mem.h>
#include <re_mbuf.h>
#include <re_sa.h>
#include <re_list.h>
#include <re_hash.h>
#include <re_fmt.h>
#include <re_uri.h>
#include <re_sys.h>
#include <re_tmr.h>
#include <re_udp.h>
#include <re_msg.h>
#include <re_sip.h>
#include "sip.h"
enum state {
TRYING,
PROCEEDING,
ACCEPTED,
COMPLETED,
CONFIRMED,
};
struct sip_strans {
struct le he;
struct le he_mrg;
struct tmr tmr;
struct tmr tmrg;
struct sa dst;
struct sip *sip;
struct sip_msg *msg;
struct mbuf *mb;
sip_cancel_h *cancelh;
void *arg;
enum state state;
uint32_t txc;
bool invite;
};
static void destructor(void *arg)
{
struct sip_strans *st = arg;
hash_unlink(&st->he);
hash_unlink(&st->he_mrg);
tmr_cancel(&st->tmr);
tmr_cancel(&st->tmrg);
mem_deref(st->msg);
mem_deref(st->mb);
}
static bool strans_cmp(const struct sip_msg *msg1, const struct sip_msg *msg2)
{
if (pl_cmp(&msg1->via.branch, &msg2->via.branch))
return false;
if (pl_cmp(&msg1->via.sentby, &msg2->via.sentby))
return false;
return true;
}
static bool cmp_handler(struct le *le, void *arg)
{
struct sip_strans *st = le->data;
const struct sip_msg *msg = arg;
if (!strans_cmp(st->msg, msg))
return false;
if (pl_cmp(&st->msg->cseq.met, &msg->cseq.met))
return false;
return true;
}
static bool cmp_ack_handler(struct le *le, void *arg)
{
struct sip_strans *st = le->data;
const struct sip_msg *msg = arg;
if (!strans_cmp(st->msg, msg))
return false;
if (pl_strcmp(&st->msg->cseq.met, "INVITE"))
return false;
return true;
}
static bool cmp_cancel_handler(struct le *le, void *arg)
{
struct sip_strans *st = le->data;
const struct sip_msg *msg = arg;
if (!strans_cmp(st->msg, msg))
return false;
if (!pl_strcmp(&st->msg->cseq.met, "CANCEL"))
return false;
return true;
}
static bool cmp_merge_handler(struct le *le, void *arg)
{
struct sip_strans *st = le->data;
const struct sip_msg *msg = arg;
if (pl_cmp(&st->msg->cseq.met, &msg->cseq.met))
return false;
if (st->msg->cseq.num != msg->cseq.num)
return false;
if (pl_cmp(&st->msg->callid, &msg->callid))
return false;
if (pl_cmp(&st->msg->from.tag, &msg->from.tag))
return false;
if (pl_cmp(&st->msg->ruri, &msg->ruri))
return false;
return true;
}
static void dummy_handler(void *arg)
{
(void)arg;
}
static void tmr_handler(void *arg)
{
struct sip_strans *st = arg;
mem_deref(st);
}
static void retransmit_handler(void *arg)
{
struct sip_strans *st = arg;
(void)sip_send(st->sip, st->msg->sock, st->msg->tp, &st->dst,
st->mb);
st->txc++;
tmr_start(&st->tmrg, MIN(SIP_T1<<st->txc, SIP_T2), retransmit_handler,
st);
}
static bool ack_handler(struct sip *sip, const struct sip_msg *msg)
{
struct sip_strans *st;
st = list_ledata(hash_lookup(sip->ht_strans,
hash_joaat_pl(&msg->via.branch),
cmp_ack_handler, (void *)msg));
if (!st)
return false;
switch (st->state) {
case ACCEPTED:
/* make sure ACKs for 2xx are passed to TU */
return false;
case COMPLETED:
if (sip_transp_reliable(st->msg->tp)) {
mem_deref(st);
break;
}
tmr_start(&st->tmr, SIP_T4, tmr_handler, st);
tmr_cancel(&st->tmrg);
st->state = CONFIRMED;
break;
default:
break;
}
return true;
}
static bool cancel_handler(struct sip *sip, const struct sip_msg *msg)
{
struct sip_strans *st;
st = list_ledata(hash_lookup(sip->ht_strans,
hash_joaat_pl(&msg->via.branch),
cmp_cancel_handler, (void *)msg));
if (!st)
return false;
((struct sip_msg *)msg)->tag = st->msg->tag;
(void)sip_reply(sip, msg, 200, "OK");
switch (st->state) {
case TRYING:
case PROCEEDING:
st->cancelh(st->arg);
break;
default:
break;
}
return true;
}
static bool request_handler(const struct sip_msg *msg, void *arg)
{
struct sip_strans *st;
struct sip *sip = arg;
if (!pl_strcmp(&msg->met, "ACK"))
return ack_handler(sip, msg);
st = list_ledata(hash_lookup(sip->ht_strans,
hash_joaat_pl(&msg->via.branch),
cmp_handler, (void *)msg));
if (st) {
switch (st->state) {
case PROCEEDING:
case COMPLETED:
(void)sip_send(st->sip, st->msg->sock, st->msg->tp,
&st->dst, st->mb);
break;
default:
break;
}
return true;
}
else if (!pl_isset(&msg->to.tag)) {
st = list_ledata(hash_lookup(sip->ht_strans_mrg,
hash_joaat_pl(&msg->callid),
cmp_merge_handler, (void *)msg));
if (st) {
(void)sip_reply(sip, msg, 482, "Loop Detected");
return true;
}
}
if (!pl_strcmp(&msg->met, "CANCEL"))
return cancel_handler(sip, msg);
return false;
}
/**
* Allocate a SIP Server Transaction
*
* @param stp Pointer to allocated SIP Server Transaction
* @param sip SIP Stack instance
* @param msg Incoming SIP message
* @param cancelh Cancel handler
* @param arg Handler argument
*
* @return 0 if success, otherwise errorcode
*/
int sip_strans_alloc(struct sip_strans **stp, struct sip *sip,
const struct sip_msg *msg, sip_cancel_h *cancelh,
void *arg)
{
struct sip_strans *st;
if (!stp || !sip || !msg)
return EINVAL;
st = mem_zalloc(sizeof(*st), destructor);
if (!st)
return ENOMEM;
hash_append(sip->ht_strans, hash_joaat_pl(&msg->via.branch),
&st->he, st);
hash_append(sip->ht_strans_mrg, hash_joaat_pl(&msg->callid),
&st->he_mrg, st);
st->invite = !pl_strcmp(&msg->met, "INVITE");
st->msg = mem_ref((void *)msg);
st->state = TRYING;
st->cancelh = cancelh ? cancelh : dummy_handler;
st->arg = arg;
st->sip = sip;
*stp = st;
return 0;
}
/**
* Reply using a SIP Server Transaction
*
* @param stp Pointer to allocated SIP Server Transaction
* @param sip SIP Stack instance
* @param msg Incoming SIP message
* @param dst Destination network address
* @param scode Response status code
* @param mb Buffer containing SIP response
*
* @return 0 if success, otherwise errorcode
*/
int sip_strans_reply(struct sip_strans **stp, struct sip *sip,
const struct sip_msg *msg, const struct sa *dst,
uint16_t scode, struct mbuf *mb)
{
struct sip_strans *st = NULL;
int err;
if (!sip || !mb || !dst || (scode < 200 && !stp))
return EINVAL;
if (stp)
st = *stp;
if (!st) {
err = sip_strans_alloc(&st, sip, msg, NULL, NULL);
if (err)
return err;
}
mem_deref(st->mb);
st->mb = mem_ref(mb);
st->dst = *dst;
err = sip_send(sip, st->msg->sock, st->msg->tp, dst, mb);
if (stp)
*stp = (err || scode >= 200) ? NULL : st;
if (err) {
mem_deref(st);
return err;
}
if (st->invite) {
if (scode < 200) {
st->state = PROCEEDING;
}
else if (scode < 300) {
tmr_start(&st->tmr, 64 * SIP_T1, tmr_handler, st);
st->state = ACCEPTED;
}
else {
tmr_start(&st->tmr, 64 * SIP_T1, tmr_handler, st);
st->state = COMPLETED;
if (!sip_transp_reliable(st->msg->tp))
tmr_start(&st->tmrg, SIP_T1,
retransmit_handler, st);
}
}
else {
if (scode < 200) {
st->state = PROCEEDING;
}
else {
if (!sip_transp_reliable(st->msg->tp)) {
tmr_start(&st->tmr, 64 * SIP_T1, tmr_handler,
st);
st->state = COMPLETED;
}
else {
mem_deref(st);
}
}
}
return 0;
}
int sip_strans_init(struct sip *sip, uint32_t sz)
{
int err;
err = sip_listen(NULL, sip, true, request_handler, sip);
if (err)
return err;
err = hash_alloc(&sip->ht_strans_mrg, sz);
if (err)
return err;
return hash_alloc(&sip->ht_strans, sz);
}
static const char *statename(enum state state)
{
switch (state) {
case TRYING: return "TRYING";
case PROCEEDING: return "PROCEEDING";
case ACCEPTED: return "ACCEPTED";
case COMPLETED: return "COMPLETED";
case CONFIRMED: return "CONFIRMED";
default: return "???";
}
}
static bool debug_handler(struct le *le, void *arg)
{
struct sip_strans *st = le->data;
struct re_printf *pf = arg;
(void)re_hprintf(pf, " %-10r %-10s %2llus (%r)\n",
&st->msg->met,
statename(st->state),
tmr_get_expire(&st->tmr)/1000,
&st->msg->via.branch);
return false;
}
int sip_strans_debug(struct re_printf *pf, const struct sip *sip)
{
int err;
err = re_hprintf(pf, "server transactions:\n");
hash_apply(sip->ht_strans, debug_handler, pf);
return err;
}