mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 23:16:14 +01:00
gresolver: More robust parsing of DNS responses
* Handle truncated responses, and invalid names https://bugzilla.gnome.org/show_bug.cgi?id=675966
This commit is contained in:
parent
49e5075707
commit
8ed955ceba
163
gio/gresolver.c
163
gio/gresolver.c
@ -994,18 +994,47 @@ _g_resolver_name_from_nameinfo (GInetAddress *address,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(G_OS_UNIX)
|
#if defined(G_OS_UNIX)
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_short (guchar **p,
|
||||||
|
guchar *end,
|
||||||
|
guint16 *value)
|
||||||
|
{
|
||||||
|
if (*p + 2 > end)
|
||||||
|
return FALSE;
|
||||||
|
GETSHORT (*value, *p);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
parse_long (guchar **p,
|
||||||
|
guchar *end,
|
||||||
|
guint32 *value)
|
||||||
|
{
|
||||||
|
if (*p + 4 > end)
|
||||||
|
return FALSE;
|
||||||
|
GETLONG (*value, *p);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static GVariant *
|
static GVariant *
|
||||||
parse_res_srv (guchar *answer,
|
parse_res_srv (guchar *answer,
|
||||||
guchar *end,
|
guchar *end,
|
||||||
guchar **p)
|
guchar *p)
|
||||||
{
|
{
|
||||||
gchar namebuf[1024];
|
gchar namebuf[1024];
|
||||||
guint16 priority, weight, port;
|
guint16 priority, weight, port;
|
||||||
|
gint n;
|
||||||
|
|
||||||
GETSHORT (priority, *p);
|
if (!parse_short (&p, end, &priority) ||
|
||||||
GETSHORT (weight, *p);
|
!parse_short (&p, end, &weight) ||
|
||||||
GETSHORT (port, *p);
|
!parse_short (&p, end, &port))
|
||||||
*p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
|
return NULL;
|
||||||
|
|
||||||
|
n = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
|
||||||
|
if (n < 0)
|
||||||
|
return NULL;
|
||||||
|
*p += n;
|
||||||
|
|
||||||
return g_variant_new ("(qqqs)",
|
return g_variant_new ("(qqqs)",
|
||||||
priority,
|
priority,
|
||||||
@ -1017,20 +1046,29 @@ parse_res_srv (guchar *answer,
|
|||||||
static GVariant *
|
static GVariant *
|
||||||
parse_res_soa (guchar *answer,
|
parse_res_soa (guchar *answer,
|
||||||
guchar *end,
|
guchar *end,
|
||||||
guchar **p)
|
guchar *p)
|
||||||
{
|
{
|
||||||
gchar mnamebuf[1024];
|
gchar mnamebuf[1024];
|
||||||
gchar rnamebuf[1024];
|
gchar rnamebuf[1024];
|
||||||
guint32 serial, refresh, retry, expire, ttl;
|
guint32 serial, refresh, retry, expire, ttl;
|
||||||
|
gint n;
|
||||||
|
|
||||||
*p += dn_expand (answer, end, *p, mnamebuf, sizeof (mnamebuf));
|
n = dn_expand (answer, end, p, mnamebuf, sizeof (mnamebuf));
|
||||||
*p += dn_expand (answer, end, *p, rnamebuf, sizeof (rnamebuf));
|
if (n < 0)
|
||||||
|
return NULL;
|
||||||
|
p += n;
|
||||||
|
|
||||||
GETLONG (serial, *p);
|
n = dn_expand (answer, end, p, rnamebuf, sizeof (rnamebuf));
|
||||||
GETLONG (refresh, *p);
|
if (n < 0)
|
||||||
GETLONG (retry, *p);
|
return NULL;
|
||||||
GETLONG (expire, *p);
|
p += n;
|
||||||
GETLONG (ttl, *p);
|
|
||||||
|
if (!parse_long (&p, end, &serial) ||
|
||||||
|
!parse_long (&p, end, &refresh) ||
|
||||||
|
!parse_long (&p, end, &retry) ||
|
||||||
|
!parse_long (&p, end, &expire) ||
|
||||||
|
!parse_long (&p, end, &ttl))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
return g_variant_new ("(ssuuuuu)",
|
return g_variant_new ("(ssuuuuu)",
|
||||||
mnamebuf,
|
mnamebuf,
|
||||||
@ -1045,11 +1083,14 @@ parse_res_soa (guchar *answer,
|
|||||||
static GVariant *
|
static GVariant *
|
||||||
parse_res_ns (guchar *answer,
|
parse_res_ns (guchar *answer,
|
||||||
guchar *end,
|
guchar *end,
|
||||||
guchar **p)
|
guchar *p)
|
||||||
{
|
{
|
||||||
gchar namebuf[1024];
|
gchar namebuf[1024];
|
||||||
|
gint n;
|
||||||
|
|
||||||
*p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
|
n = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
|
||||||
|
if (n < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
return g_variant_new ("(s)", namebuf);
|
return g_variant_new ("(s)", namebuf);
|
||||||
}
|
}
|
||||||
@ -1057,14 +1098,19 @@ parse_res_ns (guchar *answer,
|
|||||||
static GVariant *
|
static GVariant *
|
||||||
parse_res_mx (guchar *answer,
|
parse_res_mx (guchar *answer,
|
||||||
guchar *end,
|
guchar *end,
|
||||||
guchar **p)
|
guchar *p)
|
||||||
{
|
{
|
||||||
gchar namebuf[1024];
|
gchar namebuf[1024];
|
||||||
guint16 preference;
|
guint16 preference;
|
||||||
|
gint n;
|
||||||
|
|
||||||
GETSHORT (preference, *p);
|
if (!parse_short (&p, end, &preference))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
*p += dn_expand (answer, end, *p, namebuf, sizeof (namebuf));
|
n = dn_expand (answer, end, p, namebuf, sizeof (namebuf));
|
||||||
|
if (n < 0)
|
||||||
|
return NULL;
|
||||||
|
p += n;
|
||||||
|
|
||||||
return g_variant_new ("(qs)",
|
return g_variant_new ("(qs)",
|
||||||
preference,
|
preference,
|
||||||
@ -1074,24 +1120,22 @@ parse_res_mx (guchar *answer,
|
|||||||
static GVariant *
|
static GVariant *
|
||||||
parse_res_txt (guchar *answer,
|
parse_res_txt (guchar *answer,
|
||||||
guchar *end,
|
guchar *end,
|
||||||
guchar **p)
|
guchar *p)
|
||||||
{
|
{
|
||||||
GVariant *record;
|
GVariant *record;
|
||||||
GPtrArray *array;
|
GPtrArray *array;
|
||||||
guchar *at = *p;
|
|
||||||
gsize len;
|
gsize len;
|
||||||
|
|
||||||
array = g_ptr_array_new_with_free_func (g_free);
|
array = g_ptr_array_new_with_free_func (g_free);
|
||||||
while (at < end)
|
while (p < end)
|
||||||
{
|
{
|
||||||
len = *(at++);
|
len = *(p++);
|
||||||
if (len > at - end)
|
if (len > p - end)
|
||||||
break;
|
break;
|
||||||
g_ptr_array_add (array, g_strndup ((gchar *)at, len));
|
g_ptr_array_add (array, g_strndup ((gchar *)p, len));
|
||||||
at += len;
|
p += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
*p = at;
|
|
||||||
record = g_variant_new ("(@as)",
|
record = g_variant_new ("(@as)",
|
||||||
g_variant_new_strv ((const gchar **)array->pdata, array->len));
|
g_variant_new_strv ((const gchar **)array->pdata, array->len));
|
||||||
g_ptr_array_free (array, TRUE);
|
g_ptr_array_free (array, TRUE);
|
||||||
@ -1127,13 +1171,13 @@ _g_resolver_records_from_res_query (const gchar *rrname,
|
|||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
gint count;
|
gint count;
|
||||||
gchar namebuf[1024];
|
|
||||||
guchar *end, *p;
|
guchar *end, *p;
|
||||||
guint16 type, qclass, rdlength;
|
guint16 type, qclass, rdlength;
|
||||||
guint32 ttl;
|
guint32 ttl;
|
||||||
HEADER *header;
|
HEADER *header;
|
||||||
GList *records;
|
GList *records;
|
||||||
GVariant *record;
|
GVariant *record;
|
||||||
|
gint n, i;
|
||||||
|
|
||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
{
|
{
|
||||||
@ -1168,48 +1212,61 @@ _g_resolver_records_from_res_query (const gchar *rrname,
|
|||||||
|
|
||||||
/* Skip query */
|
/* Skip query */
|
||||||
count = ntohs (header->qdcount);
|
count = ntohs (header->qdcount);
|
||||||
while (count-- && p < end)
|
for (i = 0; i < count && p < end; i++)
|
||||||
{
|
{
|
||||||
p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
|
n = dn_skipname (p, end);
|
||||||
|
if (n < 0)
|
||||||
|
break;
|
||||||
|
p += n;
|
||||||
p += 4;
|
p += 4;
|
||||||
|
}
|
||||||
|
|
||||||
/* To silence gcc warnings */
|
/* Incomplete response */
|
||||||
namebuf[0] = namebuf[1];
|
if (i < count)
|
||||||
|
{
|
||||||
|
g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
|
||||||
|
_("Incomplete data received for '%s'"), rrname);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read answers */
|
/* Read answers */
|
||||||
count = ntohs (header->ancount);
|
count = ntohs (header->ancount);
|
||||||
while (count-- && p < end)
|
for (i = 0; i < count && p < end; i++)
|
||||||
{
|
{
|
||||||
p += dn_expand (answer, end, p, namebuf, sizeof (namebuf));
|
n = dn_skipname (p, end);
|
||||||
GETSHORT (type, p);
|
if (n < 0)
|
||||||
GETSHORT (qclass, p);
|
break;
|
||||||
GETLONG (ttl, p);
|
p += n;
|
||||||
|
|
||||||
|
if (!parse_short (&p, end, &type) ||
|
||||||
|
!parse_short (&p, end, &qclass) ||
|
||||||
|
!parse_long (&p, end, &ttl) ||
|
||||||
|
!parse_short (&p, end, &rdlength))
|
||||||
|
break;
|
||||||
|
|
||||||
ttl = ttl; /* To avoid -Wunused-but-set-variable */
|
ttl = ttl; /* To avoid -Wunused-but-set-variable */
|
||||||
GETSHORT (rdlength, p);
|
|
||||||
|
|
||||||
if (type != rrtype || qclass != C_IN)
|
if (p + rdlength > end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (type == rrtype && qclass == C_IN)
|
||||||
{
|
{
|
||||||
p += rdlength;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (rrtype)
|
switch (rrtype)
|
||||||
{
|
{
|
||||||
case T_SRV:
|
case T_SRV:
|
||||||
record = parse_res_srv (answer, end, &p);
|
record = parse_res_srv (answer, end, p);
|
||||||
break;
|
break;
|
||||||
case T_MX:
|
case T_MX:
|
||||||
record = parse_res_mx (answer, end, &p);
|
record = parse_res_mx (answer, end, p);
|
||||||
break;
|
break;
|
||||||
case T_SOA:
|
case T_SOA:
|
||||||
record = parse_res_soa (answer, end, &p);
|
record = parse_res_soa (answer, end, p);
|
||||||
break;
|
break;
|
||||||
case T_NS:
|
case T_NS:
|
||||||
record = parse_res_ns (answer, end, &p);
|
record = parse_res_ns (answer, end, p);
|
||||||
break;
|
break;
|
||||||
case T_TXT:
|
case T_TXT:
|
||||||
record = parse_res_txt (answer, p + rdlength, &p);
|
record = parse_res_txt (answer, p + rdlength, p);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
g_warn_if_reached ();
|
g_warn_if_reached ();
|
||||||
@ -1221,6 +1278,18 @@ _g_resolver_records_from_res_query (const gchar *rrname,
|
|||||||
records = g_list_prepend (records, record);
|
records = g_list_prepend (records, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p += rdlength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Somehow got a truncated response */
|
||||||
|
if (i < count)
|
||||||
|
{
|
||||||
|
g_list_free_full (records, (GDestroyNotify)g_variant_unref);
|
||||||
|
g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_TEMPORARY_FAILURE,
|
||||||
|
_("Incomplete data received for '%s'"), rrname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return records;
|
return records;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user