blob: eb2e26fc79a034832006b439a1644f87e0d5391d [file] [log] [blame]
/**
* @file sip/reply.c SIP 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_fmt.h>
#include <re_uri.h>
#include <re_udp.h>
#include <re_msg.h>
#include <re_sip.h>
#include "sip.h"
static int vreplyf(struct sip_strans **stp, struct mbuf **mbp, bool trans,
struct sip *sip, const struct sip_msg *msg, bool rec_route,
uint16_t scode, const char *reason,
const char *fmt, va_list ap)
{
bool rport = false;
uint32_t viac = 0;
struct mbuf *mb;
struct sa dst;
struct le *le;
int err;
if (!sip || !msg || !reason)
return EINVAL;
if (!pl_strcmp(&msg->met, "ACK"))
return 0;
mb = mbuf_alloc(1024);
if (!mb) {
err = ENOMEM;
goto out;
}
err = mbuf_printf(mb, "SIP/2.0 %u %s\r\n", scode, reason);
for (le = msg->hdrl.head; le; le = le->next) {
struct sip_hdr *hdr = le->data;
struct pl rp;
switch (hdr->id) {
case SIP_HDR_VIA:
err |= mbuf_printf(mb, "%r: ", &hdr->name);
if (viac++) {
err |= mbuf_printf(mb, "%r\r\n", &hdr->val);
break;
}
if (!msg_param_exists(&msg->via.params, "rport", &rp)){
err |= mbuf_write_pl_skip(mb, &hdr->val, &rp);
err |= mbuf_printf(mb, ";rport=%u",
sa_port(&msg->src));
rport = true;
}
else
err |= mbuf_write_pl(mb, &hdr->val);
if (rport || !sa_cmp(&msg->src, &msg->via.addr,
SA_ADDR))
err |= mbuf_printf(mb, ";received=%j",
&msg->src);
err |= mbuf_write_str(mb, "\r\n");
break;
case SIP_HDR_TO:
err |= mbuf_printf(mb, "%r: %r", &hdr->name,
&hdr->val);
if (!pl_isset(&msg->to.tag) && scode > 100)
err |= mbuf_printf(mb, ";tag=%016llx",
msg->tag);
err |= mbuf_write_str(mb, "\r\n");
break;
case SIP_HDR_RECORD_ROUTE:
if (!rec_route)
break;
/*@fallthrough@*/
case SIP_HDR_FROM:
case SIP_HDR_CALL_ID:
case SIP_HDR_CSEQ:
err |= mbuf_printf(mb, "%r: %r\r\n",
&hdr->name, &hdr->val);
break;
default:
break;
}
}
if (sip->software)
err |= mbuf_printf(mb, "Server: %s\r\n", sip->software);
if (fmt)
err |= mbuf_vprintf(mb, fmt, ap);
else
err |= mbuf_printf(mb, "Content-Length: 0\r\n\r\n");
if (err)
goto out;
mb->pos = 0;
sip_reply_addr(&dst, msg, rport);
if (trans) {
err = sip_strans_reply(stp, sip, msg, &dst, scode, mb);
}
else {
err = sip_send(sip, msg->sock, msg->tp, &dst, mb);
}
out:
if (err && stp)
*stp = mem_deref(*stp);
if (!err && mbp)
*mbp = mb;
else
mem_deref(mb);
return err;
}
/**
* Formatted reply using Server Transaction
*
* @param stp Pointer to allocated SIP Server Transaction (optional)
* @param mbp Pointer to allocated SIP message buffer (optional)
* @param sip SIP Stack instance
* @param msg Incoming SIP message
* @param rec_route True to copy Record-Route headers
* @param scode Response status code
* @param reason Response reason phrase
* @param fmt Additional formatted SIP headers and body, otherwise NULL
*
* @return 0 if success, otherwise errorcode
*/
int sip_treplyf(struct sip_strans **stp, struct mbuf **mbp, struct sip *sip,
const struct sip_msg *msg, bool rec_route, uint16_t scode,
const char *reason, const char *fmt, ...)
{
va_list ap;
int err;
va_start(ap, fmt);
err = vreplyf(stp, mbp, true, sip, msg, rec_route, scode, reason,
fmt, ap);
va_end(ap);
return err;
}
/**
* Reply using Server Transaction
*
* @param stp Pointer to allocated SIP Server Transaction (optional)
* @param sip SIP Stack instance
* @param msg Incoming SIP message
* @param scode Response status code
* @param reason Response reason phrase
*
* @return 0 if success, otherwise errorcode
*/
int sip_treply(struct sip_strans **stp, struct sip *sip,
const struct sip_msg *msg, uint16_t scode, const char *reason)
{
return sip_treplyf(stp, NULL, sip, msg, false, scode, reason, NULL);
}
/**
* Stateless formatted reply
*
* @param sip SIP Stack instance
* @param msg Incoming SIP message
* @param scode Response status code
* @param reason Response reason phrase
* @param fmt Additional formatted SIP headers and body, otherwise NULL
*
* @return 0 if success, otherwise errorcode
*/
int sip_replyf(struct sip *sip, const struct sip_msg *msg, uint16_t scode,
const char *reason, const char *fmt, ...)
{
va_list ap;
int err;
va_start(ap, fmt);
err = vreplyf(NULL, NULL, false, sip, msg, false, scode, reason,
fmt, ap);
va_end(ap);
return err;
}
/**
* Stateless reply
*
* @param sip SIP Stack instance
* @param msg Incoming SIP message
* @param scode Response status code
* @param reason Response reason phrase
*
* @return 0 if success, otherwise errorcode
*/
int sip_reply(struct sip *sip, const struct sip_msg *msg, uint16_t scode,
const char *reason)
{
return sip_replyf(sip, msg, scode, reason, NULL);
}
/**
* Get the reply address from a SIP message
*
* @param addr Network address, set on return
* @param msg SIP message
* @param rport Rport value
*/
void sip_reply_addr(struct sa *addr, const struct sip_msg *msg, bool rport)
{
uint16_t port;
struct pl pl;
if (!addr || !msg)
return;
port = sa_port(&msg->via.addr);
*addr = msg->src;
switch (msg->tp) {
case SIP_TRANSP_UDP:
if (!msg_param_decode(&msg->via.params, "maddr", &pl)) {
(void)sa_set(addr, &pl,sip_transp_port(msg->tp, port));
break;
}
if (rport)
break;
/*@fallthrough@*/
case SIP_TRANSP_TCP:
case SIP_TRANSP_TLS:
sa_set_port(addr, sip_transp_port(msg->tp, port));
break;
default:
break;
}
}