draft hex2bin, bin2hex
[oweals/gnunet.git] / src / dns / dnsparser.c
index 2e9e73f1c4d9f9fe0b9bbaf091977904a9a6fdaa..2be34fc15fd0f6f250519a6023fd5b8348ab9543 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)
+      (C) 2010-2014 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
 
       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
 
 /**
  * @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"
 
 
 
 
-// DNS-Stuff
-GNUNET_NETWORK_STRUCT_BEGIN
-/* FIXME: replace this one with the one from tcpip_tun.h! */
-struct dns_header
+/**
+ * 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)
 {
 {
-  uint16_t id GNUNET_PACKED;
-  struct GNUNET_DNSPARSER_Flags flags; 
-  uint16_t query_count GNUNET_PACKED;       // number of questions
-  uint16_t answer_rcount GNUNET_PACKED;       // number of answers
-  uint16_t authority_rcount GNUNET_PACKED;       // number of authority-records
-  uint16_t additional_rcount GNUNET_PACKED;       // number of additional records
-};
-
-struct query_line
+  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)
 {
 {
-  uint16_t type GNUNET_PACKED;
-  uint16_t class GNUNET_PACKED;
-};
+  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;
+}
 
 
-struct record_line
+
+/**
+ * Free SOA information record.
+ *
+ * @param soa record to free
+ */
+void
+GNUNET_DNSPARSER_free_soa (struct GNUNET_DNSPARSER_SoaRecord *soa)
 {
 {
-  uint16_t type GNUNET_PACKED;
-  uint16_t class GNUNET_PACKED;
-  uint32_t ttl GNUNET_PACKED;
-  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);
+}
 
 
-struct soa_data
+
+/**
+ * Free CERT information record.
+ *
+ * @param cert record to free
+ */
+void
+GNUNET_DNSPARSER_free_cert (struct GNUNET_DNSPARSER_CertRecord *cert)
 {
 {
-  uint32_t serial GNUNET_PACKED;
-  uint32_t refresh GNUNET_PACKED;
-  uint32_t retry GNUNET_PACKED;
-  uint32_t expire GNUNET_PACKED;
-  uint32_t minimum GNUNET_PACKED;
-};
+  if (NULL == cert)
+    return;
+  GNUNET_free_non_null (cert->certificate_data);
+  GNUNET_free (cert);
+}
+
 
 
-GNUNET_NETWORK_STRUCT_END
+/**
+ * Free SRV information record.
+ *
+ * @param srv record to free
+ */
+void
+GNUNET_DNSPARSER_free_srv (struct GNUNET_DNSPARSER_SrvRecord *srv)
+{
+  if (NULL == srv)
+    return;
+  GNUNET_free_non_null (srv->target);
+  GNUNET_free (srv);
+}
+
+
+/**
+ * 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);
+}
+
+
+/**
+ * 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)
@@ -90,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)
     {
@@ -105,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;
@@ -118,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,
@@ -137,150 +309,352 @@ 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));
   *off += sizeof (ql);
   q->type = ntohs (ql.type);
   memcpy (&ql, &udp_payload[*off], sizeof (ql));
   *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;
+  }
+  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;
+  }
+  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;
+  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;
+  }
+  memcpy (&dcert, &udp_payload[*off], sizeof (struct GNUNET_TUN_DnsCertRecord));
+  (*off) += sizeof (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);
+  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;
 
-  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));
   (*off) += sizeof (rl);
   r->type = ntohs (rl.type);
   memcpy (&rl, &udp_payload[*off], sizeof (rl));
   (*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 (soa) > udp_payload_length) )
-      return GNUNET_SYSERR;
-    memcpy (&soa, &udp_payload[*off], sizeof (soa));
-    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 (soa);
-    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)
+    r->data.mx = GNUNET_DNSPARSER_parse_mx (udp_payload,
+                                           udp_payload_length,
+                                           off);
+    if ( (NULL == r->data.mx) ||
+        (old_off + data_len != *off) )
+    {
+      GNUNET_break_op (0);
       return GNUNET_SYSERR;
       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_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_break_op (0);
       return GNUNET_SYSERR;
       return GNUNET_SYSERR;
+    }
     return GNUNET_OK;
   default:
     r->data.raw.data = GNUNET_malloc (data_len);
     return GNUNET_OK;
   default:
     r->data.raw.data = GNUNET_malloc (data_len);
@@ -289,7 +663,7 @@ parse_record (const char *udp_payload,
     break;
   }
   (*off) += data_len;
     break;
   }
   (*off) += data_len;
-  return GNUNET_OK;  
+  return GNUNET_OK;
 }
 
 
 }
 
 
@@ -298,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 *
@@ -306,16 +680,16 @@ GNUNET_DNSPARSER_parse (const char *udp_payload,
                        size_t udp_payload_length)
 {
   struct GNUNET_DNSPARSER_Packet *p;
                        size_t udp_payload_length)
 {
   struct GNUNET_DNSPARSER_Packet *p;
-  const struct dns_header *dns;
+  const struct GNUNET_TUN_DnsHeader *dns;
   size_t off;
   size_t off;
-  unsigned int n;  
+  unsigned int n;
   unsigned int i;
 
   unsigned int i;
 
-  if (udp_payload_length < sizeof (struct dns_header))
+  if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
     return NULL;
     return NULL;
-  dns = (const struct dns_header *) udp_payload;
-  off = sizeof (struct dns_header);
-  p = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_Packet));
+  dns = (const struct GNUNET_TUN_DnsHeader *) udp_payload;
+  off = sizeof (struct GNUNET_TUN_DnsHeader);
+  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);
@@ -325,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);
@@ -338,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);
@@ -351,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)
@@ -364,74 +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 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_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.
  *
@@ -446,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);
 }
@@ -462,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);
+    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;
 }
 
 
 }
 
 
@@ -515,28 +865,28 @@ 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);
+  ql.dns_traffic_class = htons (query->dns_traffic_class);
   memcpy (&dst[*off], &ql, sizeof (ql));
   (*off) += sizeof (ql);
   return GNUNET_OK;
   memcpy (&dst[*off], &ql, sizeof (ql));
   (*off) += sizeof (ql);
   return GNUNET_OK;
@@ -547,19 +897,19 @@ 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;
 
 {
   uint16_t mxpref;
 
@@ -568,7 +918,47 @@ add_mx (char *dst,
   mxpref = htons (mx->preference);
   memcpy (&dst[*off], &mxpref, sizeof (mxpref));
   (*off) += sizeof (mxpref);
   mxpref = htons (mx->preference);
   memcpy (&dst[*off], &mxpref, 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;
+  memcpy (&dst[*off], &dcert, sizeof (dcert));
+  (*off) += sizeof (dcert);
+  memcpy (&dst[*off], cert->certificate_data, cert->certificate_size);
+  (*off) += cert->certificate_size;
+  return GNUNET_OK;
 }
 
 
 }
 
 
@@ -576,33 +966,33 @@ 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 (soa) > dst_len)
+  if (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > dst_len)
     return GNUNET_NO;
   sd.serial = htonl (soa->serial);
   sd.refresh = htonl (soa->refresh);
     return GNUNET_NO;
   sd.serial = htonl (soa->serial);
   sd.refresh = htonl (soa->refresh);
@@ -615,17 +1005,54 @@ add_soa (char *dst,
 }
 
 
 }
 
 
+/**
+ * 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 @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
+ * @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
+ */
+int
+GNUNET_DNSPARSER_builder_add_srv (char *dst,
+                                 size_t dst_len,
+                                 size_t *off,
+                                 const struct GNUNET_DNSPARSER_SrvRecord *srv)
+{
+  struct GNUNET_TUN_DnsSrvRecord sd;
+  int ret;
+
+  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);
+  memcpy (&dst[*off], &sd, sizeof (sd));
+  (*off) += sizeof (sd);
+  if (GNUNET_OK != (ret = GNUNET_DNSPARSER_builder_add_name (dst,
+                                   dst_len,
+                                   off,
+                                   srv->target)))
+    return ret;
+  return GNUNET_OK;
+}
+
+
 /**
  * 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,
@@ -636,27 +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;
+  struct GNUNET_TUN_DnsRecordLine rl;
 
   start = *off;
 
   start = *off;
-  ret = add_name (dst, dst_len - sizeof (struct record_line), off, record->name);
-  if (ret != GNUNET_OK)
+  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 */
 
     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:
+    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)
@@ -669,55 +1105,55 @@ add_record (char *dst,
     ret = GNUNET_OK;
     break;
   }
     ret = GNUNET_OK;
     break;
   }
-  if (ret != GNUNET_OK)
+  if (GNUNET_OK != ret)
   {
     *off = start;
     return GNUNET_NO;
   }
 
   {
     *off = start;
     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))));
+  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 dns_header dns;
+{
+  struct GNUNET_TUN_DnsHeader dns;
   size_t off;
   char tmp[max];
   unsigned int i;
   int ret;
   int trc;
   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) ||
@@ -730,60 +1166,60 @@ GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
   dns.authority_rcount = htons (p->num_authority_records);
   dns.additional_rcount = htons (p->num_additional_records);
 
   dns.authority_rcount = htons (p->num_authority_records);
   dns.additional_rcount = htons (p->num_additional_records);
 
-  off = sizeof (struct dns_header);
+  off = sizeof (struct GNUNET_TUN_DnsHeader);
   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 dns_header));
+    dns.flags.message_truncated = 1;
+  memcpy (tmp, &dns, sizeof (struct GNUNET_TUN_DnsHeader));
 
   *buf = GNUNET_malloc (off);
   *buf_length = off;
 
   *buf = GNUNET_malloc (off);
   *buf_length = off;
@@ -794,330 +1230,62 @@ GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
 }
 
 
 }
 
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-/* legacy code follows */
-
 /**
 /**
- * Parse a name from DNS to a normal .-delimited, 0-terminated string.
+ * Convert a block of binary data to HEX.
  *
  *
- * @param d The destination of the name. Should have at least 255 bytes allocated.
- * @param src The DNS-Packet
- * @param idx The offset inside the Packet from which on the name should be read
- * @returns The offset of the first unparsed byte (the byte right behind the name)
+ * @param data binary data to convert
+ * @param data_size number of bytes in @a data
+ * @return HEX string (lower case)
  */
  */
-static unsigned int
-parse_dns_name (char *d, const unsigned char *src, unsigned short idx)
-{                               /*{{{ */
-  char *dest = d;
-
-  int len = src[idx++];
-
-  while (len != 0)
-  {
-    if (len & 0xC0)
-    {                           /* Compressed name, offset in this and the next octet */
-      unsigned short offset = ((len & 0x3F) << 8) | src[idx++];
-
-      parse_dns_name (dest, src, offset - 12);  /* 12 for the Header of the DNS-Packet, idx starts at 0 which is 12 bytes from the start of the packet */
-      return idx;
-    }
-    memcpy (dest, src + idx, len);
-    idx += len;
-    dest += len;
-    *dest = '.';
-    dest++;
-    len = src[idx++];
-  };
-  *dest = 0;
-
-  return idx;
+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],
+             "%x",
+             idata[off]);
+  return ret;
 }
 
 }
 
-/*}}}*/
 
 /**
 
 /**
- * Parse a complete DNS-Record from raw DNS-data to a struct dns_record
+ * Convert a HEX string to block of binary data.
  *
  *
- * @param data The DNS-data
- * @param dst Pointer to count pointers; individual pointers will be allocated
- * @param count Number of records to parse
- * @param idx The offset inside the Packet from which on the name should be read
- * @returns The offset of the first unparsed byte (the byte right behind the last record)
+ * @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
  */
  */
-static unsigned short
-parse_dns_record (unsigned char *data,  /*{{{ */
-                  struct dns_record **dst, unsigned short count,
-                  unsigned short idx)
+size_t
+GNUNET_DNSPARSER_hex_to_bin (const char *hex,
+                             void *data)
 {
 {
-  int i;
-  unsigned short _idx;
-
-  for (i = 0; i < count; i++)
-  {
-    dst[i] = GNUNET_malloc (sizeof (struct dns_record));
-    dst[i]->name = alloca (255);        // see RFC1035, no name can be longer than this.
-    char *name = dst[i]->name;
-
-    _idx = parse_dns_name (name, data, idx);
-    dst[i]->namelen = _idx - idx;
-
-    dst[i]->name = GNUNET_malloc (dst[i]->namelen);
-    memcpy (dst[i]->name, name, dst[i]->namelen);
-
-    idx = _idx;
-
-    dst[i]->type = *((unsigned short *) (data + idx));
-    idx += 2;
-    dst[i]->class = *((unsigned short *) (data + idx));
-    idx += 2;
-    dst[i]->ttl = *((unsigned int *) (data + idx));
-    idx += 4;
-    dst[i]->data_len = *((unsigned short *) (data + idx));
-    idx += 2;
-    dst[i]->data = GNUNET_malloc (ntohs (dst[i]->data_len));
-    memcpy (dst[i]->data, data + idx, ntohs (dst[i]->data_len));
-    idx += ntohs (dst[i]->data_len);
-  }
-  return idx;
-}                               /*}}} */
-
-/**
- * Parse a raw DNS-Packet into an usable struct
- */
-struct dns_pkt_parsed *
-parse_dns_packet (struct dns_pkt *pkt)
-{                               /*{{{ */
-  struct dns_pkt_parsed *ppkt = GNUNET_malloc (sizeof (struct dns_pkt_parsed));
-
-  memcpy (&ppkt->s, &pkt->s, sizeof pkt->s);
-
-  unsigned short qdcount = ntohs (ppkt->s.qdcount);
-  unsigned short ancount = ntohs (ppkt->s.ancount);
-  unsigned short nscount = ntohs (ppkt->s.nscount);
-  unsigned short arcount = ntohs (ppkt->s.arcount);
-
-  ppkt->queries = GNUNET_malloc (qdcount * sizeof (struct dns_query *));
-  ppkt->answers = GNUNET_malloc (ancount * sizeof (struct dns_record *));
-  ppkt->nameservers = GNUNET_malloc (nscount * sizeof (struct dns_record *));
-  ppkt->additional = GNUNET_malloc (arcount * sizeof (struct dns_record *));
-
-  unsigned short idx = 0, _idx; /* This keeps track how far we have parsed the data */
-
-  /* Parse the Query */
-  int i;
-
-  for (i = 0; i < qdcount; i++)
-  {                             /*{{{ */
-    ppkt->queries[i] = GNUNET_malloc (sizeof (struct dns_query));
-    char *name = alloca (255);  /* see RFC1035, it can't be more than this. */
-
-    _idx = parse_dns_name (name, pkt->data, idx);
-    ppkt->queries[i]->namelen = _idx - idx;
-    idx = _idx;
-
-    ppkt->queries[i]->name = GNUNET_malloc (ppkt->queries[i]->namelen);
-    memcpy (ppkt->queries[i]->name, name, ppkt->queries[i]->namelen);
-
-    ppkt->queries[i]->qtype = *((unsigned short *) (pkt->data + idx));
-    idx += 2;
-    ppkt->queries[i]->qclass = *((unsigned short *) (pkt->data + idx));
-    idx += 2;
-  }
-  /*}}} */
-  idx = parse_dns_record (pkt->data, ppkt->answers, ancount, idx);
-  idx = parse_dns_record (pkt->data, ppkt->nameservers, nscount, idx);
-  idx = parse_dns_record (pkt->data, ppkt->additional, arcount, idx);
-  return ppkt;
-}                               /*}}} */
-
-static void
-unparse_dns_name (char *dest, char *src, size_t len)
-{
-  char *b = dest;
-  char cnt = 0;
-
-  dest++;
-  while (*src != 0)
-  {
-    while (*src != '.' && *src != 0)
-    {
-      *dest = *src;
-      src++;
-      dest++;
-      cnt++;
-    }
-    *b = cnt;
-    cnt = 0;
-    b = dest;
-    dest++;
-    src++;
-  }
-  *b = 0;
-}
-
-struct dns_pkt *
-unparse_dns_packet (struct dns_pkt_parsed *ppkt)
-{
-  size_t size = sizeof (struct dns_pkt) - 1;
-  int i;
-
-  for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
-    size += ppkt->queries[i]->namelen + 1;
-
-  for (i = 0; i < ntohs (ppkt->s.ancount); i++)
-  {
-    size += ppkt->answers[i]->namelen + 1;
-    size += ppkt->answers[i]->data_len;
-  }
-  for (i = 0; i < ntohs (ppkt->s.nscount); i++)
-  {
-    size += ppkt->nameservers[i]->namelen + 1;
-    size += ppkt->nameservers[i]->data_len;
-  }
-  for (i = 0; i < ntohs (ppkt->s.arcount); i++)
-  {
-    size += ppkt->additional[i]->namelen + 1;
-    size += ppkt->additional[i]->data_len;
-  }
-
-  size +=
-      4 * ntohs (ppkt->s.qdcount) + 10 * (ntohs (ppkt->s.ancount) +
-                                          ntohs (ppkt->s.arcount) +
-                                          ntohs (ppkt->s.nscount));
-
-  struct dns_pkt *pkt = GNUNET_malloc (size);
-  char *pkt_c = (char *) pkt;
-
-  memcpy (&pkt->s, &ppkt->s, sizeof ppkt->s);
-  size_t idx = sizeof ppkt->s;
-
-  for (i = 0; i < ntohs (ppkt->s.qdcount); i++)
-  {
-    unparse_dns_name (&pkt_c[idx], ppkt->queries[i]->name,
-                      ppkt->queries[i]->namelen);
-    idx += ppkt->queries[i]->namelen;
-    struct dns_query_line *d = (struct dns_query_line *) &pkt_c[idx];
-
-    d->class = ppkt->queries[i]->qclass;
-    d->type = ppkt->queries[i]->qtype;
-    idx += sizeof (struct dns_query_line);
-  }
-
-  for (i = 0; i < ntohs (ppkt->s.ancount); i++)
-  {
-    unparse_dns_name (&pkt_c[idx], ppkt->answers[i]->name,
-                      ppkt->answers[i]->namelen);
-    idx += ppkt->answers[i]->namelen;
-    struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
-
-    r->type = ppkt->answers[i]->type;
-    r->class = ppkt->answers[i]->class;
-    r->ttl = ppkt->answers[i]->ttl;
-    r->data_len = ppkt->answers[i]->data_len;
-    idx += sizeof (struct dns_record_line);
-    memcpy (&r->data, ppkt->answers[i]->data, ppkt->answers[i]->data_len);
-    idx += ppkt->answers[i]->data_len;
-  }
-
-  for (i = 0; i < ntohs (ppkt->s.nscount); i++)
-  {
-    unparse_dns_name (&pkt_c[idx], ppkt->nameservers[i]->name,
-                      ppkt->nameservers[i]->namelen);
-    idx += ppkt->nameservers[i]->namelen;
-    struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
-
-    r->type = ppkt->nameservers[i]->type;
-    r->class = ppkt->nameservers[i]->class;
-    r->ttl = ppkt->nameservers[i]->ttl;
-    r->data_len = ppkt->nameservers[i]->data_len;
-    idx += sizeof (struct dns_record_line);
-    memcpy (&r->data, ppkt->nameservers[i]->data,
-            ppkt->nameservers[i]->data_len);
-    idx += ppkt->nameservers[i]->data_len;
-  }
-
-  for (i = 0; i < ntohs (ppkt->s.arcount); i++)
+  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++)
   {
   {
-    unparse_dns_name (&pkt_c[idx], ppkt->additional[i]->name,
-                      ppkt->additional[i]->namelen);
-    idx += ppkt->additional[i]->namelen;
-    struct dns_record_line *r = (struct dns_record_line *) &pkt_c[idx];
-
-    r->type = ppkt->additional[i]->type;
-    r->class = ppkt->additional[i]->class;
-    r->ttl = ppkt->additional[i]->ttl;
-    r->data_len = ppkt->additional[i]->data_len;
-    idx += sizeof (struct dns_record_line);
-    memcpy (&r->data, ppkt->additional[i]->data, ppkt->additional[i]->data_len);
-    idx += ppkt->additional[i]->data_len;
+    in[0] = tolower ((int) hex[off * 2]);
+    in[1] = tolower ((int) hex[off * 2 + 1]);
+    if (1 != sscanf (in, "%x", &h))
+      return off;
+    idata[off] = (uint8_t) h;
   }
   }
-
-  return pkt;
+  return off;
 }
 
 }
 
-void
-free_parsed_dns_packet (struct dns_pkt_parsed *ppkt)
-{
-  unsigned short qdcount = ntohs (ppkt->s.qdcount);
-  unsigned short ancount = ntohs (ppkt->s.ancount);
-  unsigned short nscount = ntohs (ppkt->s.nscount);
-  unsigned short arcount = ntohs (ppkt->s.arcount);
-
-  int i;
 
 
-  for (i = 0; i < qdcount; i++)
-  {
-    GNUNET_free (ppkt->queries[i]->name);
-    GNUNET_free (ppkt->queries[i]);
-  }
-  GNUNET_free (ppkt->queries);
-  for (i = 0; i < ancount; i++)
-  {
-    GNUNET_free (ppkt->answers[i]->name);
-    GNUNET_free (ppkt->answers[i]->data);
-    GNUNET_free (ppkt->answers[i]);
-  }
-  GNUNET_free (ppkt->answers);
-  for (i = 0; i < nscount; i++)
-  {
-    GNUNET_free (ppkt->nameservers[i]->name);
-    GNUNET_free (ppkt->nameservers[i]->data);
-    GNUNET_free (ppkt->nameservers[i]);
-  }
-  GNUNET_free (ppkt->nameservers);
-  for (i = 0; i < arcount; i++)
-  {
-    GNUNET_free (ppkt->additional[i]->name);
-    GNUNET_free (ppkt->additional[i]->data);
-    GNUNET_free (ppkt->additional[i]);
-  }
-  GNUNET_free (ppkt->additional);
-  GNUNET_free (ppkt);
-}
+/* end of dnsparser.c */