draft hex2bin, bin2hex
[oweals/gnunet.git] / src / dns / dnsparser.c
index 45408909c72ae0876bcaaa5cb1cfc5b208ed176c..2be34fc15fd0f6f250519a6023fd5b8348ab9543 100644 (file)
@@ -1,10 +1,10 @@
 /*
       This file is part of GNUnet
 /*
       This file is part of GNUnet
-      (C) 2010-2013 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
@@ -20,7 +20,7 @@
 
 /**
  * @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
  */
  * @author Philipp Toelke
  * @author Christian Grothoff
  */
@@ -47,10 +47,10 @@ GNUNET_DNSPARSER_check_label (const char *label)
 {
   char *output;
   size_t slen;
 {
   char *output;
   size_t slen;
-  
+
   if (NULL != strchr (label, '.'))
     return GNUNET_SYSERR; /* not a label! Did you mean GNUNET_DNSPARSER_check_name? */
   if (NULL != strchr (label, '.'))
     return GNUNET_SYSERR; /* not a label! Did you mean GNUNET_DNSPARSER_check_name? */
-  if (IDNA_SUCCESS != 
+  if (IDNA_SUCCESS !=
       idna_to_ascii_8z (label, &output, IDNA_ALLOW_UNASSIGNED))
     return GNUNET_SYSERR;
   slen = strlen (output);
       idna_to_ascii_8z (label, &output, IDNA_ALLOW_UNASSIGNED))
     return GNUNET_SYSERR;
   slen = strlen (output);
@@ -78,7 +78,7 @@ GNUNET_DNSPARSER_check_name (const char *name)
   char *output;
   size_t slen;
   char *tok;
   char *output;
   size_t slen;
   char *tok;
-  
+
   ldup = GNUNET_strdup (name);
   for (tok = strtok (ldup, "."); NULL != tok; tok = strtok (NULL, "."))
     if (GNUNET_OK !=
   ldup = GNUNET_strdup (name);
   for (tok = strtok (ldup, "."); NULL != tok; tok = strtok (NULL, "."))
     if (GNUNET_OK !=
@@ -88,7 +88,7 @@ GNUNET_DNSPARSER_check_name (const char *name)
       return GNUNET_SYSERR;
     }
   GNUNET_free (ldup);
       return GNUNET_SYSERR;
     }
   GNUNET_free (ldup);
-  if (IDNA_SUCCESS != 
+  if (IDNA_SUCCESS !=
       idna_to_ascii_8z (name, &output, IDNA_ALLOW_UNASSIGNED))
     return GNUNET_SYSERR;
   slen = strlen (output);
       idna_to_ascii_8z (name, &output, IDNA_ALLOW_UNASSIGNED))
     return GNUNET_SYSERR;
   slen = strlen (output);
@@ -101,6 +101,102 @@ GNUNET_DNSPARSER_check_name (const char *name)
 }
 
 
 }
 
 
+/**
+ * Free SOA information record.
+ *
+ * @param soa record to free
+ */
+void
+GNUNET_DNSPARSER_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 CERT information record.
+ *
+ * @param cert record to free
+ */
+void
+GNUNET_DNSPARSER_free_cert (struct GNUNET_DNSPARSER_CertRecord *cert)
+{
+  if (NULL == cert)
+    return;
+  GNUNET_free_non_null (cert->certificate_data);
+  GNUNET_free (cert);
+}
+
+
+/**
+ * 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.
  *
 /**
  * Parse name inside of a DNS query or record.
  *
@@ -125,12 +221,15 @@ parse_name (const char *udp_payload,
   size_t xoff;
   char *utf8;
   Idna_rc rc;
   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)
     {
@@ -140,7 +239,10 @@ 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,
                       "%.*s",
                       (int) len,
       GNUNET_asprintf (&tmp,
                       "%.*s",
                       (int) len,
@@ -179,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,
@@ -198,26 +309,49 @@ 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.
  *
 /**
  * Parse a DNS query entry.
  *
@@ -228,31 +362,207 @@ parse_name (const char *udp_payload,
  * @param q where to write the query information
  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the query is malformed
  */
  * @param q where to write the query information
  * @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;
   struct GNUNET_TUN_DnsQueryLine ql;
 
 {
   char *name;
   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;
   if (*off + sizeof (struct GNUNET_TUN_DnsQueryLine) > udp_payload_length)
   q->name = name;
   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.
  *
 /**
  * Parse a DNS record entry.
  *
@@ -263,132 +573,88 @@ parse_query (const char *udp_payload,
  * @param r where to write the record information
  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the record is malformed
  */
  * @param r where to write the record information
  * @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;
   struct GNUNET_TUN_DnsRecordLine rl;
   size_t old_off;
 {
   char *name;
   struct GNUNET_TUN_DnsRecordLine rl;
   size_t old_off;
-  struct GNUNET_TUN_DnsSoaRecord soa;
-  uint16_t mxpref;
   uint16_t data_len;
   uint16_t data_len;
-  struct GNUNET_TUN_DnsSrvRecord 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;
   if (*off + sizeof (struct GNUNET_TUN_DnsRecordLine) > udp_payload_length)
   r->name = name;
   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 (struct GNUNET_TUN_DnsSoaRecord) > udp_payload_length) )
-      return GNUNET_SYSERR;
-    memcpy (&soa, &udp_payload[*off], sizeof (struct GNUNET_TUN_DnsSoaRecord));
-    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 GNUNET_TUN_DnsSoaRecord);
-    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 GNUNET_TUN_DnsSrvRecord) > udp_payload_length)
-      return GNUNET_SYSERR;
-    memcpy (&srv, &udp_payload[*off], sizeof (struct GNUNET_TUN_DnsSrvRecord));    
-    (*off) += sizeof (struct GNUNET_TUN_DnsSrvRecord);
-    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 (NULL != tok);
-    GNUNET_assert ('_' == *tok);
-    r->data.srv->service = GNUNET_strdup (&tok[1]);
-    tok = strtok (NULL, ".");
-    if ( (NULL == tok) || ('_' != *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);
     return GNUNET_OK;
   default:
     r->data.raw.data = GNUNET_malloc (data_len);
@@ -397,7 +663,7 @@ parse_record (const char *udp_payload,
     break;
   }
   (*off) += data_len;
     break;
   }
   (*off) += data_len;
-  return GNUNET_OK;  
+  return GNUNET_OK;
 }
 
 
 }
 
 
@@ -406,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 @a 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 *
@@ -416,7 +682,7 @@ 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))
   unsigned int i;
 
   if (udp_payload_length < sizeof (struct GNUNET_TUN_DnsHeader))
@@ -433,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);
@@ -446,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);
@@ -459,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)
@@ -472,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.
  *
@@ -575,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);
 }
@@ -591,22 +782,23 @@ 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 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
  * @return #GNUNET_SYSERR if @a name is invalid
  *         #GNUNET_NO if @a name did not fit
  * @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
  * @return #GNUNET_SYSERR if @a name is invalid
  *         #GNUNET_NO if @a name did not fit
- *         #GNUNET_OK if @a name was added to 'dst'
+ *         #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 *idna_name;
 {
   const char *dot;
   const char *idna_name;
@@ -619,10 +811,10 @@ add_name (char *dst,
   if (NULL == name)
     return GNUNET_SYSERR;
 
   if (NULL == name)
     return GNUNET_SYSERR;
 
-  if (IDNA_SUCCESS != 
+  if (IDNA_SUCCESS !=
       (rc = idna_to_ascii_8z (name, &idna_start, IDNA_ALLOW_UNASSIGNED)))
   {
       (rc = idna_to_ascii_8z (name, &idna_start, IDNA_ALLOW_UNASSIGNED)))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                _("Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
                name,
                idna_strerror (rc));
                _("Failed to convert UTF-8 name `%s' to DNS IDNA format: %s\n"),
                name,
                idna_strerror (rc));
@@ -640,8 +832,11 @@ add_name (char *dst,
       len = strlen (idna_name);
     else
       len = dot - idna_name;
       len = strlen (idna_name);
     else
       len = dot - idna_name;
-    if ( (len >= 64) || (len == 0) )
-      goto fail; /* segment too long or empty */  
+    if ( (len >= 64) || (0 == len) )
+    {
+      GNUNET_break (0);
+      goto fail; /* segment too long or empty */
+    }
     dst[pos++] = (char) (uint8_t) len;
     memcpy (&dst[pos], idna_name, len);
     pos += len;
     dst[pos++] = (char) (uint8_t) len;
     memcpy (&dst[pos], idna_name, len);
     pos += len;
@@ -662,7 +857,7 @@ add_name (char *dst,
 #else
   free (idna_start);
 #endif
 #else
   free (idna_start);
 #endif
-  return GNUNET_NO; 
+  return GNUNET_NO;
 }
 
 
 }
 
 
@@ -670,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 @a 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;
   struct GNUNET_TUN_DnsQueryLine ql;
 
 {
   int ret;
   struct GNUNET_TUN_DnsQueryLine ql;
 
-  ret = add_name (dst, dst_len - sizeof (struct GNUNET_TUN_DnsQueryLine), 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;
@@ -702,7 +897,7 @@ 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
@@ -710,11 +905,11 @@ add_query (char *dst,
  *         #GNUNET_NO if @a mx did not fit
  *         #GNUNET_OK if @a mx was added to @a dst
  */
  *         #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;
 
@@ -723,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;
 }
 
 
 }
 
 
@@ -731,31 +966,31 @@ 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
  * @return #GNUNET_SYSERR if @a soa is invalid
  *         #GNUNET_NO if @a soa did not fit
  * @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 @a soa is invalid
  *         #GNUNET_NO if @a soa did not fit
- *         #GNUNET_OK if @a soa was added to 'dst'
+ *         #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 GNUNET_TUN_DnsSoaRecord sd;
   int ret;
 
 {
   struct GNUNET_TUN_DnsSoaRecord sd;
   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;
   if (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > dst_len)
     return GNUNET_NO;
     return ret;
   if (*off + sizeof (struct GNUNET_TUN_DnsSoaRecord) > dst_len)
     return GNUNET_NO;
@@ -774,19 +1009,19 @@ 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
  * @return #GNUNET_SYSERR if @a srv is invalid
  *         #GNUNET_NO if @a srv did not fit
  * @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 'dst'
+ *         #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 GNUNET_TUN_DnsSrvRecord sd;
   int ret;
 {
   struct GNUNET_TUN_DnsSrvRecord sd;
   int ret;
@@ -798,7 +1033,7 @@ add_srv (char *dst,
   sd.port = htons (srv->port);
   memcpy (&dst[*off], &sd, sizeof (sd));
   (*off) += sizeof (sd);
   sd.port = htons (srv->port);
   memcpy (&dst[*off], &sd, 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)))
@@ -811,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 @a 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,
@@ -829,42 +1064,35 @@ add_record (char *dst,
   size_t start;
   size_t pos;
   struct GNUNET_TUN_DnsRecordLine rl;
   size_t start;
   size_t pos;
   struct GNUNET_TUN_DnsRecordLine rl;
-  char *name;
-  
+
   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 GNUNET_TUN_DnsRecordLine), 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 */
 
   pos = *off + sizeof (struct GNUNET_TUN_DnsRecordLine);
   switch (record->type)
   if (GNUNET_OK != ret)
     return ret;
   /* '*off' is now the position where we will need to write the record line */
 
   pos = *off + sizeof (struct GNUNET_TUN_DnsRecordLine);
   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)
@@ -890,19 +1118,19 @@ add_record (char *dst,
     return GNUNET_NO;
   }
   rl.type = htons (record->type);
     return GNUNET_NO;
   }
   rl.type = htons (record->type);
-  rl.class = htons (record->class);
+  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;
   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;
-  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
  * simple and secure, not fast, fancy and broken like bind.
  *
  * @param p packet to pack
@@ -918,14 +1146,14 @@ GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
                       uint16_t max,
                       char **buf,
                       size_t *buf_length)
                       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) ||
@@ -942,55 +1170,55 @@ 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;    
+    dns.flags.message_truncated = 1;
   memcpy (tmp, &dns, sizeof (struct GNUNET_TUN_DnsHeader));
 
   *buf = GNUNET_malloc (off);
   memcpy (tmp, &dns, sizeof (struct GNUNET_TUN_DnsHeader));
 
   *buf = GNUNET_malloc (off);
@@ -1001,4 +1229,63 @@ GNUNET_DNSPARSER_pack (const struct GNUNET_DNSPARSER_Packet *p,
   return GNUNET_OK;
 }
 
   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],
+             "%x",
+             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 ((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 off;
+}
+
+
 /* end of dnsparser.c */
 /* end of dnsparser.c */