[glib] Add support for MX, TXT, NS and SOA records to GResolver
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] Add support for MX, TXT, NS and SOA records to GResolver
- Date: Mon, 16 Apr 2012 13:52:53 +0000 (UTC)
commit 666374c16f3d8118fe3422839d22ca32af69e4d0
Author: Stef Walter <stefw gnome org>
Date: Wed Apr 4 17:13:10 2012 +0200
Add support for MX, TXT, NS and SOA records to GResolver
* Add resolver functions for looking up DNS records of
various types. Currently implemented: MX, TXT, SOA, SRV, NS
* Return records as GVariant tuples.
* Make the GSrvTarget lookups a wrapper over this new
functionality.
* Rework the resolver test so that it has support for
looking up MX, NS, SOA, TXT records, and uses GOptionContext
https://bugzilla.gnome.org/show_bug.cgi?id=672944
docs/reference/gio/gio-sections.txt | 4 +
gio/gio.symbols | 4 +
gio/gioenums.h | 43 ++++
gio/gnetworkingprivate.h | 10 +-
gio/gresolver.c | 469 ++++++++++++++++++++++++++++++++--
gio/gresolver.h | 35 +++-
gio/gthreadedresolver.c | 118 ++++++----
gio/tests/resolver.c | 281 +++++++++++++++++++--
8 files changed, 862 insertions(+), 102 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index eee39bb..14b01c4 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1699,6 +1699,9 @@ g_resolver_lookup_service
g_resolver_lookup_service_async
g_resolver_lookup_service_finish
g_resolver_free_targets
+g_resolver_lookup_records
+g_resolver_lookup_records_async
+g_resolver_lookup_records_finish
<SUBSECTION>
G_RESOLVER_ERROR
@@ -1717,6 +1720,7 @@ G_TYPE_RESOLVER
GResolverPrivate
g_resolver_get_type
g_resolver_error_quark
+g_resolver_record_type_get_type
</SECTION>
<SECTION>
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 96b6875..0efb08e 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1713,3 +1713,7 @@ g_resources_unregister
g_static_resource_fini
g_static_resource_get_resource
g_static_resource_init
+g_resolver_lookup_records
+g_resolver_lookup_records_async
+g_resolver_lookup_records_finish
+g_resolver_record_type_get_type
diff --git a/gio/gioenums.h b/gio/gioenums.h
index dd97230..cc19d3c 100644
--- a/gio/gioenums.h
+++ b/gio/gioenums.h
@@ -631,6 +631,49 @@ typedef enum {
} GResolverError;
/**
+ * GResolverRecordType:
+ * @G_RESOLVER_RECORD_SRV: lookup DNS SRV records for a domain
+ * @G_RESOLVER_RECORD_MX: lookup DNS MX records for a domain
+ * @G_RESOLVER_RECORD_TXT: lookup DNS TXT records for a name
+ * @G_RESOLVER_RECORD_SOA: lookup DNS SOA records for a zone
+ * @G_RESOLVER_RECORD_NS: lookup DNS NS records for a domain
+ *
+ * The type of record that g_resolver_lookup_records() or
+ * g_resolver_lookup_records_async() should retrieve. The records are returned
+ * as lists of #GVariant tuples. Each record type has different values in
+ * the variant tuples returned.
+ *
+ * %G_RESOLVER_RECORD_SRV records are returned as variants with the signature
+ * '(qqqs)', containing a guint16 with the priority, a guint16 with the
+ * weight, a guint16 with the port, and a string of the hostname.
+ *
+ * %G_RESOLVER_RECORD_MX records are returned as variants with the signature
+ * '(qs)', representing a guint16 with the preference, and a string containing
+ * the mail exchanger hostname.
+ *
+ * %G_RESOLVER_RECORD_TXT records are returned as variants with the signature
+ * '(as)', representing an array of the strings in the text record.
+ *
+ * %G_RESOLVER_RECORD_SOA records are returned as variants with the signature
+ * '(ssuuuuu)', representing a string containing the primary name server, a
+ * string containing the administrator, the serial as a guint32, the refresh
+ * interval as guint32, the retry interval as a guint32, the expire timeout
+ * as a guint32, and the ttl as a guint32.
+ *
+ * %G_RESOLVER_RECORD_NS records are returned as variants with the signature
+ * '(s)', representing a string of the hostname of the name server.
+ *
+ * Since: 2.34
+ */
+typedef enum {
+ G_RESOLVER_RECORD_SRV = 1,
+ G_RESOLVER_RECORD_MX,
+ G_RESOLVER_RECORD_TXT,
+ G_RESOLVER_RECORD_SOA,
+ G_RESOLVER_RECORD_NS
+} GResolverRecordType;
+
+/**
* GResourceError:
* @G_RESOURCE_ERROR_NOT_FOUND: no file was found at the requested path
* @G_RESOURCE_ERROR_INTERNAL: unknown error
diff --git a/gio/gnetworkingprivate.h b/gio/gnetworkingprivate.h
index ce31b1e..2be3688 100644
--- a/gio/gnetworkingprivate.h
+++ b/gio/gnetworkingprivate.h
@@ -95,13 +95,19 @@ char *_g_resolver_name_from_nameinfo (GInetAddress *address,
GError **error);
#if defined(G_OS_UNIX)
-GList *_g_resolver_targets_from_res_query (const gchar *rrname,
+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)
-GList *_g_resolver_targets_from_DnsQuery (const gchar *rrname,
+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);
diff --git a/gio/gresolver.c b/gio/gresolver.c
index ad06974..9f80fc0 100644
--- a/gio/gresolver.c
+++ b/gio/gresolver.c
@@ -78,11 +78,81 @@ struct _GResolverPrivate {
*/
G_DEFINE_TYPE (GResolver, g_resolver, G_TYPE_OBJECT)
+static GList *
+srv_records_to_targets (GList *records)
+{
+ const gchar *hostname;
+ guint16 port, priority, weight;
+ GSrvTarget *target;
+ GList *l;
+
+ for (l = records; l != NULL; l = g_list_next (l))
+ {
+ g_variant_get (l->data, "(qqq&s)", &priority, &weight, &port, &hostname);
+ target = g_srv_target_new (hostname, port, priority, weight);
+ g_variant_unref (l->data);
+ l->data = target;
+ }
+
+ return g_srv_target_list_sort (records);
+}
+
+static GList *
+g_resolver_real_lookup_service (GResolver *resolver,
+ const gchar *rrname,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GList *records;
+
+ records = G_RESOLVER_GET_CLASS (resolver)->lookup_records (resolver,
+ rrname,
+ G_RESOLVER_RECORD_SRV,
+ cancellable,
+ error);
+
+ return srv_records_to_targets (records);
+}
+
+static void
+g_resolver_real_lookup_service_async (GResolver *resolver,
+ const gchar *rrname,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ G_RESOLVER_GET_CLASS (resolver)->lookup_records_async (resolver,
+ rrname,
+ G_RESOLVER_RECORD_SRV,
+ cancellable,
+ callback,
+ user_data);
+}
+
+static GList *
+g_resolver_real_lookup_service_finish (GResolver *resolver,
+ GAsyncResult *result,
+ GError **error)
+{
+ GList *records;
+
+ records = G_RESOLVER_GET_CLASS (resolver)->lookup_records_finish (resolver,
+ result,
+ error);
+
+ return srv_records_to_targets (records);
+}
+
static void
g_resolver_class_init (GResolverClass *resolver_class)
{
volatile GType type;
+ /* Automatically pass these over to the lookup_records methods */
+ resolver_class->lookup_service = g_resolver_real_lookup_service;
+ resolver_class->lookup_service_async = g_resolver_real_lookup_service_async;
+ resolver_class->lookup_service_finish = g_resolver_real_lookup_service_finish;
+
g_type_class_add_private (resolver_class, sizeof (GResolverPrivate));
/* Make sure _g_networking_init() has been called */
@@ -708,6 +778,112 @@ g_resolver_free_targets (GList *targets)
}
/**
+ * g_resolver_lookup_records:
+ * @resolver: a #GResolver
+ * @rrname: the DNS name to lookup the record for
+ * @record_type: the type of DNS record to lookup
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Synchronously performs a DNS record lookup for the given @rrname and returns
+ * a list of records as #GVariant tuples. See #GResolverRecordType for
+ * information on what the records contain for each @record_type.
+ *
+ * If the DNS resolution fails, @error (if non-%NULL) will be set to
+ * a value from #GResolverError.
+ *
+ * If @cancellable is non-%NULL, it can be used to cancel the
+ * operation, in which case @error (if non-%NULL) will be set to
+ * %G_IO_ERROR_CANCELLED.
+ *
+ * Return value: (element-type GVariant) (transfer full): a #GList of #GVariant,
+ * or %NULL on error. You must free each of the records and the list when you are
+ * done with it. (You can use g_list_free_full() with g_variant_unref() to do this.)
+ *
+ * Since: 2.34
+ */
+GList *
+g_resolver_lookup_records (GResolver *resolver,
+ const gchar *rrname,
+ GResolverRecordType record_type,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GList *records;
+
+ g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
+ g_return_val_if_fail (rrname != NULL, NULL);
+
+ g_resolver_maybe_reload (resolver);
+ records = G_RESOLVER_GET_CLASS (resolver)->
+ lookup_records (resolver, rrname, record_type, cancellable, error);
+
+ return records;
+}
+
+/**
+ * g_resolver_lookup_records_async:
+ * @resolver: a #GResolver
+ * @rrname: the DNS name to lookup the record for
+ * @record_type: the type of DNS record to lookup
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @callback: (scope async): callback to call after resolution completes
+ * @user_data: (closure): data for @callback
+ *
+ * Begins asynchronously performing a DNS lookup for the given
+ * @rrname, and eventually calls @callback, which must call
+ * g_resolver_lookup_records_finish() to get the final result. See
+ * g_resolver_lookup_records() for more details.
+ *
+ * Since: 2.34
+ */
+void
+g_resolver_lookup_records_async (GResolver *resolver,
+ const gchar *rrname,
+ GResolverRecordType record_type,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (G_IS_RESOLVER (resolver));
+ g_return_if_fail (rrname != NULL);
+
+ g_resolver_maybe_reload (resolver);
+ G_RESOLVER_GET_CLASS (resolver)->
+ lookup_records_async (resolver, rrname, record_type, cancellable, callback, user_data);
+}
+
+/**
+ * g_resolver_lookup_records_finish:
+ * @resolver: a #GResolver
+ * @result: the result passed to your #GAsyncReadyCallback
+ * @error: return location for a #GError, or %NULL
+ *
+ * Retrieves the result of a previous call to
+ * g_resolver_lookup_records_async(). Returns a list of records as #GVariant
+ * tuples. See #GResolverRecordType for information on what the records contain.
+ *
+ * If the DNS resolution failed, @error (if non-%NULL) will be set to
+ * a value from #GResolverError. If the operation was cancelled,
+ * @error will be set to %G_IO_ERROR_CANCELLED.
+ *
+ * Return value: (element-type GVariant) (transfer full): a #GList of #GVariant,
+ * or %NULL on error. You must free each of the records and the list when you are
+ * done with it. (You can use g_list_free_full() with g_variant_unref() to do this.)
+ *
+ * Since: 2.34
+ */
+GList *
+g_resolver_lookup_records_finish (GResolver *resolver,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (G_IS_RESOLVER (resolver), NULL);
+ return G_RESOLVER_GET_CLASS (resolver)->
+ lookup_records_finish (resolver, result, error);
+}
+
+/**
* g_resolver_error_quark:
*
* Gets the #GResolver Error Quark.
@@ -821,9 +997,133 @@ _g_resolver_name_from_nameinfo (GInetAddress *address,
}
#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;
+}
+
+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_targets_from_res_query (const gchar *rrname,
+_g_resolver_records_from_res_query (const gchar *rrname,
+ gint rrtype,
guchar *answer,
gint len,
gint herr,
@@ -832,11 +1132,11 @@ _g_resolver_targets_from_res_query (const gchar *rrname,
gint count;
gchar namebuf[1024];
guchar *end, *p;
- guint16 type, qclass, rdlength, priority, weight, port;
+ guint16 type, qclass, rdlength;
guint32 ttl;
HEADER *header;
- GSrvTarget *target;
- GList *targets;
+ GList *records;
+ GVariant *record;
if (len <= 0)
{
@@ -846,7 +1146,7 @@ _g_resolver_targets_from_res_query (const gchar *rrname,
if (len == 0 || herr == HOST_NOT_FOUND || herr == NO_DATA)
{
errnum = G_RESOLVER_ERROR_NOT_FOUND;
- format = _("No service record for '%s'");
+ format = _("No DNS record of the requested type for '%s'");
}
else if (herr == TRY_AGAIN)
{
@@ -863,7 +1163,7 @@ _g_resolver_targets_from_res_query (const gchar *rrname,
return NULL;
}
- targets = NULL;
+ records = NULL;
header = (HEADER *)answer;
p = answer + sizeof (HEADER);
@@ -875,6 +1175,9 @@ _g_resolver_targets_from_res_query (const gchar *rrname,
{
p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
p += 4;
+
+ /* To silence gcc warnings */
+ namebuf[0] = namebuf[1];
}
/* Read answers */
@@ -888,34 +1191,126 @@ _g_resolver_targets_from_res_query (const gchar *rrname,
ttl = ttl; /* To avoid -Wunused-but-set-variable */
GETSHORT (rdlength, p);
- if (type != T_SRV || qclass != C_IN)
+ if (type != rrtype || qclass != C_IN)
{
p += rdlength;
continue;
}
- GETSHORT (priority, p);
- GETSHORT (weight, p);
- GETSHORT (port, p);
- p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
+ 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;
+ }
- target = g_srv_target_new (namebuf, port, priority, weight);
- targets = g_list_prepend (targets, target);
+ if (record != NULL)
+ records = g_list_prepend (records, record);
}
- return g_srv_target_list_sort (targets);
+ return records;
}
+
#elif defined(G_OS_WIN32)
-/* Private method to process a DnsQuery response into GSrvTargets */
+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_targets_from_DnsQuery (const gchar *rrname,
+_g_resolver_records_from_DnsQuery (const gchar *rrname,
+ WORD dnstype,
DNS_STATUS status,
DNS_RECORD *results,
GError **error)
{
DNS_RECORD *rec;
- GSrvTarget *target;
- GList *targets;
+ gpointer record;
+ GList *records;
if (status != ERROR_SUCCESS)
{
@@ -925,7 +1320,7 @@ _g_resolver_targets_from_DnsQuery (const gchar *rrname,
if (status == DNS_ERROR_RCODE_NAME_ERROR)
{
errnum = G_RESOLVER_ERROR_NOT_FOUND;
- format = _("No service record for '%s'");
+ format = _("No DNS record of the requested type for '%s'");
}
else if (status == DNS_ERROR_RCODE_SERVER_FAILURE)
{
@@ -942,20 +1337,38 @@ _g_resolver_targets_from_DnsQuery (const gchar *rrname,
return NULL;
}
- targets = NULL;
+ records = NULL;
for (rec = results; rec; rec = rec->pNext)
{
- if (rec->wType != DNS_TYPE_SRV)
+ if (rec->wType != dnstype)
continue;
-
- target = g_srv_target_new (rec->Data.SRV.pNameTarget,
- rec->Data.SRV.wPort,
- rec->Data.SRV.wPriority,
- rec->Data.SRV.wWeight);
- targets = g_list_prepend (targets, target);
+ 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 g_srv_target_list_sort (targets);
+ return records;
}
#endif
diff --git a/gio/gresolver.h b/gio/gresolver.h
index 1187a3f..dcadf96 100644
--- a/gio/gresolver.h
+++ b/gio/gresolver.h
@@ -91,10 +91,24 @@ struct _GResolverClass {
GAsyncResult *result,
GError **error);
+ GList * ( *lookup_records) (GResolver *resolver,
+ const gchar *rrname,
+ GResolverRecordType record_type,
+ GCancellable *cancellable,
+ GError **error);
+
+ void ( *lookup_records_async) (GResolver *resolver,
+ const gchar *rrname,
+ GResolverRecordType record_type,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+ GList * ( *lookup_records_finish) (GResolver *resolver,
+ GAsyncResult *result,
+ GError **error);
+
/* Padding for future expansion */
- void (*_g_reserved1) (void);
- void (*_g_reserved2) (void);
- void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
@@ -150,6 +164,21 @@ GList *g_resolver_lookup_service_finish (GResolver *resolver,
GAsyncResult *result,
GError **error);
+GList *g_resolver_lookup_records (GResolver *resolver,
+ const gchar *rrname,
+ GResolverRecordType record_type,
+ GCancellable *cancellable,
+ GError **error);
+void g_resolver_lookup_records_async (GResolver *resolver,
+ const gchar *rrname,
+ GResolverRecordType record_type,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GList *g_resolver_lookup_records_finish (GResolver *resolver,
+ GAsyncResult *result,
+ GError **error);
+
void g_resolver_free_targets (GList *targets);
/**
diff --git a/gio/gthreadedresolver.c b/gio/gthreadedresolver.c
index 5a820be..73cb6e8 100644
--- a/gio/gthreadedresolver.c
+++ b/gio/gthreadedresolver.c
@@ -151,8 +151,9 @@ struct _GThreadedResolverRequest {
} address;
struct {
gchar *rrname;
- GList *targets;
- } service;
+ GResolverRecordType record_type;
+ GList *results;
+ } records;
} u;
GCancellable *cancellable;
@@ -515,85 +516,108 @@ lookup_by_address_finish (GResolver *resolver,
static void
-do_lookup_service (GThreadedResolverRequest *req,
- GError **error)
+do_lookup_records (GThreadedResolverRequest *req,
+ GError **error)
{
#if defined(G_OS_UNIX)
- gint len, herr;
- guchar answer[1024];
-#elif defined(G_OS_WIN32)
- DNS_STATUS status;
- DNS_RECORD *results;
-#endif
+ gint len = 512;
+ gint herr;
+ GByteArray *answer;
+ gint rrtype;
+
+ rrtype = _g_resolver_record_type_to_rrtype (req->u.records.record_type);
+ answer = g_byte_array_new ();
+ for (;;)
+ {
+ g_byte_array_set_size (answer, len * 2);
+ len = res_query (req->u.records.rrname, C_IN, rrtype, answer->data, answer->len);
+
+ /* 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.
+ */
+ }
-#if defined(G_OS_UNIX)
- len = res_query (req->u.service.rrname, C_IN, T_SRV, answer, sizeof (answer));
herr = h_errno;
- req->u.service.targets = _g_resolver_targets_from_res_query (req->u.service.rrname, answer, len, herr, error);
+ req->u.records.results = _g_resolver_records_from_res_query (req->u.records.rrname, rrtype, answer->data, len, herr, error);
+ g_byte_array_free (answer, TRUE);
+
#elif defined(G_OS_WIN32)
- status = DnsQuery_A (req->u.service.rrname, DNS_TYPE_SRV,
- DNS_QUERY_STANDARD, NULL, &results, NULL);
- req->u.service.targets = _g_resolver_targets_from_DnsQuery (req->u.service.rrname, status, results, error);
- DnsRecordListFree (results, DnsFreeRecordList);
+ DNS_STATUS status;
+ DNS_RECORD *results = NULL;
+ WORD dnstype;
+
+ dnstype = _g_resolver_record_type_to_dnstype (req->u.records.record_type);
+ status = DnsQuery_A (req->u.records.rrname, dnstype, DNS_QUERY_STANDARD, NULL, &results, NULL);
+ req->u.records.results = _g_resolver_records_from_DnsQuery (req->u.records.rrname, dnstype, status, results, error);
+ if (results != NULL)
+ DnsRecordListFree (results, DnsFreeRecordList);
#endif
}
static GList *
-lookup_service (GResolver *resolver,
- const gchar *rrname,
- GCancellable *cancellable,
- GError **error)
+lookup_records (GResolver *resolver,
+ const gchar *rrname,
+ GResolverRecordType record_type,
+ GCancellable *cancellable,
+ GError **error)
{
GThreadedResolver *gtr = G_THREADED_RESOLVER (resolver);
GThreadedResolverRequest *req;
- GList *targets;
+ GList *results;
- req = g_threaded_resolver_request_new (do_lookup_service, NULL, cancellable);
- req->u.service.rrname = (char *)rrname;
+ req = g_threaded_resolver_request_new (do_lookup_records, NULL, cancellable);
+ req->u.records.rrname = (char *)rrname;
+ req->u.records.record_type = record_type;
resolve_sync (gtr, req, error);
- targets = req->u.service.targets;
+ results = req->u.records.results;
g_threaded_resolver_request_unref (req);
- return targets;
+ return results;
}
static void
-free_lookup_service (GThreadedResolverRequest *req)
+free_lookup_records (GThreadedResolverRequest *req)
{
- g_free (req->u.service.rrname);
- if (req->u.service.targets)
- g_resolver_free_targets (req->u.service.targets);
+ g_free (req->u.records.rrname);
+ g_list_free_full (req->u.records.results, (GDestroyNotify)g_variant_unref);
}
static void
-lookup_service_async (GResolver *resolver,
+lookup_records_async (GResolver *resolver,
const char *rrname,
- GCancellable *cancellable,
+ GResolverRecordType record_type,
+ GCancellable *cancellable,
GAsyncReadyCallback callback,
- gpointer user_data)
+ gpointer user_data)
{
GThreadedResolver *gtr = G_THREADED_RESOLVER (resolver);
GThreadedResolverRequest *req;
- req = g_threaded_resolver_request_new (do_lookup_service,
- free_lookup_service,
+ req = g_threaded_resolver_request_new (do_lookup_records,
+ free_lookup_records,
cancellable);
- req->u.service.rrname = g_strdup (rrname);
- resolve_async (gtr, req, callback, user_data, lookup_service_async);
+ req->u.records.rrname = g_strdup (rrname);
+ req->u.records.record_type = record_type;
+ resolve_async (gtr, req, callback, user_data, lookup_records_async);
}
static GList *
-lookup_service_finish (GResolver *resolver,
+lookup_records_finish (GResolver *resolver,
GAsyncResult *result,
- GError **error)
+ GError **error)
{
GThreadedResolverRequest *req;
- GList *targets;
+ GList *records;
- req = resolve_finish (resolver, result, lookup_service_async, error);
- targets = req->u.service.targets;
- req->u.service.targets = NULL;
- return targets;
+ req = resolve_finish (resolver, result, lookup_records_async, error);
+ records = req->u.records.results;
+ req->u.records.results = NULL;
+ return records;
}
@@ -609,9 +633,9 @@ g_threaded_resolver_class_init (GThreadedResolverClass *threaded_class)
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_service = lookup_service;
- resolver_class->lookup_service_async = lookup_service_async;
- resolver_class->lookup_service_finish = lookup_service_finish;
+ resolver_class->lookup_records = lookup_records;
+ resolver_class->lookup_records_async = lookup_records_async;
+ resolver_class->lookup_records_finish = lookup_records_finish;
object_class->finalize = finalize;
}
diff --git a/gio/tests/resolver.c b/gio/tests/resolver.c
index 8c474b2..81a6df2 100644
--- a/gio/tests/resolver.c
+++ b/gio/tests/resolver.c
@@ -36,15 +36,20 @@ static GResolver *resolver;
static GCancellable *cancellable;
static GMainLoop *loop;
static int nlookups = 0;
+static gboolean synchronous = FALSE;
+static guint connectable_count = 0;
+static GResolverRecordType record_type = 0;
static void G_GNUC_NORETURN
usage (void)
{
fprintf (stderr, "Usage: resolver [-s] [hostname | IP | service/protocol/domain ] ...\n");
+ fprintf (stderr, "Usage: resolver [-s] [-t MX|TXT|NS|SOA] rrname ...\n");
fprintf (stderr, " resolver [-s] -c NUMBER [hostname | IP | service/protocol/domain ]\n");
fprintf (stderr, " Use -s to do synchronous lookups.\n");
fprintf (stderr, " Use -c NUMBER (and only a single resolvable argument) to test GSocketConnectable.\n");
fprintf (stderr, " The given NUMBER determines how many times the connectable will be enumerated.\n");
+ fprintf (stderr, " Use -t with MX, TXT, NS or SOA to lookup DNS records of those types.\n");
exit (1);
}
@@ -138,9 +143,9 @@ print_resolved_service (const char *service,
{
printf ("%s:%u (pri %u, weight %u)\n",
g_srv_target_get_hostname (t->data),
- g_srv_target_get_port (t->data),
- g_srv_target_get_priority (t->data),
- g_srv_target_get_weight (t->data));
+ (guint)g_srv_target_get_port (t->data),
+ (guint)g_srv_target_get_priority (t->data),
+ (guint)g_srv_target_get_weight (t->data));
g_srv_target_free (t->data);
}
g_list_free (targets);
@@ -152,11 +157,185 @@ print_resolved_service (const char *service,
}
static void
+print_resolved_mx (const char *rrname,
+ GList *records,
+ GError *error)
+{
+ const gchar *hostname;
+ guint16 priority;
+ GList *t;
+
+ G_LOCK (response);
+ printf ("Domain: %s\n", rrname);
+ if (error)
+ {
+ printf ("Error: %s\n", error->message);
+ g_error_free (error);
+ }
+ else if (!records)
+ {
+ printf ("no MX records\n");
+ }
+ else
+ {
+ for (t = records; t; t = t->next)
+ {
+ g_variant_get (t->data, "(q&s)", &priority, &hostname);
+ printf ("%s (pri %u)\n", hostname, (guint)priority);
+ g_variant_unref (t->data);
+ }
+ g_list_free (records);
+ }
+ printf ("\n");
+
+ done_lookup ();
+ G_UNLOCK (response);
+}
+
+static void
+print_resolved_txt (const char *rrname,
+ GList *records,
+ GError *error)
+{
+ const gchar **contents;
+ GList *t;
+ gint i;
+
+ G_LOCK (response);
+ printf ("Domain: %s\n", rrname);
+ if (error)
+ {
+ printf ("Error: %s\n", error->message);
+ g_error_free (error);
+ }
+ else if (!records)
+ {
+ printf ("no TXT records\n");
+ }
+ else
+ {
+ for (t = records; t; t = t->next)
+ {
+ if (t != records)
+ printf ("\n");
+ g_variant_get (t->data, "(^a&s)", &contents);
+ for (i = 0; contents[i] != NULL; i++)
+ printf ("%s\n", contents[i]);
+ g_variant_unref (t->data);
+ }
+ g_list_free (records);
+ }
+ printf ("\n");
+
+ done_lookup ();
+ G_UNLOCK (response);
+}
+
+static void
+print_resolved_soa (const char *rrname,
+ GList *records,
+ GError *error)
+{
+ GList *t;
+ const gchar *primary_ns;
+ const gchar *administrator;
+ guint32 serial, refresh, retry, expire, ttl;
+
+ G_LOCK (response);
+ printf ("Zone: %s\n", rrname);
+ if (error)
+ {
+ printf ("Error: %s\n", error->message);
+ g_error_free (error);
+ }
+ else if (!records)
+ {
+ printf ("no SOA records\n");
+ }
+ else
+ {
+ for (t = records; t; t = t->next)
+ {
+ g_variant_get (t->data, "(&s&suuuuu)", &primary_ns, &administrator,
+ &serial, &refresh, &retry, &expire, &ttl);
+ printf ("%s %s (serial %u, refresh %u, retry %u, expire %u, ttl %u)\n",
+ primary_ns, administrator, (guint)serial, (guint)refresh,
+ (guint)retry, (guint)expire, (guint)ttl);
+ g_variant_unref (t->data);
+ }
+ g_list_free (records);
+ }
+ printf ("\n");
+
+ done_lookup ();
+ G_UNLOCK (response);
+}
+
+static void
+print_resolved_ns (const char *rrname,
+ GList *records,
+ GError *error)
+{
+ GList *t;
+ const gchar *hostname;
+
+ G_LOCK (response);
+ printf ("Zone: %s\n", rrname);
+ if (error)
+ {
+ printf ("Error: %s\n", error->message);
+ g_error_free (error);
+ }
+ else if (!records)
+ {
+ printf ("no NS records\n");
+ }
+ else
+ {
+ for (t = records; t; t = t->next)
+ {
+ g_variant_get (t->data, "(&s)", &hostname);
+ printf ("%s\n", hostname);
+ g_variant_unref (t->data);
+ }
+ g_list_free (records);
+ }
+ printf ("\n");
+
+ done_lookup ();
+ G_UNLOCK (response);
+}
+
+static void
lookup_one_sync (const char *arg)
{
GError *error = NULL;
- if (strchr (arg, '/'))
+ if (record_type != 0)
+ {
+ GList *records;
+
+ records = g_resolver_lookup_records (resolver, arg, record_type, cancellable, &error);
+ switch (record_type)
+ {
+ case G_RESOLVER_RECORD_MX:
+ print_resolved_mx (arg, records, error);
+ break;
+ case G_RESOLVER_RECORD_SOA:
+ print_resolved_soa (arg, records, error);
+ break;
+ case G_RESOLVER_RECORD_NS:
+ print_resolved_ns (arg, records, error);
+ break;
+ case G_RESOLVER_RECORD_TXT:
+ print_resolved_txt (arg, records, error);
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+ }
+ else if (strchr (arg, '/'))
{
GList *targets;
/* service/protocol/domain */
@@ -245,13 +424,49 @@ lookup_service_callback (GObject *source, GAsyncResult *result,
}
static void
+lookup_records_callback (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ const char *arg = user_data;
+ GError *error = NULL;
+ GList *records;
+
+ records = g_resolver_lookup_records_finish (resolver, result, &error);
+
+ switch (record_type)
+ {
+ case G_RESOLVER_RECORD_MX:
+ print_resolved_mx (arg, records, error);
+ break;
+ case G_RESOLVER_RECORD_SOA:
+ print_resolved_soa (arg, records, error);
+ break;
+ case G_RESOLVER_RECORD_NS:
+ print_resolved_ns (arg, records, error);
+ break;
+ case G_RESOLVER_RECORD_TXT:
+ print_resolved_txt (arg, records, error);
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+}
+
+static void
start_async_lookups (char **argv, int argc)
{
int i;
for (i = 0; i < argc; i++)
{
- if (strchr (argv[i], '/'))
+ if (record_type != 0)
+ {
+ g_resolver_lookup_records_async (resolver, argv[i], record_type,
+ cancellable, lookup_records_callback, argv[i]);
+ }
+ else if (strchr (argv[i], '/'))
{
/* service/protocol/domain */
char **parts = g_strsplit (argv[i], "/", 3);
@@ -428,11 +643,42 @@ async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
}
#endif
+
+static gboolean
+record_type_arg (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ if (g_ascii_strcasecmp (value, "MX") == 0) {
+ record_type = G_RESOLVER_RECORD_MX;
+ } else if (g_ascii_strcasecmp (value, "TXT") == 0) {
+ record_type = G_RESOLVER_RECORD_TXT;
+ } else if (g_ascii_strcasecmp (value, "SOA") == 0) {
+ record_type = G_RESOLVER_RECORD_SOA;
+ } else if (g_ascii_strcasecmp (value, "NS") == 0) {
+ record_type = G_RESOLVER_RECORD_NS;
+ } else {
+ g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
+ "Specify MX, TXT, NS or SOA for the special record lookup types");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static const GOptionEntry option_entries[] = {
+ { "synchronous", 's', 0, G_OPTION_ARG_NONE, &synchronous, "Synchronous connections", NULL },
+ { "connectable", 'c', 0, G_OPTION_ARG_INT, &connectable_count, "Connectable count", "C" },
+ { "special-type", 't', 0, G_OPTION_ARG_CALLBACK, record_type_arg, "Record type like MX, TXT, NS or SOA", "RR" },
+ { NULL },
+};
+
int
main (int argc, char **argv)
{
- gboolean synchronous = FALSE;
- guint connectable_count = 0;
+ GOptionContext *context;
+ GError *error = NULL;
#ifdef G_OS_UNIX
GIOChannel *chan;
guint watch;
@@ -440,22 +686,13 @@ main (int argc, char **argv)
g_type_init ();
- /* FIXME: use GOptionContext */
- while (argc >= 2 && argv[1][0] == '-')
+ context = g_option_context_new ("lookups ...");
+ g_option_context_add_main_entries (context, option_entries, NULL);
+ if (!g_option_context_parse (context, &argc, &argv, &error))
{
- if (!strcmp (argv[1], "-s"))
- synchronous = TRUE;
- else if (!strcmp (argv[1], "-c"))
- {
- connectable_count = atoi (argv[2]);
- argv++;
- argc--;
- }
- else
- usage ();
-
- argv++;
- argc--;
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ usage();
}
if (argc < 2 || (argc > 2 && connectable_count))
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]