+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)
+ {
+ if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
+ _("No DNS record of the requested type for '%s'"), rrname);
+ }
+ else if (herr == TRY_AGAIN)
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
+ _("Temporarily unable to resolve '%s'"), rrname);
+ }
+ else
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ _("Error resolving '%s'"), 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);
+ }
+
+ if (records == NULL)
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
+ _("No DNS record of the requested type for '%s'"), rrname);
+
+ return NULL;
+ }
+ else
+ 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)
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
+ _("No DNS record of the requested type for '%s'"), rrname);
+ }
+ else if (status == DNS_ERROR_RCODE_SERVER_FAILURE)
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
+ _("Temporarily unable to resolve '%s'"), rrname);
+ }
+ else
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ _("Error resolving '%s'"), 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));
+ }
+
+ if (records == NULL)
+ {
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
+ _("No DNS record of the requested type for '%s'"), rrname);
+
+ return NULL;
+ }
+ else
+ return records;
+}
+
+#endif
+
+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);
+}