示例#1
0
文件: dnsparser.c 项目: GNUnet/gnunet
/**
 * Parse a DNS record entry.
 *
 * @param udp_payload entire 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
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the record is malformed
 */
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;
  uint16_t data_len;

  name = GNUNET_DNSPARSER_parse_name (udp_payload,
				      udp_payload_length,
				      off);
  if (NULL == name)
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  r->name = name;
  if (*off + sizeof (struct GNUNET_TUN_DnsRecordLine) > udp_payload_length)
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  GNUNET_memcpy (&rl, &udp_payload[*off], sizeof (rl));
  (*off) += sizeof (rl);
  r->type = ntohs (rl.type);
  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)
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  old_off = *off;
  switch (r->type)
  {
  case GNUNET_DNSPARSER_TYPE_NS:
  case GNUNET_DNSPARSER_TYPE_CNAME:
  case GNUNET_DNSPARSER_TYPE_PTR:
    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:
    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_OK;
  case GNUNET_DNSPARSER_TYPE_MX:
    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_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_OK;
  default:
    r->data.raw.data = GNUNET_malloc (data_len);
    r->data.raw.data_len = data_len;
    GNUNET_memcpy (r->data.raw.data, &udp_payload[*off], data_len);
    break;
  }
  (*off) += data_len;
  return GNUNET_OK;
}
/**
 * Reply to dns request with the result from our lookup.
 *
 * @param cls the closure to the request (an InterceptLookupHandle)
 * @param rd_count the number of records to return
 * @param rd the record data
 */
static void
reply_to_dns (void *cls, uint32_t rd_count,
	      const struct GNUNET_GNSRECORD_Data *rd)
{
  struct InterceptLookupHandle *ilh = cls;
  struct GNUNET_DNSPARSER_Packet *packet = ilh->packet;
  struct GNUNET_DNSPARSER_Query *query = &packet->queries[0];
  uint32_t i;
  size_t len;
  int ret;
  char *buf;
  unsigned int num_answers;
  unsigned int skip_answers;
  unsigned int skip_additional;
  size_t off;

  /* Put records in the DNS packet */
  num_answers = 0;
  for (i=0; i < rd_count; i++)
    if (rd[i].record_type == query->type)
      num_answers++;
  skip_answers = 0;
  skip_additional = 0;

  {
    struct GNUNET_DNSPARSER_Record answer_records[num_answers];
    struct GNUNET_DNSPARSER_Record additional_records[rd_count - num_answers];

    packet->answers = answer_records;
    packet->additional_records = additional_records;
    /* FIXME: need to handle #GNUNET_GNSRECORD_RF_SHADOW_RECORD option
       (by ignoring records where this flag is set if there is any
       other record of that type in the result set) */
    for (i=0; i < rd_count; i++)
    {
      if (rd[i].record_type == query->type)
      {
	answer_records[i - skip_answers].name = query->name;
	answer_records[i - skip_answers].type = rd[i].record_type;
	switch(rd[i].record_type)
	{
	case GNUNET_DNSPARSER_TYPE_NS:
	case GNUNET_DNSPARSER_TYPE_CNAME:
	case GNUNET_DNSPARSER_TYPE_PTR:
	  answer_records[i - skip_answers].data.hostname
	    = GNUNET_DNSPARSER_parse_name (rd[i].data,
					   rd[i].data_size,
					   &off);
	  if ( (off != rd[i].data_size) ||
	       (NULL == answer_records[i].data.hostname) )
	  {
	    GNUNET_break_op (0);
	    skip_answers++;
	  }
	  break;
	case GNUNET_DNSPARSER_TYPE_SOA:
	  answer_records[i - skip_answers].data.soa
	    = GNUNET_DNSPARSER_parse_soa (rd[i].data,
					  rd[i].data_size,
					  &off);
	  if ( (off != rd[i].data_size) ||
	       (NULL == answer_records[i].data.soa) )
	  {
	    GNUNET_break_op (0);
	    skip_answers++;
	  }
	  break;
	case GNUNET_DNSPARSER_TYPE_SRV:
	  /* FIXME: SRV is not yet supported */
	  skip_answers++;
	  break;
	case GNUNET_DNSPARSER_TYPE_MX:
	  answer_records[i - skip_answers].data.mx
	    = GNUNET_DNSPARSER_parse_mx (rd[i].data,
					 rd[i].data_size,
					 &off);
	  if ( (off != rd[i].data_size) ||
	       (NULL == answer_records[i].data.hostname) )
	  {
	    GNUNET_break_op (0);
	    skip_answers++;
	  }
	  break;
	default:
	  answer_records[i - skip_answers].data.raw.data_len = rd[i].data_size;
	  answer_records[i - skip_answers].data.raw.data = (char*)rd[i].data;
	  break;
	}
	GNUNET_break (0 == (rd[i - skip_answers].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
	answer_records[i - skip_answers].expiration_time.abs_value_us = rd[i].expiration_time;
	answer_records[i - skip_answers].dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
      }
      else
      {
	additional_records[i - skip_additional].name = query->name;
	additional_records[i - skip_additional].type = rd[i].record_type;
	switch(rd[i].record_type)
	{
	case GNUNET_DNSPARSER_TYPE_NS:
	case GNUNET_DNSPARSER_TYPE_CNAME:
	case GNUNET_DNSPARSER_TYPE_PTR:
	  additional_records[i - skip_additional].data.hostname
	    = GNUNET_DNSPARSER_parse_name (rd[i].data,
					   rd[i].data_size,
					   &off);
	  if ( (off != rd[i].data_size) ||
	       (NULL == additional_records[i].data.hostname) )
	  {
	    GNUNET_break_op (0);
	    skip_additional++;
	  }
	  break;
	case GNUNET_DNSPARSER_TYPE_SOA:
	  additional_records[i - skip_additional].data.soa
	    = GNUNET_DNSPARSER_parse_soa (rd[i].data,
					  rd[i].data_size,
					  &off);
	  if ( (off != rd[i].data_size) ||
	       (NULL == additional_records[i].data.hostname) )
	  {
	    GNUNET_break_op (0);
	    skip_additional++;
	  }
	  break;
	case GNUNET_DNSPARSER_TYPE_MX:
	  additional_records[i - skip_additional].data.mx
	    = GNUNET_DNSPARSER_parse_mx (rd[i].data,
					 rd[i].data_size,
					 &off);
	  if ( (off != rd[i].data_size) ||
	       (NULL == additional_records[i].data.hostname) )
	  {
	    GNUNET_break_op (0);
	    skip_additional++;
	  }
	  break;
	case GNUNET_DNSPARSER_TYPE_SRV:
	  /* FIXME: SRV is not yet supported */
	  skip_answers++;
	  break;
	default:
	  additional_records[i - skip_additional].data.raw.data_len = rd[i].data_size;
	  additional_records[i - skip_additional].data.raw.data = (char*)rd[i].data;
	  break;
	}
	GNUNET_break (0 == (rd[i - skip_additional].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
	additional_records[i - skip_additional].expiration_time.abs_value_us = rd[i].expiration_time;
	additional_records[i - skip_additional].dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
      }
    }
    packet->num_answers = num_answers - skip_answers;
    packet->num_additional_records = rd_count - num_answers - skip_additional;
    packet->flags.authoritative_answer = 1;
    if (NULL == rd)
      packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NAME_ERROR;
    else
      packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NO_ERROR;
    packet->flags.query_or_response = 1;
    ret = GNUNET_DNSPARSER_pack (packet,
				 1024, /* maximum allowed size for DNS reply */
				 &buf,
				 &len);
    if (GNUNET_OK != ret)
    {
      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
		  _("Error converting GNS response to DNS response!\n"));
    }
    else
    {
      GNUNET_DNS_request_answer (ilh->request_handle,
				 len,
				 buf);
      GNUNET_free (buf);
    }
    packet->num_answers = 0;
    packet->answers = NULL;
    packet->num_additional_records = 0;
    packet->additional_records = NULL;
    GNUNET_DNSPARSER_free_packet (packet);
  }
  GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
  GNUNET_free (ilh);
}