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/dns/rrlist.c b/src/dns/rrlist.c
new file mode 100644
index 0000000..6e7c572
--- /dev/null
+++ b/src/dns/rrlist.c
@@ -0,0 +1,243 @@
+/**
+ * @file rrlist.c  DNS Resource Records list
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <re_types.h>
+#include <re_list.h>
+#include <re_hash.h>
+#include <re_mbuf.h>
+#include <re_fmt.h>
+#include <re_dns.h>
+
+
+enum {
+	CNAME_RECURSE_MAX = 16,
+};
+
+
+struct sort {
+	uint16_t type;
+	uint32_t key;
+};
+
+
+static uint32_t sidx(const struct dnsrr *rr, uint32_t key)
+{
+	uint32_t addr[4];
+
+	switch (rr->type) {
+
+	case DNS_TYPE_A:
+		return rr->rdata.a.addr ^ key;
+
+	case DNS_TYPE_AAAA:
+		memcpy(addr, rr->rdata.aaaa.addr, 16);
+
+		return addr[0] ^ addr[1] ^ addr[2] ^ addr[3] ^ key;
+
+	case DNS_TYPE_SRV:
+		return ((hash_fast_str(rr->rdata.srv.target) & 0xfff) ^ key) +
+			rr->rdata.srv.weight;
+
+	default:
+		return 0;
+	}
+}
+
+
+static bool std_sort_handler(struct le *le1, struct le *le2, void *arg)
+{
+	struct dnsrr *rr1 = le1->data;
+	struct dnsrr *rr2 = le2->data;
+	struct sort *sort = arg;
+
+	if (sort->type != rr1->type)
+		return sort->type != rr2->type;
+
+	if (sort->type != rr2->type)
+		return true;
+
+	switch (sort->type) {
+
+	case DNS_TYPE_MX:
+		return rr1->rdata.mx.pref <= rr2->rdata.mx.pref;
+
+	case DNS_TYPE_SRV:
+		if (rr1->rdata.srv.pri == rr2->rdata.srv.pri)
+			return sidx(rr1, sort->key) >= sidx(rr2, sort->key);
+
+		return rr1->rdata.srv.pri < rr2->rdata.srv.pri;
+
+	case DNS_TYPE_NAPTR:
+		if (rr1->rdata.naptr.order == rr2->rdata.naptr.order)
+			return rr1->rdata.naptr.pref <= rr2->rdata.naptr.pref;
+
+		return rr1->rdata.naptr.order < rr2->rdata.naptr.order;
+
+	default:
+		break;
+	}
+
+	return true;
+}
+
+
+static bool addr_sort_handler(struct le *le1, struct le *le2, void *arg)
+{
+	struct dnsrr *rr1 = le1->data;
+	struct dnsrr *rr2 = le2->data;
+	struct sort *sort = arg;
+
+	return sidx(rr1, sort->key) >= sidx(rr2, sort->key);
+}
+
+
+/**
+ * Sort a list of DNS Resource Records
+ *
+ * @param rrl  DNS Resource Record list
+ * @param type DNS Record type
+ * @param key  Sort key
+ */
+void dns_rrlist_sort(struct list *rrl, uint16_t type, size_t key)
+{
+	struct sort sort = {type, (uint32_t)key>>5};
+
+	list_sort(rrl, std_sort_handler, &sort);
+}
+
+
+/**
+ * Sort a list of A/AAAA DNS Resource Records
+ *
+ * @param rrl  DNS Resource Record list
+ * @param key  Sort key
+ */
+void dns_rrlist_sort_addr(struct list *rrl, size_t key)
+{
+	struct sort sort = {0, (uint32_t)key>>5};
+
+	list_sort(rrl, addr_sort_handler, &sort);
+}
+
+
+static struct dnsrr *rrlist_apply(struct list *rrl, const char *name,
+				  uint16_t type1, uint16_t type2,
+				  uint16_t dnsclass,
+				  bool recurse, uint32_t depth,
+				  dns_rrlist_h *rrlh, void *arg)
+{
+	struct le *le = list_head(rrl);
+
+	if (depth > CNAME_RECURSE_MAX)
+		return NULL;
+
+	while (le) {
+
+		struct dnsrr *rr = le->data;
+
+		le = le->next;
+
+		if (name && str_casecmp(name, rr->name))
+			continue;
+
+		if (type1 != DNS_QTYPE_ANY && type2 != DNS_QTYPE_ANY &&
+		    rr->type != type1 && rr->type != type2 &&
+		    (rr->type != DNS_TYPE_CNAME || !recurse))
+			continue;
+
+		if (dnsclass != DNS_QCLASS_ANY && rr->dnsclass != dnsclass)
+			continue;
+
+		if (!rrlh || rrlh(rr, arg))
+			return rr;
+
+		if (recurse &&
+		    DNS_QTYPE_ANY != type1 && DNS_QTYPE_ANY != type2 &&
+		    DNS_TYPE_CNAME != type1 && DNS_TYPE_CNAME != type2 &&
+		    DNS_TYPE_CNAME == rr->type) {
+			rr = rrlist_apply(rrl, rr->rdata.cname.cname, type1,
+					  type2, dnsclass, recurse, ++depth,
+					  rrlh, arg);
+			if (rr)
+				return rr;
+		}
+	}
+
+	return NULL;
+}
+
+
+/**
+ * Apply a function handler to a list of DNS Resource Records
+ *
+ * @param rrl      DNS Resource Record list
+ * @param name     If set, filter on domain name
+ * @param type     If not DNS_QTYPE_ANY, filter on record type
+ * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class
+ * @param recurse  Cname recursion
+ * @param rrlh     Resource record handler
+ * @param arg      Handler argument
+ *
+ * @return Matching Resource Record or NULL
+ */
+struct dnsrr *dns_rrlist_apply(struct list *rrl, const char *name,
+			       uint16_t type, uint16_t dnsclass,
+			       bool recurse, dns_rrlist_h *rrlh, void *arg)
+{
+	return rrlist_apply(rrl, name, type, type, dnsclass,
+			    recurse, 0, rrlh, arg);
+}
+
+
+/**
+ * Apply a function handler to a list of DNS Resource Records (two types)
+ *
+ * @param rrl      DNS Resource Record list
+ * @param name     If set, filter on domain name
+ * @param type1    If not DNS_QTYPE_ANY, filter on record type
+ * @param type2    If not DNS_QTYPE_ANY, filter on record type
+ * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class
+ * @param recurse  Cname recursion
+ * @param rrlh     Resource record handler
+ * @param arg      Handler argument
+ *
+ * @return Matching Resource Record or NULL
+ */
+struct dnsrr *dns_rrlist_apply2(struct list *rrl, const char *name,
+				uint16_t type1, uint16_t type2,
+				uint16_t dnsclass, bool recurse,
+				dns_rrlist_h *rrlh, void *arg)
+{
+	return rrlist_apply(rrl, name, type1, type2, dnsclass,
+			    recurse, 0, rrlh, arg);
+}
+
+
+static bool find_handler(struct dnsrr *rr, void *arg)
+{
+	uint16_t type = *(uint16_t *)arg;
+
+	return rr->type == type;
+}
+
+
+/**
+ * Find a DNS Resource Record in a list
+ *
+ * @param rrl      Resource Record list
+ * @param name     If set, filter on domain name
+ * @param type     If not DNS_QTYPE_ANY, filter on record type
+ * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class
+ * @param recurse  Cname recursion
+ *
+ * @return Matching Resource Record or NULL
+ */
+struct dnsrr *dns_rrlist_find(struct list *rrl, const char *name,
+			      uint16_t type, uint16_t dnsclass, bool recurse)
+{
+	return rrlist_apply(rrl, name, type, type, dnsclass,
+			    recurse, 0, find_handler, &type);
+}