gio: move resolver utils from gresolver.c to gthreadedresolver.c
authorDan Winship <danw@gnome.org>
Thu, 29 Dec 2011 16:25:07 +0000 (11:25 -0500)
committerDan Winship <danw@gnome.org>
Wed, 12 Dec 2012 14:20:22 +0000 (15:20 +0100)
Since there is only one resolver implementation now, we can move the
resolver utility functions from gresolver.c into gthreadedresolver.c,
and remove the prototypes from gnetworkingprivate.h.

https://bugzilla.gnome.org/show_bug.cgi?id=623187

gio/gnetworkingprivate.h
gio/gresolver.c
gio/gthreadedresolver.c

index 97bc047..6259109 100644 (file)
 
 G_BEGIN_DECLS
 
-extern struct addrinfo _g_resolver_addrinfo_hints;
-
-GList *_g_resolver_addresses_from_addrinfo (const char       *hostname,
-                                           struct addrinfo  *res,
-                                           gint              gai_retval,
-                                           GError          **error);
-
-void   _g_resolver_address_to_sockaddr     (GInetAddress            *address,
-                                           struct sockaddr_storage *sa,
-                                           gsize                   *len);
-char  *_g_resolver_name_from_nameinfo      (GInetAddress     *address,
-                                           const gchar      *name,
-                                           gint              gni_retval,
-                                           GError          **error);
-
-#if defined(G_OS_UNIX)
-gint   _g_resolver_record_type_to_rrtype   (GResolverRecordType record_type);
-
-GList *_g_resolver_records_from_res_query  (const gchar      *rrname,
-                                           gint              rrtype,
-                                           guchar           *answer,
-                                           gint              len,
-                                           gint              herr,
-                                           GError          **error);
-#elif defined(G_OS_WIN32)
-WORD   _g_resolver_record_type_to_dnstype  (GResolverRecordType record_type);
-
-GList *_g_resolver_records_from_DnsQuery   (const gchar      *rrname,
-                                           WORD              dnstype,
-                                           DNS_STATUS        status,
-                                           DNS_RECORD       *results,
-                                           GError          **error);
-#endif
-
 gboolean _g_uri_parse_authority            (const char       *uri,
                                            char            **host,
                                            guint16          *port,
index 718dc48..3708ee2 100644 (file)
@@ -28,7 +28,6 @@
 #include "gnetworkingprivate.h"
 #include "gasyncresult.h"
 #include "ginetaddress.h"
-#include "ginetsocketaddress.h"
 #include "gsimpleasyncresult.h"
 #include "gtask.h"
 #include "gsrvtarget.h"
@@ -157,17 +156,6 @@ g_resolver_class_init (GResolverClass *resolver_class)
   /* Make sure _g_networking_init() has been called */
   g_type_ensure (G_TYPE_INET_ADDRESS);
 
-  /* Initialize _g_resolver_addrinfo_hints */
-#ifdef AI_ADDRCONFIG
-  _g_resolver_addrinfo_hints.ai_flags |= AI_ADDRCONFIG;
-#endif
-  /* These two 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.
-   */
-  _g_resolver_addrinfo_hints.ai_socktype = SOCK_STREAM;
-  _g_resolver_addrinfo_hints.ai_protocol = IPPROTO_TCP;
-
   /**
    * GResolver::reload:
    * @resolver: a #GResolver
@@ -870,546 +858,3 @@ g_resolver_lookup_records_finish (GResolver     *resolver,
  * Since: 2.22
  */
 G_DEFINE_QUARK (g-resolver-error-quark, g_resolver_error)
-
-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;
-    }
-}
-
-struct addrinfo _g_resolver_addrinfo_hints;
-
-/* Private method to process a getaddrinfo() response. */
-GList *
-_g_resolver_addresses_from_addrinfo (const char       *hostname,
-                                     struct addrinfo  *res,
-                                     gint              gai_retval,
-                                     GError          **error)
-{
-  struct addrinfo *ai;
-  GSocketAddress *sockaddr;
-  GInetAddress *addr;
-  GList *addrs;
-
-  if (gai_retval != 0)
-    {
-      g_set_error (error, G_RESOLVER_ERROR,
-                  g_resolver_error_from_addrinfo_error (gai_retval),
-                  _("Error resolving '%s': %s"),
-                  hostname, gai_strerror (gai_retval));
-      return NULL;
-    }
-
-  g_return_val_if_fail (res != NULL, NULL);
-
-  addrs = NULL;
-  for (ai = res; ai; ai = ai->ai_next)
-    {
-      sockaddr = g_socket_address_new_from_native (ai->ai_addr, ai->ai_addrlen);
-      if (!sockaddr || !G_IS_INET_SOCKET_ADDRESS (sockaddr))
-        continue;
-
-      addr = g_object_ref (g_inet_socket_address_get_address ((GInetSocketAddress *)sockaddr));
-      addrs = g_list_prepend (addrs, addr);
-      g_object_unref (sockaddr);
-    }
-
-  return g_list_reverse (addrs);
-}
-
-/* Private method to set up a getnameinfo() request */
-void
-_g_resolver_address_to_sockaddr (GInetAddress            *address,
-                                 struct sockaddr_storage *sa,
-                                 gsize                   *len)
-{
-  GSocketAddress *sockaddr;
-
-  sockaddr = g_inet_socket_address_new (address, 0);
-  g_socket_address_to_native (sockaddr, (struct sockaddr *)sa, sizeof (*sa), NULL);
-  *len = g_socket_address_get_native_size (sockaddr);
-  g_object_unref (sockaddr);
-}
-
-/* Private method to process a getnameinfo() response. */
-char *
-_g_resolver_name_from_nameinfo (GInetAddress  *address,
-                                const gchar   *name,
-                                gint           gni_retval,
-                                GError       **error)
-{
-  if (gni_retval != 0)
-    {
-      gchar *phys;
-
-      phys = g_inet_address_to_string (address);
-      g_set_error (error, G_RESOLVER_ERROR,
-                   g_resolver_error_from_addrinfo_error (gni_retval),
-                   _("Error reverse-resolving '%s': %s"),
-                   phys ? phys : "(unknown)", gai_strerror (gni_retval));
-      g_free (phys);
-      return NULL;
-    }
-
-  return g_strdup (name);
-}
-
-#if defined(G_OS_UNIX)
-
-static gboolean
-parse_short (guchar  **p,
-             guchar   *end,
-             guint16  *value)
-{
-  if (*p + 2 > end)
-    return FALSE;
-  GETSHORT (*value, *p);
-  return TRUE;
-}
-
-static gboolean
-parse_long (guchar  **p,
-            guchar   *end,
-            guint32  *value)
-{
-  if (*p + 4 > end)
-    return FALSE;
-  GETLONG (*value, *p);
-  return TRUE;
-}
-
-static GVariant *
-parse_res_srv (guchar  *answer,
-               guchar  *end,
-               guchar  *p)
-{
-  gchar namebuf[1024];
-  guint16 priority, weight, port;
-  gint n;
-
-  if (!parse_short (&p, end, &priority) ||
-      !parse_short (&p, end, &weight) ||
-      !parse_short (&p, end, &port))
-    return NULL;
-
-  n = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
-  if (n < 0)
-    return NULL;
-  *p += n;
-
-  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;
-  gint n;
-
-  n = dn_expand (answer, end, p, mnamebuf, sizeof (mnamebuf));
-  if (n < 0)
-    return NULL;
-  p += n;
-
-  n = dn_expand (answer, end, p, rnamebuf, sizeof (rnamebuf));
-  if (n < 0)
-    return NULL;
-  p += n;
-
-  if (!parse_long (&p, end, &serial) ||
-      !parse_long (&p, end, &refresh) ||
-      !parse_long (&p, end, &retry) ||
-      !parse_long (&p, end, &expire) ||
-      !parse_long (&p, end, &ttl))
-    return NULL;
-
-  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];
-  gint n;
-
-  n = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
-  if (n < 0)
-    return NULL;
-
-  return g_variant_new ("(s)", namebuf);
-}
-
-static GVariant *
-parse_res_mx (guchar  *answer,
-              guchar  *end,
-              guchar  *p)
-{
-  gchar namebuf[1024];
-  guint16 preference;
-  gint n;
-
-  if (!parse_short (&p, end, &preference))
-    return NULL;
-
-  n = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
-  if (n < 0)
-    return NULL;
-  p += n;
-
-  return g_variant_new ("(qs)",
-                        preference,
-                        namebuf);
-}
-
-static GVariant *
-parse_res_txt (guchar  *answer,
-               guchar  *end,
-               guchar  *p)
-{
-  GVariant *record;
-  GPtrArray *array;
-  gsize len;
-
-  array = g_ptr_array_new_with_free_func (g_free);
-  while (p < end)
-    {
-      len = *(p++);
-      if (len > p - end)
-        break;
-      g_ptr_array_add (array, g_strndup ((gchar *)p, len));
-      p += len;
-    }
-
-  record = g_variant_new ("(@as)",
-                          g_variant_new_strv ((const gchar **)array->pdata, array->len));
-  g_ptr_array_free (array, TRUE);
-  return record;
-}
-
-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);
-}
-
-/* Private method to process a res_query response into GSrvTargets */
-GList *
-_g_resolver_records_from_res_query (const gchar      *rrname,
-                                    gint              rrtype,
-                                    guchar           *answer,
-                                    gint              len,
-                                    gint              herr,
-                                    GError          **error)
-{
-  gint count;
-  guchar *end, *p;
-  guint16 type, qclass, rdlength;
-  guint32 ttl;
-  HEADER *header;
-  GList *records;
-  GVariant *record;
-  gint n, i;
-
-  if (len <= 0)
-    {
-      GResolverError errnum;
-      const gchar *format;
-
-      if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
-        {
-          errnum = G_RESOLVER_ERROR_NOT_FOUND;
-          format = _("No DNS record of the requested type for '%s'");
-        }
-      else if (herr == TRY_AGAIN)
-        {
-          errnum = G_RESOLVER_ERROR_TEMPORARY_FAILURE;
-          format = _("Temporarily unable to resolve '%s'");
-        }
-      else
-        {
-          errnum = G_RESOLVER_ERROR_INTERNAL;
-          format = _("Error resolving '%s'");
-        }
-
-      g_set_error (error, G_RESOLVER_ERROR, errnum, format, rrname);
-      return NULL;
-    }
-
-  records = NULL;
-
-  header = (HEADER *)answer;
-  p = answer + sizeof (HEADER);
-  end = answer + len;
-
-  /* Skip query */
-  count = ntohs (header->qdcount);
-  for (i = 0; i < count && p < end; i++)
-    {
-      n = dn_skipname (p, end);
-      if (n < 0)
-        break;
-      p += n;
-      p += 4;
-    }
-
-  /* Incomplete response */
-  if (i < count)
-    {
-      g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
-                   _("Incomplete data received for '%s'"), rrname);
-      return NULL;
-    }
-
-  /* Read answers */
-  count = ntohs (header->ancount);
-  for (i = 0; i < count && p < end; i++)
-    {
-      n = dn_skipname (p, end);
-      if (n < 0)
-        break;
-      p += n;
-
-      if (!parse_short (&p, end, &type) ||
-          !parse_short (&p, end, &qclass) ||
-          !parse_long (&p, end, &ttl) ||
-          !parse_short (&p, end, &rdlength))
-        break;
-
-      ttl = ttl; /* To avoid -Wunused-but-set-variable */
-
-      if (p + rdlength > end)
-        break;
-
-      if (type == rrtype && qclass == C_IN)
-        {
-          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);
-        }
-
-      p += rdlength;
-    }
-
-  /* Somehow got a truncated response */
-  if (i < count)
-    {
-      g_list_free_full (records, (GDestroyNotify)g_variant_unref);
-      g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
-                   _("Incomplete data received for '%s'"), rrname);
-      return NULL;
-    }
-
-  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;
-}
-
-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);
-}
-
-/* Private method to process a DnsQuery response into GVariants */
-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)
-    {
-      GResolverError errnum;
-      const gchar *format;
-
-      if (status == DNS_ERROR_RCODE_NAME_ERROR)
-        {
-          errnum = G_RESOLVER_ERROR_NOT_FOUND;
-          format = _("No DNS record of the requested type for '%s'");
-        }
-      else if (status == DNS_ERROR_RCODE_SERVER_FAILURE)
-        {
-          errnum = G_RESOLVER_ERROR_TEMPORARY_FAILURE;
-          format = _("Temporarily unable to resolve '%s'");
-        }
-      else
-        {
-          errnum = G_RESOLVER_ERROR_INTERNAL;
-          format = _("Error resolving '%s'");
-        }
-
-      g_set_error (error, G_RESOLVER_ERROR, errnum, format, rrname);
-      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));
-    }
-
-  return records;
-}
-
-#endif
index b0af405..def85b8 100644 (file)
 #include "gnetworkingprivate.h"
 
 #include "gcancellable.h"
+#include "ginetaddress.h"
+#include "ginetsocketaddress.h"
 #include "gtask.h"
 #include "gsocketaddress.h"
+#include "gsrvtarget.h"
 
 
 G_DEFINE_TYPE (GThreadedResolver, g_threaded_resolver, G_TYPE_RESOLVER)
@@ -42,6 +45,28 @@ g_threaded_resolver_init (GThreadedResolver *gtr)
 {
 }
 
+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;
+    }
+}
+
+static struct addrinfo addrinfo_hints;
+
 static void
 do_lookup_by_name (GTask         *task,
                    gpointer       source_object,
@@ -52,20 +77,42 @@ do_lookup_by_name (GTask         *task,
   struct addrinfo *res = NULL;
   GList *addresses;
   gint retval;
-  GError *error = NULL;
 
-  retval = getaddrinfo (hostname, NULL, &_g_resolver_addrinfo_hints, &res);
-  addresses = _g_resolver_addresses_from_addrinfo (hostname, res, retval, &error);
-  if (res)
-    freeaddrinfo (res);
+  retval = getaddrinfo (hostname, NULL, &addrinfo_hints, &res);
 
-  if (addresses)
+  if (retval == 0)
     {
+      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);
+          if (!sockaddr || !G_IS_INET_SOCKET_ADDRESS (sockaddr))
+            continue;
+
+          addr = g_object_ref (g_inet_socket_address_get_address ((GInetSocketAddress *)sockaddr));
+          addresses = g_list_prepend (addresses, addr);
+          g_object_unref (sockaddr);
+        }
+
+      addresses = g_list_reverse (addresses);
       g_task_return_pointer (task, addresses,
                              (GDestroyNotify)g_resolver_free_addresses);
     }
   else
-    g_task_return_error (task, error);
+    {
+      g_task_return_new_error (task,
+                               G_RESOLVER_ERROR,
+                               g_resolver_error_from_addrinfo_error (retval),
+                               _("Error resolving '%s': %s"),
+                               hostname, gai_strerror (retval));
+    }
+
+  if (res)
+    freeaddrinfo (res);
 }
 
 static GList *
@@ -123,19 +170,33 @@ do_lookup_by_address (GTask         *task,
   GInetAddress *address = task_data;
   struct sockaddr_storage sockaddr;
   gsize sockaddr_size;
-  gchar namebuf[NI_MAXHOST], *name;
+  GSocketAddress *gsockaddr;
+  gchar name[NI_MAXHOST];
   gint retval;
-  GError *error = NULL;
 
-  _g_resolver_address_to_sockaddr (address, &sockaddr, &sockaddr_size);
-  retval = getnameinfo ((struct sockaddr *)&sockaddr, sockaddr_size,
-                        namebuf, sizeof (namebuf), NULL, 0, NI_NAMEREQD);
-  name = _g_resolver_name_from_nameinfo (address, namebuf, retval, &error);
+  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);
 
-  if (name)
-    g_task_return_pointer (task, name, g_free);
+  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);
   else
-    g_task_return_error (task, error);
+    {
+      gchar *phys;
+
+      phys = g_inet_address_to_string (address);
+      g_task_return_new_error (task,
+                               G_RESOLVER_ERROR,
+                               g_resolver_error_from_addrinfo_error (retval),
+                               _("Error reverse-resolving '%s': %s"),
+                               phys ? phys : "(unknown)",
+                               gai_strerror (retval));
+      g_free (phys);
+    }
 }
 
 static gchar *
@@ -184,6 +245,382 @@ lookup_by_address_finish (GResolver     *resolver,
 }
 
 
+#if defined(G_OS_UNIX)
+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;
+  guint32 ttl;
+  HEADER *header;
+  GList *records;
+  GVariant *record;
+
+  if (len <= 0)
+    {
+      GResolverError errnum;
+      const gchar *format;
+
+      if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
+        {
+          errnum = G_RESOLVER_ERROR_NOT_FOUND;
+          format = _("No DNS record of the requested type for '%s'");
+        }
+      else if (herr == TRY_AGAIN)
+        {
+          errnum = G_RESOLVER_ERROR_TEMPORARY_FAILURE;
+          format = _("Temporarily unable to resolve '%s'");
+        }
+      else
+        {
+          errnum = G_RESOLVER_ERROR_INTERNAL;
+          format = _("Error resolving '%s'");
+        }
+
+      g_set_error (error, G_RESOLVER_ERROR, errnum, format, rrname);
+      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);
+      GETLONG  (ttl, p);
+      ttl = ttl; /* To avoid -Wunused-but-set-variable */
+      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);
+    }
+
+    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)
+    {
+      GResolverError errnum;
+      const gchar *format;
+
+      if (status == DNS_ERROR_RCODE_NAME_ERROR)
+        {
+          errnum = G_RESOLVER_ERROR_NOT_FOUND;
+          format = _("No DNS record of the requested type for '%s'");
+        }
+      else if (status == DNS_ERROR_RCODE_SERVER_FAILURE)
+        {
+          errnum = G_RESOLVER_ERROR_TEMPORARY_FAILURE;
+          format = _("Temporarily unable to resolve '%s'");
+        }
+      else
+        {
+          errnum = G_RESOLVER_ERROR_INTERNAL;
+          format = _("Error resolving '%s'");
+        }
+
+      g_set_error (error, G_RESOLVER_ERROR, errnum, format, rrname);
+      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));
+    }
+
+  return records;
+}
+
+#endif
+
 typedef struct {
   char *rrname;
   GResolverRecordType record_type;
@@ -211,13 +648,14 @@ do_lookup_records (GTask         *task,
   LookupRecordsData *lrd = task_data;
   GList *records;
   GError *error = NULL;
+
 #if defined(G_OS_UNIX)
   gint len = 512;
   gint herr;
   GByteArray *answer;
   gint rrtype;
 
-  rrtype = _g_resolver_record_type_to_rrtype (lrd->record_type);
+  rrtype = g_resolver_record_type_to_rrtype (lrd->record_type);
   answer = g_byte_array_new ();
   for (;;)
     {
@@ -232,28 +670,28 @@ do_lookup_records (GTask         *task,
        * On overflow some res_query's return the length needed, others
        * return the full length entered. This code works in either case.
        */
-  }
+    }
 
   herr = h_errno;
-  records = _g_resolver_records_from_res_query (lrd->rrname, rrtype, answer->data, len, herr, &error);
+  records = g_resolver_records_from_res_query (lrd->rrname, rrtype, answer->data, len, herr, &error);
   g_byte_array_free (answer, TRUE);
 
-#elif defined(G_OS_WIN32)
+#else
+
   DNS_STATUS status;
   DNS_RECORD *results = NULL;
   WORD dnstype;
 
-  dnstype = _g_resolver_record_type_to_dnstype (lrd->record_type);
+  dnstype = g_resolver_record_type_to_dnstype (lrd->record_type);
   status = DnsQuery_A (lrd->rrname, dnstype, DNS_QUERY_STANDARD, NULL, &results, NULL);
-  records = _g_resolver_records_from_DnsQuery (lrd->rrname, dnstype, status, results, &error);
+  records = g_resolver_records_from_DnsQuery (lrd->rrname, dnstype, status, results, &error);
   if (results != NULL)
     DnsRecordListFree (results, DnsFreeRecordList);
+
 #endif
 
   if (records)
-    {
-      g_task_return_pointer (task, records, (GDestroyNotify) free_records);
-    }
+    g_task_return_pointer (task, records, (GDestroyNotify) free_records);
   else
     g_task_return_error (task, error);
 }
@@ -332,4 +770,15 @@ g_threaded_resolver_class_init (GThreadedResolverClass *threaded_class)
   resolver_class->lookup_records           = lookup_records;
   resolver_class->lookup_records_async     = lookup_records_async;
   resolver_class->lookup_records_finish    = lookup_records_finish;
+
+  /* Initialize _g_resolver_addrinfo_hints */
+#ifdef AI_ADDRCONFIG
+  addrinfo_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+  /* These two 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;
 }