Line data Source code
1 : /* SPDX-License-Identifier: MIT OR GPL-3.0-only */
2 : /* resolver.c
3 : * strophe XMPP client library -- DNS resolver
4 : *
5 : * Copyright (C) 2015 Dmitry Podgorny <pasis.ua@gmail.com>
6 : *
7 : * This software is provided AS-IS with no warranty, either express
8 : * or implied.
9 : *
10 : * This program is dual licensed under the MIT or GPLv3 licenses.
11 : */
12 :
13 : /** @file
14 : * DNS resolver.
15 : */
16 :
17 : #if !defined(_WIN32) && !defined(HAVE_CARES)
18 : #include <netinet/in.h>
19 : #include <arpa/nameser.h>
20 : #include <resolv.h>
21 : #endif /* _WIN32 && HAVE_CARES */
22 :
23 : #ifdef HAVE_CARES
24 : #include <ares.h>
25 : /* for select(2) */
26 : #ifdef _WIN32
27 : #include <winsock2.h>
28 : #else /* _WIN32 */
29 : #include <sys/time.h>
30 : #include <sys/types.h>
31 : #include <unistd.h>
32 : #endif /* !_WIN32 */
33 : #endif /* HAVE_CARES */
34 :
35 : #include <string.h> /* strncpy */
36 :
37 : #include "ostypes.h"
38 : #include "snprintf.h"
39 : #include "util.h" /* xmpp_min */
40 : #include "resolver.h"
41 :
42 : #define MESSAGE_HEADER_LEN 12
43 : #define MESSAGE_RESPONSE 1
44 : #define MESSAGE_T_SRV 33
45 : #define MESSAGE_C_IN 1
46 :
47 : /*******************************************************************************
48 : * Forward declarations.
49 : ******************************************************************************/
50 :
51 : #ifdef HAVE_CARES
52 : static int resolver_ares_srv_lookup_buf(xmpp_ctx_t *ctx,
53 : const unsigned char *buf,
54 : size_t len,
55 : resolver_srv_rr_t **srv_rr_list);
56 : static int resolver_ares_srv_lookup(xmpp_ctx_t *ctx,
57 : const char *fulldomain,
58 : resolver_srv_rr_t **srv_rr_list);
59 : #endif /* HAVE_CARES */
60 :
61 : #ifndef HAVE_CARES
62 : static int resolver_raw_srv_lookup_buf(xmpp_ctx_t *ctx,
63 : const unsigned char *buf,
64 : size_t len,
65 : resolver_srv_rr_t **srv_rr_list);
66 : #endif /* !HAVE_CARES */
67 :
68 : #if defined(_WIN32) && !defined(HAVE_CARES)
69 : static int resolver_win32_srv_lookup(xmpp_ctx_t *ctx,
70 : const char *fulldomain,
71 : resolver_srv_rr_t **srv_rr_list);
72 : static int resolver_win32_srv_query(const char *fulldomain,
73 : unsigned char *buf,
74 : size_t len);
75 : #endif /* _WIN32 && !HAVE_CARES */
76 :
77 : /*******************************************************************************
78 : * Implementation.
79 : ******************************************************************************/
80 :
81 2 : void resolver_initialize(void)
82 : {
83 : #ifdef HAVE_CARES
84 : ares_library_init(ARES_LIB_INIT_ALL);
85 : #endif
86 2 : }
87 :
88 2 : void resolver_shutdown(void)
89 : {
90 : #ifdef HAVE_CARES
91 : ares_library_cleanup();
92 : #endif
93 2 : }
94 :
95 0 : resolver_srv_rr_t *resolver_srv_rr_new(xmpp_ctx_t *ctx,
96 : const char *host,
97 : unsigned short port,
98 : unsigned short prio,
99 : unsigned short weight)
100 : {
101 0 : resolver_srv_rr_t *rr = strophe_alloc(ctx, sizeof(*rr));
102 0 : if (rr) {
103 0 : memset(rr, 0, sizeof(*rr));
104 0 : rr->port = port;
105 0 : rr->priority = prio;
106 0 : rr->weight = weight;
107 0 : if (host) {
108 0 : snprintf(rr->target, sizeof(rr->target), "%s", host);
109 : }
110 : }
111 0 : return rr;
112 : }
113 :
114 0 : static void resolver_srv_list_sort(resolver_srv_rr_t **srv_rr_list)
115 : {
116 0 : resolver_srv_rr_t *rr_head;
117 0 : resolver_srv_rr_t *rr_current;
118 0 : resolver_srv_rr_t *rr_next;
119 0 : resolver_srv_rr_t *rr_prev;
120 0 : int swap;
121 :
122 0 : rr_head = *srv_rr_list;
123 :
124 0 : if ((rr_head == NULL) || (rr_head->next == NULL)) {
125 : /* Empty or single record list */
126 : return;
127 : }
128 :
129 0 : do {
130 0 : rr_prev = NULL;
131 0 : rr_current = rr_head;
132 0 : rr_next = rr_head->next;
133 0 : swap = 0;
134 0 : while (rr_next != NULL) {
135 : /*
136 : * RFC2052: A client MUST attempt to contact the target host
137 : * with the lowest-numbered priority it can reach.
138 : * RFC2052: When selecting a target host among the
139 : * those that have the same priority, the chance of trying
140 : * this one first SHOULD be proportional to its weight.
141 : */
142 0 : if ((rr_current->priority > rr_next->priority) ||
143 0 : (rr_current->priority == rr_next->priority &&
144 0 : rr_current->weight < rr_next->weight)) {
145 : /* Swap node */
146 0 : swap = 1;
147 0 : if (rr_prev != NULL) {
148 0 : rr_prev->next = rr_next;
149 : } else {
150 : /* Swap head node */
151 : rr_head = rr_next;
152 : }
153 0 : rr_current->next = rr_next->next;
154 0 : rr_next->next = rr_current;
155 :
156 0 : rr_prev = rr_next;
157 0 : rr_next = rr_current->next;
158 : } else {
159 : /* Next node */
160 0 : rr_prev = rr_current;
161 0 : rr_current = rr_next;
162 0 : rr_next = rr_next->next;
163 : }
164 : }
165 0 : } while (swap != 0);
166 :
167 0 : *srv_rr_list = rr_head;
168 : }
169 :
170 0 : int resolver_srv_lookup_buf(xmpp_ctx_t *ctx,
171 : const unsigned char *buf,
172 : size_t len,
173 : resolver_srv_rr_t **srv_rr_list)
174 : {
175 0 : int set;
176 :
177 : #ifdef HAVE_CARES
178 : set = resolver_ares_srv_lookup_buf(ctx, buf, len, srv_rr_list);
179 : #else
180 0 : set = resolver_raw_srv_lookup_buf(ctx, buf, len, srv_rr_list);
181 0 : if (set != XMPP_DOMAIN_FOUND && *srv_rr_list != NULL) {
182 0 : resolver_srv_free(ctx, *srv_rr_list);
183 0 : *srv_rr_list = NULL;
184 : }
185 : #endif
186 0 : resolver_srv_list_sort(srv_rr_list);
187 :
188 0 : return set;
189 : }
190 :
191 0 : int resolver_srv_lookup(xmpp_ctx_t *ctx,
192 : const char *service,
193 : const char *proto,
194 : const char *domain,
195 : resolver_srv_rr_t **srv_rr_list)
196 : {
197 : #define RESOLVER_BUF_MAX 65536
198 0 : unsigned char *buf;
199 0 : char fulldomain[2048];
200 0 : int len;
201 0 : int set = XMPP_DOMAIN_NOT_FOUND;
202 :
203 0 : (void)buf;
204 0 : (void)len;
205 :
206 0 : strophe_snprintf(fulldomain, sizeof(fulldomain), "_%s._%s.%s", service,
207 : proto, domain);
208 :
209 0 : *srv_rr_list = NULL;
210 :
211 : #ifdef HAVE_CARES
212 :
213 : set = resolver_ares_srv_lookup(ctx, fulldomain, srv_rr_list);
214 :
215 : #else /* HAVE_CARES */
216 :
217 : #ifdef _WIN32
218 : set = resolver_win32_srv_lookup(ctx, fulldomain, srv_rr_list);
219 : if (set == XMPP_DOMAIN_FOUND)
220 : return set;
221 : #endif /* _WIN32 */
222 :
223 0 : buf = strophe_alloc(ctx, RESOLVER_BUF_MAX);
224 0 : if (buf == NULL)
225 0 : return XMPP_DOMAIN_NOT_FOUND;
226 :
227 : #ifdef _WIN32
228 : len = resolver_win32_srv_query(fulldomain, buf, RESOLVER_BUF_MAX);
229 : #else /* _WIN32 */
230 0 : len = res_query(fulldomain, MESSAGE_C_IN, MESSAGE_T_SRV, buf,
231 : RESOLVER_BUF_MAX);
232 : #endif /* _WIN32 */
233 :
234 0 : if (len > 0)
235 0 : set = resolver_srv_lookup_buf(ctx, buf, (size_t)len, srv_rr_list);
236 :
237 0 : strophe_free(ctx, buf);
238 :
239 : #endif /* HAVE_CARES */
240 :
241 : return set;
242 : }
243 :
244 0 : void resolver_srv_free(xmpp_ctx_t *ctx, resolver_srv_rr_t *srv_rr_list)
245 : {
246 0 : resolver_srv_rr_t *rr;
247 :
248 0 : while (srv_rr_list != NULL) {
249 0 : rr = srv_rr_list->next;
250 0 : strophe_free(ctx, srv_rr_list);
251 0 : srv_rr_list = rr;
252 : }
253 0 : }
254 :
255 : #ifndef HAVE_CARES
256 : /*******************************************************************************
257 : * Resolver raw implementation.
258 : *
259 : * This code is common for both unix and win32.
260 : ******************************************************************************/
261 :
262 : struct message_header {
263 : uint16_t id;
264 : uint8_t octet2;
265 : uint8_t octet3;
266 : uint16_t qdcount;
267 : uint16_t ancount;
268 : uint16_t nscount;
269 : uint16_t arcount;
270 : };
271 :
272 : /* the same as ntohs(), but receives pointer to the value */
273 0 : static uint16_t xmpp_ntohs_ptr(const void *ptr)
274 : {
275 0 : const uint8_t *p = (const uint8_t *)ptr;
276 :
277 0 : return (uint16_t)((p[0] << 8U) + p[1]);
278 : }
279 :
280 0 : static uint8_t message_header_qr(const struct message_header *header)
281 : {
282 0 : return (header->octet2 >> 7) & 1;
283 : }
284 :
285 0 : static uint8_t message_header_rcode(const struct message_header *header)
286 : {
287 0 : return header->octet3 & 0x0f;
288 : }
289 :
290 : /*
291 : * Append a label or a dot to the target name with buffer overflow checks.
292 : * Returns length of the non-truncated resulting string, may be bigger than
293 : * name_max.
294 : */
295 0 : static size_t message_name_append_safe(char *name,
296 : size_t name_len,
297 : size_t name_max,
298 : const char *tail,
299 : size_t tail_len)
300 : {
301 0 : size_t copy_len;
302 :
303 0 : copy_len = name_max > name_len ? name_max - name_len : 0;
304 0 : copy_len = xmpp_min(tail_len, copy_len);
305 0 : if (copy_len > 0)
306 0 : memcpy(&name[name_len], tail, copy_len);
307 :
308 0 : return name_len + tail_len;
309 : }
310 :
311 : /* Returns length of the compressed name. This is NOT the same as strlen(). */
312 0 : static unsigned message_name_get(const unsigned char *buf,
313 : size_t buf_len,
314 : unsigned buf_offset,
315 : char *name,
316 : size_t name_max)
317 : {
318 0 : size_t name_len = 0;
319 0 : unsigned i = buf_offset;
320 0 : unsigned pointer;
321 0 : unsigned rc;
322 0 : unsigned char label_len;
323 :
324 0 : while (1) {
325 0 : if (i >= buf_len)
326 : return 0;
327 0 : label_len = buf[i++];
328 0 : if (label_len == 0)
329 : break;
330 :
331 : /* Label */
332 0 : if ((label_len & 0xc0) == 0) {
333 0 : if (i + label_len - 1 >= buf_len)
334 : return 0;
335 0 : if (name != NULL) {
336 0 : name_len = message_name_append_safe(name, name_len, name_max,
337 0 : (char *)&buf[i], label_len);
338 0 : name_len =
339 0 : message_name_append_safe(name, name_len, name_max, ".", 1);
340 : }
341 : i += label_len;
342 :
343 : /* Pointer */
344 0 : } else if ((label_len & 0xc0) == 0xc0) {
345 0 : if (i >= buf_len)
346 : return 0;
347 0 : pointer = (label_len & 0x3f) << 8 | buf[i++];
348 : /* Prevent infinite looping */
349 0 : if (pointer >= buf_offset)
350 : return 0;
351 0 : if (name != NULL && name_len >= name_max && name_max > 0) {
352 : /* We have filled the name buffer. Don't pass it recursively. */
353 0 : name[name_max - 1] = '\0';
354 0 : name = NULL;
355 0 : name_max = 0;
356 : }
357 0 : rc = message_name_get(
358 : buf, buf_len, pointer, name != NULL ? &name[name_len] : NULL,
359 : name_max > name_len ? name_max - name_len : 0);
360 0 : if (rc == 0)
361 : return 0;
362 : /* Pointer is always the last. */
363 : break;
364 :
365 : /* The 10 and 01 combinations are reserved for future use. */
366 : } else {
367 : return 0;
368 : }
369 : }
370 0 : if (label_len == 0) {
371 0 : if (name_len == 0)
372 : name_len = 1;
373 : /*
374 : * At this point name_len is length of the resulting name,
375 : * including '\0'. This value can be exported to allocate buffer
376 : * of precise size.
377 : */
378 0 : if (name != NULL && name_max > 0) {
379 : /*
380 : * Overwrite leading '.' with a '\0'. If the resulting name is
381 : * bigger than name_max it is truncated.
382 : */
383 0 : name[xmpp_min(name_len, name_max) - 1] = '\0';
384 : }
385 : }
386 :
387 0 : return i - buf_offset;
388 : }
389 :
390 : static unsigned
391 0 : message_name_len(const unsigned char *buf, size_t buf_len, unsigned buf_offset)
392 : {
393 0 : return message_name_get(buf, buf_len, buf_offset, NULL, SIZE_MAX);
394 : }
395 :
396 : #define BUF_OVERFLOW_CHECK(ptr, len) \
397 : do { \
398 : if ((ptr) >= (len)) { \
399 : if (*srv_rr_list != NULL) \
400 : resolver_srv_free(ctx, *srv_rr_list); \
401 : *srv_rr_list = NULL; \
402 : return XMPP_DOMAIN_NOT_FOUND; \
403 : } \
404 : } while (0)
405 :
406 0 : static int resolver_raw_srv_lookup_buf(xmpp_ctx_t *ctx,
407 : const unsigned char *buf,
408 : size_t len,
409 : resolver_srv_rr_t **srv_rr_list)
410 : {
411 0 : unsigned i;
412 0 : unsigned j;
413 0 : unsigned name_len;
414 0 : unsigned rdlength;
415 0 : uint16_t type;
416 0 : uint16_t class;
417 0 : struct message_header header;
418 0 : resolver_srv_rr_t *rr;
419 :
420 0 : *srv_rr_list = NULL;
421 :
422 0 : if (len < MESSAGE_HEADER_LEN)
423 : return XMPP_DOMAIN_NOT_FOUND;
424 :
425 0 : header.id = xmpp_ntohs_ptr(&buf[0]);
426 0 : header.octet2 = buf[2];
427 0 : header.octet3 = buf[3];
428 0 : header.qdcount = xmpp_ntohs_ptr(&buf[4]);
429 0 : header.ancount = xmpp_ntohs_ptr(&buf[6]);
430 0 : header.nscount = xmpp_ntohs_ptr(&buf[8]);
431 0 : header.arcount = xmpp_ntohs_ptr(&buf[10]);
432 0 : if (message_header_qr(&header) != MESSAGE_RESPONSE ||
433 0 : message_header_rcode(&header) != 0) {
434 : return XMPP_DOMAIN_NOT_FOUND;
435 : }
436 : j = MESSAGE_HEADER_LEN;
437 :
438 : /* skip question section */
439 0 : for (i = 0; i < header.qdcount; ++i) {
440 0 : BUF_OVERFLOW_CHECK(j, len);
441 0 : name_len = message_name_len(buf, len, j);
442 : /* error in name format */
443 0 : if (name_len == 0)
444 : return XMPP_DOMAIN_NOT_FOUND;
445 0 : j += name_len + 4;
446 : }
447 :
448 0 : for (i = 0; i < header.ancount; ++i) {
449 0 : BUF_OVERFLOW_CHECK(j, len);
450 0 : name_len = message_name_len(buf, len, j);
451 : /* error in name format */
452 0 : if (name_len == 0)
453 : return XMPP_DOMAIN_NOT_FOUND;
454 0 : j += name_len;
455 0 : BUF_OVERFLOW_CHECK(j + 16, len);
456 0 : type = xmpp_ntohs_ptr(&buf[j]);
457 0 : class = xmpp_ntohs_ptr(&buf[j + 2]);
458 0 : rdlength = xmpp_ntohs_ptr(&buf[j + 8]);
459 0 : j += 10;
460 0 : if (type == MESSAGE_T_SRV && class == MESSAGE_C_IN) {
461 0 : rr = resolver_srv_rr_new(ctx, NULL, 0, 0, 0);
462 0 : if (rr) {
463 0 : rr->next = *srv_rr_list;
464 0 : rr->priority = xmpp_ntohs_ptr(&buf[j]);
465 0 : rr->weight = xmpp_ntohs_ptr(&buf[j + 2]);
466 0 : rr->port = xmpp_ntohs_ptr(&buf[j + 4]);
467 0 : name_len = message_name_get(buf, len, j + 6, rr->target,
468 : sizeof(rr->target));
469 0 : if (name_len > 0)
470 0 : *srv_rr_list = rr;
471 : else
472 0 : strophe_free(ctx, rr); /* skip broken record */
473 : }
474 : }
475 0 : j += rdlength;
476 : }
477 :
478 0 : return *srv_rr_list != NULL ? XMPP_DOMAIN_FOUND : XMPP_DOMAIN_NOT_FOUND;
479 : }
480 :
481 : #endif /* !HAVE_CARES */
482 :
483 : #ifdef HAVE_CARES
484 : /*******************************************************************************
485 : * Resolver implementation using c-ares library.
486 : ******************************************************************************/
487 :
488 : struct resolver_ares_ctx {
489 : xmpp_ctx_t *ctx;
490 : int result;
491 : resolver_srv_rr_t *srv_rr_list;
492 : };
493 :
494 : static int resolver_ares_srv_lookup_buf(xmpp_ctx_t *ctx,
495 : const unsigned char *buf,
496 : size_t len,
497 : resolver_srv_rr_t **srv_rr_list)
498 : {
499 : struct ares_srv_reply *srv;
500 : struct ares_srv_reply *item;
501 : resolver_srv_rr_t *rr;
502 : int rc;
503 :
504 : *srv_rr_list = NULL;
505 :
506 : rc = ares_parse_srv_reply(buf, len, &srv);
507 : if (rc != ARES_SUCCESS)
508 : return XMPP_DOMAIN_NOT_FOUND;
509 :
510 : item = srv;
511 : while (item != NULL) {
512 : rr = strophe_alloc(ctx, sizeof(*rr));
513 : if (rr == NULL)
514 : break;
515 : rr->next = *srv_rr_list;
516 : rr->priority = item->priority;
517 : rr->weight = item->weight;
518 : rr->port = item->port;
519 : strncpy(rr->target, item->host, sizeof(rr->target) - 1);
520 : rr->target[sizeof(rr->target) - 1] = '\0';
521 : *srv_rr_list = rr;
522 : item = item->next;
523 : }
524 : ares_free_data(srv);
525 :
526 : return *srv_rr_list == NULL ? XMPP_DOMAIN_NOT_FOUND : XMPP_DOMAIN_FOUND;
527 : }
528 :
529 : static void ares_srv_lookup_callback(
530 : void *arg, int status, int timeouts, unsigned char *buf, int len)
531 : {
532 : struct resolver_ares_ctx *actx = arg;
533 :
534 : (void)timeouts;
535 :
536 : if (status != ARES_SUCCESS)
537 : actx->result = XMPP_DOMAIN_NOT_FOUND;
538 : else
539 : actx->result = resolver_ares_srv_lookup_buf(actx->ctx, buf, len,
540 : &actx->srv_rr_list);
541 : }
542 :
543 : static int resolver_ares_srv_lookup(xmpp_ctx_t *ctx,
544 : const char *fulldomain,
545 : resolver_srv_rr_t **srv_rr_list)
546 : {
547 : struct resolver_ares_ctx actx;
548 : ares_channel chan;
549 : struct timeval tv;
550 : struct timeval *tvp;
551 : fd_set rfds;
552 : fd_set wfds;
553 : int nfds;
554 : int rc;
555 :
556 : actx.ctx = ctx;
557 : actx.result = XMPP_DOMAIN_NOT_FOUND;
558 : actx.srv_rr_list = NULL;
559 :
560 : rc = ares_init(&chan);
561 : if (rc == ARES_SUCCESS) {
562 : ares_query(chan, fulldomain, MESSAGE_C_IN, MESSAGE_T_SRV,
563 : ares_srv_lookup_callback, &actx);
564 : while (1) {
565 : FD_ZERO(&rfds);
566 : FD_ZERO(&wfds);
567 : nfds = ares_fds(chan, &rfds, &wfds);
568 : if (nfds == 0)
569 : break;
570 : tvp = ares_timeout(chan, NULL, &tv);
571 : select(nfds, &rfds, &wfds, NULL, tvp);
572 : ares_process(chan, &rfds, &wfds);
573 : }
574 : ares_destroy(chan);
575 : }
576 :
577 : *srv_rr_list = actx.srv_rr_list;
578 : return actx.result;
579 : }
580 :
581 : #endif /* HAVE_CARES */
582 :
583 : #if defined(_WIN32) && !defined(HAVE_CARES)
584 : /*******************************************************************************
585 : * Next part was copied from sock.c and contains old win32 code.
586 : *
587 : * The idea is to get raw response from a name server and pass it to
588 : * resolver_srv_lookup_buf(). In fact, resolver_win32_srv_query() replaces
589 : * the call of res_query().
590 : * Dnsapi code is moved to a separated function resolver_srv_win32_lookup() and
591 : * changed to meet new API.
592 : *
593 : * XXX If the code is compiled it should work like before.
594 : ******************************************************************************/
595 :
596 : #include <winsock2.h>
597 : #include <ws2tcpip.h>
598 : #include <windns.h>
599 : #include <iphlpapi.h>
600 :
601 : struct dnsquery_header {
602 : unsigned short id;
603 : unsigned char qr;
604 : unsigned char opcode;
605 : unsigned char aa;
606 : unsigned char tc;
607 : unsigned char rd;
608 : unsigned char ra;
609 : unsigned char z;
610 : unsigned char rcode;
611 : unsigned short qdcount;
612 : unsigned short ancount;
613 : unsigned short nscount;
614 : unsigned short arcount;
615 : };
616 :
617 : struct dnsquery_question {
618 : char qname[1024];
619 : unsigned short qtype;
620 : unsigned short qclass;
621 : };
622 :
623 : static void netbuf_add_16bitnum(unsigned char *buf,
624 : int buflen,
625 : int *offset,
626 : unsigned short num)
627 : {
628 : unsigned char *start = buf + *offset;
629 : unsigned char *p = start;
630 :
631 : /* assuming big endian */
632 : *p++ = (num >> 8) & 0xff;
633 : *p++ = (num)&0xff;
634 :
635 : *offset += 2;
636 : }
637 :
638 : static void
639 : netbuf_add_domain_name(unsigned char *buf, int buflen, int *offset, char *name)
640 : {
641 : unsigned char *start = buf + *offset;
642 : unsigned char *p = start;
643 : unsigned char *wordstart, *wordend;
644 :
645 : wordstart = (unsigned char *)name;
646 :
647 : while (*wordstart) {
648 : int len;
649 : wordend = wordstart;
650 : while (*wordend && *wordend != '.') {
651 : wordend++;
652 : }
653 :
654 : len = (int)(wordend - wordstart);
655 :
656 : if (len > 0x3F) {
657 : len = 0x3F;
658 : }
659 :
660 : *p++ = len;
661 :
662 : while (wordstart != wordend) {
663 : *p++ = *wordstart++;
664 : }
665 :
666 : if (*wordstart == '.') {
667 : wordstart++;
668 : }
669 : }
670 :
671 : *p++ = '\0';
672 :
673 : *offset += (int)(p - start);
674 : }
675 :
676 : static void netbuf_add_dnsquery_header(unsigned char *buf,
677 : int buflen,
678 : int *offset,
679 : struct dnsquery_header *header)
680 : {
681 : unsigned char *p;
682 :
683 : netbuf_add_16bitnum(buf, buflen, offset, header->id);
684 :
685 : p = buf + *offset;
686 : *p++ = ((header->qr & 0x01) << 7) | ((header->opcode & 0x0F) << 3) |
687 : ((header->aa & 0x01) << 2) | ((header->tc & 0x01) << 1) |
688 : ((header->rd & 0x01));
689 : *p++ = ((header->ra & 0x01) << 7) | ((header->z & 0x07) << 4) |
690 : ((header->rcode & 0x0F));
691 : *offset += 2;
692 :
693 : netbuf_add_16bitnum(buf, buflen, offset, header->qdcount);
694 : netbuf_add_16bitnum(buf, buflen, offset, header->ancount);
695 : netbuf_add_16bitnum(buf, buflen, offset, header->nscount);
696 : netbuf_add_16bitnum(buf, buflen, offset, header->arcount);
697 : }
698 :
699 : static void netbuf_add_dnsquery_question(unsigned char *buf,
700 : int buflen,
701 : int *offset,
702 : struct dnsquery_question *question)
703 : {
704 : netbuf_add_domain_name(buf, buflen, offset, question->qname);
705 : netbuf_add_16bitnum(buf, buflen, offset, question->qtype);
706 : netbuf_add_16bitnum(buf, buflen, offset, question->qclass);
707 : }
708 :
709 : static int resolver_win32_srv_lookup(xmpp_ctx_t *ctx,
710 : const char *fulldomain,
711 : resolver_srv_rr_t **srv_rr_list)
712 : {
713 : resolver_srv_rr_t *rr;
714 : HINSTANCE hdnsapi = NULL;
715 :
716 : DNS_STATUS(WINAPI * pDnsQuery_A)
717 : (PCSTR, WORD, DWORD, PIP4_ARRAY, DNS_RECORDA **, PVOID *);
718 : void(WINAPI * pDnsRecordListFree)(DNS_RECORDA *, DNS_FREE_TYPE);
719 :
720 : if (hdnsapi = LoadLibrary("dnsapi.dll")) {
721 : pDnsQuery_A = (void *)GetProcAddress(hdnsapi, "DnsQuery_A");
722 : pDnsRecordListFree =
723 : (void *)GetProcAddress(hdnsapi, "DnsRecordListFree");
724 :
725 : if (pDnsQuery_A && pDnsRecordListFree) {
726 : DNS_RECORDA *dnsrecords = NULL;
727 : DNS_STATUS error;
728 :
729 : error = pDnsQuery_A(fulldomain, DNS_TYPE_SRV, DNS_QUERY_STANDARD,
730 : NULL, &dnsrecords, NULL);
731 :
732 : if (error == 0) {
733 : DNS_RECORDA *current = dnsrecords;
734 :
735 : while (current) {
736 : if (current->wType == DNS_TYPE_SRV) {
737 : rr = strophe_alloc(ctx, sizeof(*rr));
738 : if (rr == NULL)
739 : break;
740 : rr->next = *srv_rr_list;
741 : rr->port = current->Data.Srv.wPort;
742 : rr->priority = current->Data.Srv.wPriority;
743 : rr->weight = current->Data.Srv.wWeight;
744 : strophe_snprintf(rr->target, sizeof(rr->target), "%s",
745 : current->Data.Srv.pNameTarget);
746 : *srv_rr_list = rr;
747 : }
748 : current = current->pNext;
749 : }
750 : }
751 :
752 : pDnsRecordListFree(dnsrecords, DnsFreeRecordList);
753 : }
754 :
755 : FreeLibrary(hdnsapi);
756 : }
757 : resolver_srv_list_sort(srv_rr_list);
758 :
759 : return *srv_rr_list != NULL ? XMPP_DOMAIN_FOUND : XMPP_DOMAIN_NOT_FOUND;
760 : }
761 :
762 : static int
763 : resolver_win32_srv_query(const char *fulldomain, unsigned char *buf, size_t len)
764 : {
765 : int set = 0;
766 : int insize = 0;
767 :
768 : /* if dnsapi didn't work/isn't there, try querying the dns server manually
769 : */
770 : if (!set) {
771 : struct dnsquery_header header;
772 : struct dnsquery_question question;
773 : int offset = 0;
774 : int addrlen;
775 : sock_t sock;
776 : struct sockaddr_in dnsaddr;
777 : char dnsserverips[16][256];
778 : int numdnsservers = 0;
779 : int j;
780 :
781 : /* Try getting the DNS server ips from GetNetworkParams() in iphlpapi
782 : * first */
783 : if (!numdnsservers) {
784 : HINSTANCE hiphlpapi = NULL;
785 : DWORD(WINAPI * pGetNetworkParams)(PFIXED_INFO, PULONG);
786 :
787 : if (hiphlpapi = LoadLibrary("Iphlpapi.dll")) {
788 : pGetNetworkParams =
789 : (void *)GetProcAddress(hiphlpapi, "GetNetworkParams");
790 :
791 : if (pGetNetworkParams) {
792 : FIXED_INFO *fi;
793 : ULONG len;
794 : DWORD error;
795 : char buffer[65535];
796 :
797 : len = 65535;
798 : fi = (FIXED_INFO *)buffer;
799 :
800 : if ((error = pGetNetworkParams(fi, &len)) ==
801 : ERROR_SUCCESS) {
802 : IP_ADDR_STRING *pias = &(fi->DnsServerList);
803 :
804 : while (pias && numdnsservers < 16) {
805 : strcpy(dnsserverips[numdnsservers++],
806 : pias->IpAddress.String);
807 : pias = pias->Next;
808 : }
809 : }
810 : }
811 : }
812 : FreeLibrary(hiphlpapi);
813 : }
814 :
815 : /* Next, try getting the DNS server ips from the registry */
816 : if (!numdnsservers) {
817 : HKEY search;
818 : LONG error;
819 :
820 : error = RegOpenKeyEx(
821 : HKEY_LOCAL_MACHINE,
822 : "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters", 0,
823 : KEY_READ, &search);
824 :
825 : if (error != ERROR_SUCCESS) {
826 : error = RegOpenKeyEx(
827 : HKEY_LOCAL_MACHINE,
828 : "SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP", 0,
829 : KEY_READ, &search);
830 : }
831 :
832 : if (error == ERROR_SUCCESS) {
833 : char name[512];
834 : DWORD len = 512;
835 :
836 : error = RegQueryValueEx(search, "NameServer", NULL, NULL,
837 : (LPBYTE)name, &len);
838 :
839 : if (error != ERROR_SUCCESS) {
840 : error = RegQueryValueEx(search, "DhcpNameServer", NULL,
841 : NULL, (LPBYTE)name, &len);
842 : }
843 :
844 : if (error == ERROR_SUCCESS) {
845 : char *parse = "0123456789.", *start, *end;
846 : start = name;
847 : end = name;
848 : name[len] = '\0';
849 :
850 : while (*start && numdnsservers < 16) {
851 : while (strchr(parse, *end)) {
852 : end++;
853 : }
854 :
855 : strncpy(dnsserverips[numdnsservers++], start,
856 : end - start);
857 :
858 : while (*end && !strchr(parse, *end)) {
859 : end++;
860 : }
861 :
862 : start = end;
863 : }
864 : }
865 : }
866 :
867 : RegCloseKey(search);
868 : }
869 :
870 : if (!numdnsservers) {
871 : HKEY searchlist;
872 : LONG error;
873 :
874 : error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
875 : "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\P"
876 : "arameters\\Interfaces",
877 : 0, KEY_READ, &searchlist);
878 :
879 : if (error == ERROR_SUCCESS) {
880 : unsigned int i;
881 : DWORD numinterfaces = 0;
882 :
883 : RegQueryInfoKey(searchlist, NULL, NULL, NULL, &numinterfaces,
884 : NULL, NULL, NULL, NULL, NULL, NULL, NULL);
885 :
886 : for (i = 0; i < numinterfaces; i++) {
887 : char name[512];
888 : DWORD len = 512;
889 : HKEY searchentry;
890 :
891 : RegEnumKeyEx(searchlist, i, (LPTSTR)name, &len, NULL, NULL,
892 : NULL, NULL);
893 :
894 : if (RegOpenKeyEx(searchlist, name, 0, KEY_READ,
895 : &searchentry) == ERROR_SUCCESS) {
896 : if (RegQueryValueEx(searchentry, "DhcpNameServer", NULL,
897 : NULL, (LPBYTE)name,
898 : &len) == ERROR_SUCCESS) {
899 : char *parse = "0123456789.", *start, *end;
900 : start = name;
901 : end = name;
902 : name[len] = '\0';
903 :
904 : while (*start && numdnsservers < 16) {
905 : while (strchr(parse, *end)) {
906 : end++;
907 : }
908 :
909 : strncpy(dnsserverips[numdnsservers++], start,
910 : end - start);
911 :
912 : while (*end && !strchr(parse, *end)) {
913 : end++;
914 : }
915 :
916 : start = end;
917 : }
918 : } else if (RegQueryValueEx(searchentry, "NameServer",
919 : NULL, NULL, (LPBYTE)name,
920 : &len) == ERROR_SUCCESS) {
921 : char *parse = "0123456789.", *start, *end;
922 : start = name;
923 : end = name;
924 : name[len] = '\0';
925 :
926 : while (*start && numdnsservers < 16) {
927 : while (strchr(parse, *end)) {
928 : end++;
929 : }
930 :
931 : strncpy(dnsserverips[numdnsservers++], start,
932 : end - start);
933 :
934 : while (*end && !strchr(parse, *end)) {
935 : end++;
936 : }
937 :
938 : start = end;
939 : }
940 : }
941 : RegCloseKey(searchentry);
942 : }
943 : }
944 : RegCloseKey(searchlist);
945 : }
946 : }
947 :
948 : /* If we have a DNS server, use it */
949 : if (numdnsservers) {
950 : ULONG nonblocking = 1;
951 : int i;
952 :
953 : memset(&header, 0, sizeof(header));
954 : header.id = 12345; /* FIXME: Get a better id here */
955 : header.rd = 1;
956 : header.qdcount = 1;
957 :
958 : netbuf_add_dnsquery_header(buf, (int)len, &offset, &header);
959 :
960 : memset(&question, 0, sizeof(question));
961 : strncpy(question.qname, fulldomain, 1024);
962 : question.qtype = MESSAGE_T_SRV; /* SRV */
963 : question.qclass = MESSAGE_C_IN; /* INTERNET! */
964 :
965 : netbuf_add_dnsquery_question(buf, (int)len, &offset, &question);
966 :
967 : insize = 0;
968 : for (i = 0; i < numdnsservers && insize <= 0; i++) {
969 : sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
970 : ioctlsocket(sock, FIONBIO, &nonblocking);
971 :
972 : memset(&dnsaddr, 0, sizeof(dnsaddr));
973 :
974 : dnsaddr.sin_family = AF_INET;
975 : dnsaddr.sin_port = htons(53);
976 : dnsaddr.sin_addr.s_addr = inet_addr(dnsserverips[i]);
977 :
978 : addrlen = sizeof(dnsaddr);
979 : sendto(sock, (char *)buf, offset, 0,
980 : (struct sockaddr *)&dnsaddr, addrlen);
981 : for (j = 0; j < 50; j++) {
982 : insize = recvfrom(sock, (char *)buf, (int)len, 0,
983 : (struct sockaddr *)&dnsaddr, &addrlen);
984 : if (insize == SOCKET_ERROR) {
985 : if (sock_error() == WSAEWOULDBLOCK) {
986 : Sleep(100);
987 : } else {
988 : break;
989 : }
990 : } else {
991 : break;
992 : }
993 : }
994 :
995 : closesocket(sock);
996 : }
997 : set = insize > 0;
998 : }
999 : }
1000 :
1001 : return set ? insize : -1;
1002 : }
1003 :
1004 : #endif /* _WIN32 && !HAVE_CARES */
|