/** * 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; } GNUNET_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; GNUNET_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 query entry. * * @param udp_payload entire 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 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the query is malformed */ 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; name = GNUNET_DNSPARSER_parse_name (udp_payload, udp_payload_length, off); if (NULL == name) { GNUNET_break_op (0); return GNUNET_SYSERR; } q->name = name; if (*off + sizeof (struct GNUNET_TUN_DnsQueryLine) > udp_payload_length) { GNUNET_break_op (0); return GNUNET_SYSERR; } GNUNET_memcpy (&ql, &udp_payload[*off], sizeof (ql)); *off += sizeof (ql); q->type = ntohs (ql.type); q->dns_traffic_class = ntohs (ql.dns_traffic_class); 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; } GNUNET_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 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); }
/** * Convert the 'value' of a record to a string. * * @param cls closure, unused * @param type type of the record * @param data value in binary encoding * @param data_size number of bytes in @a data * @return NULL on error, otherwise human-readable representation of the value */ static char * gns_value_to_string (void *cls, uint32_t type, const void *data, size_t data_size) { const char *cdata; switch (type) { case GNUNET_GNSRECORD_TYPE_PKEY: if (data_size != sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) return NULL; return GNUNET_CRYPTO_ecdsa_public_key_to_string (data); case GNUNET_GNSRECORD_TYPE_NICK: return GNUNET_strndup (data, data_size); case GNUNET_GNSRECORD_TYPE_LEHO: return GNUNET_strndup (data, data_size); case GNUNET_GNSRECORD_TYPE_GNS2DNS: { char *ns; char *ip; size_t off; char *nstr; off = 0; ns = GNUNET_DNSPARSER_parse_name (data, data_size, &off); ip = GNUNET_DNSPARSER_parse_name (data, data_size, &off); if ( (NULL == ns) || (NULL == ip) || (off != data_size) ) { GNUNET_break_op (0); GNUNET_free_non_null (ns); GNUNET_free_non_null (ip); return NULL; } GNUNET_asprintf (&nstr, "%s@%s", ns, ip); GNUNET_free_non_null (ns); GNUNET_free_non_null (ip); return nstr; } case GNUNET_GNSRECORD_TYPE_VPN: { const struct GNUNET_TUN_GnsVpnRecord *vpn; char* vpn_str; cdata = data; if ( (data_size <= sizeof (struct GNUNET_TUN_GnsVpnRecord)) || ('\0' != cdata[data_size - 1]) ) return NULL; /* malformed */ vpn = data; GNUNET_asprintf (&vpn_str, "%u %s %s", (unsigned int) ntohs (vpn->proto), (const char*) GNUNET_i2s_full (&vpn->peer), (const char*) &vpn[1]); return vpn_str; } case GNUNET_GNSRECORD_TYPE_BOX: { const struct GNUNET_GNSRECORD_BoxRecord *box; uint32_t rt; char *box_str; char *ival; if (data_size < sizeof (struct GNUNET_GNSRECORD_BoxRecord)) return NULL; /* malformed */ box = data; rt = ntohl (box->record_type); ival = GNUNET_GNSRECORD_value_to_string (rt, &box[1], data_size - sizeof (struct GNUNET_GNSRECORD_BoxRecord)); if (NULL == ival) return NULL; /* malformed */ GNUNET_asprintf (&box_str, "%u %u %u %s", (unsigned int) ntohs (box->protocol), (unsigned int) ntohs (box->service), (unsigned int) rt, ival); GNUNET_free (ival); return box_str; } default: return NULL; } }
/** * Convert the 'value' of a record to a string. * * @param cls closure, unused * @param type type of the record * @param data value in binary encoding * @param data_size number of bytes in @a data * @return NULL on error, otherwise human-readable representation of the value */ static char * gns_value_to_string (void *cls, uint32_t type, const void *data, size_t data_size) { const char *cdata; switch (type) { case GNUNET_GNSRECORD_TYPE_PKEY: if (data_size != sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) return NULL; return GNUNET_CRYPTO_ecdsa_public_key_to_string (data); case GNUNET_GNSRECORD_TYPE_NICK: return GNUNET_strndup (data, data_size); case GNUNET_GNSRECORD_TYPE_LEHO: return GNUNET_strndup (data, data_size); case GNUNET_GNSRECORD_TYPE_GNS2DNS: { char *ns; char *ip; size_t off; char *nstr; off = 0; ns = GNUNET_DNSPARSER_parse_name (data, data_size, &off); ip = GNUNET_DNSPARSER_parse_name (data, data_size, &off); if ( (NULL == ns) || (NULL == ip) || (off != data_size) ) { GNUNET_break_op (0); GNUNET_free_non_null (ns); GNUNET_free_non_null (ip); return NULL; } GNUNET_asprintf (&nstr, "%s@%s", ns, ip); GNUNET_free_non_null (ns); GNUNET_free_non_null (ip); return nstr; } case GNUNET_GNSRECORD_TYPE_VPN: { struct GNUNET_TUN_GnsVpnRecord vpn; char* vpn_str; cdata = data; if ( (data_size <= sizeof (vpn)) || ('\0' != cdata[data_size - 1]) ) return NULL; /* malformed */ /* need to memcpy for alignment */ memcpy (&vpn, data, sizeof (vpn)); GNUNET_asprintf (&vpn_str, "%u %s %s", (unsigned int) ntohs (vpn.proto), (const char*) GNUNET_i2s_full (&vpn.peer), (const char*) &cdata[sizeof (vpn)]); return vpn_str; } case GNUNET_GNSRECORD_TYPE_BOX: { struct GNUNET_GNSRECORD_BoxRecord box; uint32_t rt; char *box_str; char *ival; cdata = data; if (data_size < sizeof (struct GNUNET_GNSRECORD_BoxRecord)) return NULL; /* malformed */ memcpy (&box, data, sizeof (box)); rt = ntohl (box.record_type); ival = GNUNET_GNSRECORD_value_to_string (rt, &cdata[sizeof (box)], data_size - sizeof (box)); if (NULL == ival) return NULL; /* malformed */ GNUNET_asprintf (&box_str, "%u %u %u %s", (unsigned int) ntohs (box.protocol), (unsigned int) ntohs (box.service), (unsigned int) rt, ival); GNUNET_free (ival); return box_str; } case GNUNET_GNSRECORD_TYPE_REVERSE: { struct GNUNET_GNSRECORD_ReverseRecord rev; char *rev_str; char *pkey_str; if (data_size < sizeof (struct GNUNET_GNSRECORD_ReverseRecord)) return NULL; /* malformed */ memcpy (&rev, data, sizeof (rev)); cdata = data; pkey_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&rev.pkey); GNUNET_asprintf (&rev_str, "%s %s %"SCNu64, &cdata[sizeof (rev)], pkey_str, rev.expiration.abs_value_us); GNUNET_free (pkey_str); return rev_str; } default: return NULL; } }