blob: 4c3aa6f243edf3e290e908aac07bf5947c599f9c [file] [log] [blame]
/**
* @file sipsess/reply.c SIP Session Reply
*
* 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_tmr.h>
#include <re_msg.h>
#include <re_sip.h>
#include <re_sipsess.h>
#include "sipsess.h"
struct sipsess_reply {
struct le le;
struct tmr tmr;
struct tmr tmrg;
const struct sip_msg *msg;
struct mbuf *mb;
struct sipsess *sess;
bool awaiting_answer;
uint32_t seq;
uint32_t txc;
};
static void destructor(void *arg)
{
struct sipsess_reply *reply = arg;
list_unlink(&reply->le);
tmr_cancel(&reply->tmr);
tmr_cancel(&reply->tmrg);
mem_deref((void *)reply->msg);
mem_deref(reply->mb);
}
static void tmr_handler(void *arg)
{
struct sipsess_reply *reply = arg;
struct sipsess *sess = reply->sess;
mem_deref(reply);
/* wait for all pending ACKs */
if (sess->replyl.head)
return;
/* we want to send bye */
sess->established = true;
if (!sess->terminated)
sipsess_terminate(sess, ETIMEDOUT, NULL);
else
mem_deref(sess);
}
static void retransmit_handler(void *arg)
{
struct sipsess_reply *reply = arg;
(void)sip_send(reply->sess->sip, reply->msg->sock, reply->msg->tp,
&reply->msg->src, reply->mb);
reply->txc++;
tmr_start(&reply->tmrg, MIN(SIP_T1<<reply->txc, SIP_T2),
retransmit_handler, reply);
}
int sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg,
uint16_t scode, const char *reason, struct mbuf *desc,
const char *fmt, va_list *ap)
{
struct sipsess_reply *reply;
struct sip_contact contact;
int err = ENOMEM;
reply = mem_zalloc(sizeof(*reply), destructor);
if (!reply)
goto out;
list_append(&sess->replyl, &reply->le, reply);
reply->seq = msg->cseq.num;
reply->msg = mem_ref((void *)msg);
reply->sess = sess;
sip_contact_set(&contact, sess->cuser, &msg->dst, msg->tp);
err = sip_treplyf(&sess->st, &reply->mb, sess->sip,
msg, true, scode, reason,
"%H"
"%v"
"%s%s%s"
"Content-Length: %zu\r\n"
"\r\n"
"%b",
sip_contact_print, &contact,
fmt, ap,
desc ? "Content-Type: " : "",
desc ? sess->ctype : "",
desc ? "\r\n" : "",
desc ? mbuf_get_left(desc) : (size_t)0,
desc ? mbuf_buf(desc) : NULL,
desc ? mbuf_get_left(desc) : (size_t)0);
if (err)
goto out;
tmr_start(&reply->tmr, 64 * SIP_T1, tmr_handler, reply);
tmr_start(&reply->tmrg, SIP_T1, retransmit_handler, reply);
if (!mbuf_get_left(msg->mb) && desc) {
reply->awaiting_answer = true;
sess->awaiting_answer = true;
}
out:
if (err) {
sess->st = mem_deref(sess->st);
mem_deref(reply);
}
return err;
}
static bool cmp_handler(struct le *le, void *arg)
{
struct sipsess_reply *reply = le->data;
const struct sip_msg *msg = arg;
return msg->cseq.num == reply->seq;
}
int sipsess_reply_ack(struct sipsess *sess, const struct sip_msg *msg,
bool *awaiting_answer)
{
struct sipsess_reply *reply;
reply = list_ledata(list_apply(&sess->replyl, false, cmp_handler,
(void *)msg));
if (!reply)
return ENOENT;
*awaiting_answer = reply->awaiting_answer;
mem_deref(reply);
return 0;
}