rps tests: cosmetics
[oweals/gnunet.git] / src / dns / dnsparser.c
index 8b8cd22fd3b6bcc2fb683c8ac955fb8f4934fe3c..1fe6f595fef6151beb5432040b2437c5d0a92679 100644 (file)
@@ -1,10 +1,10 @@
 /*
       This file is part of GNUnet
 /*
       This file is part of GNUnet
-      (C) 2010, 2011, 2012 Christian Grothoff (and other contributing authors)
+      Copyright (C) 2010-2014 GNUnet e.V.
 
       GNUnet is free software; you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published
 
       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
+      by the Free Software Foundation; either version 3, or (at your
       option) any later version.
 
       GNUnet is distributed in the hope that it will be useful, but
       option) any later version.
 
       GNUnet is distributed in the hope that it will be useful, but
 
       You should have received a copy of the GNU General Public License
       along with GNUnet; see the file COPYING.  If not, write to the
 
       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.
+      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+      Boston, MA 02110-1301, USA.
  */
 
 /**
  * @file dns/dnsparser.c
  */
 
 /**
  * @file dns/dnsparser.c
- * @brief helper library to parse DNS packets. 
+ * @brief helper library to parse DNS packets.
  * @author Philipp Toelke
  * @author Christian Grothoff
  */
 #include "platform.h"
  * @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_util_lib.h"
 #include "gnunet_dnsparser_lib.h"
+#include "gnunet_tun_lib.h"
 
 
 
 
-GNUNET_NETWORK_STRUCT_BEGIN
-
-/* FIXME: replace this one with the one from tcpip_tun.h!? */
 /**
 /**
- * Head of a any DNS message.
+ * 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
  */
  */
-struct GNUNET_TUN_DnsHeader
+int
+GNUNET_DNSPARSER_check_label (const char *label)
 {
 {
-  /**
-   * Request/response ID. (NBO)
-   */
-  uint16_t id GNUNET_PACKED;
-
-  /**
-   * Flags for the operation.
-   */
-  struct GNUNET_DNSPARSER_Flags flags; 
-
-  /**
-   * number of questions (NBO)
-   */
-  uint16_t query_count GNUNET_PACKED;
-
-  /**
-   * number of answers (NBO)
-   */
-  uint16_t answer_rcount GNUNET_PACKED;
-
-  /**
-   * number of authority-records (NBO)
-   */
-  uint16_t authority_rcount GNUNET_PACKED;
-
-  /**
-   * number of additional records (NBO)
-   */
-  uint16_t additional_rcount GNUNET_PACKED;
-};
+  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;
+}
 
 
 /**
 
 
 /**
- * DNS query prefix.
+ * 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
  */
  */
-struct query_line
+int
+GNUNET_DNSPARSER_check_name (const char *name)
 {
 {
-  /**
-   * Desired type (GNUNET_DNSPARSER_TYPE_XXX). (NBO)
-   */
-  uint16_t type GNUNET_PACKED;
+  char *ldup;
+  char *output;
+  size_t slen;
+  char *tok;
 
 
-  /**
-   * Desired class (usually GNUNET_DNSPARSER_CLASS_INTERNET). (NBO)
-   */
-  uint16_t class GNUNET_PACKED;
-};
+  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;
+}
 
 
 /**
 
 
 /**
- * General DNS record prefix.
+ * Free SOA information record.
+ *
+ * @param soa record to free
  */
  */
-struct record_line
+void
+GNUNET_DNSPARSER_free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
 {
 {
-  /**
-   * Record type (GNUNET_DNSPARSER_TYPE_XXX). (NBO)
-   */
-  uint16_t type GNUNET_PACKED;
-
-  /**
-   * Record class (usually GNUNET_DNSPARSER_CLASS_INTERNET). (NBO)
-   */
-  uint16_t class GNUNET_PACKED;
-
-  /**
-   * Expiration for the record (in seconds). (NBO)
-   */
-  uint32_t ttl GNUNET_PACKED;
-
-  /**
-   * Number of bytes of data that follow. (NBO)
-   */
-  uint16_t data_len GNUNET_PACKED;
-};
+  if (NULL == soa)
+    return;
+  GNUNET_free_non_null (soa->mname);
+  GNUNET_free_non_null (soa->rname);
+  GNUNET_free (soa);
+}
 
 
 /**
 
 
 /**
- * Payload of DNS SOA record (header).
+ * Free CERT information record.
+ *
+ * @param cert record to free
  */
  */
-struct soa_data
+void
+GNUNET_DNSPARSER_free_cert (struct GNUNET_DNSPARSER_CertRecord *cert)
 {
 {
-  /**
-   * The version number of the original copy of the zone.   (NBO)
-   */
-  uint32_t serial GNUNET_PACKED;
-  
-  /**
-   * Time interval before the zone should be refreshed. (NBO)
-   */
-  uint32_t refresh GNUNET_PACKED;
-  
-  /**
-   * Time interval that should elapse before a failed refresh should
-   * be retried. (NBO)
-   */
-  uint32_t retry GNUNET_PACKED;
-  /**
-   * Time value that specifies the upper limit on the time interval
-   * that can elapse before the zone is no longer authoritative. (NBO)
-   */
-  uint32_t expire GNUNET_PACKED;
-
-  /**
-   * The bit minimum TTL field that should be exported with any RR
-   * from this zone. (NBO)
-   */
-  uint32_t minimum GNUNET_PACKED;
-};
+  if (NULL == cert)
+    return;
+  GNUNET_free_non_null (cert->certificate_data);
+  GNUNET_free (cert);
+}
 
 
 /**
 
 
 /**
- * Payload of DNS SRV record (header).
+ * Free SRV information record.
+ *
+ * @param srv record to free
  */
  */
-struct srv_data
+void
+GNUNET_DNSPARSER_free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
 {
 {
+  if (NULL == srv)
+    return;
+  GNUNET_free_non_null (srv->target);
+  GNUNET_free (srv);
+}
 
 
-  /**
-   * Preference for this entry (lower value is higher preference).  Clients
-   * will contact hosts from the lowest-priority group first and fall back
-   * to higher priorities if the low-priority entries are unavailable. (NBO)
-   */
-  uint16_t prio GNUNET_PACKED;
-
-  /**
-   * Relative weight for records with the same priority.  Clients will use
-   * the hosts of the same (lowest) priority with a probability proportional
-   * to the weight given. (NBO)
-   */
-  uint16_t weight GNUNET_PACKED;
 
 
-  /**
-   * TCP or UDP port of the service. (NBO)
-   */
-  uint16_t port GNUNET_PACKED;
+/**
+ * Free MX information record.
+ *
+ * @param mx record to free
+ */
+void
+GNUNET_DNSPARSER_free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
+{
+  if (NULL == mx)
+    return;
+  GNUNET_free_non_null (mx->mxhost);
+  GNUNET_free (mx);
+}
 
 
-  /* followed by 'target' name */
-};
 
 
-GNUNET_NETWORK_STRUCT_END
+/**
+ * Free the given DNS record.
+ *
+ * @param r record to free
+ */
+void
+GNUNET_DNSPARSER_free_record (struct GNUNET_DNSPARSER_Record *r)
+{
+  GNUNET_free_non_null (r->name);
+  switch (r->type)
+  {
+  case GNUNET_DNSPARSER_TYPE_MX:
+    GNUNET_DNSPARSER_free_mx (r->data.mx);
+    break;
+  case GNUNET_DNSPARSER_TYPE_SOA:
+    GNUNET_DNSPARSER_free_soa (r->data.soa);
+    break;
+  case GNUNET_DNSPARSER_TYPE_SRV:
+    GNUNET_DNSPARSER_free_srv (r->data.srv);
+    break;
+  case GNUNET_DNSPARSER_TYPE_CERT:
+    GNUNET_DNSPARSER_free_cert (r->data.cert);
+    break;
+  case GNUNET_DNSPARSER_TYPE_NS:
+  case GNUNET_DNSPARSER_TYPE_CNAME:
+  case GNUNET_DNSPARSER_TYPE_PTR:
+    GNUNET_free_non_null (r->data.hostname);
+    break;
+  default:
+    GNUNET_free_non_null (r->data.raw.data);
+    break;
+  }
+}
 
 
 /**
  * Parse name inside of a DNS query or record.
  *
  * @param udp_payload entire UDP payload
 
 
 /**
  * Parse name inside of a DNS query or record.
  *
  * @param udp_payload entire UDP payload
- * @param udp_payload_length length of 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)
  * @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)
@@ -201,12 +219,17 @@ parse_name (const char *udp_payload,
   char *xstr;
   uint8_t len;
   size_t xoff;
   char *xstr;
   uint8_t len;
   size_t xoff;
-  
+  char *utf8;
+  Idna_rc rc;
+
   ret = GNUNET_strdup ("");
   while (1)
   {
     if (*off >= udp_payload_length)
   ret = GNUNET_strdup ("");
   while (1)
   {
     if (*off >= udp_payload_length)
+    {
+      GNUNET_break_op (0);
       goto error;
       goto error;
+    }
     len = input[*off];
     if (0 == len)
     {
     len = input[*off];
     if (0 == len)
     {
@@ -216,12 +239,41 @@ parse_name (const char *udp_payload,
     if (len < 64)
     {
       if (*off + 1 + len > udp_payload_length)
     if (len < 64)
     {
       if (*off + 1 + len > udp_payload_length)
+      {
+       GNUNET_break_op (0);
        goto error;
        goto error;
+      }
       GNUNET_asprintf (&tmp,
       GNUNET_asprintf (&tmp,
-                      "%s%.*s.",
-                      ret,
+                      "%.*s",
                       (int) len,
                       &udp_payload[*off + 1]);
                       (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;
       GNUNET_free (ret);
       ret = tmp;
       *off += 1 + len;
@@ -229,17 +281,26 @@ parse_name (const char *udp_payload,
     else if ((64 | 128) == (len & (64 | 128)) )
     {
       if (depth > 32)
     else if ((64 | 128) == (len & (64 | 128)) )
     {
       if (depth > 32)
+      {
+       GNUNET_break_op (0);
        goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
        goto error; /* hard bound on stack to prevent "infinite" recursion, disallow! */
+      }
       /* pointer to string */
       if (*off + 1 > udp_payload_length)
       /* pointer to string */
       if (*off + 1 > udp_payload_length)
+      {
+       GNUNET_break_op (0);
        goto error;
        goto error;
+      }
       xoff = ((len - (64 | 128)) << 8) + input[*off+1];
       xstr = parse_name (udp_payload,
                         udp_payload_length,
                         &xoff,
                         depth + 1);
       if (NULL == xstr)
       xoff = ((len - (64 | 128)) << 8) + input[*off+1];
       xstr = parse_name (udp_payload,
                         udp_payload_length,
                         &xoff,
                         depth + 1);
       if (NULL == xstr)
+      {
+       GNUNET_break_op (0);
        goto error;
        goto error;
+      }
       GNUNET_asprintf (&tmp,
                       "%s%s.",
                       ret,
       GNUNET_asprintf (&tmp,
                       "%s%s.",
                       ret,
@@ -248,205 +309,361 @@ parse_name (const char *udp_payload,
       GNUNET_free (xstr);
       ret = tmp;
       if (strlen (ret) > udp_payload_length)
       GNUNET_free (xstr);
       ret = tmp;
       if (strlen (ret) > udp_payload_length)
+      {
+       GNUNET_break_op (0);
        goto error; /* we are looping (building an infinite string) */
        goto error; /* we are looping (building an infinite string) */
+      }
       *off += 2;
       /* pointers always terminate names */
       break;
       *off += 2;
       /* pointers always terminate names */
       break;
-    } 
+    }
     else
     {
       /* neither pointer nor inline string, not supported... */
     else
     {
       /* neither pointer nor inline string, not supported... */
+      GNUNET_break_op (0);
       goto error;
     }
   }
   if (0 < strlen(ret))
     ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
   return ret;
       goto error;
     }
   }
   if (0 < strlen(ret))
     ret[strlen(ret)-1] = '\0'; /* eat tailing '.' */
   return ret;
- error:  
+ error:
+  GNUNET_break_op (0);
   GNUNET_free (ret);
   return NULL;
 }
 
 
   GNUNET_free (ret);
   return NULL;
 }
 
 
+/**
+ * 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)
+ * @return name as 0-terminated C string on success, NULL if the payload is malformed
+ */
+char *
+GNUNET_DNSPARSER_parse_name (const char *udp_payload,
+                            size_t udp_payload_length,
+                            size_t *off)
+{
+  return parse_name (udp_payload, udp_payload_length, off, 0);
+}
+
+
 /**
  * Parse a DNS query entry.
  *
  * @param udp_payload entire UDP payload
 /**
  * Parse a DNS query entry.
  *
  * @param udp_payload entire UDP payload
- * @param udp_payload_length length of udp_payload
+ * @param udp_payload_length length of @a udp_payload
  * @param off pointer to the offset of the query to parse in the udp_payload (to be
  *                    incremented by the size of the query)
  * @param q where to write the query information
  * @param off pointer to the offset of the query to parse in the udp_payload (to be
  *                    incremented by the size of the query)
  * @param q where to write the query information
- * @return GNUNET_OK on success, GNUNET_SYSERR if the query is malformed
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the query is malformed
  */
  */
-static int
-parse_query (const char *udp_payload,
-            size_t udp_payload_length,
-            size_t *off,
-            struct GNUNET_DNSPARSER_Query *q)
+int
+GNUNET_DNSPARSER_parse_query (const char *udp_payload,
+                             size_t udp_payload_length,
+                             size_t *off,
+                             struct GNUNET_DNSPARSER_Query *q)
 {
   char *name;
 {
   char *name;
-  struct query_line ql;
+  struct GNUNET_TUN_DnsQueryLine ql;
 
 
-  name = parse_name (udp_payload, 
-                    udp_payload_length,
-                    off, 0);
+  name = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                     udp_payload_length,
+                                     off);
   if (NULL == name)
   if (NULL == name)
+  {
+    GNUNET_break_op (0);
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
+  }
   q->name = name;
   q->name = name;
-  if (*off + sizeof (struct query_line) > udp_payload_length)
+  if (*off + sizeof (struct GNUNET_TUN_DnsQueryLine) > udp_payload_length)
+  {
+    GNUNET_break_op (0);
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
-  memcpy (&ql, &udp_payload[*off], sizeof (ql));
+  }
+  GNUNET_memcpy (&ql, &udp_payload[*off], sizeof (ql));
   *off += sizeof (ql);
   q->type = ntohs (ql.type);
   *off += sizeof (ql);
   q->type = ntohs (ql.type);
-  q->class = ntohs (ql.class);
+  q->dns_traffic_class = ntohs (ql.dns_traffic_class);
   return GNUNET_OK;
 }
 
 
   return GNUNET_OK;
 }
 
 
+/**
+ * Parse a DNS SOA record.
+ *
+ * @param udp_payload reference to UDP packet
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the query to parse in the SOA record (to be
+ *                    incremented by the size of the record), unchanged on error
+ * @return the parsed SOA record, NULL on error
+ */
+struct GNUNET_DNSPARSER_SoaRecord *
+GNUNET_DNSPARSER_parse_soa (const char *udp_payload,
+                           size_t udp_payload_length,
+                           size_t *off)
+{
+  struct GNUNET_DNSPARSER_SoaRecord *soa;
+  struct GNUNET_TUN_DnsSoaRecord soa_bin;
+  size_t old_off;
+
+  old_off = *off;
+  soa = GNUNET_new (struct GNUNET_DNSPARSER_SoaRecord);
+  soa->mname = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                           udp_payload_length,
+                                           off);
+  soa->rname = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                           udp_payload_length,
+                                           off);
+  if ( (NULL == soa->mname) ||
+       (NULL == soa->rname) ||
+       (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > udp_payload_length) )
+  {
+    GNUNET_break_op (0);
+    GNUNET_DNSPARSER_free_soa (soa);
+    *off = old_off;
+    return NULL;
+  }
+  GNUNET_memcpy (&soa_bin,
+         &udp_payload[*off],
+         sizeof (struct GNUNET_TUN_DnsSoaRecord));
+  soa->serial = ntohl (soa_bin.serial);
+  soa->refresh = ntohl (soa_bin.refresh);
+  soa->retry = ntohl (soa_bin.retry);
+  soa->expire = ntohl (soa_bin.expire);
+  soa->minimum_ttl = ntohl (soa_bin.minimum);
+  (*off) += sizeof (struct GNUNET_TUN_DnsSoaRecord);
+  return soa;
+}
+
+
+/**
+ * Parse a DNS MX record.
+ *
+ * @param udp_payload reference to UDP packet
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the query to parse in the MX record (to be
+ *                    incremented by the size of the record), unchanged on error
+ * @return the parsed MX record, NULL on error
+ */
+struct GNUNET_DNSPARSER_MxRecord *
+GNUNET_DNSPARSER_parse_mx (const char *udp_payload,
+                          size_t udp_payload_length,
+                          size_t *off)
+{
+  struct GNUNET_DNSPARSER_MxRecord *mx;
+  uint16_t mxpref;
+  size_t old_off;
+
+  old_off = *off;
+  if (*off + sizeof (uint16_t) > udp_payload_length)
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  GNUNET_memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));
+  (*off) += sizeof (uint16_t);
+  mx = GNUNET_new (struct GNUNET_DNSPARSER_MxRecord);
+  mx->preference = ntohs (mxpref);
+  mx->mxhost = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                           udp_payload_length,
+                                           off);
+  if (NULL == mx->mxhost)
+  {
+    GNUNET_break_op (0);
+    GNUNET_DNSPARSER_free_mx (mx);
+    *off = old_off;
+    return NULL;
+  }
+  return mx;
+}
+
+
+/**
+ * Parse a DNS SRV record.
+ *
+ * @param udp_payload reference to UDP packet
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the query to parse in the SRV record (to be
+ *                    incremented by the size of the record), unchanged on error
+ * @return the parsed SRV record, NULL on error
+ */
+struct GNUNET_DNSPARSER_SrvRecord *
+GNUNET_DNSPARSER_parse_srv (const char *udp_payload,
+                           size_t udp_payload_length,
+                           size_t *off)
+{
+  struct GNUNET_DNSPARSER_SrvRecord *srv;
+  struct GNUNET_TUN_DnsSrvRecord srv_bin;
+  size_t old_off;
+
+  old_off = *off;
+  if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > udp_payload_length)
+    return NULL;
+  GNUNET_memcpy (&srv_bin,
+         &udp_payload[*off],
+         sizeof (struct GNUNET_TUN_DnsSrvRecord));
+  (*off) += sizeof (struct GNUNET_TUN_DnsSrvRecord);
+  srv = GNUNET_new (struct GNUNET_DNSPARSER_SrvRecord);
+  srv->priority = ntohs (srv_bin.prio);
+  srv->weight = ntohs (srv_bin.weight);
+  srv->port = ntohs (srv_bin.port);
+  srv->target = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                            udp_payload_length,
+                                            off);
+  if (NULL == srv->target)
+  {
+    GNUNET_DNSPARSER_free_srv (srv);
+    *off = old_off;
+    return NULL;
+  }
+  return srv;
+}
+
+
+/**
+ * Parse a DNS CERT record.
+ *
+ * @param udp_payload reference to UDP packet
+ * @param udp_payload_length length of @a udp_payload
+ * @param off pointer to the offset of the query to parse in the CERT record (to be
+ *                    incremented by the size of the record), unchanged on error
+ * @return the parsed CERT record, NULL on error
+ */
+struct GNUNET_DNSPARSER_CertRecord *
+GNUNET_DNSPARSER_parse_cert (const char *udp_payload,
+                             size_t udp_payload_length,
+                             size_t *off)
+{
+  struct GNUNET_DNSPARSER_CertRecord *cert;
+  struct GNUNET_TUN_DnsCertRecord dcert;
+
+  if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) >= udp_payload_length)
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  GNUNET_memcpy (&dcert, &udp_payload[*off], sizeof (struct GNUNET_TUN_DnsCertRecord));
+  (*off) += sizeof (struct GNUNET_TUN_DnsCertRecord);
+  cert = GNUNET_new (struct GNUNET_DNSPARSER_CertRecord);
+  cert->cert_type = ntohs (dcert.cert_type);
+  cert->cert_tag = ntohs (dcert.cert_tag);
+  cert->algorithm = dcert.algorithm;
+  cert->certificate_size = udp_payload_length - (*off);
+  cert->certificate_data = GNUNET_malloc (cert->certificate_size);
+  GNUNET_memcpy (cert->certificate_data,
+          &udp_payload[*off],
+          cert->certificate_size);
+  (*off) += cert->certificate_size;
+  return cert;
+}
+
+
 /**
  * Parse a DNS record entry.
  *
  * @param udp_payload entire UDP payload
 /**
  * Parse a DNS record entry.
  *
  * @param udp_payload entire UDP payload
- * @param udp_payload_length length of udp_payload
+ * @param udp_payload_length length of @a udp_payload
  * @param off pointer to the offset of the record to parse in the udp_payload (to be
  *                    incremented by the size of the record)
  * @param r where to write the record information
  * @param off pointer to the offset of the record to parse in the udp_payload (to be
  *                    incremented by the size of the record)
  * @param r where to write the record information
- * @return GNUNET_OK on success, GNUNET_SYSERR if the record is malformed
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the record is malformed
  */
  */
-static int
-parse_record (const char *udp_payload,
-             size_t udp_payload_length,
-             size_t *off,
-             struct GNUNET_DNSPARSER_Record *r)
+int
+GNUNET_DNSPARSER_parse_record (const char *udp_payload,
+                              size_t udp_payload_length,
+                              size_t *off,
+                              struct GNUNET_DNSPARSER_Record *r)
 {
   char *name;
 {
   char *name;
-  struct record_line rl;
+  struct GNUNET_TUN_DnsRecordLine rl;
   size_t old_off;
   size_t old_off;
-  struct soa_data soa;
-  uint16_t mxpref;
   uint16_t data_len;
   uint16_t data_len;
-  struct srv_data srv;
-  char *ndup;
-  char *tok;
 
 
-  name = parse_name (udp_payload, 
-                    udp_payload_length,
-                    off, 0);
+  name = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                     udp_payload_length,
+                                     off);
   if (NULL == name)
   if (NULL == name)
+  {
+    GNUNET_break_op (0);
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
+  }
   r->name = name;
   r->name = name;
-  if (*off + sizeof (struct record_line) > udp_payload_length)
+  if (*off + sizeof (struct GNUNET_TUN_DnsRecordLine) > udp_payload_length)
+  {
+    GNUNET_break_op (0);
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
-  memcpy (&rl, &udp_payload[*off], sizeof (rl));
+  }
+  GNUNET_memcpy (&rl, &udp_payload[*off], sizeof (rl));
   (*off) += sizeof (rl);
   r->type = ntohs (rl.type);
   (*off) += sizeof (rl);
   r->type = ntohs (rl.type);
-  r->class = ntohs (rl.class);
+  r->dns_traffic_class = ntohs (rl.dns_traffic_class);
   r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
                                                                                        ntohl (rl.ttl)));
   data_len = ntohs (rl.data_len);
   if (*off + data_len > udp_payload_length)
   r->expiration_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
                                                                                        ntohl (rl.ttl)));
   data_len = ntohs (rl.data_len);
   if (*off + data_len > udp_payload_length)
+  {
+    GNUNET_break_op (0);
     return GNUNET_SYSERR;
     return GNUNET_SYSERR;
+  }
+  old_off = *off;
   switch (r->type)
   {
   case GNUNET_DNSPARSER_TYPE_NS:
   case GNUNET_DNSPARSER_TYPE_CNAME:
   case GNUNET_DNSPARSER_TYPE_PTR:
   switch (r->type)
   {
   case GNUNET_DNSPARSER_TYPE_NS:
   case GNUNET_DNSPARSER_TYPE_CNAME:
   case GNUNET_DNSPARSER_TYPE_PTR:
-    old_off = *off;
-    r->data.hostname = parse_name (udp_payload,
-                                  udp_payload_length,
-                                  off, 0);    
+    r->data.hostname = GNUNET_DNSPARSER_parse_name (udp_payload,
+                                                   udp_payload_length,
+                                                   off);
     if ( (NULL == r->data.hostname) ||
         (old_off + data_len != *off) )
       return GNUNET_SYSERR;
     return GNUNET_OK;
   case GNUNET_DNSPARSER_TYPE_SOA:
     if ( (NULL == r->data.hostname) ||
         (old_off + data_len != *off) )
       return GNUNET_SYSERR;
     return GNUNET_OK;
   case GNUNET_DNSPARSER_TYPE_SOA:
-    old_off = *off;
-    r->data.soa = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_SoaRecord));
-    r->data.soa->mname = parse_name (udp_payload,
-                                    udp_payload_length,
-                                    off, 0);
-    r->data.soa->rname = parse_name (udp_payload,
-                                    udp_payload_length,
-                                    off, 0);
-    if ( (NULL == r->data.soa->mname) ||
-        (NULL == r->data.soa->rname) ||
-        (*off + sizeof (struct soa_data) > udp_payload_length) )
-      return GNUNET_SYSERR;
-    memcpy (&soa, &udp_payload[*off], sizeof (struct soa_data));
-    r->data.soa->serial = ntohl (soa.serial);
-    r->data.soa->refresh = ntohl (soa.refresh);
-    r->data.soa->retry = ntohl (soa.retry);
-    r->data.soa->expire = ntohl (soa.expire);
-    r->data.soa->minimum_ttl = ntohl (soa.minimum);
-    (*off) += sizeof (struct soa_data);
-    if (old_off + data_len != *off) 
+    r->data.soa = GNUNET_DNSPARSER_parse_soa (udp_payload,
+                                             udp_payload_length,
+                                             off);
+    if ( (NULL == r->data.soa) ||
+        (old_off + data_len != *off) )
+    {
+      GNUNET_break_op (0);
       return GNUNET_SYSERR;
       return GNUNET_SYSERR;
+    }
     return GNUNET_OK;
   case GNUNET_DNSPARSER_TYPE_MX:
     return GNUNET_OK;
   case GNUNET_DNSPARSER_TYPE_MX:
-    old_off = *off;
-    if (*off + sizeof (uint16_t) > udp_payload_length)
-      return GNUNET_SYSERR;
-    memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));    
-    (*off) += sizeof (uint16_t);
-    r->data.mx = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_MxRecord));
-    r->data.mx->preference = ntohs (mxpref);
-    r->data.mx->mxhost = parse_name (udp_payload,
-                                    udp_payload_length,
-                                    off, 0);
-    if (old_off + data_len != *off) 
-      return GNUNET_SYSERR;
-    return GNUNET_OK;
-  case GNUNET_DNSPARSER_TYPE_SRV:
-    if ('_' != *r->name)
-      return GNUNET_SYSERR; /* all valid srv names must start with "_" */
-    if (NULL == strstr (r->name, "._"))
-      return GNUNET_SYSERR; /* necessary string from "._$PROTO" not present */
-    old_off = *off;
-    if (*off + sizeof (struct srv_data) > udp_payload_length)
-      return GNUNET_SYSERR;
-    memcpy (&srv, &udp_payload[*off], sizeof (struct srv_data));    
-    (*off) += sizeof (struct srv_data);
-    r->data.srv = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_SrvRecord));
-    r->data.srv->priority = ntohs (srv.prio);
-    r->data.srv->weight = ntohs (srv.weight);
-    r->data.srv->port = ntohs (srv.port);
-    /* parse 'data.hostname' into components, which are
-       "_$SERVICE._$PROTO.$DOMAIN_NAME" */
-    ndup = GNUNET_strdup (r->name);
-    tok = strtok (ndup, ".");
-    GNUNET_assert ('_' == *tok);
-    r->data.srv->service = GNUNET_strdup (&tok[1]);
-    tok = strtok (NULL, ".");
-    if ('_' != *tok)
+    r->data.mx = GNUNET_DNSPARSER_parse_mx (udp_payload,
+                                           udp_payload_length,
+                                           off);
+    if ( (NULL == r->data.mx) ||
+        (old_off + data_len != *off) )
     {
     {
-      GNUNET_free (r->data.srv);
-      GNUNET_free (ndup);
+      GNUNET_break_op (0);
       return GNUNET_SYSERR;
     }
       return GNUNET_SYSERR;
     }
-    r->data.srv->proto = GNUNET_strdup (&tok[1]);
-    tok = strtok (NULL, ".");
-    if (NULL == tok)
+    return GNUNET_OK;
+  case GNUNET_DNSPARSER_TYPE_SRV:
+    r->data.srv = GNUNET_DNSPARSER_parse_srv (udp_payload,
+                                             udp_payload_length,
+                                             off);
+    if ( (NULL == r->data.srv) ||
+        (old_off + data_len != *off) )
     {
     {
-      GNUNET_free (r->data.srv);
-      GNUNET_free (ndup);
+      GNUNET_break_op (0);
       return GNUNET_SYSERR;
     }
       return GNUNET_SYSERR;
     }
-    r->data.srv->domain_name = GNUNET_strdup (tok);
-    GNUNET_free (ndup);
-    r->data.srv->target = parse_name (udp_payload,
-                                     udp_payload_length,
-                                     off, 0);
-    if (old_off + data_len != *off) 
-      return GNUNET_SYSERR;
     return GNUNET_OK;
   default:
     r->data.raw.data = GNUNET_malloc (data_len);
     r->data.raw.data_len = data_len;
     return GNUNET_OK;
   default:
     r->data.raw.data = GNUNET_malloc (data_len);
     r->data.raw.data_len = data_len;
-    memcpy (r->data.raw.data, &udp_payload[*off], data_len);
+    GNUNET_memcpy (r->data.raw.data, &udp_payload[*off], data_len);
     break;
   }
   (*off) += data_len;
     break;
   }
   (*off) += data_len;
-  return GNUNET_OK;  
+  return GNUNET_OK;
 }
 
 
 }
 
 
@@ -455,7 +672,7 @@ parse_record (const char *udp_payload,
  * processing and manipulation.
  *
  * @param udp_payload wire-format of the DNS packet
  * processing and manipulation.
  *
  * @param udp_payload wire-format of the DNS packet
- * @param udp_payload_length number of bytes in udp_payload 
+ * @param udp_payload_length number of bytes in @a udp_payload
  * @return NULL on error, otherwise the parsed packet
  */
 struct GNUNET_DNSPARSER_Packet *
  * @return NULL on error, otherwise the parsed packet
  */
 struct GNUNET_DNSPARSER_Packet *
@@ -465,14 +682,14 @@ GNUNET_DNSPARSER_parse (const char *udp_payload,
   struct GNUNET_DNSPARSER_Packet *p;
   const struct GNUNET_TUN_DnsHeader *dns;
   size_t off;
   struct GNUNET_DNSPARSER_Packet *p;
   const struct GNUNET_TUN_DnsHeader *dns;
   size_t off;
-  unsigned int n;  
+  unsigned int n;
   unsigned int i;
 
   if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
     return NULL;
   dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
   off = sizeof (struct GNUNET_TUN_DnsHeader);
   unsigned int i;
 
   if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
     return NULL;
   dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
   off = sizeof (struct GNUNET_TUN_DnsHeader);
-  p = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_Packet));
+  p = GNUNET_new (struct GNUNET_DNSPARSER_Packet);
   p->flags = dns->flags;
   p->id = dns->id;
   n = ntohs (dns->query_count);
   p->flags = dns->flags;
   p->id = dns->id;
   n = ntohs (dns->query_count);
@@ -482,10 +699,10 @@ GNUNET_DNSPARSER_parse (const char *udp_payload,
     p->num_queries = n;
     for (i=0;i<n;i++)
       if (GNUNET_OK !=
     p->num_queries = n;
     for (i=0;i<n;i++)
       if (GNUNET_OK !=
-         parse_query (udp_payload,
-                      udp_payload_length,
-                      &off,
-                      &p->queries[i]))
+         GNUNET_DNSPARSER_parse_query (udp_payload,
+                                       udp_payload_length,
+                                       &off,
+                                       &p->queries[i]))
        goto error;
   }
   n = ntohs (dns->answer_rcount);
        goto error;
   }
   n = ntohs (dns->answer_rcount);
@@ -495,10 +712,10 @@ GNUNET_DNSPARSER_parse (const char *udp_payload,
     p->num_answers = n;
     for (i=0;i<n;i++)
       if (GNUNET_OK !=
     p->num_answers = n;
     for (i=0;i<n;i++)
       if (GNUNET_OK !=
-         parse_record (udp_payload,
-                       udp_payload_length,
-                       &off,
-                       &p->answers[i]))
+         GNUNET_DNSPARSER_parse_record (udp_payload,
+                                        udp_payload_length,
+                                        &off,
+                                        &p->answers[i]))
        goto error;
   }
   n = ntohs (dns->authority_rcount);
        goto error;
   }
   n = ntohs (dns->authority_rcount);
@@ -508,11 +725,11 @@ GNUNET_DNSPARSER_parse (const char *udp_payload,
     p->num_authority_records = n;
     for (i=0;i<n;i++)
       if (GNUNET_OK !=
     p->num_authority_records = n;
     for (i=0;i<n;i++)
       if (GNUNET_OK !=
-         parse_record (udp_payload,
-                       udp_payload_length,
-                       &off,
-                       &p->authority_records[i]))
-       goto error;  
+         GNUNET_DNSPARSER_parse_record (udp_payload,
+                                        udp_payload_length,
+                                        &off,
+                                        &p->authority_records[i]))
+       goto error;
   }
   n = ntohs (dns->additional_rcount);
   if (n > 0)
   }
   n = ntohs (dns->additional_rcount);
   if (n > 0)
@@ -521,95 +738,20 @@ GNUNET_DNSPARSER_parse (const char *udp_payload,
     p->num_additional_records = n;
     for (i=0;i<n;i++)
       if (GNUNET_OK !=
     p->num_additional_records = n;
     for (i=0;i<n;i++)
       if (GNUNET_OK !=
-         parse_record (udp_payload,
-                       udp_payload_length,
-                       &off,
-                       &p->additional_records[i]))
-       goto error;   
+         GNUNET_DNSPARSER_parse_record (udp_payload,
+                                        udp_payload_length,
+                                        &off,
+                                        &p->additional_records[i]))
+       goto error;
   }
   return p;
  error:
   }
   return p;
  error:
+  GNUNET_break_op (0);
   GNUNET_DNSPARSER_free_packet (p);
   return NULL;
 }
 
 
   GNUNET_DNSPARSER_free_packet (p);
   return NULL;
 }
 
 
-/**
- * Free SOA information record.
- *
- * @param soa record to free
- */
-static void
-free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
-{
-  if (NULL == soa)
-    return;
-  GNUNET_free_non_null (soa->mname);
-  GNUNET_free_non_null (soa->rname);
-  GNUNET_free (soa);      
-}
-
-
-/**
- * Free SRV information record.
- *
- * @param srv record to free
- */
-static void
-free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
-{
-  if (NULL == srv)
-    return;
-  GNUNET_free_non_null (srv->target);
-  GNUNET_free_non_null (srv->domain_name);
-  GNUNET_free_non_null (srv->proto);
-  GNUNET_free_non_null (srv->service);
-  GNUNET_free (srv);      
-}
-
-
-/**
- * Free MX information record.
- *
- * @param mx record to free
- */
-static void
-free_mx (struct GNUNET_DNSPARSER_MxRecord *mx)
-{
-  if (NULL == mx)
-    return;
-  GNUNET_free_non_null (mx->mxhost);
-  GNUNET_free (mx);      
-}
-
-
-static void
-free_record (struct GNUNET_DNSPARSER_Record *r)
-{
-  GNUNET_free_non_null (r->name);
-  switch (r->type)
-  {
-  case GNUNET_DNSPARSER_TYPE_MX:
-    free_mx (r->data.mx);
-    break;
-  case GNUNET_DNSPARSER_TYPE_SOA:
-    free_soa (r->data.soa);
-    break;
-  case GNUNET_DNSPARSER_TYPE_SRV:
-    free_srv (r->data.srv);
-    break;
-  case GNUNET_DNSPARSER_TYPE_NS:
-  case GNUNET_DNSPARSER_TYPE_CNAME:
-  case GNUNET_DNSPARSER_TYPE_PTR:
-    GNUNET_free_non_null (r->data.hostname);
-    break;
-  default:
-    GNUNET_free_non_null (r->data.raw.data);
-    break;
-  }
-}
-
-
 /**
  * Free memory taken by a packet.
  *
 /**
  * Free memory taken by a packet.
  *
@@ -624,13 +766,13 @@ GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
     GNUNET_free_non_null (p->queries[i].name);
   GNUNET_free_non_null (p->queries);
   for (i=0;i<p->num_answers;i++)
     GNUNET_free_non_null (p->queries[i].name);
   GNUNET_free_non_null (p->queries);
   for (i=0;i<p->num_answers;i++)
-    free_record (&p->answers[i]);
+    GNUNET_DNSPARSER_free_record (&p->answers[i]);
   GNUNET_free_non_null (p->answers);
   for (i=0;i<p->num_authority_records;i++)
   GNUNET_free_non_null (p->answers);
   for (i=0;i<p->num_authority_records;i++)
-    free_record (&p->authority_records[i]);
+    GNUNET_DNSPARSER_free_record (&p->authority_records[i]);
   GNUNET_free_non_null (p->authority_records);
   for (i=0;i<p->num_additional_records;i++)
   GNUNET_free_non_null (p->authority_records);
   for (i=0;i<p->num_additional_records;i++)
-    free_record (&p->additional_records[i]);
+    GNUNET_DNSPARSER_free_record (&p->additional_records[i]);
   GNUNET_free_non_null (p->additional_records);
   GNUNET_free (p);
 }
   GNUNET_free_non_null (p->additional_records);
   GNUNET_free (p);
 }
@@ -640,52 +782,82 @@ GNUNET_DNSPARSER_free_packet (struct GNUNET_DNSPARSER_Packet *p)
 
 
 /**
 
 
 /**
- * Add a DNS name to the UDP packet at the given location.
+ * Add a DNS name to the UDP packet at the given location, converting
+ * the name to IDNA notation as necessary.
  *
  *
- * @param dst where to write the name
- * @param dst_len number of bytes in dst
+ * @param dst where to write the name (UDP packet)
+ * @param dst_len number of bytes in @a dst
  * @param off pointer to offset where to write the name (increment by bytes used)
  *            must not be changed if there is an error
  * @param name name to write
  * @param off pointer to offset where to write the name (increment by bytes used)
  *            must not be changed if there is an error
  * @param name name to write
- * @return GNUNET_SYSERR if 'name' is invalid
- *         GNUNET_NO if 'name' did not fit
- *         GNUNET_OK if 'name' was added to 'dst'
+ * @return #GNUNET_SYSERR if @a name is invalid
+ *         #GNUNET_NO if @a name did not fit
+ *         #GNUNET_OK if @a name was added to @a dst
  */
  */
-static int
-add_name (char *dst,
-         size_t dst_len,
-         size_t *off,
-         const char *name)
+int
+GNUNET_DNSPARSER_builder_add_name (char *dst,
+                                  size_t dst_len,
+                                  size_t *off,
+                                  const char *name)
 {
   const char *dot;
 {
   const char *dot;
+  const char *idna_name;
+  char *idna_start;
   size_t start;
   size_t pos;
   size_t len;
   size_t start;
   size_t pos;
   size_t len;
+  Idna_rc rc;
 
   if (NULL == name)
     return GNUNET_SYSERR;
 
   if (NULL == name)
     return GNUNET_SYSERR;
-  start = *off;
-  if (start + strlen (name) + 2 > dst_len)
+
+  if (IDNA_SUCCESS !=
+      (rc = idna_to_ascii_8z (name, &idna_start, IDNA_ALLOW_UNASSIGNED)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
+               name,
+               idna_strerror (rc));
     return GNUNET_NO;
     return GNUNET_NO;
+  }
+  idna_name = idna_start;
+  start = *off;
+  if (start + strlen (idna_name) + 2 > dst_len)
+    goto fail;
   pos = start;
   do
   {
   pos = start;
   do
   {
-    dot = strchr (name, '.');
+    dot = strchr (idna_name, '.');
     if (NULL == dot)
     if (NULL == dot)
-      len = strlen (name);
+      len = strlen (idna_name);
     else
     else
-      len = dot - name;
-    if ( (len >= 64) || (len == 0) )
-      return GNUNET_NO; /* segment too long or empty */
+      len = dot - idna_name;
+    if ( (len >= 64) || (0 == len) )
+    {
+      GNUNET_break (0);
+      goto fail; /* segment too long or empty */
+    }
     dst[pos++] = (char) (uint8_t) len;
     dst[pos++] = (char) (uint8_t) len;
-    memcpy (&dst[pos], name, len);
+    GNUNET_memcpy (&dst[pos], idna_name, len);
     pos += len;
     pos += len;
-    name += len + 1; /* also skip dot */
+    idna_name += len + 1; /* also skip dot */
   }
   while (NULL != dot);
   dst[pos++] = '\0'; /* terminator */
   *off = pos;
   }
   while (NULL != dot);
   dst[pos++] = '\0'; /* terminator */
   *off = pos;
+#if WINDOWS
+  idn_free (idna_start);
+#else
+  free (idna_start);
+#endif
   return GNUNET_OK;
   return GNUNET_OK;
+ fail:
+#if WINDOWS
+  idn_free (idna_start);
+#else
+  free (idna_start);
+#endif
+  return GNUNET_NO;
 }
 
 
 }
 
 
@@ -693,29 +865,29 @@ add_name (char *dst,
  * Add a DNS query to the UDP packet at the given location.
  *
  * @param dst where to write the query
  * Add a DNS query to the UDP packet at the given location.
  *
  * @param dst where to write the query
- * @param dst_len number of bytes in dst
+ * @param dst_len number of bytes in @a dst
  * @param off pointer to offset where to write the query (increment by bytes used)
  *            must not be changed if there is an error
  * @param query query to write
  * @param off pointer to offset where to write the query (increment by bytes used)
  *            must not be changed if there is an error
  * @param query query to write
- * @return GNUNET_SYSERR if 'query' is invalid
- *         GNUNET_NO if 'query' did not fit
- *         GNUNET_OK if 'query' was added to 'dst'
+ * @return #GNUNET_SYSERR if @a query is invalid
+ *         #GNUNET_NO if @a query did not fit
+ *         #GNUNET_OK if @a query was added to @a dst
  */
  */
-static int
-add_query (char *dst,
-          size_t dst_len,
-          size_t *off,
-          const struct GNUNET_DNSPARSER_Query *query)
+int
+GNUNET_DNSPARSER_builder_add_query (char *dst,
+                                   size_t dst_len,
+                                   size_t *off,
+                                   const struct GNUNET_DNSPARSER_Query *query)
 {
   int ret;
 {
   int ret;
-  struct query_line ql;
+  struct GNUNET_TUN_DnsQueryLine ql;
 
 
-  ret = add_name (dst, dst_len - sizeof (struct query_line), off, query->name);
+  ret = GNUNET_DNSPARSER_builder_add_name (dst, dst_len - sizeof (struct GNUNET_TUN_DnsQueryLine), off, query->name);
   if (ret != GNUNET_OK)
     return ret;
   ql.type = htons (query->type);
   if (ret != GNUNET_OK)
     return ret;
   ql.type = htons (query->type);
-  ql.class = htons (query->class);
-  memcpy (&dst[*off], &ql, sizeof (ql));
+  ql.dns_traffic_class = htons (query->dns_traffic_class);
+  GNUNET_memcpy (&dst[*off], &ql, sizeof (ql));
   (*off) += sizeof (ql);
   return GNUNET_OK;
 }
   (*off) += sizeof (ql);
   return GNUNET_OK;
 }
@@ -725,28 +897,68 @@ add_query (char *dst,
  * Add an MX record to the UDP packet at the given location.
  *
  * @param dst where to write the mx record
  * Add an MX record to the UDP packet at the given location.
  *
  * @param dst where to write the mx record
- * @param dst_len number of bytes in dst
+ * @param dst_len number of bytes in @a dst
  * @param off pointer to offset where to write the mx information (increment by bytes used);
  *            can also change if there was an error
  * @param mx mx information to write
  * @param off pointer to offset where to write the mx information (increment by bytes used);
  *            can also change if there was an error
  * @param mx mx information to write
- * @return GNUNET_SYSERR if 'mx' is invalid
- *         GNUNET_NO if 'mx' did not fit
- *         GNUNET_OK if 'mx' was added to 'dst'
+ * @return #GNUNET_SYSERR if @a mx is invalid
+ *         #GNUNET_NO if @a mx did not fit
+ *         #GNUNET_OK if @a mx was added to @a dst
  */
  */
-static int
-add_mx (char *dst,
-       size_t dst_len,
-       size_t *off,
-       const struct GNUNET_DNSPARSER_MxRecord *mx)
+int
+GNUNET_DNSPARSER_builder_add_mx (char *dst,
+                                size_t dst_len,
+                                size_t *off,
+                                const struct GNUNET_DNSPARSER_MxRecord *mx)
 {
   uint16_t mxpref;
 
   if (*off + sizeof (uint16_t) > dst_len)
     return GNUNET_NO;
   mxpref = htons (mx->preference);
 {
   uint16_t mxpref;
 
   if (*off + sizeof (uint16_t) > dst_len)
     return GNUNET_NO;
   mxpref = htons (mx->preference);
-  memcpy (&dst[*off], &mxpref, sizeof (mxpref));
+  GNUNET_memcpy (&dst[*off], &mxpref, sizeof (mxpref));
   (*off) += sizeof (mxpref);
   (*off) += sizeof (mxpref);
-  return add_name (dst, dst_len, off, mx->mxhost);
+  return GNUNET_DNSPARSER_builder_add_name (dst, dst_len, off, mx->mxhost);
+}
+
+
+/**
+ * Add a CERT record to the UDP packet at the given location.
+ *
+ * @param dst where to write the CERT record
+ * @param dst_len number of bytes in @a dst
+ * @param off pointer to offset where to write the CERT information (increment by bytes used);
+ *            can also change if there was an error
+ * @param cert CERT information to write
+ * @return #GNUNET_SYSERR if @a cert is invalid
+ *         #GNUNET_NO if @a cert did not fit
+ *         #GNUNET_OK if @a cert was added to @a dst
+ */
+int
+GNUNET_DNSPARSER_builder_add_cert (char *dst,
+                                   size_t dst_len,
+                                   size_t *off,
+                                   const struct GNUNET_DNSPARSER_CertRecord *cert)
+{
+  struct GNUNET_TUN_DnsCertRecord dcert;
+
+  if ( (cert->cert_type > UINT16_MAX) ||
+       (cert->cert_tag > UINT16_MAX) ||
+       (cert->algorithm > UINT8_MAX) )
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (*off + sizeof (struct GNUNET_TUN_DnsCertRecord) + cert->certificate_size > dst_len)
+    return GNUNET_NO;
+  dcert.cert_type = htons ((uint16_t) cert->cert_type);
+  dcert.cert_tag = htons ((uint16_t) cert->cert_tag);
+  dcert.algorithm = (uint8_t) cert->algorithm;
+  GNUNET_memcpy (&dst[*off], &dcert, sizeof (dcert));
+  (*off) += sizeof (dcert);
+  GNUNET_memcpy (&dst[*off], cert->certificate_data, cert->certificate_size);
+  (*off) += cert->certificate_size;
+  return GNUNET_OK;
 }
 
 
 }
 
 
@@ -754,40 +966,40 @@ add_mx (char *dst,
  * Add an SOA record to the UDP packet at the given location.
  *
  * @param dst where to write the SOA record
  * Add an SOA record to the UDP packet at the given location.
  *
  * @param dst where to write the SOA record
- * @param dst_len number of bytes in dst
+ * @param dst_len number of bytes in @a dst
  * @param off pointer to offset where to write the SOA information (increment by bytes used)
  *            can also change if there was an error
  * @param soa SOA information to write
  * @param off pointer to offset where to write the SOA information (increment by bytes used)
  *            can also change if there was an error
  * @param soa SOA information to write
- * @return GNUNET_SYSERR if 'soa' is invalid
- *         GNUNET_NO if 'soa' did not fit
- *         GNUNET_OK if 'soa' was added to 'dst'
+ * @return #GNUNET_SYSERR if @a soa is invalid
+ *         #GNUNET_NO if @a soa did not fit
+ *         #GNUNET_OK if @a soa was added to @a dst
  */
  */
-static int
-add_soa (char *dst,
-        size_t dst_len,
-        size_t *off,
-        const struct GNUNET_DNSPARSER_SoaRecord *soa)
+int
+GNUNET_DNSPARSER_builder_add_soa (char *dst,
+                                 size_t dst_len,
+                                 size_t *off,
+                                 const struct GNUNET_DNSPARSER_SoaRecord *soa)
 {
 {
-  struct soa_data sd;
+  struct GNUNET_TUN_DnsSoaRecord sd;
   int ret;
 
   int ret;
 
-  if ( (GNUNET_OK != (ret = add_name (dst,
-                                     dst_len,
-                                     off,
-                                     soa->mname))) ||
-       (GNUNET_OK != (ret = add_name (dst,
-                                     dst_len,
-                                     off,
-                                     soa->rname)) ) )
+  if ( (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
+                                                               dst_len,
+                                                               off,
+                                                               soa->mname))) ||
+       (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
+                                                               dst_len,
+                                                               off,
+                                                               soa->rname)) ) )
     return ret;
     return ret;
-  if (*off + sizeof (struct soa_data) > dst_len)
+  if (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > dst_len)
     return GNUNET_NO;
   sd.serial = htonl (soa->serial);
   sd.refresh = htonl (soa->refresh);
   sd.retry = htonl (soa->retry);
   sd.expire = htonl (soa->expire);
   sd.minimum = htonl (soa->minimum_ttl);
     return GNUNET_NO;
   sd.serial = htonl (soa->serial);
   sd.refresh = htonl (soa->refresh);
   sd.retry = htonl (soa->retry);
   sd.expire = htonl (soa->expire);
   sd.minimum = htonl (soa->minimum_ttl);
-  memcpy (&dst[*off], &sd, sizeof (sd));
+  GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
   (*off) += sizeof (sd);
   return GNUNET_OK;
 }
   (*off) += sizeof (sd);
   return GNUNET_OK;
 }
@@ -797,31 +1009,31 @@ add_soa (char *dst,
  * Add an SRV record to the UDP packet at the given location.
  *
  * @param dst where to write the SRV record
  * Add an SRV record to the UDP packet at the given location.
  *
  * @param dst where to write the SRV record
- * @param dst_len number of bytes in dst
+ * @param dst_len number of bytes in @a dst
  * @param off pointer to offset where to write the SRV information (increment by bytes used)
  *            can also change if there was an error
  * @param srv SRV information to write
  * @param off pointer to offset where to write the SRV information (increment by bytes used)
  *            can also change if there was an error
  * @param srv SRV information to write
- * @return GNUNET_SYSERR if 'srv' is invalid
- *         GNUNET_NO if 'srv' did not fit
- *         GNUNET_OK if 'srv' was added to 'dst'
+ * @return #GNUNET_SYSERR if @a srv is invalid
+ *         #GNUNET_NO if @a srv did not fit
+ *         #GNUNET_OK if @a srv was added to @a dst
  */
  */
-static int
-add_srv (char *dst,
-        size_t dst_len,
-        size_t *off,
-        const struct GNUNET_DNSPARSER_SrvRecord *srv)
+int
+GNUNET_DNSPARSER_builder_add_srv (char *dst,
+                                 size_t dst_len,
+                                 size_t *off,
+                                 const struct GNUNET_DNSPARSER_SrvRecord *srv)
 {
 {
-  struct srv_data sd;
+  struct GNUNET_TUN_DnsSrvRecord sd;
   int ret;
 
   int ret;
 
-  if (*off + sizeof (struct srv_data) > dst_len)
+  if (*off + sizeof (struct GNUNET_TUN_DnsSrvRecord) > dst_len)
     return GNUNET_NO;
   sd.prio = htons (srv->priority);
   sd.weight = htons (srv->weight);
   sd.port = htons (srv->port);
     return GNUNET_NO;
   sd.prio = htons (srv->priority);
   sd.weight = htons (srv->weight);
   sd.port = htons (srv->port);
-  memcpy (&dst[*off], &sd, sizeof (sd));
+  GNUNET_memcpy (&dst[*off], &sd, sizeof (sd));
   (*off) += sizeof (sd);
   (*off) += sizeof (sd);
-  if (GNUNET_OK != (ret = add_name (dst,
+  if (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
                                    dst_len,
                                    off,
                                    srv->target)))
                                    dst_len,
                                    off,
                                    srv->target)))
@@ -834,13 +1046,13 @@ add_srv (char *dst,
  * Add a DNS record to the UDP packet at the given location.
  *
  * @param dst where to write the query
  * Add a DNS record to the UDP packet at the given location.
  *
  * @param dst where to write the query
- * @param dst_len number of bytes in dst
+ * @param dst_len number of bytes in @a dst
  * @param off pointer to offset where to write the query (increment by bytes used)
  *            must not be changed if there is an error
  * @param record record to write
  * @param off pointer to offset where to write the query (increment by bytes used)
  *            must not be changed if there is an error
  * @param record record to write
- * @return GNUNET_SYSERR if 'record' is invalid
- *         GNUNET_NO if 'record' did not fit
- *         GNUNET_OK if 'record' was added to 'dst'
+ * @return #GNUNET_SYSERR if @a record is invalid
+ *         #GNUNET_NO if @a record did not fit
+ *         #GNUNET_OK if @a record was added to @a dst
  */
 static int
 add_record (char *dst,
  */
 static int
 add_record (char *dst,
@@ -851,43 +1063,36 @@ add_record (char *dst,
   int ret;
   size_t start;
   size_t pos;
   int ret;
   size_t start;
   size_t pos;
-  struct record_line rl;
-  char *name;
-  
+  struct GNUNET_TUN_DnsRecordLine rl;
+
   start = *off;
   start = *off;
-  /* for SRV records, we can create the name from the details
-     of the record if needed */
-  name = record->name;
-  if  ( (GNUNET_DNSPARSER_TYPE_SRV == record->type) &&
-       (NULL == name) )
-    GNUNET_asprintf (&name,
-                    "_%s._%s.%s",
-                    record->data.srv->service,
-                    record->data.srv->proto,
-                    record->data.srv->domain_name);
-  ret = add_name (dst, dst_len - sizeof (struct record_line), off, name);
-  if (name != record->name)
-    GNUNET_free (name);
+  ret = GNUNET_DNSPARSER_builder_add_name (dst,
+                                           dst_len - sizeof (struct GNUNET_TUN_DnsRecordLine),
+                                           off,
+                                           record->name);
   if (GNUNET_OK != ret)
     return ret;
   /* '*off' is now the position where we will need to write the record line */
 
   if (GNUNET_OK != ret)
     return ret;
   /* '*off' is now the position where we will need to write the record line */
 
-  pos = *off + sizeof (struct record_line);
+  pos = *off + sizeof (struct GNUNET_TUN_DnsRecordLine);
   switch (record->type)
   switch (record->type)
-  { 
+  {
   case GNUNET_DNSPARSER_TYPE_MX:
   case GNUNET_DNSPARSER_TYPE_MX:
-    ret = add_mx (dst, dst_len, &pos, record->data.mx);    
+    ret = GNUNET_DNSPARSER_builder_add_mx (dst, dst_len, &pos, record->data.mx);
+    break;
+  case GNUNET_DNSPARSER_TYPE_CERT:
+    ret = GNUNET_DNSPARSER_builder_add_cert (dst, dst_len, &pos, record->data.cert);
     break;
   case GNUNET_DNSPARSER_TYPE_SOA:
     break;
   case GNUNET_DNSPARSER_TYPE_SOA:
-    ret = add_soa (dst, dst_len, &pos, record->data.soa);
+    ret = GNUNET_DNSPARSER_builder_add_soa (dst, dst_len, &pos, record->data.soa);
     break;
   case GNUNET_DNSPARSER_TYPE_NS:
   case GNUNET_DNSPARSER_TYPE_CNAME:
   case GNUNET_DNSPARSER_TYPE_PTR:
     break;
   case GNUNET_DNSPARSER_TYPE_NS:
   case GNUNET_DNSPARSER_TYPE_CNAME:
   case GNUNET_DNSPARSER_TYPE_PTR:
-    ret = add_name (dst, dst_len, &pos, record->data.hostname);
+    ret = GNUNET_DNSPARSER_builder_add_name (dst, dst_len, &pos, record->data.hostname);
     break;
   case GNUNET_DNSPARSER_TYPE_SRV:
     break;
   case GNUNET_DNSPARSER_TYPE_SRV:
-    ret = add_srv (dst, dst_len, &pos, record->data.srv);
+    ret = GNUNET_DNSPARSER_builder_add_srv (dst, dst_len, &pos, record->data.srv);
     break;
   default:
     if (pos + record->data.raw.data_len > dst_len)
     break;
   default:
     if (pos + record->data.raw.data_len > dst_len)
@@ -895,7 +1100,7 @@ add_record (char *dst,
       ret = GNUNET_NO;
       break;
     }
       ret = GNUNET_NO;
       break;
     }
-    memcpy (&dst[pos], record->data.raw.data, record->data.raw.data_len);
+    GNUNET_memcpy (&dst[pos], record->data.raw.data, record->data.raw.data_len);
     pos += record->data.raw.data_len;
     ret = GNUNET_OK;
     break;
     pos += record->data.raw.data_len;
     ret = GNUNET_OK;
     break;
@@ -906,49 +1111,49 @@ add_record (char *dst,
     return GNUNET_NO;
   }
 
     return GNUNET_NO;
   }
 
-  if (pos - (*off + sizeof (struct record_line)) > UINT16_MAX)
+  if (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine)) > UINT16_MAX)
   {
     /* record data too long */
     *off = start;
     return GNUNET_NO;
   }
   rl.type = htons (record->type);
   {
     /* record data too long */
     *off = start;
     return GNUNET_NO;
   }
   rl.type = htons (record->type);
-  rl.class = htons (record->class);
-  rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value / 1000); /* in seconds */
-  rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct record_line))));
-  memcpy (&dst[*off], &rl, sizeof (struct record_line));
+  rl.dns_traffic_class = htons (record->dns_traffic_class);
+  rl.ttl = htonl (GNUNET_TIME_absolute_get_remaining (record->expiration_time).rel_value_us / 1000LL / 1000LL); /* in seconds */
+  rl.data_len = htons ((uint16_t) (pos - (*off + sizeof (struct GNUNET_TUN_DnsRecordLine))));
+  GNUNET_memcpy (&dst[*off], &rl, sizeof (struct GNUNET_TUN_DnsRecordLine));
   *off = pos;
   *off = pos;
-  return GNUNET_OK;  
+  return GNUNET_OK;
 }
 
 
 /**
 }
 
 
 /**
- * Given a DNS packet, generate the corresponding UDP payload.
+ * Given a DNS packet @a p, generate the corresponding UDP payload.
  * Note that we do not attempt to pack the strings with pointers
  * Note that we do not attempt to pack the strings with pointers
- * as this would complicate the code and this is about being 
+ * as this would complicate the code and this is about being
  * simple and secure, not fast, fancy and broken like bind.
  *
  * @param p packet to pack
  * @param max maximum allowed size for the resulting UDP payload
  * @param buf set to a buffer with the packed message
  * simple and secure, not fast, fancy and broken like bind.
  *
  * @param p packet to pack
  * @param max maximum allowed size for the resulting UDP payload
  * @param buf set to a buffer with the packed message
- * @param buf_length set to the length of buf
- * @return GNUNET_SYSERR if 'p' is invalid
- *         GNUNET_NO if 'p' was truncated (but there is still a result in 'buf')
- *         GNUNET_OK if 'p' was packed completely into '*buf'
+ * @param buf_length set to the length of @a buf
+ * @return #GNUNET_SYSERR if @a p is invalid
+ *         #GNUNET_NO if @a p was truncated (but there is still a result in @a buf)
+ *         #GNUNET_OK if @a p was packed completely into @a buf
  */
 int
 GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
                       uint16_t max,
                       char **buf,
                       size_t *buf_length)
  */
 int
 GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
                       uint16_t max,
                       char **buf,
                       size_t *buf_length)
-{  
+{
   struct GNUNET_TUN_DnsHeader dns;
   size_t off;
   char tmp[max];
   unsigned int i;
   int ret;
   int trc;
   struct GNUNET_TUN_DnsHeader dns;
   size_t off;
   char tmp[max];
   unsigned int i;
   int ret;
   int trc;
-  
+
   if ( (p->num_queries > UINT16_MAX) ||
        (p->num_answers > UINT16_MAX) ||
        (p->num_authority_records > UINT16_MAX) ||
   if ( (p->num_queries > UINT16_MAX) ||
        (p->num_answers > UINT16_MAX) ||
        (p->num_authority_records > UINT16_MAX) ||
@@ -965,63 +1170,138 @@ GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
   trc = GNUNET_NO;
   for (i=0;i<p->num_queries;i++)
   {
   trc = GNUNET_NO;
   for (i=0;i<p->num_queries;i++)
   {
-    ret = add_query (tmp, sizeof (tmp), &off, &p->queries[i]);  
+    ret = GNUNET_DNSPARSER_builder_add_query (tmp,
+                                             sizeof (tmp),
+                                             &off,
+                                             &p->queries[i]);
     if (GNUNET_SYSERR == ret)
       return GNUNET_SYSERR;
     if (GNUNET_NO == ret)
     {
       dns.query_count = htons ((uint16_t) (i-1));
     if (GNUNET_SYSERR == ret)
       return GNUNET_SYSERR;
     if (GNUNET_NO == ret)
     {
       dns.query_count = htons ((uint16_t) (i-1));
-      trc = GNUNET_YES;      
+      trc = GNUNET_YES;
       break;
     }
   }
   for (i=0;i<p->num_answers;i++)
   {
       break;
     }
   }
   for (i=0;i<p->num_answers;i++)
   {
-    ret = add_record (tmp, sizeof (tmp), &off, &p->answers[i]);  
+    ret = add_record (tmp,
+                     sizeof (tmp),
+                     &off,
+                     &p->answers[i]);
     if (GNUNET_SYSERR == ret)
       return GNUNET_SYSERR;
     if (GNUNET_NO == ret)
     {
       dns.answer_rcount = htons ((uint16_t) (i-1));
     if (GNUNET_SYSERR == ret)
       return GNUNET_SYSERR;
     if (GNUNET_NO == ret)
     {
       dns.answer_rcount = htons ((uint16_t) (i-1));
-      trc = GNUNET_YES;      
+      trc = GNUNET_YES;
       break;
     }
   }
   for (i=0;i<p->num_authority_records;i++)
   {
       break;
     }
   }
   for (i=0;i<p->num_authority_records;i++)
   {
-    ret = add_record (tmp, sizeof (tmp), &off, &p->authority_records[i]);  
+    ret = add_record (tmp,
+                     sizeof (tmp),
+                     &off,
+                     &p->authority_records[i]);
     if (GNUNET_SYSERR == ret)
       return GNUNET_SYSERR;
     if (GNUNET_NO == ret)
     {
       dns.authority_rcount = htons ((uint16_t) (i-1));
     if (GNUNET_SYSERR == ret)
       return GNUNET_SYSERR;
     if (GNUNET_NO == ret)
     {
       dns.authority_rcount = htons ((uint16_t) (i-1));
-      trc = GNUNET_YES;      
+      trc = GNUNET_YES;
       break;
     }
   }
   for (i=0;i<p->num_additional_records;i++)
   {
       break;
     }
   }
   for (i=0;i<p->num_additional_records;i++)
   {
-    ret = add_record (tmp, sizeof (tmp), &off, &p->additional_records[i]);  
+    ret = add_record (tmp,
+                     sizeof (tmp),
+                     &off,
+                     &p->additional_records[i]);
     if (GNUNET_SYSERR == ret)
       return GNUNET_SYSERR;
     if (GNUNET_NO == ret)
     {
       dns.additional_rcount = htons (i-1);
     if (GNUNET_SYSERR == ret)
       return GNUNET_SYSERR;
     if (GNUNET_NO == ret)
     {
       dns.additional_rcount = htons (i-1);
-      trc = GNUNET_YES;      
+      trc = GNUNET_YES;
       break;
     }
   }
 
   if (GNUNET_YES == trc)
       break;
     }
   }
 
   if (GNUNET_YES == trc)
-    dns.flags.message_truncated = 1;    
-  memcpy (tmp, &dns, sizeof (struct GNUNET_TUN_DnsHeader));
+    dns.flags.message_truncated = 1;
+  GNUNET_memcpy (tmp,
+                &dns,
+                sizeof (struct GNUNET_TUN_DnsHeader));
 
   *buf = GNUNET_malloc (off);
   *buf_length = off;
 
   *buf = GNUNET_malloc (off);
   *buf_length = off;
-  memcpy (*buf, tmp, off);
+  GNUNET_memcpy (*buf,
+                tmp,
+                off);
   if (GNUNET_YES == trc)
     return GNUNET_NO;
   return GNUNET_OK;
 }
 
   if (GNUNET_YES == trc)
     return GNUNET_NO;
   return GNUNET_OK;
 }
 
+
+/**
+ * Convert a block of binary data to HEX.
+ *
+ * @param data binary data to convert
+ * @param data_size number of bytes in @a data
+ * @return HEX string (lower case)
+ */
+char *
+GNUNET_DNSPARSER_bin_to_hex (const void *data,
+                             size_t data_size)
+{
+  char *ret;
+  size_t off;
+  const uint8_t *idata;
+
+  idata = data;
+  ret = GNUNET_malloc (data_size * 2 + 1);
+  for (off = 0; off < data_size; off++)
+    sprintf (&ret[off * 2],
+             "%02x",
+             idata[off]);
+  return ret;
+}
+
+
+/**
+ * Convert a HEX string to block of binary data.
+ *
+ * @param hex HEX string to convert (may contain mixed case)
+ * @param data where to write result, must be
+ *             at least `strlen(hex)/2` bytes long
+ * @return number of bytes written to data
+ */
+size_t
+GNUNET_DNSPARSER_hex_to_bin (const char *hex,
+                             void *data)
+{
+  size_t data_size;
+  size_t off;
+  uint8_t *idata;
+  unsigned int h;
+  char in[3];
+
+  data_size = strlen (hex) / 2;
+  idata = data;
+  in[2] = '\0';
+  for (off = 0; off < data_size; off++)
+  {
+    in[0] = tolower ((unsigned char) hex[off * 2]);
+    in[1] = tolower ((unsigned char) hex[off * 2 + 1]);
+    if (1 != sscanf (in, "%x", &h))
+      return off;
+    idata[off] = (uint8_t) h;
+  }
+  return off;
+}
+
+
 /* end of dnsparser.c */
 /* end of dnsparser.c */