* @param udp_payload_length length of udp_payload
* @param off pointer to the offset of the name to parse in the udp_payload (to be
* incremented by the size of the name)
+ * @param depth current depth of our recursion (to prevent stack overflow)
* @return name as 0-terminated C string on success, NULL if the payload is malformed
*/
static char *
parse_name (const char *udp_payload,
size_t udp_payload_length,
- size_t *off)
+ size_t *off,
+ unsigned int depth)
{
const uint8_t *input = (const uint8_t *) udp_payload;
char *ret;
}
else if ((64 | 128) == (len & (64 | 128)) )
{
+ if (depth > 32)
+ goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
/* pointer to string */
if (*off + 1 > udp_payload_length)
goto error;
xoff = ((len - (64 | 128)) << 8) + input[*off+1];
xstr = parse_name (udp_payload,
udp_payload_length,
- &xoff);
+ &xoff,
+ depth + 1);
+ if (NULL == xstr)
+ goto error;
GNUNET_asprintf (&tmp,
"%s%s.",
ret,
GNUNET_free (ret);
GNUNET_free (xstr);
ret = tmp;
+ if (strlen (ret) > udp_payload_length)
+ goto error; /* we are looping (building an infinite string) */
*off += 2;
/* pointers always terminate names */
break;
name = parse_name (udp_payload,
udp_payload_length,
- off);
+ off, 0);
if (NULL == name)
return GNUNET_SYSERR;
q->name = name;
name = parse_name (udp_payload,
udp_payload_length,
- off);
+ off, 0);
if (NULL == name)
return GNUNET_SYSERR;
r->name = name;
old_off = *off;
r->data.hostname = parse_name (udp_payload,
udp_payload_length,
- off);
+ off, 0);
if ( (NULL == r->data.hostname) ||
(old_off + r->data_len != *off) )
return GNUNET_SYSERR;
r->data.soa = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_SoaRecord));
r->data.soa->mname = parse_name (udp_payload,
udp_payload_length,
- off);
+ off, 0);
r->data.soa->rname = parse_name (udp_payload,
udp_payload_length,
- off);
+ off, 0);
if ( (NULL == r->data.soa->mname) ||
(NULL == r->data.soa->rname) ||
(*off + sizeof (soa) > udp_payload_length) )
r->data.mx->preference = ntohs (mxpref);
r->data.mx->mxhost = parse_name (udp_payload,
udp_payload_length,
- off);
+ off, 0);
if (old_off + r->data_len != *off)
return GNUNET_SYSERR;
return GNUNET_OK;