/** * We're done modifying all records in the response. Submit the reply * and free the resources of the rc. * * @param rc context to process */ static void finish_request (struct ReplyContext *rc) { char *buf; size_t buf_len; if (GNUNET_SYSERR == GNUNET_DNSPARSER_pack (rc->dns, MAX_DNS_SIZE, &buf, &buf_len)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to pack DNS request. Dropping.\n")); GNUNET_DNS_request_drop (rc->rh); } else { GNUNET_STATISTICS_update (stats, gettext_noop ("# DNS requests mapped to VPN"), 1, GNUNET_NO); GNUNET_DNS_request_answer (rc->rh, buf_len, buf); GNUNET_free (buf); } GNUNET_DNSPARSER_free_packet (rc->dns); GNUNET_free (rc); }
/** * Signature of a function that is called whenever the DNS service * encounters a DNS request and needs to do something with it. The * function has then the chance to generate or modify the response by * calling one of the three "GNUNET_DNS_request_*" continuations. * * When a request is intercepted, this function is called first to * give the client a chance to do the complete address resolution; * "rdata" will be NULL for this first call for a DNS request, unless * some other client has already filled in a response. * * If multiple clients exist, all of them are called before the global * DNS. The global DNS is only called if all of the clients' * functions call GNUNET_DNS_request_forward. Functions that call * GNUNET_DNS_request_forward will be called again before a final * response is returned to the application. If any of the clients' * functions call GNUNET_DNS_request_drop, the response is dropped. * * @param cls closure * @param rh request handle to user for reply * @param request_length number of bytes in request * @param request udp payload of the DNS request */ static void modify_request (void *cls, struct GNUNET_DNS_RequestHandle *rh, size_t request_length, const char *request) { struct GNUNET_DNSPARSER_Packet *p; unsigned int i; char *buf; size_t len; int ret; p = GNUNET_DNSPARSER_parse (request, request_length); if (NULL == p) { fprintf (stderr, "Received malformed DNS packet, leaving it untouched\n"); GNUNET_DNS_request_forward (rh); return; } for (i=0;i<p->num_answers;i++) modify_record (&p->answers[i]); buf = NULL; ret = GNUNET_DNSPARSER_pack (p, 1024, &buf, &len); GNUNET_DNSPARSER_free_packet (p); if (GNUNET_OK != ret) { if (GNUNET_NO == ret) fprintf (stderr, "Modified DNS response did not fit, keeping old response\n"); else GNUNET_break (0); /* our modifications should have been sane! */ GNUNET_DNS_request_forward (rh); } else { if (verbosity > 0) fprintf (stdout, "Injecting modified DNS response\n"); GNUNET_DNS_request_answer (rh, len, buf); } GNUNET_free_non_null (buf); }
/** * 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); }