blob: c79b2c4efedcb0e3f80b33fdd92958c112840097 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
2 * @file dnsdisc.c DNS Discovery of a STUN Server
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6#include <string.h>
7#include <re_types.h>
8#include <re_fmt.h>
9#include <re_mem.h>
10#include <re_mbuf.h>
11#include <re_list.h>
12#include <re_sa.h>
13#include <re_dns.h>
14#include <re_stun.h>
15
16
17#define DEBUG_MODULE "dnsdisc"
18#define DEBUG_LEVEL 5
19#include <re_dbg.h>
20
21
22/** DNS Query */
23struct stun_dns {
24 char domain[256]; /**< Cached domain name */
25 stun_dns_h *dnsh; /**< DNS Response handler */
26 void *arg; /**< Handler argument */
27 struct sa srv; /**< Resolved server address */
28 struct dnsc *dnsc; /**< DNS Client */
29 struct dns_query *dq; /**< Current DNS query */
30 int af; /**< Preferred Address family*/
31 uint16_t port; /**< Default Port */
32};
33
34const char *stun_proto_udp = "udp"; /**< UDP Protocol */
35const char *stun_proto_tcp = "tcp"; /**< TCP Protocol */
36
37const char *stun_usage_binding = "stun"; /**< Binding usage */
38const char *stuns_usage_binding = "stuns"; /**< Binding usage TLS */
39const char *stun_usage_relay = "turn";
40const char *stuns_usage_relay = "turns";
41const char *stun_usage_behavior = "stun-behavior";
42const char *stuns_usage_behavior = "stun-behaviors";
43
44
45static void resolved(const struct stun_dns *dns, int err)
46{
47 stun_dns_h *dnsh = dns->dnsh;
48 void *dnsh_arg = dns->arg;
49
50 DEBUG_INFO("resolved: %J (%m)\n", &dns->srv, err);
51
52 dnsh(err, &dns->srv, dnsh_arg);
53}
54
55
56static void a_handler(int err, const struct dnshdr *hdr, struct list *ansl,
57 struct list *authl, struct list *addl, void *arg)
58{
59 struct stun_dns *dns = arg;
60 struct dnsrr *rr;
61
62 (void)hdr;
63 (void)authl;
64 (void)addl;
65
66 /* Find A answers */
67 rr = dns_rrlist_find(ansl, NULL, DNS_TYPE_A, DNS_CLASS_IN, false);
68 if (!rr) {
69 err = err ? err : EDESTADDRREQ;
70 goto out;
71 }
72
73 sa_set_in(&dns->srv, rr->rdata.a.addr, sa_port(&dns->srv));
74
75 DEBUG_INFO("A answer: %j\n", &dns->srv);
76
77 out:
78 resolved(dns, err);
79}
80
81
82#ifdef HAVE_INET6
83static void aaaa_handler(int err, const struct dnshdr *hdr, struct list *ansl,
84 struct list *authl, struct list *addl, void *arg)
85{
86 struct stun_dns *dns = arg;
87 struct dnsrr *rr;
88
89 (void)hdr;
90 (void)authl;
91 (void)addl;
92
93 /* Find A answers */
94 rr = dns_rrlist_find(ansl, NULL, DNS_TYPE_AAAA, DNS_CLASS_IN, false);
95 if (!rr) {
96 err = err ? err : EDESTADDRREQ;
97 goto out;
98 }
99
100 sa_set_in6(&dns->srv, rr->rdata.aaaa.addr, sa_port(&dns->srv));
101
102 DEBUG_INFO("AAAA answer: %j\n", &dns->srv);
103
104 out:
105 resolved(dns, err);
106}
107#endif
108
109
110static int a_or_aaaa_query(struct stun_dns *dns, const char *name)
111{
112 dns->dq = mem_deref(dns->dq);
113
114 switch (dns->af) {
115
116 case AF_INET:
117 return dnsc_query(&dns->dq, dns->dnsc, name, DNS_TYPE_A,
118 DNS_CLASS_IN, true, a_handler, dns);
119
120#ifdef HAVE_INET6
121 case AF_INET6:
122 return dnsc_query(&dns->dq, dns->dnsc, name, DNS_TYPE_AAAA,
123 DNS_CLASS_IN, true, aaaa_handler, dns);
124#endif
125
126 default:
127 return EAFNOSUPPORT;
128 }
129}
130
131
132static void srv_handler(int err, const struct dnshdr *hdr, struct list *ansl,
133 struct list *authl, struct list *addl, void *arg)
134{
135 struct stun_dns *dns = arg;
136 struct dnsrr *rr, *arr;
137
138 (void)hdr;
139 (void)authl;
140
141 dns_rrlist_sort(ansl, DNS_TYPE_SRV, (size_t)dns->arg);
142
143 /* Find SRV answers */
144 rr = dns_rrlist_find(ansl, NULL, DNS_TYPE_SRV, DNS_CLASS_IN, false);
145 if (!rr) {
146 DEBUG_INFO("no SRV entry, trying A lookup on \"%s\"\n",
147 dns->domain);
148
149 sa_set_in(&dns->srv, 0, dns->port);
150
151 err = a_or_aaaa_query(dns, dns->domain);
152 if (err)
153 goto out;
154
155 return;
156 }
157
158 DEBUG_INFO("SRV answer: %s:%u\n", rr->rdata.srv.target,
159 rr->rdata.srv.port);
160
161 /* Look for Additional information */
162 switch (dns->af) {
163
164 case AF_INET:
165 arr = dns_rrlist_find(addl, rr->rdata.srv.target,
166 DNS_TYPE_A, DNS_CLASS_IN, true);
167 if (arr) {
168 sa_set_in(&dns->srv, arr->rdata.a.addr,
169 rr->rdata.srv.port);
170 DEBUG_INFO("additional A: %j\n", &dns->srv);
171 goto out;
172 }
173 break;
174
175#ifdef HAVE_INET6
176 case AF_INET6:
177 arr = dns_rrlist_find(addl, rr->rdata.srv.target,
178 DNS_TYPE_AAAA, DNS_CLASS_IN, true);
179 if (arr) {
180 sa_set_in6(&dns->srv, arr->rdata.aaaa.addr,
181 rr->rdata.srv.port);
182 DEBUG_INFO("additional AAAA: %j\n", &dns->srv);
183 goto out;
184 }
185 break;
186#endif
187 }
188
189 sa_set_in(&dns->srv, 0, rr->rdata.srv.port);
190
191 err = a_or_aaaa_query(dns, rr->rdata.srv.target);
192 if (err) {
193 DEBUG_WARNING("SRV: A lookup failed (%m)\n", err);
194 goto out;
195 }
196
197 DEBUG_INFO("SRV handler: doing A/AAAA lookup..\n");
198
199 return;
200
201 out:
202 resolved(dns, err);
203}
204
205
206static void dnsdisc_destructor(void *data)
207{
208 struct stun_dns *dns = data;
209
210 mem_deref(dns->dq);
211}
212
213
214/**
215 * Do a DNS Discovery of a STUN Server
216 *
217 * @param dnsp Pointer to allocated DNS Discovery object
218 * @param dnsc DNS Client
219 * @param service Name of service to discover (e.g. "stun")
220 * @param proto Transport protocol (e.g. "udp")
221 * @param af Preferred Address Family
222 * @param domain Domain name or IP address of STUN server
223 * @param port Port number (if 0 do SRV lookup)
224 * @param dnsh DNS Response handler
225 * @param arg Handler argument
226 *
227 * @return 0 if success, otherwise errorcode
228 */
229int stun_server_discover(struct stun_dns **dnsp, struct dnsc *dnsc,
230 const char *service, const char *proto,
231 int af, const char *domain, uint16_t port,
232 stun_dns_h *dnsh, void *arg)
233{
234 struct stun_dns *dns;
235 int err;
236
237 if (!dnsp || !service || !proto || !domain || !domain[0] || !dnsh)
238 return EINVAL;
239
240 dns = mem_zalloc(sizeof(*dns), dnsdisc_destructor);
241 if (!dns)
242 return ENOMEM;
243
244 dns->port = service[strlen(service)-1] == 's' ? STUNS_PORT : STUN_PORT;
245 dns->dnsh = dnsh;
246 dns->arg = arg;
247 dns->dnsc = dnsc;
248 dns->af = af;
249
250 /* Numeric IP address - no lookup */
251 if (0 == sa_set_str(&dns->srv, domain, port ? port : dns->port)) {
252
253 DEBUG_INFO("IP (%s)\n", domain);
254
255 resolved(dns, 0);
256 err = 0;
257 goto out; /* free now */
258 }
259 /* Port specified - use AAAA or A lookup */
260 else if (port) {
261 sa_set_in(&dns->srv, 0, port);
262 DEBUG_INFO("resolving A query: (%s)\n", domain);
263
264 err = a_or_aaaa_query(dns, domain);
265 if (err) {
266 DEBUG_WARNING("%s: A/AAAA lookup failed (%m)\n",
267 domain, err);
268 goto out;
269 }
270 }
271 /* SRV lookup */
272 else {
273 char q[256];
274 str_ncpy(dns->domain, domain, sizeof(dns->domain));
275 (void)re_snprintf(q, sizeof(q), "_%s._%s.%s", service, proto,
276 domain);
277 DEBUG_INFO("resolving SRV query: (%s)\n", q);
278 err = dnsc_query(&dns->dq, dnsc, q, DNS_TYPE_SRV, DNS_CLASS_IN,
279 true, srv_handler, dns);
280 if (err) {
281 DEBUG_WARNING("%s: SRV lookup failed (%m)\n", q, err);
282 goto out;
283 }
284 }
285
286 *dnsp = dns;
287
288 return 0;
289
290 out:
291 mem_deref(dns);
292 return err;
293}