/** * 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); }