mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 06:56:14 +01:00
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
This commit is contained in:
parent
cec17df598
commit
666374c16f
@ -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>
|
||||
|
@ -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
|
||||
|
@ -630,6 +630,49 @@ typedef enum {
|
||||
G_RESOLVER_ERROR_INTERNAL
|
||||
} 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
|
||||
|
@ -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);
|
||||
|
471
gio/gresolver.c
471
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 */
|
||||
@ -707,6 +777,112 @@ g_resolver_free_targets (GList *targets)
|
||||
g_list_free (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:
|
||||
*
|
||||
@ -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));
|
||||
|
||||
target = g_srv_target_new (namebuf, port, priority, weight);
|
||||
targets = g_list_prepend (targets, target);
|
||||
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;
|
||||
}
|
||||
|
||||
return g_srv_target_list_sort (targets);
|
||||
if (record != NULL)
|
||||
records = g_list_prepend (records, record);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -151,8 +151,9 @@ struct _GThreadedResolverRequest {
|
||||
} address;
|
||||
struct {
|
||||
gchar *rrname;
|
||||
GList *targets;
|
||||
} service;
|
||||
GResolverRecordType record_type;
|
||||
GList *results;
|
||||
} records;
|
||||
} u;
|
||||
|
||||
GCancellable *cancellable;
|
||||
@ -515,59 +516,81 @@ lookup_by_address_finish (GResolver *resolver,
|
||||
|
||||
|
||||
static void
|
||||
do_lookup_service (GThreadedResolverRequest *req,
|
||||
do_lookup_records (GThreadedResolverRequest *req,
|
||||
GError **error)
|
||||
{
|
||||
#if defined(G_OS_UNIX)
|
||||
gint len, herr;
|
||||
guchar answer[1024];
|
||||
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.
|
||||
*/
|
||||
}
|
||||
|
||||
herr = h_errno;
|
||||
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)
|
||||
DNS_STATUS status;
|
||||
DNS_RECORD *results;
|
||||
#endif
|
||||
DNS_RECORD *results = NULL;
|
||||
WORD dnstype;
|
||||
|
||||
#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);
|
||||
#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);
|
||||
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,
|
||||
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,
|
||||
GResolverRecordType record_type,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
@ -575,25 +598,26 @@ lookup_service_async (GResolver *resolver,
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
@ -151,12 +156,186 @@ print_resolved_service (const char *service,
|
||||
G_UNLOCK (response);
|
||||
}
|
||||
|
||||
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 */
|
||||
@ -244,6 +423,37 @@ lookup_service_callback (GObject *source, GAsyncResult *result,
|
||||
print_resolved_service (service, targets, error);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -251,7 +461,12 @@ start_async_lookups (char **argv, int argc)
|
||||
|
||||
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
|
||||
g_printerr ("%s\n", error->message);
|
||||
g_error_free (error);
|
||||
usage();
|
||||
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (argc < 2 || (argc > 2 && connectable_count))
|
||||
|
Loading…
Reference in New Issue
Block a user