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/stun/msg.c b/src/stun/msg.c
new file mode 100644
index 0000000..dda3d8d
--- /dev/null
+++ b/src/stun/msg.c
@@ -0,0 +1,485 @@
+/**
+ * @file stun/msg.c  STUN message encoding
+ *
+ * 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_fmt.h>
+#include <re_md5.h>
+#include <re_sha.h>
+#include <re_hmac.h>
+#include <re_crc32.h>
+#include <re_stun.h>
+#include "stun.h"
+
+
+enum {
+	MI_SIZE = 24,
+	FP_SIZE = 8
+};
+
+
+/**
+   Defines a STUN Message object
+
+   <pre>
+
+   .---------------------.      /|\           /|\
+   | STUN Header         |       |             |
+   |---------------------|       |             |
+   |         ....        |       |--------.    |
+   |      N Attributes   |       |        |    |----.
+   |         ....        |      \|/       |    |    |
+   |---------------------|                |    |    |
+   |  MESSAGE-INTEGRITY  | <-(HMAC-SHA1)--'   \|/   |
+   |---------------------|                          |
+   |     FINGERPRINT     | <-(CRC-32)---------------'
+   '---------------------'
+   </pre>
+*/
+struct stun_msg {
+	struct stun_hdr hdr;
+	struct list attrl;
+	struct mbuf *mb;
+	size_t start;
+};
+
+
+static uint32_t fingerprint(const uint8_t *buf, size_t len)
+{
+	return (uint32_t)crc32(0, buf, (unsigned int)len) ^ 0x5354554e;
+}
+
+
+static void destructor(void *arg)
+{
+	struct stun_msg *msg = arg;
+
+	list_flush(&msg->attrl);
+	mem_deref(msg->mb);
+}
+
+
+/**
+ * Decode a buffer to a STUN Message
+ *
+ * @param msgpp Pointer to allocation STUN message
+ * @param mb    Buffer containing the raw STUN packet
+ * @param ua    Unknown attributes (optional)
+ *
+ * @return 0 if success, otherwise errorcode
+ *
+ * @note `mb' will be referenced
+ */
+int stun_msg_decode(struct stun_msg **msgpp, struct mbuf *mb,
+		    struct stun_unknown_attr *ua)
+{
+	struct stun_msg *msg;
+	struct stun_hdr hdr;
+	size_t start, extra;
+	int err;
+
+	if (!msgpp || !mb)
+		return EINVAL;
+
+	start = mb->pos;
+
+	err = stun_hdr_decode(mb, &hdr);
+	if (err) {
+		mb->pos = start;
+		return err;
+	}
+
+	msg = mem_zalloc(sizeof(*msg), destructor);
+	if (!msg) {
+		mb->pos = start;
+		return ENOMEM;
+	}
+
+	msg->hdr = hdr;
+	msg->mb = mem_ref(mb);
+	msg->start = start;
+
+	if (ua)
+		ua->typec = 0;
+
+	/* mbuf_get_left(mb) >= hdr.len checked in stun_hdr_decode() above */
+	extra = mbuf_get_left(mb) - hdr.len;
+
+	while (mbuf_get_left(mb) - extra >= 4) {
+
+		struct stun_attr *attr;
+
+		err = stun_attr_decode(&attr, mb, hdr.tid, ua);
+		if (err)
+			break;
+
+		list_append(&msg->attrl, &attr->le, attr);
+	}
+
+	if (err)
+		mem_deref(msg);
+	else
+		*msgpp = msg;
+
+	mb->pos = start;
+
+	return err;
+}
+
+
+/**
+ * Get the STUN message type
+ *
+ * @param msg STUN Message
+ *
+ * @return STUN Message type
+ */
+uint16_t stun_msg_type(const struct stun_msg *msg)
+{
+	return msg ? msg->hdr.type : 0;
+}
+
+
+/**
+ * Get the STUN message class
+ *
+ * @param msg STUN Message
+ *
+ * @return STUN Message class
+ */
+uint16_t stun_msg_class(const struct stun_msg *msg)
+{
+	return STUN_CLASS(stun_msg_type(msg));
+}
+
+
+/**
+ * Get the STUN message method
+ *
+ * @param msg STUN Message
+ *
+ * @return STUN Message method
+ */
+uint16_t stun_msg_method(const struct stun_msg *msg)
+{
+	return STUN_METHOD(stun_msg_type(msg));
+}
+
+
+/**
+ * Get the STUN message Transaction-ID
+ *
+ * @param msg STUN Message
+ *
+ * @return STUN Message Transaction-ID
+ */
+const uint8_t *stun_msg_tid(const struct stun_msg *msg)
+{
+	return msg ? msg->hdr.tid : NULL;
+}
+
+
+/**
+ * Check if a STUN Message has the magic cookie
+ *
+ * @param msg STUN Message
+ *
+ * @return true if Magic Cookie, otherwise false
+ */
+bool stun_msg_mcookie(const struct stun_msg *msg)
+{
+	return msg && (STUN_MAGIC_COOKIE == msg->hdr.cookie);
+}
+
+
+/**
+ * Lookup a STUN attribute in a STUN message
+ *
+ * @param msg  STUN Message
+ * @param type STUN Attribute type
+ *
+ * @return STUN Attribute if found, otherwise NULL
+ */
+struct stun_attr *stun_msg_attr(const struct stun_msg *msg, uint16_t type)
+{
+	struct le *le = msg ? list_head(&msg->attrl) : NULL;
+
+	while (le) {
+		struct stun_attr *attr = le->data;
+
+		le = le->next;
+
+		if (attr->type == type)
+			return attr;
+	}
+
+	return NULL;
+}
+
+
+/**
+ * Apply a function handler to all STUN attribute
+ *
+ * @param msg  STUN Message
+ * @param h    Attribute handler
+ * @param arg  Handler argument
+ *
+ * @return STUN attribute if handler returned true, otherwise NULL
+ */
+struct stun_attr *stun_msg_attr_apply(const struct stun_msg *msg,
+				      stun_attr_h *h, void *arg)
+{
+	struct le *le = msg ? list_head(&msg->attrl) : NULL;
+
+	while (le) {
+		struct stun_attr *attr = le->data;
+
+		le = le->next;
+
+		if (h && h(attr, arg))
+			return (attr);
+	}
+
+	return NULL;
+}
+
+
+/**
+ * Encode a STUN message
+ *
+ * @param mb      Buffer to encode message into
+ * @param method  STUN Method
+ * @param class   STUN Method class
+ * @param tid     Transaction ID
+ * @param ec      STUN error code (optional)
+ * @param key     Authentication key (optional)
+ * @param keylen  Number of bytes in authentication key
+ * @param fp      Use STUN Fingerprint attribute
+ * @param padding Padding byte
+ * @param attrc   Number of attributes to encode (variable arguments)
+ * @param ap      Variable list of attribute-tuples
+ *                Each attribute has 2 arguments, attribute type and value
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int stun_msg_vencode(struct mbuf *mb, uint16_t method, uint8_t class,
+		     const uint8_t *tid, const struct stun_errcode *ec,
+		     const uint8_t *key, size_t keylen, bool fp,
+		     uint8_t padding, uint32_t attrc, va_list ap)
+{
+	struct stun_hdr hdr;
+	size_t start;
+	int err = 0;
+	uint32_t i;
+
+	if (!mb || !tid)
+		return EINVAL;
+
+	start = mb->pos;
+	mb->pos += STUN_HEADER_SIZE;
+
+	hdr.type   = STUN_TYPE(method, class);
+	hdr.cookie = STUN_MAGIC_COOKIE;
+	memcpy(hdr.tid, tid, STUN_TID_SIZE);
+
+	if (ec)
+		err |= stun_attr_encode(mb, STUN_ATTR_ERR_CODE, ec,
+					NULL, padding);
+
+	for (i=0; i<attrc; i++) {
+
+		uint16_t type = va_arg(ap, int);
+		const void *v = va_arg(ap, const void *);
+
+		if (!v)
+			continue;
+
+		err |= stun_attr_encode(mb, type, v, hdr.tid, padding);
+	}
+
+	/* header */
+	hdr.len = mb->pos - start - STUN_HEADER_SIZE + (key ? MI_SIZE : 0);
+	mb->pos = start;
+	err |= stun_hdr_encode(mb, &hdr);
+	mb->pos += hdr.len - (key ? MI_SIZE : 0);
+
+	if (key) {
+		uint8_t mi[20];
+
+		mb->pos = start;
+		hmac_sha1(key, keylen, mbuf_buf(mb), mbuf_get_left(mb),
+			  mi, sizeof(mi));
+
+		mb->pos += STUN_HEADER_SIZE + hdr.len - MI_SIZE;
+		err |= stun_attr_encode(mb, STUN_ATTR_MSG_INTEGRITY, mi,
+					NULL, padding);
+	}
+
+	if (fp) {
+		uint32_t fprnt;
+
+		/* header */
+		hdr.len = mb->pos - start - STUN_HEADER_SIZE + FP_SIZE;
+		mb->pos = start;
+		err |= stun_hdr_encode(mb, &hdr);
+
+		mb->pos = start;
+		fprnt = fingerprint(mbuf_buf(mb), mbuf_get_left(mb));
+
+		mb->pos += STUN_HEADER_SIZE + hdr.len - FP_SIZE;
+		err |= stun_attr_encode(mb, STUN_ATTR_FINGERPRINT, &fprnt,
+					NULL, padding);
+	}
+
+	return err;
+}
+
+
+/**
+ * Encode a STUN message
+ *
+ * @param mb      Buffer to encode message into
+ * @param method  STUN Method
+ * @param class   STUN Method class
+ * @param tid     Transaction ID
+ * @param ec      STUN error code (optional)
+ * @param key     Authentication key (optional)
+ * @param keylen  Number of bytes in authentication key
+ * @param fp      Use STUN Fingerprint attribute
+ * @param padding Padding byte
+ * @param attrc   Number of attributes to encode (variable arguments)
+ * @param ...     Variable list of attribute-tuples
+ *                Each attribute has 2 arguments, attribute type and value
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int stun_msg_encode(struct mbuf *mb, uint16_t method, uint8_t class,
+		    const uint8_t *tid, const struct stun_errcode *ec,
+		    const uint8_t *key, size_t keylen, bool fp,
+		    uint8_t padding, uint32_t attrc, ...)
+{
+	va_list ap;
+	int err;
+
+	va_start(ap, attrc);
+	err = stun_msg_vencode(mb, method, class, tid, ec, key, keylen, fp,
+			       padding, attrc, ap);
+	va_end(ap);
+
+	return err;
+}
+
+
+/**
+ * Verify the Message-Integrity of a STUN message
+ *
+ * @param msg    STUN Message
+ * @param key    Authentication key
+ * @param keylen Number of bytes in authentication key
+ *
+ * @return 0 if verified, otherwise errorcode
+ */
+int stun_msg_chk_mi(const struct stun_msg *msg, const uint8_t *key,
+		    size_t keylen)
+{
+	uint8_t hmac[SHA_DIGEST_LENGTH];
+	struct stun_attr *mi, *fp;
+
+	if (!msg)
+		return EINVAL;
+
+	mi = stun_msg_attr(msg, STUN_ATTR_MSG_INTEGRITY);
+	if (!mi)
+		return EPROTO;
+
+	msg->mb->pos = msg->start;
+
+	fp = stun_msg_attr(msg, STUN_ATTR_FINGERPRINT);
+	if (fp) {
+		((struct stun_msg *)msg)->hdr.len -= FP_SIZE;
+		(void)stun_hdr_encode(msg->mb, &msg->hdr);
+		msg->mb->pos -= STUN_HEADER_SIZE;
+	}
+
+	hmac_sha1(key, keylen, mbuf_buf(msg->mb),
+		  STUN_HEADER_SIZE + msg->hdr.len - MI_SIZE,
+		  hmac, sizeof(hmac));
+
+	if (fp) {
+		((struct stun_msg *)msg)->hdr.len += FP_SIZE;
+		(void)stun_hdr_encode(msg->mb, &msg->hdr);
+		msg->mb->pos -= STUN_HEADER_SIZE;
+	}
+
+	if (memcmp(mi->v.msg_integrity, hmac, SHA_DIGEST_LENGTH))
+		return EBADMSG;
+
+	return 0;
+}
+
+
+/**
+ * Check the Fingerprint of a STUN message
+ *
+ * @param msg STUN Message
+ *
+ * @return 0 if fingerprint matches, otherwise errorcode
+ */
+int stun_msg_chk_fingerprint(const struct stun_msg *msg)
+{
+	struct stun_attr *fp;
+	uint32_t fprnt;
+
+	if (!msg)
+		return EINVAL;
+
+	fp = stun_msg_attr(msg, STUN_ATTR_FINGERPRINT);
+	if (!fp)
+		return EPROTO;
+
+	msg->mb->pos = msg->start;
+
+	fprnt = fingerprint(mbuf_buf(msg->mb),
+			    STUN_HEADER_SIZE + msg->hdr.len - FP_SIZE);
+
+	if (fprnt != fp->v.fingerprint)
+		return EBADMSG;
+
+	return 0;
+}
+
+
+static bool attr_print(const struct stun_attr *attr, void *arg)
+{
+	(void)arg;
+
+	stun_attr_dump(attr);
+
+	return false;
+}
+
+
+/**
+ * Print a STUN message to STDOUT
+ *
+ * @param msg STUN Message
+ */
+void stun_msg_dump(const struct stun_msg *msg)
+{
+	if (!msg)
+		return;
+
+	(void)re_printf("%s %s (len=%u cookie=%08x tid=%w)\n",
+			stun_method_name(stun_msg_method(msg)),
+			stun_class_name(stun_msg_class(msg)),
+			msg->hdr.len, msg->hdr.cookie,
+			msg->hdr.tid, sizeof(msg->hdr.tid));
+
+	stun_msg_attr_apply(msg, attr_print, NULL);
+}