LCOV - code coverage report
Current view: top level - src - resolver.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 2.4 % 170 4
Test Date: 2024-07-22 12:36:40 Functions: 20.0 % 10 2

            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 */
        

Generated by: LCOV version 2.0-1