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