Squashed 'third_party/rawrtc/rew/' content from commit 24c91fd83

Change-Id: Ica2fcc790472ecd5b195d20da982c4e84139cbdd
git-subtree-dir: third_party/rawrtc/rew
git-subtree-split: 24c91fd839b40b11f727c902fa46d20874da33fb
diff --git a/src/pcp/option.c b/src/pcp/option.c
new file mode 100644
index 0000000..5ca9e0b
--- /dev/null
+++ b/src/pcp/option.c
@@ -0,0 +1,228 @@
+/**
+ * @file option.c  PCP options
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_mbuf.h>
+#include <re_list.h>
+#include <re_sa.h>
+#include <re_pcp.h>
+#include "pcp.h"
+
+
+static void destructor(void *arg)
+{
+	struct pcp_option *opt = arg;
+
+	list_unlink(&opt->le);
+
+	switch (opt->code) {
+
+	case PCP_OPTION_DESCRIPTION:
+		mem_deref(opt->u.description);
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+int pcp_option_encode(struct mbuf *mb, enum pcp_option_code code,
+		      const void *v)
+{
+	const struct sa *sa = v;
+	const struct pcp_option_filter *filt = v;
+	size_t start, len;
+	int err = 0;
+
+	if (!mb)
+		return EINVAL;
+
+	mb->pos += 4;
+	start = mb->pos;
+
+	switch (code) {
+
+	case PCP_OPTION_THIRD_PARTY:
+		if (!sa)
+			return EINVAL;
+		err |= pcp_ipaddr_encode(mb, sa);
+		break;
+
+	case PCP_OPTION_PREFER_FAILURE:
+		/* no payload */
+		break;
+
+	case PCP_OPTION_FILTER:
+		if (!filt)
+			return EINVAL;
+		err |= mbuf_write_u8(mb, 0x00);
+		err |= mbuf_write_u8(mb, filt->prefix_length);
+		err |= mbuf_write_u16(mb, htons(sa_port(&filt->remote_peer)));
+		err |= pcp_ipaddr_encode(mb, &filt->remote_peer);
+		break;
+
+	case PCP_OPTION_DESCRIPTION:
+		if (!v)
+			return EINVAL;
+		err |= mbuf_write_str(mb, v);
+		break;
+
+	default:
+		(void)re_fprintf(stderr,
+				 "pcp: unsupported option %d\n", code);
+		return EINVAL;
+	}
+
+	/* header */
+	len = mb->pos - start;
+
+	mb->pos = start - 4;
+	err |= mbuf_write_u8(mb, code);
+	err |= mbuf_write_u8(mb, 0x00);
+	err |= mbuf_write_u16(mb, htons(len));
+	mb->pos += len;
+
+	/* padding */
+	while ((mb->pos - start) & 0x03)
+		err |= mbuf_write_u8(mb, 0x00);
+
+	return err;
+}
+
+
+int pcp_option_decode(struct pcp_option **optp, struct mbuf *mb)
+{
+	struct pcp_option *opt;
+	size_t start, len;
+	uint16_t port;
+	int err = 0;
+
+	if (!optp || !mb)
+		return EINVAL;
+
+	if (mbuf_get_left(mb) < 4)
+		return EBADMSG;
+
+	opt = mem_zalloc(sizeof(*opt), destructor);
+	if (!opt)
+		return ENOMEM;
+
+	opt->code = mbuf_read_u8(mb);
+	(void)mbuf_read_u8(mb);
+	len = ntohs(mbuf_read_u16(mb));
+
+	if (mbuf_get_left(mb) < len)
+		goto badmsg;
+
+	start = mb->pos;
+
+	switch (opt->code) {
+
+	case PCP_OPTION_THIRD_PARTY:
+		if (len < 16)
+			goto badmsg;
+		err = pcp_ipaddr_decode(mb, &opt->u.third_party);
+		break;
+
+	case PCP_OPTION_PREFER_FAILURE:
+		/* no payload */
+		break;
+
+	case PCP_OPTION_FILTER:
+		if (len < 20)
+			goto badmsg;
+		(void)mbuf_read_u8(mb);
+		opt->u.filter.prefix_length = mbuf_read_u8(mb);
+		port = ntohs(mbuf_read_u16(mb));
+		err = pcp_ipaddr_decode(mb, &opt->u.filter.remote_peer);
+		sa_set_port(&opt->u.filter.remote_peer, port);
+		break;
+
+	case PCP_OPTION_DESCRIPTION:
+		err = mbuf_strdup(mb, &opt->u.description, len);
+		break;
+
+	default:
+		mb->pos += len;
+
+		(void)re_printf("pcp: ignore option code %d (len=%zu)\n",
+				opt->code, len);
+		break;
+	}
+
+	if (err)
+		goto error;
+
+	/* padding */
+	while (((mb->pos - start) & 0x03) && mbuf_get_left(mb))
+		++mb->pos;
+
+	*optp = opt;
+
+	return 0;
+
+ badmsg:
+	err = EBADMSG;
+ error:
+	mem_deref(opt);
+
+	return err;
+}
+
+
+static const char *pcp_option_name(enum pcp_option_code code)
+{
+	switch (code) {
+
+	case PCP_OPTION_THIRD_PARTY:    return "THIRD_PARTY";
+	case PCP_OPTION_PREFER_FAILURE: return "PREFER_FAILURE";
+	case PCP_OPTION_FILTER:         return "FILTER";
+	case PCP_OPTION_DESCRIPTION:    return "DESCRIPTION";
+	default: return "?";
+	}
+}
+
+
+int pcp_option_print(struct re_printf *pf, const struct pcp_option *opt)
+{
+	int err;
+
+	if (!opt)
+		return 0;
+
+	err = re_hprintf(pf, " %-25s", pcp_option_name(opt->code));
+
+	switch (opt->code) {
+
+	case PCP_OPTION_THIRD_PARTY:
+		err |= re_hprintf(pf, "address=%j",
+				  &opt->u.third_party);
+		break;
+
+	case PCP_OPTION_PREFER_FAILURE:
+		break;
+
+	case PCP_OPTION_FILTER:
+		err |= re_hprintf(pf, "prefix_length=%u, remote_peer=%J",
+				  opt->u.filter.prefix_length,
+				  &opt->u.filter.remote_peer);
+		break;
+
+	case PCP_OPTION_DESCRIPTION:
+		err |= re_hprintf(pf, "'%s'", opt->u.description);
+		break;
+
+	default:
+		err |= re_hprintf(pf, "???");
+		break;
+	}
+
+	err |= re_hprintf(pf, "\n");
+
+	return err;
+}