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/dnsdisc.c b/src/stun/dnsdisc.c
new file mode 100644
index 0000000..c79b2c4
--- /dev/null
+++ b/src/stun/dnsdisc.c
@@ -0,0 +1,293 @@
+/**
+ * @file dnsdisc.c  DNS Discovery of a STUN Server
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#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_dns.h>
+#include <re_stun.h>
+
+
+#define DEBUG_MODULE "dnsdisc"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/** DNS Query */
+struct stun_dns {
+	char domain[256];      /**< Cached domain name      */
+	stun_dns_h *dnsh;      /**< DNS Response handler    */
+	void *arg;             /**< Handler argument        */
+	struct sa srv;         /**< Resolved server address */
+	struct dnsc *dnsc;     /**< DNS Client              */
+	struct dns_query *dq;  /**< Current DNS query       */
+	int af;                /**< Preferred Address family*/
+	uint16_t port;         /**< Default Port            */
+};
+
+const char *stun_proto_udp = "udp";   /**< UDP Protocol  */
+const char *stun_proto_tcp = "tcp";   /**< TCP Protocol  */
+
+const char *stun_usage_binding   = "stun";  /**< Binding usage */
+const char *stuns_usage_binding  = "stuns"; /**< Binding usage TLS */
+const char *stun_usage_relay     = "turn";
+const char *stuns_usage_relay    = "turns";
+const char *stun_usage_behavior  = "stun-behavior";
+const char *stuns_usage_behavior = "stun-behaviors";
+
+
+static void resolved(const struct stun_dns *dns, int err)
+{
+	stun_dns_h *dnsh = dns->dnsh;
+	void *dnsh_arg = dns->arg;
+
+	DEBUG_INFO("resolved: %J (%m)\n", &dns->srv, err);
+
+	dnsh(err, &dns->srv, dnsh_arg);
+}
+
+
+static void a_handler(int err, const struct dnshdr *hdr, struct list *ansl,
+		      struct list *authl, struct list *addl, void *arg)
+{
+	struct stun_dns *dns = arg;
+	struct dnsrr *rr;
+
+	(void)hdr;
+	(void)authl;
+	(void)addl;
+
+	/* Find A answers */
+	rr = dns_rrlist_find(ansl, NULL, DNS_TYPE_A, DNS_CLASS_IN, false);
+	if (!rr) {
+		err = err ? err : EDESTADDRREQ;
+		goto out;
+	}
+
+	sa_set_in(&dns->srv, rr->rdata.a.addr, sa_port(&dns->srv));
+
+	DEBUG_INFO("A answer: %j\n", &dns->srv);
+
+ out:
+	resolved(dns, err);
+}
+
+
+#ifdef HAVE_INET6
+static void aaaa_handler(int err, const struct dnshdr *hdr, struct list *ansl,
+			 struct list *authl, struct list *addl, void *arg)
+{
+	struct stun_dns *dns = arg;
+	struct dnsrr *rr;
+
+	(void)hdr;
+	(void)authl;
+	(void)addl;
+
+	/* Find A answers */
+	rr = dns_rrlist_find(ansl, NULL, DNS_TYPE_AAAA, DNS_CLASS_IN, false);
+	if (!rr) {
+		err = err ? err : EDESTADDRREQ;
+		goto out;
+	}
+
+	sa_set_in6(&dns->srv, rr->rdata.aaaa.addr, sa_port(&dns->srv));
+
+	DEBUG_INFO("AAAA answer: %j\n", &dns->srv);
+
+ out:
+	resolved(dns, err);
+}
+#endif
+
+
+static int a_or_aaaa_query(struct stun_dns *dns, const char *name)
+{
+	dns->dq = mem_deref(dns->dq);
+
+	switch (dns->af) {
+
+	case AF_INET:
+		return dnsc_query(&dns->dq, dns->dnsc, name, DNS_TYPE_A,
+				  DNS_CLASS_IN, true, a_handler, dns);
+
+#ifdef HAVE_INET6
+	case AF_INET6:
+		return dnsc_query(&dns->dq, dns->dnsc, name, DNS_TYPE_AAAA,
+				  DNS_CLASS_IN, true, aaaa_handler, dns);
+#endif
+
+	default:
+		return EAFNOSUPPORT;
+	}
+}
+
+
+static void srv_handler(int err, const struct dnshdr *hdr, struct list *ansl,
+			struct list *authl, struct list *addl, void *arg)
+{
+	struct stun_dns *dns = arg;
+	struct dnsrr *rr, *arr;
+
+	(void)hdr;
+	(void)authl;
+
+	dns_rrlist_sort(ansl, DNS_TYPE_SRV, (size_t)dns->arg);
+
+	/* Find SRV answers */
+	rr = dns_rrlist_find(ansl, NULL, DNS_TYPE_SRV, DNS_CLASS_IN, false);
+	if (!rr) {
+		DEBUG_INFO("no SRV entry, trying A lookup on \"%s\"\n",
+			   dns->domain);
+
+		sa_set_in(&dns->srv, 0, dns->port);
+
+		err = a_or_aaaa_query(dns, dns->domain);
+		if (err)
+			goto out;
+
+		return;
+	}
+
+	DEBUG_INFO("SRV answer: %s:%u\n", rr->rdata.srv.target,
+		   rr->rdata.srv.port);
+
+	/* Look for Additional information */
+	switch (dns->af) {
+
+	case AF_INET:
+		arr = dns_rrlist_find(addl, rr->rdata.srv.target,
+				      DNS_TYPE_A, DNS_CLASS_IN, true);
+		if (arr) {
+			sa_set_in(&dns->srv, arr->rdata.a.addr,
+				  rr->rdata.srv.port);
+			DEBUG_INFO("additional A: %j\n", &dns->srv);
+			goto out;
+		}
+		break;
+
+#ifdef HAVE_INET6
+	case AF_INET6:
+		arr = dns_rrlist_find(addl, rr->rdata.srv.target,
+				      DNS_TYPE_AAAA, DNS_CLASS_IN, true);
+		if (arr) {
+			sa_set_in6(&dns->srv, arr->rdata.aaaa.addr,
+				  rr->rdata.srv.port);
+			DEBUG_INFO("additional AAAA: %j\n", &dns->srv);
+			goto out;
+		}
+		break;
+#endif
+	}
+
+	sa_set_in(&dns->srv, 0, rr->rdata.srv.port);
+
+	err = a_or_aaaa_query(dns, rr->rdata.srv.target);
+	if (err) {
+		DEBUG_WARNING("SRV: A lookup failed (%m)\n", err);
+		goto out;
+	}
+
+	DEBUG_INFO("SRV handler: doing A/AAAA lookup..\n");
+
+	return;
+
+ out:
+	resolved(dns, err);
+}
+
+
+static void dnsdisc_destructor(void *data)
+{
+	struct stun_dns *dns = data;
+
+	mem_deref(dns->dq);
+}
+
+
+/**
+ * Do a DNS Discovery of a STUN Server
+ *
+ * @param dnsp    Pointer to allocated DNS Discovery object
+ * @param dnsc    DNS Client
+ * @param service Name of service to discover (e.g. "stun")
+ * @param proto   Transport protocol (e.g. "udp")
+ * @param af      Preferred Address Family
+ * @param domain  Domain name or IP address of STUN server
+ * @param port    Port number (if 0 do SRV lookup)
+ * @param dnsh    DNS Response handler
+ * @param arg     Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int stun_server_discover(struct stun_dns **dnsp, struct dnsc *dnsc,
+			 const char *service, const char *proto,
+			 int af, const char *domain, uint16_t port,
+			 stun_dns_h *dnsh, void *arg)
+{
+	struct stun_dns *dns;
+	int err;
+
+	if (!dnsp || !service || !proto || !domain || !domain[0] || !dnsh)
+		return EINVAL;
+
+	dns = mem_zalloc(sizeof(*dns), dnsdisc_destructor);
+	if (!dns)
+		return ENOMEM;
+
+	dns->port = service[strlen(service)-1] == 's' ? STUNS_PORT : STUN_PORT;
+	dns->dnsh = dnsh;
+	dns->arg  = arg;
+	dns->dnsc = dnsc;
+	dns->af   = af;
+
+	/* Numeric IP address - no lookup */
+	if (0 == sa_set_str(&dns->srv, domain, port ? port : dns->port)) {
+
+		DEBUG_INFO("IP (%s)\n", domain);
+
+		resolved(dns, 0);
+		err = 0;
+		goto out; /* free now */
+	}
+	/* Port specified - use AAAA or A lookup */
+	else if (port) {
+		sa_set_in(&dns->srv, 0, port);
+		DEBUG_INFO("resolving A query: (%s)\n", domain);
+
+		err = a_or_aaaa_query(dns, domain);
+		if (err) {
+			DEBUG_WARNING("%s: A/AAAA lookup failed (%m)\n",
+				      domain, err);
+			goto out;
+		}
+	}
+	/* SRV lookup */
+	else {
+		char q[256];
+		str_ncpy(dns->domain, domain, sizeof(dns->domain));
+		(void)re_snprintf(q, sizeof(q), "_%s._%s.%s", service, proto,
+				  domain);
+		DEBUG_INFO("resolving SRV query: (%s)\n", q);
+		err = dnsc_query(&dns->dq, dnsc, q, DNS_TYPE_SRV, DNS_CLASS_IN,
+				 true, srv_handler, dns);
+		if (err) {
+			DEBUG_WARNING("%s: SRV lookup failed (%m)\n", q, err);
+			goto out;
+		}
+	}
+
+	*dnsp = dns;
+
+	return 0;
+
+ out:
+	mem_deref(dns);
+	return err;
+}