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;
+}