/*
This file is part of GNUnet
(C) 2010-2013 Christian Grothoff (and other contributing authors)
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation; either version 2, or (at your
option) any later version.
GNUnet 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
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNUnet; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/**
* @file dns/dnsparser.c
* @brief helper library to parse DNS packets.
* @author Philipp Toelke
* @author Christian Grothoff
*/
#include "platform.h"
#include <idna.h>
#if WINDOWS
#include <idn-free.h>
#endif
#include "gnunet_util_lib.h"
#include "gnunet_dnsparser_lib.h"
#include "gnunet_tun_lib.h"
/**
* Check if a label in UTF-8 format can be coded into valid IDNA.
* This can fail if the ASCII-conversion becomes longer than 63 characters.
*
* @param label label to check (UTF-8 string)
* @return #GNUNET_OK if the label can be converted to IDNA,
* #GNUNET_SYSERR if the label is not valid for DNS names
*/
int
GNUNET_DNSPARSER_check_label (const char *label)
{
char *output;
size_t slen;
if (NULL != strchr (label, '.'))
return GNUNET_SYSERR; /* not a label! Did you mean GNUNET_DNSPARSER_check_name? */
if (IDNA_SUCCESS !=
idna_to_ascii_8z (label, &output, IDNA_ALLOW_UNASSIGNED))
return GNUNET_SYSERR;
slen = strlen (output);
#if WINDOWS
idn_free (output);
#else
free (output);
#endif
return (slen > 63) ? GNUNET_SYSERR : GNUNET_OK;
}
/**
* Check if a label in UTF-8 format can be coded into valid IDNA.
* This can fail if the ASCII-conversion becomes longer than 253 characters.
*
* @param name name to check (UTF-8 string)
* @return #GNUNET_OK if the label can be converted to IDNA,
* #GNUNET_SYSERR if the label is not valid for DNS names
*/
int
GNUNET_DNSPARSER_check_name (const char *name)
{
char *ldup;
char *output;
size_t slen;
char *tok;
ldup = GNUNET_strdup (name);
for (tok = strtok (ldup, "."); NULL != tok; tok = strtok (NULL, "."))
if (GNUNET_OK !=
GNUNET_DNSPARSER_check_label (tok))
{
GNUNET_free (ldup);
return GNUNET_SYSERR;
}
GNUNET_free (ldup);
if (IDNA_SUCCESS !=
idna_to_ascii_8z (name, &output, IDNA_ALLOW_UNASSIGNED))
return GNUNET_SYSERR;
slen = strlen (output);
#if WINDOWS
idn_free (output);
#else
free (output);
#endif
return (slen > 253) ? GNUNET_SYSERR : GNUNET_OK;
}
/**
* Parse name inside of a DNS query or record.
*
* @param udp_payload entire UDP payload
* @param udp_payload_length length of @a 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,
unsigned int depth)
{
const uint8_t *input = (const uint8_t *) udp_payload;
char *ret;
char *tmp;
char *xstr;
uint8_t len;
size_t xoff;
char *utf8;
Idna_rc rc;
ret = GNUNET_strdup ("");
while (1)
{
if (*off >= udp_payload_length)
goto error;
len = input[*off];
if (0 == len)
{
(*off)++;
break;
}
if (len < 64)
{
if (*off + 1 + len > udp_payload_length)
goto error;
GNUNET_asprintf (&tmp,
"%.*s",
(int) len,
&udp_payload[*off + 1]);
if (IDNA_SUCCESS !=
(rc = idna_to_unicode_8z8z (tmp, &utf8, IDNA_ALLOW_UNASSIGNED)))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
_("Failed to convert DNS IDNA name `%s' to UTF-8: %s\n"),
tmp,
idna_strerror (rc));
GNUNET_free (tmp);
GNUNET_asprintf (&tmp,
"%s%.*s.",
ret,
(int) len,
&udp_payload[*off + 1]);
}
else
{
GNUNET_free (tmp);
GNUNET_asprintf (&tmp,
"%s%s.",
ret,
utf8);
#if WINDOWS
idn_free (utf8);
#else
free (utf8);
#endif
}
GNUNET_free (ret);
ret = tmp;
*off += 1 + len;
}
else if ((64 | 128) == (len & (64 | 128)) )
{