gthreadedresolver.c 32.7 KB
Newer Older
1 2 3
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */

/* GIO - GLib Input, Output and Streaming Library
Matthias Clasen's avatar
Matthias Clasen committed
4
 *
5
 * Copyright (C) 2008 Red Hat, Inc.
6
 * Copyright (C) 2018 Igalia S.L.
7 8 9 10
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12 13 14 15 16 17 18
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
19
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 21 22 23 24 25 26 27 28 29 30 31 32
 */

#include "config.h"
#include <glib.h>
#include "glibintl.h"

#include <stdio.h>
#include <string.h>

#include "gthreadedresolver.h"
#include "gnetworkingprivate.h"

#include "gcancellable.h"
33 34
#include "ginetaddress.h"
#include "ginetsocketaddress.h"
35
#include "gtask.h"
36
#include "gsocketaddress.h"
37
#include "gsrvtarget.h"
38 39 40 41 42 43 44 45 46


G_DEFINE_TYPE (GThreadedResolver, g_threaded_resolver, G_TYPE_RESOLVER)

static void
g_threaded_resolver_init (GThreadedResolver *gtr)
{
}

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
static GResolverError
g_resolver_error_from_addrinfo_error (gint err)
{
  switch (err)
    {
    case EAI_FAIL:
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
    case EAI_NODATA:
#endif
    case EAI_NONAME:
      return G_RESOLVER_ERROR_NOT_FOUND;

    case EAI_AGAIN:
      return G_RESOLVER_ERROR_TEMPORARY_FAILURE;

    default:
      return G_RESOLVER_ERROR_INTERNAL;
    }
}

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
typedef struct {
  char *hostname;
  int address_family;
} LookupData;

static LookupData *
lookup_data_new (const char *hostname,
                 int         address_family)
{
  LookupData *data = g_new (LookupData, 1);
  data->hostname = g_strdup (hostname);
  data->address_family = address_family;
  return data;
}

static void
lookup_data_free (LookupData *data)
{
  g_free (data->hostname);
  g_free (data);
}
88

89
static void
90 91 92 93
do_lookup_by_name (GTask         *task,
                   gpointer       source_object,
                   gpointer       task_data,
                   GCancellable  *cancellable)
94
{
95 96
  LookupData *lookup_data = task_data;
  const char *hostname = lookup_data->hostname;
97
  struct addrinfo *res = NULL;
98
  GList *addresses;
99
  gint retval;
100
  struct addrinfo addrinfo_hints = { 0 };
101

102 103 104 105 106 107 108 109 110 111 112
#ifdef AI_ADDRCONFIG
  addrinfo_hints.ai_flags = AI_ADDRCONFIG;
#endif
  /* socktype and protocol don't actually matter, they just get copied into the
  * returned addrinfo structures (and then we ignore them). But if
  * we leave them unset, we'll get back duplicate answers.
  */
  addrinfo_hints.ai_socktype = SOCK_STREAM;
  addrinfo_hints.ai_protocol = IPPROTO_TCP;

  addrinfo_hints.ai_family = lookup_data->address_family;
113
  retval = getaddrinfo (hostname, NULL, &addrinfo_hints, &res);
114

115
  if (retval == 0)
116
    {
117 118 119 120 121 122 123 124
      struct addrinfo *ai;
      GSocketAddress *sockaddr;
      GInetAddress *addr;

      addresses = NULL;
      for (ai = res; ai; ai = ai->ai_next)
        {
          sockaddr = g_socket_address_new_from_native (ai->ai_addr, ai->ai_addrlen);
125
          if (!sockaddr)
126
            continue;
127 128 129 130 131
          if (!G_IS_INET_SOCKET_ADDRESS (sockaddr))
            {
              g_clear_object (&sockaddr);
              continue;
            }
132 133 134 135 136 137

          addr = g_object_ref (g_inet_socket_address_get_address ((GInetSocketAddress *)sockaddr));
          addresses = g_list_prepend (addresses, addr);
          g_object_unref (sockaddr);
        }

138 139 140 141 142 143 144 145 146 147 148 149
      if (addresses != NULL)
        {
          addresses = g_list_reverse (addresses);
          g_task_return_pointer (task, addresses,
                                 (GDestroyNotify)g_resolver_free_addresses);
        }
      else
        {
          /* All addresses failed to be converted to GSocketAddresses. */
          g_task_return_new_error (task,
                                   G_RESOLVER_ERROR,
                                   G_RESOLVER_ERROR_NOT_FOUND,
150
                                   _("Error resolving “%s”: %s"),
151 152 153
                                   hostname,
                                   _("No valid addresses were found"));
        }
154 155
    }
  else
156
    {
157 158 159
#ifdef G_OS_WIN32
      gchar *error_message = g_win32_error_message (WSAGetLastError ());
#else
160 161 162
      gchar *error_message = g_locale_to_utf8 (gai_strerror (retval), -1, NULL, NULL, NULL);
      if (error_message == NULL)
        error_message = g_strdup ("[Invalid UTF-8]");
163 164
#endif

165 166 167
      g_task_return_new_error (task,
                               G_RESOLVER_ERROR,
                               g_resolver_error_from_addrinfo_error (retval),
168
                               _("Error resolving “%s”: %s"),
169 170
                               hostname, error_message);
      g_free (error_message);
171 172 173 174
    }

  if (res)
    freeaddrinfo (res);
175 176 177 178 179 180 181 182
}

static GList *
lookup_by_name (GResolver     *resolver,
                const gchar   *hostname,
                GCancellable  *cancellable,
                GError       **error)
{
183
  GTask *task;
184
  GList *addresses;
185
  LookupData *data;
186

187
  data = lookup_data_new (hostname, AF_UNSPEC);
188
  task = g_task_new (resolver, cancellable, NULL, NULL);
189
  g_task_set_source_tag (task, lookup_by_name);
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
  g_task_set_task_data (task, data, (GDestroyNotify)lookup_data_free);
  g_task_set_return_on_cancel (task, TRUE);
  g_task_run_in_thread_sync (task, do_lookup_by_name);
  addresses = g_task_propagate_pointer (task, error);
  g_object_unref (task);

  return addresses;
}

static int
flags_to_family (GResolverNameLookupFlags flags)
{
  int address_family = AF_UNSPEC;

  if (flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY)
    address_family = AF_INET;

  if (flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY)
    {
      address_family = AF_INET6;
      /* You can only filter by one family at a time */
      g_return_val_if_fail (!(flags & G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY), address_family);
    }

  return address_family;
}

static GList *
lookup_by_name_with_flags (GResolver                 *resolver,
                           const gchar               *hostname,
                           GResolverNameLookupFlags   flags,
                           GCancellable              *cancellable,
                           GError                   **error)
{
  GTask *task;
  GList *addresses;
  LookupData *data;

  data = lookup_data_new (hostname, AF_UNSPEC);
  task = g_task_new (resolver, cancellable, NULL, NULL);
  g_task_set_source_tag (task, lookup_by_name_with_flags);
  g_task_set_task_data (task, data, (GDestroyNotify)lookup_data_free);
232 233 234 235
  g_task_set_return_on_cancel (task, TRUE);
  g_task_run_in_thread_sync (task, do_lookup_by_name);
  addresses = g_task_propagate_pointer (task, error);
  g_object_unref (task);
236 237 238 239 240

  return addresses;
}

static void
241 242 243 244 245 246
lookup_by_name_with_flags_async (GResolver                *resolver,
                                 const gchar              *hostname,
                                 GResolverNameLookupFlags  flags,
                                 GCancellable             *cancellable,
                                 GAsyncReadyCallback       callback,
                                 gpointer                  user_data)
247
{
248
  GTask *task;
249
  LookupData *data;
250

251
  data = lookup_data_new (hostname, flags_to_family (flags));
252
  task = g_task_new (resolver, cancellable, callback, user_data);
253 254
  g_task_set_source_tag (task, lookup_by_name_with_flags_async);
  g_task_set_task_data (task, data, (GDestroyNotify)lookup_data_free);
255 256 257
  g_task_set_return_on_cancel (task, TRUE);
  g_task_run_in_thread (task, do_lookup_by_name);
  g_object_unref (task);
258 259
}

260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
static void
lookup_by_name_async (GResolver           *resolver,
                      const gchar         *hostname,
                      GCancellable        *cancellable,
                      GAsyncReadyCallback  callback,
                      gpointer             user_data)
{
  lookup_by_name_with_flags_async (resolver,
                                   hostname,
                                   G_RESOLVER_NAME_LOOKUP_FLAGS_DEFAULT,
                                   cancellable,
                                   callback,
                                   user_data);
}

275 276 277 278 279
static GList *
lookup_by_name_finish (GResolver     *resolver,
                       GAsyncResult  *result,
                       GError       **error)
{
280
  g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
281

282
  return g_task_propagate_pointer (G_TASK (result), error);
283 284
}

285 286 287 288 289 290 291 292 293
static GList *
lookup_by_name_with_flags_finish (GResolver     *resolver,
                                  GAsyncResult  *result,
                                  GError       **error)
{
  g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);

  return g_task_propagate_pointer (G_TASK (result), error);
}
294 295

static void
296 297 298 299
do_lookup_by_address (GTask         *task,
                      gpointer       source_object,
                      gpointer       task_data,
                      GCancellable  *cancellable)
300
{
301
  GInetAddress *address = task_data;
302 303
  struct sockaddr_storage sockaddr;
  gsize sockaddr_size;
304 305
  GSocketAddress *gsockaddr;
  gchar name[NI_MAXHOST];
306 307
  gint retval;

308 309 310 311 312
  gsockaddr = g_inet_socket_address_new (address, 0);
  g_socket_address_to_native (gsockaddr, (struct sockaddr *)&sockaddr,
                              sizeof (sockaddr), NULL);
  sockaddr_size = g_socket_address_get_native_size (gsockaddr);
  g_object_unref (gsockaddr);
313

314 315 316 317
  retval = getnameinfo ((struct sockaddr *)&sockaddr, sockaddr_size,
                        name, sizeof (name), NULL, 0, NI_NAMEREQD);
  if (retval == 0)
    g_task_return_pointer (task, g_strdup (name), g_free);
318
  else
319 320 321
    {
      gchar *phys;

322 323 324
#ifdef G_OS_WIN32
      gchar *error_message = g_win32_error_message (WSAGetLastError ());
#else
325 326 327
      gchar *error_message = g_locale_to_utf8 (gai_strerror (retval), -1, NULL, NULL, NULL);
      if (error_message == NULL)
        error_message = g_strdup ("[Invalid UTF-8]");
328 329
#endif

330 331 332 333
      phys = g_inet_address_to_string (address);
      g_task_return_new_error (task,
                               G_RESOLVER_ERROR,
                               g_resolver_error_from_addrinfo_error (retval),
334
                               _("Error reverse-resolving “%s”: %s"),
335
                               phys ? phys : "(unknown)",
336
                               error_message);
337
      g_free (phys);
338
      g_free (error_message);
339
    }
340 341 342 343 344 345 346 347
}

static gchar *
lookup_by_address (GResolver        *resolver,
                   GInetAddress     *address,
                   GCancellable     *cancellable,
                   GError          **error)
{
348
  GTask *task;
349 350
  gchar *name;

351
  task = g_task_new (resolver, cancellable, NULL, NULL);
352
  g_task_set_source_tag (task, lookup_by_address);
353 354 355 356 357
  g_task_set_task_data (task, g_object_ref (address), g_object_unref);
  g_task_set_return_on_cancel (task, TRUE);
  g_task_run_in_thread_sync (task, do_lookup_by_address);
  name = g_task_propagate_pointer (task, error);
  g_object_unref (task);
358 359 360 361 362 363 364 365 366 367 368

  return name;
}

static void
lookup_by_address_async (GResolver           *resolver,
                         GInetAddress        *address,
                         GCancellable        *cancellable,
                         GAsyncReadyCallback  callback,
                         gpointer             user_data)
{
369 370 371
  GTask *task;

  task = g_task_new (resolver, cancellable, callback, user_data);
372
  g_task_set_source_tag (task, lookup_by_address_async);
373 374 375 376
  g_task_set_task_data (task, g_object_ref (address), g_object_unref);
  g_task_set_return_on_cancel (task, TRUE);
  g_task_run_in_thread (task, do_lookup_by_address);
  g_object_unref (task);
377 378 379 380 381 382 383
}

static gchar *
lookup_by_address_finish (GResolver     *resolver,
                          GAsyncResult  *result,
                          GError       **error)
{
384
  g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
385

386
  return g_task_propagate_pointer (G_TASK (result), error);
387 388 389
}


390
#if defined(G_OS_UNIX)
391

392
#if defined __BIONIC__ && !defined BIND_4_COMPAT
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
/* Copy from bionic/libc/private/arpa_nameser_compat.h
 * and bionic/libc/private/arpa_nameser.h */
typedef struct {
	unsigned	id :16;		/* query identification number */
#if BYTE_ORDER == BIG_ENDIAN
			/* fields in third byte */
	unsigned	qr: 1;		/* response flag */
	unsigned	opcode: 4;	/* purpose of message */
	unsigned	aa: 1;		/* authoritive answer */
	unsigned	tc: 1;		/* truncated message */
	unsigned	rd: 1;		/* recursion desired */
			/* fields in fourth byte */
	unsigned	ra: 1;		/* recursion available */
	unsigned	unused :1;	/* unused bits (MBZ as of 4.9.3a3) */
	unsigned	ad: 1;		/* authentic data from named */
	unsigned	cd: 1;		/* checking disabled by resolver */
	unsigned	rcode :4;	/* response code */
#endif
#if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
			/* fields in third byte */
	unsigned	rd :1;		/* recursion desired */
	unsigned	tc :1;		/* truncated message */
	unsigned	aa :1;		/* authoritive answer */
	unsigned	opcode :4;	/* purpose of message */
	unsigned	qr :1;		/* response flag */
			/* fields in fourth byte */
	unsigned	rcode :4;	/* response code */
	unsigned	cd: 1;		/* checking disabled by resolver */
	unsigned	ad: 1;		/* authentic data from named */
	unsigned	unused :1;	/* unused bits (MBZ as of 4.9.3a3) */
	unsigned	ra :1;		/* recursion available */
#endif
			/* remaining bytes */
	unsigned	qdcount :16;	/* number of question entries */
	unsigned	ancount :16;	/* number of answer entries */
	unsigned	nscount :16;	/* number of authority entries */
	unsigned	arcount :16;	/* number of resource entries */
} HEADER;

#define NS_INT32SZ	4	/* #/bytes of data in a uint32_t */
#define NS_INT16SZ	2	/* #/bytes of data in a uint16_t */

#define NS_GET16(s, cp) do { \
	const u_char *t_cp = (const u_char *)(cp); \
	(s) = ((uint16_t)t_cp[0] << 8) \
	    | ((uint16_t)t_cp[1]) \
	    ; \
	(cp) += NS_INT16SZ; \
} while (/*CONSTCOND*/0)

#define NS_GET32(l, cp) do { \
	const u_char *t_cp = (const u_char *)(cp); \
	(l) = ((uint32_t)t_cp[0] << 24) \
	    | ((uint32_t)t_cp[1] << 16) \
	    | ((uint32_t)t_cp[2] << 8) \
	    | ((uint32_t)t_cp[3]) \
	    ; \
	(cp) += NS_INT32SZ; \
} while (/*CONSTCOND*/0)

#define	GETSHORT		NS_GET16
#define	GETLONG			NS_GET32

#define C_IN 1

/* From bionic/libc/private/resolv_private.h */
int dn_expand(const u_char *, const u_char *, const u_char *, char *, int);
#define dn_skipname __dn_skipname
int dn_skipname(const u_char *, const u_char *);

/* From bionic/libc/private/arpa_nameser_compat.h */
#define T_MX		ns_t_mx
#define T_TXT		ns_t_txt
#define T_SOA		ns_t_soa
#define T_NS		ns_t_ns

/* From bionic/libc/private/arpa_nameser.h */
typedef enum __ns_type {
	ns_t_invalid = 0,	/* Cookie. */
	ns_t_a = 1,		/* Host address. */
	ns_t_ns = 2,		/* Authoritative server. */
	ns_t_md = 3,		/* Mail destination. */
	ns_t_mf = 4,		/* Mail forwarder. */
	ns_t_cname = 5,		/* Canonical name. */
	ns_t_soa = 6,		/* Start of authority zone. */
	ns_t_mb = 7,		/* Mailbox domain name. */
	ns_t_mg = 8,		/* Mail group member. */
	ns_t_mr = 9,		/* Mail rename name. */
	ns_t_null = 10,		/* Null resource record. */
	ns_t_wks = 11,		/* Well known service. */
	ns_t_ptr = 12,		/* Domain name pointer. */
	ns_t_hinfo = 13,	/* Host information. */
	ns_t_minfo = 14,	/* Mailbox information. */
	ns_t_mx = 15,		/* Mail routing information. */
	ns_t_txt = 16,		/* Text strings. */
	ns_t_rp = 17,		/* Responsible person. */
	ns_t_afsdb = 18,	/* AFS cell database. */
	ns_t_x25 = 19,		/* X_25 calling address. */
	ns_t_isdn = 20,		/* ISDN calling address. */
	ns_t_rt = 21,		/* Router. */
	ns_t_nsap = 22,		/* NSAP address. */
	ns_t_nsap_ptr = 23,	/* Reverse NSAP lookup (deprecated). */
	ns_t_sig = 24,		/* Security signature. */
	ns_t_key = 25,		/* Security key. */
	ns_t_px = 26,		/* X.400 mail mapping. */
	ns_t_gpos = 27,		/* Geographical position (withdrawn). */
	ns_t_aaaa = 28,		/* Ip6 Address. */
	ns_t_loc = 29,		/* Location Information. */
	ns_t_nxt = 30,		/* Next domain (security). */
	ns_t_eid = 31,		/* Endpoint identifier. */
	ns_t_nimloc = 32,	/* Nimrod Locator. */
	ns_t_srv = 33,		/* Server Selection. */
	ns_t_atma = 34,		/* ATM Address */
	ns_t_naptr = 35,	/* Naming Authority PoinTeR */
	ns_t_kx = 36,		/* Key Exchange */
	ns_t_cert = 37,		/* Certification record */
	ns_t_a6 = 38,		/* IPv6 address (deprecates AAAA) */
	ns_t_dname = 39,	/* Non-terminal DNAME (for IPv6) */
	ns_t_sink = 40,		/* Kitchen sink (experimentatl) */
	ns_t_opt = 41,		/* EDNS0 option (meta-RR) */
	ns_t_apl = 42,		/* Address prefix list (RFC 3123) */
	ns_t_tkey = 249,	/* Transaction key */
	ns_t_tsig = 250,	/* Transaction signature. */
	ns_t_ixfr = 251,	/* Incremental zone transfer. */
	ns_t_axfr = 252,	/* Transfer zone of authority. */
	ns_t_mailb = 253,	/* Transfer mailbox records. */
	ns_t_maila = 254,	/* Transfer mail agent records. */
	ns_t_any = 255,		/* Wildcard match. */
	ns_t_zxfr = 256,	/* BIND-specific, nonstandard. */
	ns_t_max = 65536
} ns_type;

#endif /* __BIONIC__ */

527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
static GVariant *
parse_res_srv (guchar  *answer,
               guchar  *end,
               guchar **p)
{
  gchar namebuf[1024];
  guint16 priority, weight, port;

  GETSHORT (priority, *p);
  GETSHORT (weight, *p);
  GETSHORT (port, *p);
  *p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));

  return g_variant_new ("(qqqs)",
                        priority,
                        weight,
                        port,
                        namebuf);
}

static GVariant *
parse_res_soa (guchar  *answer,
               guchar  *end,
               guchar **p)
{
  gchar mnamebuf[1024];
  gchar rnamebuf[1024];
  guint32 serial, refresh, retry, expire, ttl;

  *p += dn_expand (answer, end, *p, mnamebuf, sizeof (mnamebuf));
  *p += dn_expand (answer, end, *p, rnamebuf, sizeof (rnamebuf));

  GETLONG (serial, *p);
  GETLONG (refresh, *p);
  GETLONG (retry, *p);
  GETLONG (expire, *p);
  GETLONG (ttl, *p);

  return g_variant_new ("(ssuuuuu)",
                        mnamebuf,
                        rnamebuf,
                        serial,
                        refresh,
                        retry,
                        expire,
                        ttl);
}

static GVariant *
parse_res_ns (guchar  *answer,
              guchar  *end,
              guchar **p)
{
  gchar namebuf[1024];

  *p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));

  return g_variant_new ("(s)", namebuf);
}

static GVariant *
parse_res_mx (guchar  *answer,
              guchar  *end,
              guchar **p)
{
  gchar namebuf[1024];
  guint16 preference;

  GETSHORT (preference, *p);

  *p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));

  return g_variant_new ("(qs)",
                        preference,
                        namebuf);
}

static GVariant *
parse_res_txt (guchar  *answer,
               guchar  *end,
               guchar **p)
{
  GVariant *record;
  GPtrArray *array;
  guchar *at = *p;
  gsize len;

  array = g_ptr_array_new_with_free_func (g_free);
  while (at < end)
    {
      len = *(at++);
      if (len > at - end)
        break;
      g_ptr_array_add (array, g_strndup ((gchar *)at, len));
      at += len;
    }

  *p = at;
  record = g_variant_new ("(@as)",
                          g_variant_new_strv ((const gchar **)array->pdata, array->len));
  g_ptr_array_free (array, TRUE);
  return record;
}

static gint
g_resolver_record_type_to_rrtype (GResolverRecordType type)
{
  switch (type)
  {
    case G_RESOLVER_RECORD_SRV:
      return T_SRV;
    case G_RESOLVER_RECORD_TXT:
      return T_TXT;
    case G_RESOLVER_RECORD_SOA:
      return T_SOA;
    case G_RESOLVER_RECORD_NS:
      return T_NS;
    case G_RESOLVER_RECORD_MX:
      return T_MX;
  }
  g_return_val_if_reached (-1);
}

static GList *
g_resolver_records_from_res_query (const gchar      *rrname,
                                   gint              rrtype,
                                   guchar           *answer,
                                   gint              len,
                                   gint              herr,
                                   GError          **error)
{
  gint count;
  gchar namebuf[1024];
  guchar *end, *p;
  guint16 type, qclass, rdlength;
  HEADER *header;
  GList *records;
  GVariant *record;

  if (len <= 0)
    {
      if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
        {
670
          g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
671
                       _("No DNS record of the requested type for “%s”"), rrname);
672 673 674
        }
      else if (herr == TRY_AGAIN)
        {
675
          g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
676
                       _("Temporarily unable to resolve “%s”"), rrname);
677 678 679
        }
      else
        {
680
          g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
681
                       _("Error resolving “%s”"), rrname);
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
        }

      return NULL;
    }

  records = NULL;

  header = (HEADER *)answer;
  p = answer + sizeof (HEADER);
  end = answer + len;

  /* Skip query */
  count = ntohs (header->qdcount);
  while (count-- && p < end)
    {
      p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
      p += 4;

      /* To silence gcc warnings */
      namebuf[0] = namebuf[1];
    }

  /* Read answers */
  count = ntohs (header->ancount);
  while (count-- && p < end)
    {
      p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
      GETSHORT (type, p);
      GETSHORT (qclass, p);
711
      p += 4; /* ignore the ttl (type=long) value */
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
      GETSHORT (rdlength, p);

      if (type != rrtype || qclass != C_IN)
        {
          p += rdlength;
          continue;
        }

      switch (rrtype)
        {
        case T_SRV:
          record = parse_res_srv (answer, end, &p);
          break;
        case T_MX:
          record = parse_res_mx (answer, end, &p);
          break;
        case T_SOA:
          record = parse_res_soa (answer, end, &p);
          break;
        case T_NS:
          record = parse_res_ns (answer, end, &p);
          break;
        case T_TXT:
          record = parse_res_txt (answer, p + rdlength, &p);
          break;
        default:
          g_warn_if_reached ();
          record = NULL;
          break;
        }

      if (record != NULL)
        records = g_list_prepend (records, record);
    }

747 748 749
  if (records == NULL)
    {
      g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
750
                   _("No DNS record of the requested type for “%s”"), rrname);
751 752 753 754

      return NULL;
    }
  else
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
    return records;
}

#elif defined(G_OS_WIN32)

static GVariant *
parse_dns_srv (DNS_RECORD *rec)
{
  return g_variant_new ("(qqqs)",
                        (guint16)rec->Data.SRV.wPriority,
                        (guint16)rec->Data.SRV.wWeight,
                        (guint16)rec->Data.SRV.wPort,
                        rec->Data.SRV.pNameTarget);
}

static GVariant *
parse_dns_soa (DNS_RECORD *rec)
{
  return g_variant_new ("(ssuuuuu)",
                        rec->Data.SOA.pNamePrimaryServer,
                        rec->Data.SOA.pNameAdministrator,
                        (guint32)rec->Data.SOA.dwSerialNo,
                        (guint32)rec->Data.SOA.dwRefresh,
                        (guint32)rec->Data.SOA.dwRetry,
                        (guint32)rec->Data.SOA.dwExpire,
                        (guint32)rec->Data.SOA.dwDefaultTtl);
}

static GVariant *
parse_dns_ns (DNS_RECORD *rec)
{
  return g_variant_new ("(s)", rec->Data.NS.pNameHost);
}

static GVariant *
parse_dns_mx (DNS_RECORD *rec)
{
  return g_variant_new ("(qs)",
                        (guint16)rec->Data.MX.wPreference,
                        rec->Data.MX.pNameExchange);
}

static GVariant *
parse_dns_txt (DNS_RECORD *rec)
{
  GVariant *record;
  GPtrArray *array;
  DWORD i;

  array = g_ptr_array_new ();
  for (i = 0; i < rec->Data.TXT.dwStringCount; i++)
    g_ptr_array_add (array, rec->Data.TXT.pStringArray[i]);
  record = g_variant_new ("(@as)",
                          g_variant_new_strv ((const gchar **)array->pdata, array->len));
  g_ptr_array_free (array, TRUE);
  return record;
}

static WORD
g_resolver_record_type_to_dnstype (GResolverRecordType type)
{
  switch (type)
  {
    case G_RESOLVER_RECORD_SRV:
      return DNS_TYPE_SRV;
    case G_RESOLVER_RECORD_TXT:
      return DNS_TYPE_TEXT;
    case G_RESOLVER_RECORD_SOA:
      return DNS_TYPE_SOA;
    case G_RESOLVER_RECORD_NS:
      return DNS_TYPE_NS;
    case G_RESOLVER_RECORD_MX:
      return DNS_TYPE_MX;
  }
  g_return_val_if_reached (-1);
}

static GList *
g_resolver_records_from_DnsQuery (const gchar  *rrname,
                                  WORD          dnstype,
                                  DNS_STATUS    status,
                                  DNS_RECORD   *results,
                                  GError      **error)
{
  DNS_RECORD *rec;
  gpointer record;
  GList *records;

  if (status != ERROR_SUCCESS)
    {
      if (status == DNS_ERROR_RCODE_NAME_ERROR)
        {
847
          g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
848
                       _("No DNS record of the requested type for “%s”"), rrname);
849 850 851
        }
      else if (status == DNS_ERROR_RCODE_SERVER_FAILURE)
        {
852
          g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
853
                       _("Temporarily unable to resolve “%s”"), rrname);
854 855 856
        }
      else
        {
857
          g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
858
                       _("Error resolving “%s”"), rrname);
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
        }

      return NULL;
    }

  records = NULL;
  for (rec = results; rec; rec = rec->pNext)
    {
      if (rec->wType != dnstype)
        continue;
      switch (dnstype)
        {
        case DNS_TYPE_SRV:
          record = parse_dns_srv (rec);
          break;
        case DNS_TYPE_SOA:
          record = parse_dns_soa (rec);
          break;
        case DNS_TYPE_NS:
          record = parse_dns_ns (rec);
          break;
        case DNS_TYPE_MX:
          record = parse_dns_mx (rec);
          break;
        case DNS_TYPE_TEXT:
          record = parse_dns_txt (rec);
          break;
        default:
          g_warn_if_reached ();
          record = NULL;
          break;
        }
      if (record != NULL)
        records = g_list_prepend (records, g_variant_ref_sink (record));
    }

895 896 897
  if (records == NULL)
    {
      g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
898
                   _("No DNS record of the requested type for “%s”"), rrname);
899 900 901 902 903

      return NULL;
    }
  else
    return records;
904 905 906 907
}

#endif

908 909 910 911 912 913 914 915 916 917 918 919
typedef struct {
  char *rrname;
  GResolverRecordType record_type;
} LookupRecordsData;

static void
free_lookup_records_data (LookupRecordsData *lrd)
{
  g_free (lrd->rrname);
  g_slice_free (LookupRecordsData, lrd);
}

920 921 922 923 924 925
static void
free_records (GList *records)
{
  g_list_free_full (records, (GDestroyNotify) g_variant_unref);
}

926 927
#if defined(G_OS_UNIX)
#ifdef __BIONIC__
928
#ifndef C_IN
929
#define C_IN 1
930
#endif
931 932 933 934
int res_query(const char *, int, int, u_char *, int);
#endif
#endif

935
static void
936 937 938 939
do_lookup_records (GTask         *task,
                   gpointer       source_object,
                   gpointer       task_data,
                   GCancellable  *cancellable)
940
{
941
  LookupRecordsData *lrd = task_data;
942
  GList *records;
943
  GError *error = NULL;
944

945
#if defined(G_OS_UNIX)
946 947 948 949 950
  gint len = 512;
  gint herr;
  GByteArray *answer;
  gint rrtype;

951 952 953 954 955 956 957 958 959 960
#ifdef HAVE_RES_NQUERY
  /* Load the resolver state. This is done once per worker thread, and the
   * #GResolver::reload signal is ignored (since we always reload). This could
   * be improved by having an explicit worker thread pool, with each thread
   * containing some state which is initialised at thread creation time and
   * updated in response to #GResolver::reload.
   *
   * What we have currently is not particularly worse than using res_query() in
   * worker threads, since it would transparently call res_init() for each new
   * worker thread. (Although the workers would get reused by the
961 962 963 964
   * #GThreadPool.)
   *
   * FreeBSD requires the state to be zero-filled before calling res_ninit(). */
  struct __res_state res = { 0, };
965 966 967 968 969 970 971 972
  if (res_ninit (&res) != 0)
    {
      g_task_return_new_error (task, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
                               _("Error resolving “%s”"), lrd->rrname);
      return;
    }
#endif

973
  rrtype = g_resolver_record_type_to_rrtype (lrd->record_type);
974 975 976 977
  answer = g_byte_array_new ();
  for (;;)
    {
      g_byte_array_set_size (answer, len * 2);
978 979 980
#if defined(HAVE_RES_NQUERY)
      len = res_nquery (&res, lrd->rrname, C_IN, rrtype, answer->data, answer->len);
#else
981
      len = res_query (lrd->rrname, C_IN, rrtype, answer->data, answer->len);
982
#endif
983 984 985 986 987 988 989 990 991

      /* If answer fit in the buffer then we're done */
      if (len < 0 || len < (gint)answer->len)
        break;

      /*
       * On overflow some res_query's return the length needed, others
       * return the full length entered. This code works in either case.
       */
992
    }
993 994

  herr = h_errno;
995
  records = g_resolver_records_from_res_query (lrd->rrname, rrtype, answer->data, len, herr, &error);
996 997
  g_byte_array_free (answer, TRUE);

998 999
#ifdef HAVE_RES_NQUERY

1000 1001 1002 1003 1004
#if defined(HAVE_RES_NDESTROY)
  res_ndestroy (&res);
#elif defined(HAVE_RES_NCLOSE)
  res_nclose (&res);
#elif defined(HAVE_RES_NINIT)
1005
#error "Your platform has res_ninit() but not res_nclose() or res_ndestroy(). Please file a bug at https://gitlab.gnome.org/GNOME/glib/issues/new"
1006 1007
#endif

1008 1009
#endif  /* HAVE_RES_NQUERY */

1010 1011
#else

1012 1013 1014 1015
  DNS_STATUS status;
  DNS_RECORD *results = NULL;
  WORD dnstype;

1016
  dnstype = g_resolver_record_type_to_dnstype (lrd->record_type);
1017
  status = DnsQuery_A (lrd->rrname, dnstype, DNS_QUERY_STANDARD, NULL, &results, NULL);
1018
  records = g_resolver_records_from_DnsQuery (lrd->rrname, dnstype, status, results, &error);
1019 1020
  if (results != NULL)
    DnsRecordListFree (results, DnsFreeRecordList);
1021

1022
#endif
1023

1024
  if (records)
1025
    g_task_return_pointer (task, records, (GDestroyNotify) free_records);
1026 1027
  else
    g_task_return_error (task, error);
1028 1029 1030
}

static GList *
1031 1032
lookup_records (GResolver              *resolver,
                const gchar            *rrname,
1033 1034 1035
                GResolverRecordType     record_type,
                GCancellable           *cancellable,
                GError                **error)
1036
{
1037
  GTask *task;
1038
  GList *records;
1039
  LookupRecordsData *lrd;
1040

1041
  task = g_task_new (resolver, cancellable, NULL, NULL);
1042
  g_task_set_source_tag (task, lookup_records);
1043 1044 1045 1046 1047 1048 1049 1050

  lrd = g_slice_new (LookupRecordsData);
  lrd->rrname = g_strdup (rrname);
  lrd->record_type = record_type;
  g_task_set_task_data (task, lrd, (GDestroyNotify) free_lookup_records_data);

  g_task_set_return_on_cancel (task, TRUE);
  g_task_run_in_thread_sync (task, do_lookup_records);
1051
  records = g_task_propagate_pointer (task, error);
1052 1053
  g_object_unref (task);

1054
  return records;
1055 1056 1057
}

static void
1058
lookup_records_async (GResolver           *resolver,
1059
                      const char          *rrname,
1060 1061
                      GResolverRecordType  record_type,
                      GCancellable        *cancellable,
1062
                      GAsyncReadyCallback  callback,
1063
                      gpointer             user_data)
1064
{
1065 1066 1067 1068
  GTask *task;
  LookupRecordsData *lrd;

  task = g_task_new (resolver, cancellable, callback, user_data);
1069
  g_task_set_source_tag (task, lookup_records_async);
1070 1071 1072 1073 1074 1075 1076 1077 1078

  lrd = g_slice_new (LookupRecordsData);
  lrd->rrname = g_strdup (rrname);
  lrd->record_type = record_type;
  g_task_set_task_data (task, lrd, (GDestroyNotify) free_lookup_records_data);

  g_task_set_return_on_cancel (task, TRUE);
  g_task_run_in_thread (task, do_lookup_records);
  g_object_unref (task);
1079 1080 1081
}

static GList *
1082
lookup_records_finish (GResolver     *resolver,
1083
                       GAsyncResult  *result,
1084
                       GError       **error)
1085
{
1086
  g_return_val_if_fail (g_task_is_valid (result, resolver), NULL);
1087

1088
  return g_task_propagate_pointer (G_TASK (result), error);
1089 1090 1091 1092 1093 1094 1095 1096
}


static void
g_threaded_resolver_class_init (GThreadedResolverClass *threaded_class)
{
  GResolverClass *resolver_class = G_RESOLVER_CLASS (threaded_class);

1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
  resolver_class->lookup_by_name                   = lookup_by_name;
  resolver_class->lookup_by_name_async             = lookup_by_name_async;
  resolver_class->lookup_by_name_finish            = lookup_by_name_finish;
  resolver_class->lookup_by_name_with_flags        = lookup_by_name_with_flags;
  resolver_class->lookup_by_name_with_flags_async  = lookup_by_name_with_flags_async;
  resolver_class->lookup_by_name_with_flags_finish = lookup_by_name_with_flags_finish;
  resolver_class->lookup_by_address                = lookup_by_address;
  resolver_class->lookup_by_address_async          = lookup_by_address_async;
  resolver_class->lookup_by_address_finish         = lookup_by_address_finish;
  resolver_class->lookup_records                   = lookup_records;
  resolver_class->lookup_records_async             = lookup_records_async;
  resolver_class->lookup_records_finish            = lookup_records_finish;
1109
}