mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-04 10:16:17 +01:00
Merge branch 'resolver-tests' into 'main'
tests: Add more tests for GResolver response parsing See merge request GNOME/glib!2566
This commit is contained in:
commit
61e2a9afe2
@ -529,18 +529,56 @@ typedef enum __ns_type {
|
||||
|
||||
#endif /* __BIONIC__ */
|
||||
|
||||
/* Wrapper around dn_expand() which does associated length checks and returns
|
||||
* errors as #GError. */
|
||||
static gboolean
|
||||
expand_name (const gchar *rrname,
|
||||
const guint8 *answer,
|
||||
const guint8 *end,
|
||||
const guint8 **p,
|
||||
gchar *namebuf,
|
||||
gsize namebuf_len,
|
||||
GError **error)
|
||||
{
|
||||
int expand_result;
|
||||
|
||||
expand_result = dn_expand (answer, end, *p, namebuf, namebuf_len);
|
||||
if (expand_result < 0 || end - *p < expand_result)
|
||||
{
|
||||
g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
|
||||
/* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
|
||||
_("Error parsing DNS %s record: malformed DNS packet"), rrname);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*p += expand_result;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
parse_res_srv (const guint8 *answer,
|
||||
const guint8 *end,
|
||||
const guint8 **p)
|
||||
const guint8 **p,
|
||||
GError **error)
|
||||
{
|
||||
gchar namebuf[1024];
|
||||
guint16 priority, weight, port;
|
||||
|
||||
if (end - *p < 6)
|
||||
{
|
||||
g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
|
||||
/* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
|
||||
_("Error parsing DNS %s record: malformed DNS packet"), "SRV");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GETSHORT (priority, *p);
|
||||
GETSHORT (weight, *p);
|
||||
GETSHORT (port, *p);
|
||||
*p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
|
||||
|
||||
if (!expand_name ("SRV", answer, end, p, namebuf, sizeof (namebuf), error))
|
||||
return NULL;
|
||||
|
||||
return g_variant_new ("(qqqs)",
|
||||
priority,
|
||||
@ -552,14 +590,26 @@ parse_res_srv (const guint8 *answer,
|
||||
static GVariant *
|
||||
parse_res_soa (const guint8 *answer,
|
||||
const guint8 *end,
|
||||
const guint8 **p)
|
||||
const guint8 **p,
|
||||
GError **error)
|
||||
{
|
||||
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));
|
||||
if (!expand_name ("SOA", answer, end, p, mnamebuf, sizeof (mnamebuf), error))
|
||||
return NULL;
|
||||
|
||||
if (!expand_name ("SOA", answer, end, p, rnamebuf, sizeof (rnamebuf), error))
|
||||
return NULL;
|
||||
|
||||
if (end - *p < 20)
|
||||
{
|
||||
g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
|
||||
/* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
|
||||
_("Error parsing DNS %s record: malformed DNS packet"), "SOA");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GETLONG (serial, *p);
|
||||
GETLONG (refresh, *p);
|
||||
@ -580,11 +630,13 @@ parse_res_soa (const guint8 *answer,
|
||||
static GVariant *
|
||||
parse_res_ns (const guint8 *answer,
|
||||
const guint8 *end,
|
||||
const guint8 **p)
|
||||
const guint8 **p,
|
||||
GError **error)
|
||||
{
|
||||
gchar namebuf[1024];
|
||||
|
||||
*p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
|
||||
if (!expand_name ("NS", answer, end, p, namebuf, sizeof (namebuf), error))
|
||||
return NULL;
|
||||
|
||||
return g_variant_new ("(s)", namebuf);
|
||||
}
|
||||
@ -592,14 +644,24 @@ parse_res_ns (const guint8 *answer,
|
||||
static GVariant *
|
||||
parse_res_mx (const guint8 *answer,
|
||||
const guint8 *end,
|
||||
const guint8 **p)
|
||||
const guint8 **p,
|
||||
GError **error)
|
||||
{
|
||||
gchar namebuf[1024];
|
||||
guint16 preference;
|
||||
|
||||
if (end - *p < 2)
|
||||
{
|
||||
g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
|
||||
/* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
|
||||
_("Error parsing DNS %s record: malformed DNS packet"), "MX");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GETSHORT (preference, *p);
|
||||
|
||||
*p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
|
||||
if (!expand_name ("MX", answer, end, p, namebuf, sizeof (namebuf), error))
|
||||
return NULL;
|
||||
|
||||
return g_variant_new ("(qs)",
|
||||
preference,
|
||||
@ -609,19 +671,35 @@ parse_res_mx (const guint8 *answer,
|
||||
static GVariant *
|
||||
parse_res_txt (const guint8 *answer,
|
||||
const guint8 *end,
|
||||
const guint8 **p)
|
||||
const guint8 **p,
|
||||
GError **error)
|
||||
{
|
||||
GVariant *record;
|
||||
GPtrArray *array;
|
||||
const guint8 *at = *p;
|
||||
gsize len;
|
||||
|
||||
if (end - *p == 0)
|
||||
{
|
||||
g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
|
||||
/* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
|
||||
_("Error parsing DNS %s record: malformed DNS packet"), "TXT");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
array = g_ptr_array_new_with_free_func (g_free);
|
||||
while (at < end)
|
||||
{
|
||||
len = *(at++);
|
||||
if (len > (gsize) (end - at))
|
||||
break;
|
||||
{
|
||||
g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
|
||||
/* Translators: the placeholder is a DNS record type, such as ‘MX’ or ‘SRV’ */
|
||||
_("Error parsing DNS %s record: malformed DNS packet"), "TXT");
|
||||
g_ptr_array_free (array, TRUE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_ptr_array_add (array, g_strndup ((gchar *)at, len));
|
||||
at += len;
|
||||
}
|
||||
@ -633,7 +711,7 @@ parse_res_txt (const guint8 *answer,
|
||||
return record;
|
||||
}
|
||||
|
||||
static gint
|
||||
gint
|
||||
g_resolver_record_type_to_rrtype (GResolverRecordType type)
|
||||
{
|
||||
switch (type)
|
||||
@ -660,13 +738,15 @@ g_resolver_records_from_res_query (const gchar *rrname,
|
||||
gint herr,
|
||||
GError **error)
|
||||
{
|
||||
gint count;
|
||||
uint16_t count;
|
||||
gchar namebuf[1024];
|
||||
const guint8 *end, *p;
|
||||
guint16 type, qclass, rdlength;
|
||||
const HEADER *header;
|
||||
GList *records;
|
||||
GVariant *record;
|
||||
gsize len_unsigned;
|
||||
GError *parsing_error = NULL;
|
||||
|
||||
if (len <= 0)
|
||||
{
|
||||
@ -689,18 +769,44 @@ g_resolver_records_from_res_query (const gchar *rrname,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We know len ≥ 0 now. */
|
||||
len_unsigned = (gsize) len;
|
||||
|
||||
if (len_unsigned < sizeof (HEADER))
|
||||
{
|
||||
g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
|
||||
/* Translators: the first placeholder is a domain name, the
|
||||
* second is an error message */
|
||||
_("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
records = NULL;
|
||||
|
||||
header = (HEADER *)answer;
|
||||
p = answer + sizeof (HEADER);
|
||||
end = answer + len;
|
||||
end = answer + len_unsigned;
|
||||
|
||||
/* Skip query */
|
||||
count = ntohs (header->qdcount);
|
||||
while (count-- && p < end)
|
||||
{
|
||||
p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
|
||||
p += 4;
|
||||
int expand_result;
|
||||
|
||||
expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
|
||||
if (expand_result < 0 || end - p < expand_result + 4)
|
||||
{
|
||||
/* Not possible to recover parsing as the length of the rest of the
|
||||
* record is unknown or is too short. */
|
||||
g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
|
||||
/* Translators: the first placeholder is a domain name, the
|
||||
* second is an error message */
|
||||
_("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p += expand_result;
|
||||
p += 4; /* skip TYPE and CLASS */
|
||||
|
||||
/* To silence gcc warnings */
|
||||
namebuf[0] = namebuf[1];
|
||||
@ -710,12 +816,35 @@ g_resolver_records_from_res_query (const gchar *rrname,
|
||||
count = ntohs (header->ancount);
|
||||
while (count-- && p < end)
|
||||
{
|
||||
p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
|
||||
int expand_result;
|
||||
|
||||
expand_result = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
|
||||
if (expand_result < 0 || end - p < expand_result + 10)
|
||||
{
|
||||
/* Not possible to recover parsing as the length of the rest of the
|
||||
* record is unknown or is too short. */
|
||||
g_set_error (&parsing_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
|
||||
/* Translators: the first placeholder is a domain name, the
|
||||
* second is an error message */
|
||||
_("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
|
||||
break;
|
||||
}
|
||||
|
||||
p += expand_result;
|
||||
GETSHORT (type, p);
|
||||
GETSHORT (qclass, p);
|
||||
p += 4; /* ignore the ttl (type=long) value */
|
||||
GETSHORT (rdlength, p);
|
||||
|
||||
if (end - p < rdlength)
|
||||
{
|
||||
g_set_error (&parsing_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
|
||||
/* Translators: the first placeholder is a domain name, the
|
||||
* second is an error message */
|
||||
_("Error resolving “%s”: %s"), rrname, _("Malformed DNS packet"));
|
||||
break;
|
||||
}
|
||||
|
||||
if (type != rrtype || qclass != C_IN)
|
||||
{
|
||||
p += rdlength;
|
||||
@ -725,31 +854,40 @@ g_resolver_records_from_res_query (const gchar *rrname,
|
||||
switch (rrtype)
|
||||
{
|
||||
case T_SRV:
|
||||
record = parse_res_srv (answer, end, &p);
|
||||
record = parse_res_srv (answer, p + rdlength, &p, &parsing_error);
|
||||
break;
|
||||
case T_MX:
|
||||
record = parse_res_mx (answer, end, &p);
|
||||
record = parse_res_mx (answer, p + rdlength, &p, &parsing_error);
|
||||
break;
|
||||
case T_SOA:
|
||||
record = parse_res_soa (answer, end, &p);
|
||||
record = parse_res_soa (answer, p + rdlength, &p, &parsing_error);
|
||||
break;
|
||||
case T_NS:
|
||||
record = parse_res_ns (answer, end, &p);
|
||||
record = parse_res_ns (answer, p + rdlength, &p, &parsing_error);
|
||||
break;
|
||||
case T_TXT:
|
||||
record = parse_res_txt (answer, p + rdlength, &p);
|
||||
record = parse_res_txt (answer, p + rdlength, &p, &parsing_error);
|
||||
break;
|
||||
default:
|
||||
g_warn_if_reached ();
|
||||
g_debug ("Unrecognised DNS record type %u", rrtype);
|
||||
record = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (record != NULL)
|
||||
records = g_list_prepend (records, record);
|
||||
|
||||
if (parsing_error != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (records == NULL)
|
||||
if (parsing_error != NULL)
|
||||
{
|
||||
g_propagate_prefixed_error (error, parsing_error, _("Failed to parse DNS response for “%s”: "), rrname);
|
||||
g_list_free_full (records, (GDestroyNotify)g_variant_unref);
|
||||
return NULL;
|
||||
}
|
||||
else 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);
|
||||
|
@ -43,6 +43,7 @@ GLIB_AVAILABLE_IN_ALL
|
||||
GType g_threaded_resolver_get_type (void) G_GNUC_CONST;
|
||||
|
||||
/* Used for a private test API */
|
||||
#ifdef G_OS_UNIX
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GList *g_resolver_records_from_res_query (const gchar *rrname,
|
||||
gint rrtype,
|
||||
@ -50,6 +51,9 @@ GList *g_resolver_records_from_res_query (const gchar *rrname,
|
||||
gssize len,
|
||||
gint herr,
|
||||
GError **error);
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gint g_resolver_record_type_to_rrtype (GResolverRecordType type);
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -74,6 +74,14 @@ if host_system != 'windows'
|
||||
endif
|
||||
endif
|
||||
|
||||
# dn_comp()
|
||||
if cc.links('''#include <resolv.h>
|
||||
int main (int argc, char ** argv) {
|
||||
return dn_comp(NULL, NULL, 0, NULL, NULL) == -1;
|
||||
} ''', args : network_args, name : 'dn_comp()')
|
||||
glib_conf.set('HAVE_DN_COMP', 1)
|
||||
endif
|
||||
|
||||
# res_nclose()
|
||||
if cc.links('''#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
|
@ -191,6 +191,7 @@ if host_machine.system() != 'windows'
|
||||
},
|
||||
'gdbus-peer-object-manager' : {},
|
||||
'live-g-file' : {},
|
||||
'resolver-parsing' : {'dependencies' : [network_libs]},
|
||||
'socket-address' : {},
|
||||
'stream-rw_all' : {},
|
||||
'unix-fd' : {},
|
||||
|
879
gio/tests/resolver-parsing.c
Normal file
879
gio/tests/resolver-parsing.c
Normal file
@ -0,0 +1,879 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Igalia S.L.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Patrick Griffis <pgriffis@igalia.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gnetworking.h>
|
||||
|
||||
#define GIO_COMPILATION
|
||||
#include "gthreadedresolver.h"
|
||||
#undef GIO_COMPILATION
|
||||
|
||||
#ifdef HAVE_DN_COMP
|
||||
static void
|
||||
dns_builder_add_uint8 (GByteArray *builder,
|
||||
guint8 value)
|
||||
{
|
||||
g_byte_array_append (builder, &value, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
dns_builder_add_uint16 (GByteArray *builder,
|
||||
guint16 value)
|
||||
{
|
||||
dns_builder_add_uint8 (builder, (value >> 8) & 0xFF);
|
||||
dns_builder_add_uint8 (builder, (value) & 0xFF);
|
||||
}
|
||||
|
||||
static void
|
||||
dns_builder_add_uint32 (GByteArray *builder,
|
||||
guint32 value)
|
||||
{
|
||||
dns_builder_add_uint8 (builder, (value >> 24) & 0xFF);
|
||||
dns_builder_add_uint8 (builder, (value >> 16) & 0xFF);
|
||||
dns_builder_add_uint8 (builder, (value >> 8) & 0xFF);
|
||||
dns_builder_add_uint8 (builder, (value) & 0xFF);
|
||||
}
|
||||
|
||||
static void
|
||||
dns_builder_add_length_prefixed_string (GByteArray *builder,
|
||||
const char *string)
|
||||
{
|
||||
guint8 length;
|
||||
|
||||
g_assert (strlen (string) <= G_MAXUINT8);
|
||||
|
||||
length = (guint8) strlen (string);
|
||||
dns_builder_add_uint8 (builder, length);
|
||||
|
||||
/* Don't include trailing NUL */
|
||||
g_byte_array_append (builder, (const guchar *)string, length);
|
||||
}
|
||||
|
||||
static void
|
||||
dns_builder_add_domain (GByteArray *builder,
|
||||
const char *string)
|
||||
{
|
||||
int ret;
|
||||
guchar buffer[256];
|
||||
|
||||
ret = dn_comp (string, buffer, sizeof (buffer), NULL, NULL);
|
||||
g_assert (ret != -1);
|
||||
|
||||
g_byte_array_append (builder, buffer, ret);
|
||||
}
|
||||
|
||||
/* Append an invalid domain name to the DNS response. This is implemented by
|
||||
* appending a single label followed by a pointer back to that label. This is
|
||||
* invalid regardless of any other context in the response as its expansion is
|
||||
* infinite.
|
||||
*
|
||||
* See https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.4
|
||||
*
|
||||
* In order to create a pointer to the label, the label’s final offset in the
|
||||
* DNS response must be known. The current length of @builder, plus @offset, is
|
||||
* used for this. Hence, @offset is the additional offset (in bytes) to add, and
|
||||
* typically corresponds to the length of the parent #GByteArray that @builder
|
||||
* will eventually be added to. Potentially plus 2 bytes for the rdlength, as
|
||||
* per dns_builder_add_answer_data(). */
|
||||
static void
|
||||
dns_builder_add_invalid_domain (GByteArray *builder,
|
||||
gsize offset)
|
||||
{
|
||||
offset += builder->len;
|
||||
g_assert ((offset & 0xc0) == 0);
|
||||
|
||||
dns_builder_add_uint8 (builder, 1);
|
||||
dns_builder_add_uint8 (builder, 'f');
|
||||
dns_builder_add_uint8 (builder, 0xc0 | offset);
|
||||
}
|
||||
|
||||
static void
|
||||
dns_builder_add_answer_data (GByteArray *builder,
|
||||
GByteArray *answer)
|
||||
{
|
||||
dns_builder_add_uint16 (builder, answer->len); /* rdlength */
|
||||
g_byte_array_append (builder, answer->data, answer->len);
|
||||
}
|
||||
|
||||
static GByteArray *
|
||||
dns_header (void)
|
||||
{
|
||||
GByteArray *answer = g_byte_array_sized_new (2046);
|
||||
|
||||
/* Start with a header, we ignore everything except ancount.
|
||||
https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.1 */
|
||||
dns_builder_add_uint16 (answer, 0); /* ID */
|
||||
dns_builder_add_uint16 (answer, 0); /* |QR| Opcode |AA|TC|RD|RA| Z | RCODE | */
|
||||
dns_builder_add_uint16 (answer, 0); /* QDCOUNT */
|
||||
dns_builder_add_uint16 (answer, 1); /* ANCOUNT (1 answer) */
|
||||
dns_builder_add_uint16 (answer, 0); /* NSCOUNT */
|
||||
dns_builder_add_uint16 (answer, 0); /* ARCOUNT */
|
||||
|
||||
return g_steal_pointer (&answer);
|
||||
}
|
||||
|
||||
static void
|
||||
assert_query_fails (const gchar *rrname,
|
||||
GResolverRecordType record_type,
|
||||
GByteArray *answer)
|
||||
{
|
||||
GList *records = NULL;
|
||||
GError *local_error = NULL;
|
||||
|
||||
records = g_resolver_records_from_res_query (rrname,
|
||||
g_resolver_record_type_to_rrtype (record_type),
|
||||
answer->data,
|
||||
answer->len,
|
||||
0,
|
||||
&local_error);
|
||||
|
||||
g_assert_error (local_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL);
|
||||
g_assert_null (records);
|
||||
g_clear_error (&local_error);
|
||||
}
|
||||
|
||||
static void
|
||||
assert_query_succeeds (const gchar *rrname,
|
||||
GResolverRecordType record_type,
|
||||
GByteArray *answer,
|
||||
const gchar *expected_answer_variant_str)
|
||||
{
|
||||
GList *records = NULL;
|
||||
GVariant *answer_variant, *expected_answer_variant = NULL;
|
||||
GError *local_error = NULL;
|
||||
|
||||
records = g_resolver_records_from_res_query (rrname,
|
||||
g_resolver_record_type_to_rrtype (record_type),
|
||||
answer->data,
|
||||
answer->len,
|
||||
0,
|
||||
&local_error);
|
||||
|
||||
g_assert_no_error (local_error);
|
||||
g_assert_nonnull (records);
|
||||
|
||||
/* Test the results. */
|
||||
answer_variant = records->data;
|
||||
expected_answer_variant = g_variant_new_parsed (expected_answer_variant_str);
|
||||
g_assert_cmpvariant (answer_variant, expected_answer_variant);
|
||||
|
||||
g_variant_unref (expected_answer_variant);
|
||||
g_list_free_full (records, (GDestroyNotify) g_variant_unref);
|
||||
}
|
||||
#endif /* HAVE_DN_COMP */
|
||||
|
||||
static void
|
||||
test_invalid_header (void)
|
||||
{
|
||||
const struct
|
||||
{
|
||||
const guint8 *answer;
|
||||
gsize answer_len;
|
||||
GResolverError expected_error_code;
|
||||
}
|
||||
vectors[] =
|
||||
{
|
||||
/* No answer: */
|
||||
{ (const guint8 *) "", 0, G_RESOLVER_ERROR_NOT_FOUND },
|
||||
/* Definitely too short to be a valid header: */
|
||||
{ (const guint8 *) "\x20", 1, G_RESOLVER_ERROR_INTERNAL },
|
||||
/* One byte too short to be a valid header: */
|
||||
{ (const guint8 *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 11, G_RESOLVER_ERROR_INTERNAL },
|
||||
/* Valid header indicating no answers: */
|
||||
{ (const guint8 *) "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, G_RESOLVER_ERROR_NOT_FOUND },
|
||||
};
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (vectors); i++)
|
||||
{
|
||||
GList *records = NULL;
|
||||
GError *local_error = NULL;
|
||||
|
||||
records = g_resolver_records_from_res_query ("example.org",
|
||||
g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS),
|
||||
vectors[i].answer,
|
||||
vectors[i].answer_len,
|
||||
0,
|
||||
&local_error);
|
||||
|
||||
g_assert_error (local_error, G_RESOLVER_ERROR, (gint) vectors[i].expected_error_code);
|
||||
g_assert_null (records);
|
||||
g_clear_error (&local_error);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_unknown_record_type (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL;
|
||||
GList *records = NULL;
|
||||
GError *local_error = NULL;
|
||||
const guint type_id = 20; /* ISDN, not supported anywhere */
|
||||
|
||||
/* An answer with an unsupported type chosen from
|
||||
* https://en.wikipedia.org/wiki/List_of_DNS_record_types#[1]_Obsolete_record_types */
|
||||
answer = dns_header ();
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, type_id);
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
dns_builder_add_uint16 (answer, 0); /* rdlength */
|
||||
|
||||
records = g_resolver_records_from_res_query ("example.org",
|
||||
type_id,
|
||||
answer->data,
|
||||
answer->len,
|
||||
0,
|
||||
&local_error);
|
||||
|
||||
g_assert_error (local_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND);
|
||||
g_assert_null (records);
|
||||
g_clear_error (&local_error);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_mx_valid (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *mx_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9 */
|
||||
mx_rdata = g_byte_array_new ();
|
||||
dns_builder_add_uint16 (mx_rdata, 0); /* preference */
|
||||
dns_builder_add_domain (mx_rdata, "mail.example.org");
|
||||
dns_builder_add_answer_data (answer, mx_rdata);
|
||||
g_byte_array_unref (mx_rdata);
|
||||
|
||||
assert_query_succeeds ("example.org", G_RESOLVER_RECORD_MX, answer,
|
||||
"(@q 0, 'mail.example.org')");
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_mx_invalid (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *mx_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
|
||||
*
|
||||
* Use an invalid domain to trigger parsing failure. */
|
||||
mx_rdata = g_byte_array_new ();
|
||||
dns_builder_add_uint16 (mx_rdata, 0); /* preference */
|
||||
dns_builder_add_invalid_domain (mx_rdata, answer->len + 2);
|
||||
dns_builder_add_answer_data (answer, mx_rdata);
|
||||
g_byte_array_unref (mx_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_mx_invalid_too_short (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *mx_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
|
||||
*
|
||||
* Miss out the domain field to trigger failure */
|
||||
mx_rdata = g_byte_array_new ();
|
||||
dns_builder_add_uint16 (mx_rdata, 0); /* preference */
|
||||
/* missing domain field */
|
||||
dns_builder_add_answer_data (answer, mx_rdata);
|
||||
g_byte_array_unref (mx_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_mx_invalid_too_short2 (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *mx_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_MX));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* MX rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.9
|
||||
*
|
||||
* Miss out all fields to trigger failure */
|
||||
mx_rdata = g_byte_array_new ();
|
||||
/* missing preference and domain fields */
|
||||
dns_builder_add_answer_data (answer, mx_rdata);
|
||||
g_byte_array_unref (mx_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_MX, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_ns_valid (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *ns_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* NS rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.11 */
|
||||
ns_rdata = g_byte_array_new ();
|
||||
dns_builder_add_domain (ns_rdata, "ns.example.org");
|
||||
dns_builder_add_answer_data (answer, ns_rdata);
|
||||
g_byte_array_unref (ns_rdata);
|
||||
|
||||
assert_query_succeeds ("example.org", G_RESOLVER_RECORD_NS, answer,
|
||||
"('ns.example.org',)");
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_ns_invalid (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *ns_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_NS));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* NS rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.11
|
||||
*
|
||||
* Use an invalid domain to trigger parsing failure. */
|
||||
ns_rdata = g_byte_array_new ();
|
||||
dns_builder_add_invalid_domain (ns_rdata, answer->len + 2);
|
||||
dns_builder_add_answer_data (answer, ns_rdata);
|
||||
g_byte_array_unref (ns_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_NS, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_soa_valid (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *soa_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13 */
|
||||
soa_rdata = g_byte_array_new ();
|
||||
dns_builder_add_domain (soa_rdata, "mname.example.org");
|
||||
dns_builder_add_domain (soa_rdata, "rname.example.org");
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* serial */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* retry */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* expire */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
|
||||
dns_builder_add_answer_data (answer, soa_rdata);
|
||||
g_byte_array_unref (soa_rdata);
|
||||
|
||||
assert_query_succeeds ("example.org", G_RESOLVER_RECORD_SOA, answer,
|
||||
"('mname.example.org', 'rname.example.org', @u 0, @u 0, @u 0, @u 0, @u 0)");
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_soa_invalid_mname (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *soa_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
|
||||
*
|
||||
* Use an invalid domain to trigger parsing failure. */
|
||||
soa_rdata = g_byte_array_new ();
|
||||
dns_builder_add_invalid_domain (soa_rdata, answer->len + 2); /* mname */
|
||||
dns_builder_add_domain (soa_rdata, "rname.example.org");
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* serial */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* retry */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* expire */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
|
||||
dns_builder_add_answer_data (answer, soa_rdata);
|
||||
g_byte_array_unref (soa_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_soa_invalid_rname (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *soa_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
|
||||
*
|
||||
* Use an invalid domain to trigger parsing failure. */
|
||||
soa_rdata = g_byte_array_new ();
|
||||
dns_builder_add_domain (soa_rdata, "mname.example.org");
|
||||
dns_builder_add_invalid_domain (soa_rdata, answer->len + 2); /* rname */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* serial */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* retry */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* expire */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* minimum */
|
||||
dns_builder_add_answer_data (answer, soa_rdata);
|
||||
g_byte_array_unref (soa_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_soa_invalid_too_short (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *soa_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SOA));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* SOA rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.13
|
||||
*
|
||||
* Miss out one of the fields to trigger a failure. */
|
||||
soa_rdata = g_byte_array_new ();
|
||||
dns_builder_add_domain (soa_rdata, "mname.example.org");
|
||||
dns_builder_add_domain (soa_rdata, "rname.example.org");
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* serial */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* refresh */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* retry */
|
||||
dns_builder_add_uint32 (soa_rdata, 0); /* expire */
|
||||
/* missing minimum field */
|
||||
dns_builder_add_answer_data (answer, soa_rdata);
|
||||
g_byte_array_unref (soa_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_SOA, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_txt_valid (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *txt_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14 */
|
||||
txt_rdata = g_byte_array_new ();
|
||||
dns_builder_add_length_prefixed_string (txt_rdata, "some test content");
|
||||
dns_builder_add_answer_data (answer, txt_rdata);
|
||||
g_byte_array_unref (txt_rdata);
|
||||
|
||||
assert_query_succeeds ("example.org", G_RESOLVER_RECORD_TXT, answer,
|
||||
"(['some test content'],)");
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_txt_valid_multiple_strings (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *txt_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14 */
|
||||
txt_rdata = g_byte_array_new ();
|
||||
dns_builder_add_length_prefixed_string (txt_rdata, "some test content");
|
||||
dns_builder_add_length_prefixed_string (txt_rdata, "more test content");
|
||||
dns_builder_add_answer_data (answer, txt_rdata);
|
||||
g_byte_array_unref (txt_rdata);
|
||||
|
||||
assert_query_succeeds ("example.org", G_RESOLVER_RECORD_TXT, answer,
|
||||
"(['some test content', 'more test content'],)");
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_txt_invalid_empty (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *txt_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
|
||||
*
|
||||
* Provide zero character strings (i.e. an empty rdata section) to trigger
|
||||
* failure. */
|
||||
txt_rdata = g_byte_array_new ();
|
||||
dns_builder_add_answer_data (answer, txt_rdata);
|
||||
g_byte_array_unref (txt_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_TXT, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_txt_invalid_overflow (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *txt_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_TXT));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* TXT rdata, https://datatracker.ietf.org/doc/html/rfc1035#section-3.3.14
|
||||
*
|
||||
* Use a character string whose length exceeds the remaining length in the
|
||||
* answer record, to trigger failure. */
|
||||
txt_rdata = g_byte_array_new ();
|
||||
dns_builder_add_uint8 (txt_rdata, 10); /* length, but no content */
|
||||
dns_builder_add_answer_data (answer, txt_rdata);
|
||||
g_byte_array_unref (txt_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_TXT, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_srv_valid (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *srv_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782 */
|
||||
srv_rdata = g_byte_array_new ();
|
||||
dns_builder_add_uint16 (srv_rdata, 0); /* priority */
|
||||
dns_builder_add_uint16 (srv_rdata, 0); /* weight */
|
||||
dns_builder_add_uint16 (srv_rdata, 0); /* port */
|
||||
dns_builder_add_domain (srv_rdata, "target.example.org");
|
||||
dns_builder_add_answer_data (answer, srv_rdata);
|
||||
g_byte_array_unref (srv_rdata);
|
||||
|
||||
assert_query_succeeds ("example.org", G_RESOLVER_RECORD_SRV, answer,
|
||||
"(@q 0, @q 0, @q 0, 'target.example.org')");
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_srv_invalid (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *srv_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
|
||||
*
|
||||
* Use an invalid domain to trigger parsing failure. */
|
||||
srv_rdata = g_byte_array_new ();
|
||||
dns_builder_add_uint16 (srv_rdata, 0); /* priority */
|
||||
dns_builder_add_uint16 (srv_rdata, 0); /* weight */
|
||||
dns_builder_add_uint16 (srv_rdata, 0); /* port */
|
||||
dns_builder_add_invalid_domain (srv_rdata, answer->len + 2);
|
||||
dns_builder_add_answer_data (answer, srv_rdata);
|
||||
g_byte_array_unref (srv_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_srv_invalid_too_short (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *srv_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
|
||||
*
|
||||
* Miss out the target field to trigger failure */
|
||||
srv_rdata = g_byte_array_new ();
|
||||
dns_builder_add_uint16 (srv_rdata, 0); /* priority */
|
||||
dns_builder_add_uint16 (srv_rdata, 0); /* weight */
|
||||
dns_builder_add_uint16 (srv_rdata, 0); /* port */
|
||||
/* missing target field */
|
||||
dns_builder_add_answer_data (answer, srv_rdata);
|
||||
g_byte_array_unref (srv_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
test_srv_invalid_too_short2 (void)
|
||||
{
|
||||
#ifndef HAVE_DN_COMP
|
||||
g_test_skip ("The dn_comp() function was not available.");
|
||||
return;
|
||||
#else
|
||||
GByteArray *answer = NULL, *srv_rdata = NULL;
|
||||
|
||||
answer = dns_header ();
|
||||
|
||||
/* Resource record */
|
||||
dns_builder_add_domain (answer, "example.org");
|
||||
dns_builder_add_uint16 (answer, g_resolver_record_type_to_rrtype (G_RESOLVER_RECORD_SRV));
|
||||
dns_builder_add_uint16 (answer, 1); /* qclass=C_IN */
|
||||
dns_builder_add_uint32 (answer, 0); /* ttl (ignored) */
|
||||
|
||||
/* SRV rdata, https://datatracker.ietf.org/doc/html/rfc2782
|
||||
*
|
||||
* Miss out the target and port fields to trigger failure */
|
||||
srv_rdata = g_byte_array_new ();
|
||||
dns_builder_add_uint16 (srv_rdata, 0); /* priority */
|
||||
dns_builder_add_uint16 (srv_rdata, 0); /* weight */
|
||||
/* missing port and target fields */
|
||||
dns_builder_add_answer_data (answer, srv_rdata);
|
||||
g_byte_array_unref (srv_rdata);
|
||||
|
||||
assert_query_fails ("example.org", G_RESOLVER_RECORD_SRV, answer);
|
||||
|
||||
g_byte_array_free (answer, TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
|
||||
|
||||
g_test_add_func ("/gresolver/invalid-header", test_invalid_header);
|
||||
g_test_add_func ("/gresolver/unknown-record-type", test_unknown_record_type);
|
||||
g_test_add_func ("/gresolver/mx/valid", test_mx_valid);
|
||||
g_test_add_func ("/gresolver/mx/invalid", test_mx_invalid);
|
||||
g_test_add_func ("/gresolver/mx/invalid/too-short", test_mx_invalid_too_short);
|
||||
g_test_add_func ("/gresolver/mx/invalid/too-short2", test_mx_invalid_too_short2);
|
||||
g_test_add_func ("/gresolver/ns/valid", test_ns_valid);
|
||||
g_test_add_func ("/gresolver/ns/invalid", test_ns_invalid);
|
||||
g_test_add_func ("/gresolver/soa/valid", test_soa_valid);
|
||||
g_test_add_func ("/gresolver/soa/invalid/mname", test_soa_invalid_mname);
|
||||
g_test_add_func ("/gresolver/soa/invalid/rname", test_soa_invalid_rname);
|
||||
g_test_add_func ("/gresolver/soa/invalid/too-short", test_soa_invalid_too_short);
|
||||
g_test_add_func ("/gresolver/srv/valid", test_srv_valid);
|
||||
g_test_add_func ("/gresolver/srv/invalid", test_srv_invalid);
|
||||
g_test_add_func ("/gresolver/srv/invalid/too-short", test_srv_invalid_too_short);
|
||||
g_test_add_func ("/gresolver/srv/invalid/too-short2", test_srv_invalid_too_short2);
|
||||
g_test_add_func ("/gresolver/txt/valid", test_txt_valid);
|
||||
g_test_add_func ("/gresolver/txt/valid/multiple-strings", test_txt_valid_multiple_strings);
|
||||
g_test_add_func ("/gresolver/txt/invalid/empty", test_txt_invalid_empty);
|
||||
g_test_add_func ("/gresolver/txt/invalid/overflow", test_txt_invalid_overflow);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
@ -44,12 +44,12 @@ static G_NORETURN void
|
||||
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, "Usage: resolver [-s] [-t MX|TXT|NS|SOA|SRV] 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 look up DNS records of those types.\n");
|
||||
fprintf (stderr, " Use -t with MX, TXT, NS, SOA or SRV to look up DNS records of those types.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
@ -232,6 +232,46 @@ print_resolved_txt (const char *rrname,
|
||||
G_UNLOCK (response);
|
||||
}
|
||||
|
||||
static void
|
||||
print_resolved_srv (const char *rrname,
|
||||
GList *records,
|
||||
GError *error)
|
||||
{
|
||||
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 SRV records\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
GList *t;
|
||||
|
||||
for (t = records; t != NULL; t = t->next)
|
||||
{
|
||||
guint16 priority, weight, port;
|
||||
const gchar *target;
|
||||
|
||||
g_variant_get (t->data, "(qqq&s)", &priority, &weight, &port, &target);
|
||||
|
||||
printf ("%s (priority %u, weight %u, port %u)\n",
|
||||
target, (guint) priority, (guint) weight, (guint) port);
|
||||
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,
|
||||
@ -331,6 +371,9 @@ lookup_one_sync (const char *arg)
|
||||
case G_RESOLVER_RECORD_TXT:
|
||||
print_resolved_txt (arg, records, error);
|
||||
break;
|
||||
case G_RESOLVER_RECORD_SRV:
|
||||
print_resolved_srv (arg, records, error);
|
||||
break;
|
||||
default:
|
||||
g_warn_if_reached ();
|
||||
break;
|
||||
@ -449,6 +492,9 @@ lookup_records_callback (GObject *source,
|
||||
case G_RESOLVER_RECORD_TXT:
|
||||
print_resolved_txt (arg, records, error);
|
||||
break;
|
||||
case G_RESOLVER_RECORD_SRV:
|
||||
print_resolved_srv (arg, records, error);
|
||||
break;
|
||||
default:
|
||||
g_warn_if_reached ();
|
||||
break;
|
||||
@ -659,9 +705,11 @@ record_type_arg (const gchar *option_name,
|
||||
record_type = G_RESOLVER_RECORD_SOA;
|
||||
} else if (g_ascii_strcasecmp (value, "NS") == 0) {
|
||||
record_type = G_RESOLVER_RECORD_NS;
|
||||
} else if (g_ascii_strcasecmp (value, "SRV") == 0) {
|
||||
record_type = G_RESOLVER_RECORD_SRV;
|
||||
} 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");
|
||||
"Specify MX, TXT, NS, SOA or SRV for the special record lookup types");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user