+ char *name;
+ struct record_line rl;
+ size_t old_off;
+ struct soa_data soa;
+ uint16_t mxpref;
+ uint16_t data_len;
+ struct srv_data srv;
+ char *ndup;
+ char *tok;
+
+ name = parse_name (udp_payload,
+ udp_payload_length,
+ off, 0);
+ if (NULL == name)
+ return GNUNET_SYSERR;
+ r->name = name;
+ if (*off + sizeof (struct record_line) > udp_payload_length)
+ return GNUNET_SYSERR;
+ memcpy (&rl, &udp_payload[*off], sizeof (rl));
+ (*off) += sizeof (rl);
+ r->type = ntohs (rl.type);
+ r->class = ntohs (rl.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)
+ return GNUNET_SYSERR;
+ 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);
+ if ( (NULL == r->data.hostname) ||
+ (old_off + data_len != *off) )
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+ case GNUNET_DNSPARSER_TYPE_SOA:
+ old_off = *off;
+ r->data.soa = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_SoaRecord));
+ r->data.soa->mname = parse_name (udp_payload,
+ udp_payload_length,
+ off, 0);
+ r->data.soa->rname = parse_name (udp_payload,
+ udp_payload_length,
+ off, 0);
+ if ( (NULL == r->data.soa->mname) ||
+ (NULL == r->data.soa->rname) ||
+ (*off + sizeof (struct soa_data) > udp_payload_length) )
+ return GNUNET_SYSERR;
+ memcpy (&soa, &udp_payload[*off], sizeof (struct soa_data));
+ r->data.soa->serial = ntohl (soa.serial);
+ r->data.soa->refresh = ntohl (soa.refresh);
+ r->data.soa->retry = ntohl (soa.retry);
+ r->data.soa->expire = ntohl (soa.expire);
+ r->data.soa->minimum_ttl = ntohl (soa.minimum);
+ (*off) += sizeof (struct soa_data);
+ if (old_off + data_len != *off)
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+ case GNUNET_DNSPARSER_TYPE_MX:
+ old_off = *off;
+ if (*off + sizeof (uint16_t) > udp_payload_length)
+ return GNUNET_SYSERR;
+ memcpy (&mxpref, &udp_payload[*off], sizeof (uint16_t));
+ (*off) += sizeof (uint16_t);
+ r->data.mx = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_MxRecord));
+ r->data.mx->preference = ntohs (mxpref);
+ r->data.mx->mxhost = parse_name (udp_payload,
+ udp_payload_length,
+ off, 0);
+ if (old_off + data_len != *off)
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+ case GNUNET_DNSPARSER_TYPE_SRV:
+ if ('_' != *r->name)
+ return GNUNET_SYSERR; /* all valid srv names must start with "_" */
+ if (NULL == strstr (r->name, "._"))
+ return GNUNET_SYSERR; /* necessary string from "._$PROTO" not present */
+ old_off = *off;
+ if (*off + sizeof (struct srv_data) > udp_payload_length)
+ return GNUNET_SYSERR;
+ memcpy (&srv, &udp_payload[*off], sizeof (struct srv_data));
+ (*off) += sizeof (struct srv_data);
+ r->data.srv = GNUNET_malloc (sizeof (struct GNUNET_DNSPARSER_SrvRecord));
+ r->data.srv->priority = ntohs (srv.prio);
+ r->data.srv->weight = ntohs (srv.weight);
+ r->data.srv->port = ntohs (srv.port);
+ /* parse 'data.hostname' into components, which are
+ "_$SERVICE._$PROTO.$DOMAIN_NAME" */
+ ndup = GNUNET_strdup (r->name);
+ tok = strtok (ndup, ".");
+ GNUNET_assert (NULL != tok);
+ GNUNET_assert ('_' == *tok);
+ r->data.srv->service = GNUNET_strdup (&tok[1]);
+ tok = strtok (NULL, ".");
+ if ( (NULL == tok) || ('_' != *tok) )
+ {
+ GNUNET_free (r->data.srv);
+ GNUNET_free (ndup);
+ return GNUNET_SYSERR;
+ }
+ r->data.srv->proto = GNUNET_strdup (&tok[1]);
+ tok = strtok (NULL, ".");
+ if (NULL == tok)
+ {
+ GNUNET_free (r->data.srv);
+ GNUNET_free (ndup);
+ return GNUNET_SYSERR;
+ }
+ r->data.srv->domain_name = GNUNET_strdup (tok);
+ GNUNET_free (ndup);
+ r->data.srv->target = parse_name (udp_payload,
+ udp_payload_length,
+ off, 0);
+ if (old_off + data_len != *off)
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+ default:
+ r->data.raw.data = GNUNET_malloc (data_len);
+ r->data.raw.data_len = data_len;
+ memcpy (r->data.raw.data, &udp_payload[*off], data_len);
+ break;
+ }
+ (*off) += data_len;
+ return GNUNET_OK;