Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b
Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/sip/dialog.c b/src/sip/dialog.c
new file mode 100644
index 0000000..5863e20
--- /dev/null
+++ b/src/sip/dialog.c
@@ -0,0 +1,659 @@
+/**
+ * @file dialog.c SIP Dialog
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#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 {
+ ROUTE_OFFSET = 7,
+ X64_STRSIZE = 17,
+};
+
+struct sip_dialog {
+ struct uri route;
+ struct mbuf *mb;
+ char *callid;
+ char *ltag;
+ char *rtag;
+ char *uri;
+ uint32_t hash;
+ uint32_t lseq;
+ uint32_t rseq;
+ size_t cpos;
+};
+
+
+struct route_enc {
+ struct mbuf *mb;
+ size_t end;
+};
+
+
+static int x64_strdup(char **strp, uint64_t val)
+{
+ char *str;
+
+ str = mem_alloc(X64_STRSIZE, NULL);
+ if (!str)
+ return ENOMEM;
+
+ (void)re_snprintf(str, X64_STRSIZE, "%016llx", val);
+
+ *strp = str;
+
+ return 0;
+}
+
+
+static void destructor(void *arg)
+{
+ struct sip_dialog *dlg = arg;
+
+ mem_deref(dlg->callid);
+ mem_deref(dlg->ltag);
+ mem_deref(dlg->rtag);
+ mem_deref(dlg->uri);
+ mem_deref(dlg->mb);
+}
+
+
+/**
+ * Allocate a SIP Dialog
+ *
+ * @param dlgp Pointer to allocated SIP Dialog
+ * @param uri Target URI
+ * @param to_uri To URI
+ * @param from_name From displayname (optional)
+ * @param from_uri From URI
+ * @param routev Route vector
+ * @param routec Route count
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_dialog_alloc(struct sip_dialog **dlgp,
+ const char *uri, const char *to_uri,
+ const char *from_name, const char *from_uri,
+ const char *routev[], uint32_t routec)
+{
+ const uint64_t ltag = rand_u64();
+ struct sip_dialog *dlg;
+ struct sip_addr addr;
+ size_t rend = 0;
+ struct pl pl;
+ uint32_t i;
+ int err;
+
+ if (!dlgp || !uri || !to_uri || !from_uri)
+ return EINVAL;
+
+ dlg = mem_zalloc(sizeof(*dlg), destructor);
+ if (!dlg)
+ return ENOMEM;
+
+ dlg->hash = hash_fast_str(from_uri);
+ dlg->lseq = rand_u16();
+
+ err = str_dup(&dlg->uri, uri);
+ if (err)
+ goto out;
+
+ err = x64_strdup(&dlg->callid, rand_u64());
+ if (err)
+ goto out;
+
+ err = x64_strdup(&dlg->ltag, ltag);
+ if (err)
+ goto out;
+
+ dlg->mb = mbuf_alloc(512);
+ if (!dlg->mb) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ for (i=0; i<routec; i++) {
+ err |= mbuf_printf(dlg->mb, "Route: <%s;lr>\r\n", routev[i]);
+ if (i == 0)
+ rend = dlg->mb->pos - 2;
+ }
+ err |= mbuf_printf(dlg->mb, "To: <%s>\r\n", to_uri);
+ dlg->cpos = dlg->mb->pos;
+ err |= mbuf_printf(dlg->mb, "From: %s%s%s<%s>;tag=%016llx\r\n",
+ from_name ? "\"" : "", from_name,
+ from_name ? "\" " : "",
+ from_uri, ltag);
+ if (err)
+ goto out;
+
+ dlg->mb->pos = 0;
+
+ if (rend) {
+ pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;
+ pl.l = rend - ROUTE_OFFSET;
+ err = sip_addr_decode(&addr, &pl);
+ dlg->route = addr.uri;
+ }
+ else {
+ pl_set_str(&pl, dlg->uri);
+ err = uri_decode(&dlg->route, &pl);
+ }
+
+ out:
+ if (err)
+ mem_deref(dlg);
+ else
+ *dlgp = dlg;
+
+ return err;
+}
+
+
+static bool record_route_handler(const struct sip_hdr *hdr,
+ const struct sip_msg *msg,
+ void *arg)
+{
+ struct route_enc *renc = arg;
+ (void)msg;
+
+ if (mbuf_printf(renc->mb, "Route: %r\r\n", &hdr->val))
+ return true;
+
+ if (!renc->end)
+ renc->end = renc->mb->pos - 2;
+
+ return false;
+}
+
+
+/**
+ * Accept and create a SIP Dialog from an incoming SIP Message
+ *
+ * @param dlgp Pointer to allocated SIP Dialog
+ * @param msg SIP Message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_dialog_accept(struct sip_dialog **dlgp, const struct sip_msg *msg)
+{
+ const struct sip_hdr *contact;
+ struct sip_dialog *dlg;
+ struct route_enc renc;
+ struct sip_addr addr;
+ struct pl pl;
+ int err;
+
+ if (!dlgp || !msg || !msg->req)
+ return EINVAL;
+
+ contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
+
+ if (!contact || !msg->callid.p)
+ return EBADMSG;
+
+ if (sip_addr_decode(&addr, &contact->val))
+ return EBADMSG;
+
+ dlg = mem_zalloc(sizeof(*dlg), destructor);
+ if (!dlg)
+ return ENOMEM;
+
+ dlg->hash = rand_u32();
+ dlg->lseq = rand_u16();
+ dlg->rseq = msg->cseq.num;
+
+ err = pl_strdup(&dlg->uri, &addr.auri);
+ if (err)
+ goto out;
+
+ err = pl_strdup(&dlg->callid, &msg->callid);
+ if (err)
+ goto out;
+
+ err = x64_strdup(&dlg->ltag, msg->tag);
+ if (err)
+ goto out;
+
+ err = pl_strdup(&dlg->rtag, &msg->from.tag);
+ if (err)
+ goto out;
+
+ dlg->mb = mbuf_alloc(512);
+ if (!dlg->mb) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ renc.mb = dlg->mb;
+ renc.end = 0;
+
+ err |= sip_msg_hdr_apply(msg, true, SIP_HDR_RECORD_ROUTE,
+ record_route_handler, &renc) ? ENOMEM : 0;
+ err |= mbuf_printf(dlg->mb, "To: %r\r\n", &msg->from.val);
+ err |= mbuf_printf(dlg->mb, "From: %r;tag=%016llx\r\n", &msg->to.val,
+ msg->tag);
+ if (err)
+ goto out;
+
+ dlg->mb->pos = 0;
+
+ if (renc.end) {
+ pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;
+ pl.l = renc.end - ROUTE_OFFSET;
+ err = sip_addr_decode(&addr, &pl);
+ dlg->route = addr.uri;
+ }
+ else {
+ pl_set_str(&pl, dlg->uri);
+ err = uri_decode(&dlg->route, &pl);
+ }
+
+ out:
+ if (err)
+ mem_deref(dlg);
+ else
+ *dlgp = dlg;
+
+ return err;
+}
+
+
+/**
+ * Initialize a SIP Dialog from an incoming SIP Message
+ *
+ * @param dlg SIP Dialog to initialize
+ * @param msg SIP Message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg)
+{
+ char *uri = NULL, *rtag = NULL;
+ const struct sip_hdr *contact;
+ struct route_enc renc;
+ struct sip_addr addr;
+ struct pl pl;
+ int err;
+
+ if (!dlg || dlg->rtag || !dlg->cpos || !msg)
+ return EINVAL;
+
+ contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
+
+ if (!contact)
+ return EBADMSG;
+
+ if (sip_addr_decode(&addr, &contact->val))
+ return EBADMSG;
+
+ renc.mb = mbuf_alloc(512);
+ if (!renc.mb)
+ return ENOMEM;
+
+ err = pl_strdup(&uri, &addr.auri);
+ if (err)
+ goto out;
+
+ err = pl_strdup(&rtag, msg->req ? &msg->from.tag : &msg->to.tag);
+ if (err)
+ goto out;
+
+ renc.end = 0;
+
+ err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE,
+ record_route_handler, &renc) ? ENOMEM : 0;
+ err |= mbuf_printf(renc.mb, "To: %r\r\n",
+ msg->req ? &msg->from.val : &msg->to.val);
+
+ dlg->mb->pos = dlg->cpos;
+ err |= mbuf_write_mem(renc.mb, mbuf_buf(dlg->mb),
+ mbuf_get_left(dlg->mb));
+ dlg->mb->pos = 0;
+
+ if (err)
+ goto out;
+
+ renc.mb->pos = 0;
+
+ if (renc.end) {
+ pl.p = (const char *)mbuf_buf(renc.mb) + ROUTE_OFFSET;
+ pl.l = renc.end - ROUTE_OFFSET;
+ err = sip_addr_decode(&addr, &pl);
+ if (err)
+ goto out;
+
+ dlg->route = addr.uri;
+ }
+ else {
+ struct uri tmp;
+
+ pl_set_str(&pl, uri);
+ err = uri_decode(&tmp, &pl);
+ if (err)
+ goto out;
+
+ dlg->route = tmp;
+ }
+
+ mem_deref(dlg->mb);
+ mem_deref(dlg->uri);
+
+ dlg->mb = mem_ref(renc.mb);
+ dlg->rtag = mem_ref(rtag);
+ dlg->uri = mem_ref(uri);
+ dlg->rseq = msg->req ? msg->cseq.num : 0;
+ dlg->cpos = 0;
+
+ out:
+ mem_deref(renc.mb);
+ mem_deref(rtag);
+ mem_deref(uri);
+
+ return err;
+}
+
+
+/**
+ * Fork a SIP Dialog from an incoming SIP Message
+ *
+ * @param dlgp Pointer to allocated SIP Dialog
+ * @param odlg Original SIP Dialog
+ * @param msg SIP Message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg,
+ const struct sip_msg *msg)
+{
+ const struct sip_hdr *contact;
+ struct sip_dialog *dlg;
+ struct route_enc renc;
+ struct sip_addr addr;
+ struct pl pl;
+ int err;
+
+ if (!dlgp || !odlg || !odlg->cpos || !msg)
+ return EINVAL;
+
+ contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
+
+ if (!contact || !msg->callid.p)
+ return EBADMSG;
+
+ if (sip_addr_decode(&addr, &contact->val))
+ return EBADMSG;
+
+ dlg = mem_zalloc(sizeof(*dlg), destructor);
+ if (!dlg)
+ return ENOMEM;
+
+ dlg->callid = mem_ref(odlg->callid);
+ dlg->ltag = mem_ref(odlg->ltag);
+ dlg->hash = odlg->hash;
+ dlg->lseq = odlg->lseq;
+ dlg->rseq = msg->req ? msg->cseq.num : 0;
+
+ err = pl_strdup(&dlg->uri, &addr.auri);
+ if (err)
+ goto out;
+
+ err = pl_strdup(&dlg->rtag, msg->req ? &msg->from.tag : &msg->to.tag);
+ if (err)
+ goto out;
+
+ dlg->mb = mbuf_alloc(512);
+ if (!dlg->mb) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ renc.mb = dlg->mb;
+ renc.end = 0;
+
+ err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE,
+ record_route_handler, &renc) ? ENOMEM : 0;
+ err |= mbuf_printf(dlg->mb, "To: %r\r\n",
+ msg->req ? &msg->from.val : &msg->to.val);
+
+ odlg->mb->pos = odlg->cpos;
+ err |= mbuf_write_mem(dlg->mb, mbuf_buf(odlg->mb),
+ mbuf_get_left(odlg->mb));
+ odlg->mb->pos = 0;
+
+ if (err)
+ goto out;
+
+ dlg->mb->pos = 0;
+
+ if (renc.end) {
+ pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET;
+ pl.l = renc.end - ROUTE_OFFSET;
+ err = sip_addr_decode(&addr, &pl);
+ dlg->route = addr.uri;
+ }
+ else {
+ pl_set_str(&pl, dlg->uri);
+ err = uri_decode(&dlg->route, &pl);
+ }
+
+ out:
+ if (err)
+ mem_deref(dlg);
+ else
+ *dlgp = dlg;
+
+ return err;
+}
+
+
+/**
+ * Update an existing SIP Dialog from a SIP Message
+ *
+ * @param dlg SIP Dialog to update
+ * @param msg SIP Message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_dialog_update(struct sip_dialog *dlg, const struct sip_msg *msg)
+{
+ const struct sip_hdr *contact;
+ struct sip_addr addr;
+ char *uri;
+ int err;
+
+ if (!dlg || !msg)
+ return EINVAL;
+
+ contact = sip_msg_hdr(msg, SIP_HDR_CONTACT);
+ if (!contact)
+ return EBADMSG;
+
+ if (sip_addr_decode(&addr, &contact->val))
+ return EBADMSG;
+
+ err = pl_strdup(&uri, &addr.auri);
+ if (err)
+ return err;
+
+ if (dlg->route.scheme.p == dlg->uri) {
+
+ struct uri tmp;
+ struct pl pl;
+
+ pl_set_str(&pl, uri);
+ err = uri_decode(&tmp, &pl);
+ if (err)
+ goto out;
+
+ dlg->route = tmp;
+ }
+
+ mem_deref(dlg->uri);
+ dlg->uri = mem_ref(uri);
+
+ out:
+ mem_deref(uri);
+
+ return err;
+}
+
+
+/**
+ * Check if a remote sequence number is valid
+ *
+ * @param dlg SIP Dialog
+ * @param msg SIP Message
+ *
+ * @return True if valid, False if invalid
+ */
+bool sip_dialog_rseq_valid(struct sip_dialog *dlg, const struct sip_msg *msg)
+{
+ if (!dlg || !msg || !msg->req)
+ return false;
+
+ if (msg->cseq.num < dlg->rseq)
+ return false;
+
+ dlg->rseq = msg->cseq.num;
+
+ return true;
+}
+
+
+int sip_dialog_encode(struct mbuf *mb, struct sip_dialog *dlg, uint32_t cseq,
+ const char *met)
+{
+ int err = 0;
+
+ if (!mb || !dlg || !met)
+ return EINVAL;
+
+ err |= mbuf_write_mem(mb, mbuf_buf(dlg->mb), mbuf_get_left(dlg->mb));
+ err |= mbuf_printf(mb, "Call-ID: %s\r\n", dlg->callid);
+ err |= mbuf_printf(mb, "CSeq: %u %s\r\n", strcmp(met, "ACK") ?
+ dlg->lseq++ : cseq, met);
+
+ return err;
+}
+
+
+const char *sip_dialog_uri(const struct sip_dialog *dlg)
+{
+ return dlg ? dlg->uri : NULL;
+}
+
+
+const struct uri *sip_dialog_route(const struct sip_dialog *dlg)
+{
+ return dlg ? &dlg->route : NULL;
+}
+
+
+uint32_t sip_dialog_hash(const struct sip_dialog *dlg)
+{
+ return dlg ? dlg->hash : 0;
+}
+
+
+/**
+ * Get the Call-ID from a SIP Dialog
+ *
+ * @param dlg SIP Dialog
+ *
+ * @return Call-ID string
+ */
+const char *sip_dialog_callid(const struct sip_dialog *dlg)
+{
+ return dlg ? dlg->callid : NULL;
+}
+
+
+/**
+ * Get the local sequence number from a SIP Dialog
+ *
+ * @param dlg SIP Dialog
+ *
+ * @return Local sequence number
+ */
+uint32_t sip_dialog_lseq(const struct sip_dialog *dlg)
+{
+ return dlg ? dlg->lseq : 0;
+}
+
+
+/**
+ * Check if a SIP Dialog is established
+ *
+ * @param dlg SIP Dialog
+ *
+ * @return True if established, False if not
+ */
+bool sip_dialog_established(const struct sip_dialog *dlg)
+{
+ return dlg && dlg->rtag;
+}
+
+
+/**
+ * Compare a SIP Dialog against a SIP Message
+ *
+ * @param dlg SIP Dialog
+ * @param msg SIP Message
+ *
+ * @return True if match, False if no match
+ */
+bool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg)
+{
+ if (!dlg || !msg)
+ return false;
+
+ if (pl_strcmp(&msg->callid, dlg->callid))
+ return false;
+
+ if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag))
+ return false;
+
+ if (pl_strcmp(msg->req ? &msg->from.tag : &msg->to.tag, dlg->rtag))
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Compare a half SIP Dialog against a SIP Message
+ *
+ * @param dlg SIP Dialog
+ * @param msg SIP Message
+ *
+ * @return True if match, False if no match
+ */
+bool sip_dialog_cmp_half(const struct sip_dialog *dlg,
+ const struct sip_msg *msg)
+{
+ if (!dlg || !msg)
+ return false;
+
+ if (pl_strcmp(&msg->callid, dlg->callid))
+ return false;
+
+ if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag))
+ return false;
+
+ return true;
+}