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/msg.c b/src/sip/msg.c
new file mode 100644
index 0000000..2647e0b
--- /dev/null
+++ b/src/sip/msg.c
@@ -0,0 +1,682 @@
+/**
+ * @file sip/msg.c  SIP Message decode
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <ctype.h>
+#include <re_types.h>
+#include <re_mem.h>
+#include <re_sys.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_udp.h>
+#include <re_msg.h>
+#include <re_sip.h>
+#include "sip.h"
+
+
+enum {
+	HDR_HASH_SIZE = 32,
+	STARTLINE_MAX = 8192,
+};
+
+
+static void hdr_destructor(void *arg)
+{
+	struct sip_hdr *hdr = arg;
+
+	list_unlink(&hdr->le);
+	hash_unlink(&hdr->he);
+}
+
+
+static void destructor(void *arg)
+{
+	struct sip_msg *msg = arg;
+
+	list_flush(&msg->hdrl);
+	hash_flush(msg->hdrht);
+	mem_deref(msg->hdrht);
+	mem_deref(msg->sock);
+	mem_deref(msg->mb);
+}
+
+
+static enum sip_hdrid hdr_hash(const struct pl *name)
+{
+	if (!name->l)
+		return SIP_HDR_NONE;
+
+	if (name->l > 1) {
+		switch (name->p[0]) {
+
+		case 'x':
+		case 'X':
+			if (name->p[1] == '-')
+				return SIP_HDR_NONE;
+
+			/*@fallthrough@*/
+
+		default:
+			return (enum sip_hdrid)
+				(hash_joaat_ci(name->p, name->l) & 0xfff);
+		}
+	}
+
+	/* compact headers */
+	switch (tolower(name->p[0])) {
+
+	case 'a': return SIP_HDR_ACCEPT_CONTACT;
+	case 'b': return SIP_HDR_REFERRED_BY;
+	case 'c': return SIP_HDR_CONTENT_TYPE;
+	case 'd': return SIP_HDR_REQUEST_DISPOSITION;
+	case 'e': return SIP_HDR_CONTENT_ENCODING;
+	case 'f': return SIP_HDR_FROM;
+	case 'i': return SIP_HDR_CALL_ID;
+	case 'j': return SIP_HDR_REJECT_CONTACT;
+	case 'k': return SIP_HDR_SUPPORTED;
+	case 'l': return SIP_HDR_CONTENT_LENGTH;
+	case 'm': return SIP_HDR_CONTACT;
+	case 'n': return SIP_HDR_IDENTITY_INFO;
+	case 'o': return SIP_HDR_EVENT;
+	case 'r': return SIP_HDR_REFER_TO;
+	case 's': return SIP_HDR_SUBJECT;
+	case 't': return SIP_HDR_TO;
+	case 'u': return SIP_HDR_ALLOW_EVENTS;
+	case 'v': return SIP_HDR_VIA;
+	case 'x': return SIP_HDR_SESSION_EXPIRES;
+	case 'y': return SIP_HDR_IDENTITY;
+	default:  return SIP_HDR_NONE;
+	}
+}
+
+
+static inline bool hdr_comma_separated(enum sip_hdrid id)
+{
+	switch (id) {
+
+	case SIP_HDR_ACCEPT:
+	case SIP_HDR_ACCEPT_CONTACT:
+	case SIP_HDR_ACCEPT_ENCODING:
+	case SIP_HDR_ACCEPT_LANGUAGE:
+	case SIP_HDR_ACCEPT_RESOURCE_PRIORITY:
+	case SIP_HDR_ALERT_INFO:
+	case SIP_HDR_ALLOW:
+	case SIP_HDR_ALLOW_EVENTS:
+	case SIP_HDR_AUTHENTICATION_INFO:
+	case SIP_HDR_CALL_INFO:
+	case SIP_HDR_CONTACT:
+	case SIP_HDR_CONTENT_ENCODING:
+	case SIP_HDR_CONTENT_LANGUAGE:
+	case SIP_HDR_ERROR_INFO:
+	case SIP_HDR_HISTORY_INFO:
+	case SIP_HDR_IN_REPLY_TO:
+	case SIP_HDR_P_ASSERTED_IDENTITY:
+	case SIP_HDR_P_ASSOCIATED_URI:
+	case SIP_HDR_P_EARLY_MEDIA:
+	case SIP_HDR_P_MEDIA_AUTHORIZATION:
+	case SIP_HDR_P_PREFERRED_IDENTITY:
+	case SIP_HDR_P_REFUSED_URI_LIST:
+	case SIP_HDR_P_VISITED_NETWORK_ID:
+	case SIP_HDR_PATH:
+	case SIP_HDR_PERMISSION_MISSING:
+	case SIP_HDR_PROXY_REQUIRE:
+	case SIP_HDR_REASON:
+	case SIP_HDR_RECORD_ROUTE:
+	case SIP_HDR_REJECT_CONTACT:
+	case SIP_HDR_REQUEST_DISPOSITION:
+	case SIP_HDR_REQUIRE:
+	case SIP_HDR_RESOURCE_PRIORITY:
+	case SIP_HDR_ROUTE:
+	case SIP_HDR_SECURITY_CLIENT:
+	case SIP_HDR_SECURITY_SERVER:
+	case SIP_HDR_SECURITY_VERIFY:
+	case SIP_HDR_SERVICE_ROUTE:
+	case SIP_HDR_SUPPORTED:
+	case SIP_HDR_TRIGGER_CONSENT:
+	case SIP_HDR_UNSUPPORTED:
+	case SIP_HDR_VIA:
+	case SIP_HDR_WARNING:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+
+static inline int hdr_add(struct sip_msg *msg, const struct pl *name,
+			  enum sip_hdrid id, const char *p, ssize_t l,
+			  bool atomic, bool line)
+{
+	struct sip_hdr *hdr;
+	int err = 0;
+
+	hdr = mem_zalloc(sizeof(*hdr), hdr_destructor);
+	if (!hdr)
+		return ENOMEM;
+
+	hdr->name  = *name;
+	hdr->val.p = p;
+	hdr->val.l = MAX(l, 0);
+	hdr->id    = id;
+
+	switch (id) {
+
+	case SIP_HDR_VIA:
+	case SIP_HDR_ROUTE:
+		if (!atomic)
+			break;
+
+		hash_append(msg->hdrht, id, &hdr->he, mem_ref(hdr));
+		list_append(&msg->hdrl, &hdr->le, mem_ref(hdr));
+		break;
+
+	default:
+		if (atomic)
+			hash_append(msg->hdrht, id, &hdr->he, mem_ref(hdr));
+		if (line)
+			list_append(&msg->hdrl, &hdr->le, mem_ref(hdr));
+		break;
+	}
+
+	/* parse common headers */
+	switch (id) {
+
+	case SIP_HDR_VIA:
+		if (!atomic || pl_isset(&msg->via.sentby))
+			break;
+
+		err = sip_via_decode(&msg->via, &hdr->val);
+		break;
+
+	case SIP_HDR_TO:
+		err = sip_addr_decode((struct sip_addr *)&msg->to, &hdr->val);
+		if (err)
+			break;
+
+		(void)msg_param_decode(&msg->to.params, "tag", &msg->to.tag);
+		msg->to.val = hdr->val;
+		break;
+
+	case SIP_HDR_FROM:
+		err = sip_addr_decode((struct sip_addr *)&msg->from,
+				      &hdr->val);
+		if (err)
+			break;
+
+		(void)msg_param_decode(&msg->from.params, "tag",
+				       &msg->from.tag);
+		msg->from.val = hdr->val;
+		break;
+
+	case SIP_HDR_CALL_ID:
+		msg->callid = hdr->val;
+		break;
+
+	case SIP_HDR_CSEQ:
+		err = sip_cseq_decode(&msg->cseq, &hdr->val);
+		break;
+
+	case SIP_HDR_MAX_FORWARDS:
+		msg->maxfwd = hdr->val;
+		break;
+
+	case SIP_HDR_CONTENT_TYPE:
+		err = msg_ctype_decode(&msg->ctyp, &hdr->val);
+		break;
+
+	case SIP_HDR_CONTENT_LENGTH:
+		msg->clen = hdr->val;
+		break;
+
+	case SIP_HDR_EXPIRES:
+		msg->expires = hdr->val;
+		break;
+
+	default:
+		/* re_printf("%r = %u\n", &hdr->name, id); */
+		break;
+	}
+
+	mem_deref(hdr);
+
+	return err;
+}
+
+
+/**
+ * Decode a SIP message
+ *
+ * @param msgp Pointer to allocated SIP Message
+ * @param mb   Buffer containing SIP Message
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sip_msg_decode(struct sip_msg **msgp, struct mbuf *mb)
+{
+	struct pl x, y, z, e, name;
+	const char *p, *v, *cv;
+	struct sip_msg *msg;
+	bool comsep, quote;
+	enum sip_hdrid id = SIP_HDR_NONE;
+	uint32_t ws, lf;
+	size_t l;
+	int err;
+
+	if (!msgp || !mb)
+		return EINVAL;
+
+	p = (const char *)mbuf_buf(mb);
+	l = mbuf_get_left(mb);
+
+	if (re_regex(p, l, "[^ \t\r\n]+ [^ \t\r\n]+ [^\r\n]*[\r]*[\n]1",
+		     &x, &y, &z, NULL, &e) || x.p != (char *)mbuf_buf(mb))
+		return (l > STARTLINE_MAX) ? EBADMSG : ENODATA;
+
+	msg = mem_zalloc(sizeof(*msg), destructor);
+	if (!msg)
+		return ENOMEM;
+
+	err = hash_alloc(&msg->hdrht, HDR_HASH_SIZE);
+	if (err)
+		goto out;
+
+	msg->tag = rand_u64();
+	msg->mb  = mem_ref(mb);
+	msg->req = (0 == pl_strcmp(&z, "SIP/2.0"));
+
+	if (msg->req) {
+
+		msg->met = x;
+		msg->ruri = y;
+		msg->ver = z;
+
+		if (uri_decode(&msg->uri, &y)) {
+			err = EBADMSG;
+			goto out;
+		}
+	}
+	else {
+		msg->ver    = x;
+		msg->scode  = pl_u32(&y);
+		msg->reason = z;
+
+		if (!msg->scode) {
+			err = EBADMSG;
+			goto out;
+		}
+	}
+
+	l -= e.p + e.l - p;
+	p = e.p + e.l;
+
+	name.p = v = cv = NULL;
+	name.l = ws = lf = 0;
+	comsep = false;
+	quote = false;
+
+	for (; l > 0; p++, l--) {
+
+		switch (*p) {
+
+		case ' ':
+		case '\t':
+			lf = 0; /* folding */
+			++ws;
+			break;
+
+		case '\r':
+			++ws;
+			break;
+
+		case '\n':
+			++ws;
+
+			if (!lf++)
+				break;
+
+			++p; --l; /* eoh */
+
+			/*@fallthrough@*/
+
+		default:
+			if (lf || (*p == ',' && comsep && !quote)) {
+
+				if (!name.l) {
+					err = EBADMSG;
+					goto out;
+				}
+
+				err = hdr_add(msg, &name, id, cv ? cv : p,
+					      cv ? p - cv - ws : 0,
+					      true, cv == v && lf);
+				if (err)
+					goto out;
+
+				if (!lf) { /* comma separated */
+					cv = NULL;
+					break;
+				}
+
+				if (cv != v) {
+					err = hdr_add(msg, &name, id,
+						      v ? v : p,
+						      v ? p - v - ws : 0,
+						      false, true);
+					if (err)
+						goto out;
+				}
+
+				if (lf > 1) { /* eoh */
+					err = 0;
+					goto out;
+				}
+
+				comsep = false;
+				name.p = NULL;
+				cv = v = NULL;
+				lf = 0;
+			}
+
+			if (!name.p) {
+				name.p = p;
+				name.l = 0;
+				ws = 0;
+			}
+
+			if (!name.l) {
+				if (*p != ':') {
+					ws = 0;
+					break;
+				}
+
+				name.l = MAX((int)(p - name.p - ws), 0);
+				if (!name.l) {
+					err = EBADMSG;
+					goto out;
+				}
+
+				id = hdr_hash(&name);
+				comsep = hdr_comma_separated(id);
+				break;
+			}
+
+			if (!cv) {
+				quote = false;
+				cv = p;
+			}
+
+			if (!v) {
+				v = p;
+			}
+
+			if (*p == '"')
+				quote = !quote;
+
+			ws = 0;
+			break;
+		}
+	}
+
+	err = ENODATA;
+
+ out:
+	if (err)
+		mem_deref(msg);
+	else {
+		*msgp = msg;
+		mb->pos = mb->end - l;
+	}
+
+	return err;
+}
+
+
+/**
+ * Get a SIP Header from a SIP Message
+ *
+ * @param msg SIP Message
+ * @param id  SIP Header ID
+ *
+ * @return SIP Header if found, NULL if not found
+ */
+const struct sip_hdr *sip_msg_hdr(const struct sip_msg *msg, enum sip_hdrid id)
+{
+	return sip_msg_hdr_apply(msg, true, id, NULL, NULL);
+}
+
+
+/**
+ * Apply a function handler to certain SIP Headers
+ *
+ * @param msg SIP Message
+ * @param fwd True to traverse forwards, false to traverse backwards
+ * @param id  SIP Header ID
+ * @param h   Function handler
+ * @param arg Handler argument
+ *
+ * @return SIP Header if handler returns true, otherwise NULL
+ */
+const struct sip_hdr *sip_msg_hdr_apply(const struct sip_msg *msg,
+					bool fwd, enum sip_hdrid id,
+					sip_hdr_h *h, void *arg)
+{
+	struct list *lst;
+	struct le *le;
+
+	if (!msg)
+		return NULL;
+
+	lst = hash_list(msg->hdrht, id);
+
+	le = fwd ? list_head(lst) : list_tail(lst);
+
+	while (le) {
+		const struct sip_hdr *hdr = le->data;
+
+		le = fwd ? le->next : le->prev;
+
+		if (hdr->id != id)
+			continue;
+
+		if (!h || h(hdr, msg, arg))
+			return hdr;
+	}
+
+	return NULL;
+}
+
+
+/**
+ * Get an unknown SIP Header from a SIP Message
+ *
+ * @param msg  SIP Message
+ * @param name Header name
+ *
+ * @return SIP Header if found, NULL if not found
+ */
+const struct sip_hdr *sip_msg_xhdr(const struct sip_msg *msg, const char *name)
+{
+	return sip_msg_xhdr_apply(msg, true, name, NULL, NULL);
+}
+
+
+/**
+ * Apply a function handler to certain unknown SIP Headers
+ *
+ * @param msg  SIP Message
+ * @param fwd  True to traverse forwards, false to traverse backwards
+ * @param name SIP Header name
+ * @param h    Function handler
+ * @param arg  Handler argument
+ *
+ * @return SIP Header if handler returns true, otherwise NULL
+ */
+const struct sip_hdr *sip_msg_xhdr_apply(const struct sip_msg *msg,
+					 bool fwd, const char *name,
+					 sip_hdr_h *h, void *arg)
+{
+	struct list *lst;
+	struct le *le;
+	struct pl pl;
+
+	if (!msg || !name)
+		return NULL;
+
+	pl_set_str(&pl, name);
+
+	lst = hash_list(msg->hdrht, hdr_hash(&pl));
+
+	le = fwd ? list_head(lst) : list_tail(lst);
+
+	while (le) {
+		const struct sip_hdr *hdr = le->data;
+
+		le = fwd ? le->next : le->prev;
+
+		if (pl_casecmp(&hdr->name, &pl))
+			continue;
+
+		if (!h || h(hdr, msg, arg))
+			return hdr;
+	}
+
+	return NULL;
+}
+
+
+static bool count_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,
+			  void *arg)
+{
+	uint32_t *n = arg;
+	(void)hdr;
+	(void)msg;
+
+	++(*n);
+
+	return false;
+}
+
+
+/**
+ * Count the number of SIP Headers
+ *
+ * @param msg SIP Message
+ * @param id  SIP Header ID
+ *
+ * @return Number of SIP Headers
+ */
+uint32_t sip_msg_hdr_count(const struct sip_msg *msg, enum sip_hdrid id)
+{
+	uint32_t n = 0;
+
+	sip_msg_hdr_apply(msg, true, id, count_handler, &n);
+
+	return n;
+}
+
+
+/**
+ * Count the number of unknown SIP Headers
+ *
+ * @param msg  SIP Message
+ * @param name SIP Header name
+ *
+ * @return Number of SIP Headers
+ */
+uint32_t sip_msg_xhdr_count(const struct sip_msg *msg, const char *name)
+{
+	uint32_t n = 0;
+
+	sip_msg_xhdr_apply(msg, true, name, count_handler, &n);
+
+	return n;
+}
+
+
+static bool value_handler(const struct sip_hdr *hdr, const struct sip_msg *msg,
+			  void *arg)
+{
+	(void)msg;
+
+	return 0 == pl_strcasecmp(&hdr->val, (const char *)arg);
+}
+
+
+/**
+ * Check if a SIP Header matches a certain value
+ *
+ * @param msg   SIP Message
+ * @param id    SIP Header ID
+ * @param value Header value to check
+ *
+ * @return True if value matches, false if not
+ */
+bool sip_msg_hdr_has_value(const struct sip_msg *msg, enum sip_hdrid id,
+			   const char *value)
+{
+	return NULL != sip_msg_hdr_apply(msg, true, id, value_handler,
+					 (void *)value);
+}
+
+
+/**
+ * Check if an unknown SIP Header matches a certain value
+ *
+ * @param msg   SIP Message
+ * @param name  SIP Header name
+ * @param value Header value to check
+ *
+ * @return True if value matches, false if not
+ */
+bool sip_msg_xhdr_has_value(const struct sip_msg *msg, const char *name,
+			    const char *value)
+{
+	return NULL != sip_msg_xhdr_apply(msg, true, name, value_handler,
+					  (void *)value);
+}
+
+
+/**
+ * Print a SIP Message to stdout
+ *
+ * @param msg SIP Message
+ */
+void sip_msg_dump(const struct sip_msg *msg)
+{
+	struct le *le;
+	uint32_t i;
+
+	if (!msg)
+		return;
+
+	for (i=0; i<HDR_HASH_SIZE; i++) {
+
+		le = list_head(hash_list(msg->hdrht, i));
+
+		while (le) {
+			const struct sip_hdr *hdr = le->data;
+
+			le = le->next;
+
+			(void)re_printf("%02u '%r'='%r'\n", i, &hdr->name,
+					&hdr->val);
+		}
+	}
+
+	le = list_head(&msg->hdrl);
+
+	while (le) {
+		const struct sip_hdr *hdr = le->data;
+
+		le = le->next;
+
+		(void)re_printf("%02u '%r'='%r'\n", hdr->id, &hdr->name,
+				&hdr->val);
+	}
+}