diff options
Diffstat (limited to 'src/gns/gnunet-service-gns_interceptor.c')
-rw-r--r-- | src/gns/gnunet-service-gns_interceptor.c | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/src/gns/gnunet-service-gns_interceptor.c b/src/gns/gnunet-service-gns_interceptor.c new file mode 100644 index 0000000..adb09ca --- /dev/null +++ b/src/gns/gnunet-service-gns_interceptor.c @@ -0,0 +1,394 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * + * @file gns/gnunet-service-gns_interceptor.c + * @brief GNUnet GNS interceptor logic + * @author Martin Schanzenbach + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_transport_service.h" +#include "gnunet_dns_service.h" +#include "gnunet_dnsparser_lib.h" +#include "gnunet-service-gns_resolver.h" +#include "gns.h" + +#define MAX_DNS_LABEL_LENGTH 63 + +/** + * Handle to a DNS intercepted + * reslution request + */ +struct InterceptLookupHandle +{ + /* the request handle to reply to */ + struct GNUNET_DNS_RequestHandle *request_handle; + + /* the dns parser packet received */ + struct GNUNET_DNSPARSER_Packet *packet; + + /* the query parsed from the packet */ + struct GNUNET_DNSPARSER_Query *query; +}; + + +/** + * Our handle to the DNS handler library + */ +static struct GNUNET_DNS_Handle *dns_handle; + +/** + * The root zone for this interceptor + */ +static struct GNUNET_CRYPTO_ShortHashCode our_zone; + +/** + * Our priv key + */ +static struct GNUNET_CRYPTO_RsaPrivateKey *our_key; + +/** + * Default timeout + */ +static struct GNUNET_TIME_Relative default_lookup_timeout; + +/** + * 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_NAMESTORE_RecordData *rd) +{ + int i; + size_t len; + int ret; + char *buf; + struct InterceptLookupHandle* ilh = (struct InterceptLookupHandle*)cls; + struct GNUNET_DNSPARSER_Packet *packet = ilh->packet; + unsigned int num_answers = 0; + + + /** + * Put records in the DNS packet and modify it + * to a response + */ + for (i=0; i < rd_count; i++) + { + if (rd[i].record_type == ilh->query->type) + num_answers++; + } + + 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; + + for (i=0; i < rd_count; i++) + { + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, + "Adding type %d to DNS response\n", rd[i].record_type); + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Name: %s\n", ilh->query->name); + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Record %d/%d\n", i+1, rd_count); + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Record len %d\n", rd[i].data_size); + + if (rd[i].record_type == ilh->query->type) + { + answer_records[i].name = ilh->query->name; + answer_records[i].type = rd[i].record_type; + switch(rd[i].record_type) + { + case GNUNET_GNS_RECORD_TYPE_NS: + case GNUNET_GNS_RECORD_TYPE_CNAME: + case GNUNET_GNS_RECORD_TYPE_PTR: + answer_records[i].data.hostname = (char*)rd[i].data; + break; + case GNUNET_GNS_RECORD_TYPE_SOA: + answer_records[i].data.soa = + (struct GNUNET_DNSPARSER_SoaRecord *)rd[i].data; + break; + case GNUNET_GNS_RECORD_MX: + answer_records[i].data.mx = + (struct GNUNET_DNSPARSER_MxRecord *)rd[i].data; + break; + default: + answer_records[i].data.raw.data_len = rd[i].data_size; + answer_records[i].data.raw.data = (char*)rd[i].data; + } + answer_records[i].expiration_time = rd[i].expiration; + answer_records[i].class = GNUNET_DNSPARSER_CLASS_INTERNET;//hmmn + } + else + { + additional_records[i].name = ilh->query->name; + additional_records[i].type = rd[i].record_type; + switch(rd[i].record_type) + { + case GNUNET_GNS_RECORD_TYPE_NS: + case GNUNET_GNS_RECORD_TYPE_CNAME: + case GNUNET_GNS_RECORD_TYPE_PTR: + additional_records[i].data.hostname = (char*)rd[i].data; + break; + case GNUNET_GNS_RECORD_TYPE_SOA: + additional_records[i].data.soa = + (struct GNUNET_DNSPARSER_SoaRecord *)rd[i].data; + break; + case GNUNET_GNS_RECORD_MX: + additional_records[i].data.mx = + (struct GNUNET_DNSPARSER_MxRecord *)rd[i].data; + break; + default: + additional_records[i].data.raw.data_len = rd[i].data_size; + additional_records[i].data.raw.data = (char*)rd[i].data; + } + additional_records[i].expiration_time = rd[i].expiration; + additional_records[i].class = GNUNET_DNSPARSER_CLASS_INTERNET;//hmmn + } + } + + packet->num_answers = num_answers; + packet->num_additional_records = rd_count-(num_answers); + + packet->flags.authoritative_answer = 1; + + if (rd == NULL) + packet->flags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NAME_ERROR; + else + packet->flags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NO_ERROR; + + packet->flags.query_or_response = 1; + + + /** + * Reply to DNS + */ + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, + "Building DNS response\n"); + ret = GNUNET_DNSPARSER_pack (packet, + 1024, /* FIXME magic from dns redirector */ + &buf, + &len); + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, + "Built DNS response! (ret=%d,len=%d)\n", ret, len); + if (ret == GNUNET_OK) + { + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, + "Answering DNS request\n"); + GNUNET_DNS_request_answer(ilh->request_handle, + len, + buf); + + GNUNET_free(buf); + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Answered DNS request\n"); + } + else + { + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, + "Error building DNS response! (ret=%d)", ret); + } + + packet->num_answers = 0; + packet->answers = NULL; + packet->num_additional_records = 0; + packet->additional_records = NULL; + GNUNET_DNSPARSER_free_packet(packet); + GNUNET_free(ilh); +} + + +/** + * Entry point for name resolution + * Setup a new query and try to resolve + * + * @param request the request handle of the DNS request from a client + * @param p the DNS query packet we received + * @param q the DNS query we received parsed from p + */ +static void +start_resolution_for_dns(struct GNUNET_DNS_RequestHandle *request, + struct GNUNET_DNSPARSER_Packet *p, + struct GNUNET_DNSPARSER_Query *q) +{ + struct InterceptLookupHandle* ilh; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting resolution for %s (type=%d)!\n", + q->name, q->type); + + ilh = GNUNET_malloc(sizeof(struct InterceptLookupHandle)); + ilh->packet = p; + ilh->query = q; + ilh->request_handle = request; + + /* Start resolution in our zone */ + gns_resolver_lookup_record(our_zone, our_zone, q->type, q->name, + our_key, + default_lookup_timeout, + &reply_to_dns, ilh); +} + + + +/** + * The DNS request handler + * Called for every incoming DNS request. + * + * @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 +handle_dns_request(void *cls, + struct GNUNET_DNS_RequestHandle *rh, + size_t request_length, + const char *request) +{ + struct GNUNET_DNSPARSER_Packet *p; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hijacked a DNS request...processing\n"); + p = GNUNET_DNSPARSER_parse (request, request_length); + + if (NULL == p) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Received malformed DNS packet, leaving it untouched\n"); + GNUNET_DNS_request_forward (rh); + GNUNET_DNSPARSER_free_packet (p); + return; + } + + /** + * Check tld and decide if we or + * legacy dns is responsible + * + * FIXME now in theory there could be more than 1 query in the request + * but if this is case we get into trouble: + * either we query the GNS or the DNS. We cannot do both! + * So I suggest to either only allow a single query per request or + * only allow GNS or DNS requests. + * The way it is implemented here now is buggy and will lead to erratic + * behaviour (if multiple queries are present). + */ + if (p->num_queries == 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No Queries in DNS packet... forwarding\n"); + GNUNET_DNS_request_forward (rh); + GNUNET_DNSPARSER_free_packet(p); + return; + } + + if (p->num_queries > 1) + { + /* Note: We could also look for .gnunet */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + ">1 queriy in DNS packet... odd. We only process #1\n"); + } + + + /** + * Check for .gnunet/.zkey + */ + + if ((is_gnunet_tld(p->queries[0].name) == GNUNET_YES) || + (is_zkey_tld(p->queries[0].name) == GNUNET_YES) || + (strcmp(p->queries[0].name, GNUNET_GNS_TLD) == 0)) + { + start_resolution_for_dns(rh, p, p->queries); + } + else + { + /** + * This request does not concern us. Forward to real DNS. + */ + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, + "Request for %s is forwarded to DNS\n", p->queries[0].name); + GNUNET_DNS_request_forward (rh); + GNUNET_DNSPARSER_free_packet (p); + } + +} + + +/** + * Initialized the interceptor + * + * @param zone the zone to work in + * @param key the prov key of the zone (can be null, needed for caching) + * @param c the configuration + * @return GNUNET_OK on success + */ +int +gns_interceptor_init(struct GNUNET_CRYPTO_ShortHashCode zone, + struct GNUNET_CRYPTO_RsaPrivateKey *key, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + unsigned long long default_lookup_timeout_secs = 0; + + GNUNET_log(GNUNET_ERROR_TYPE_INFO, + "DNS hijacking enabled... connecting to service.\n"); + + our_zone = zone; + our_key = key; + /** + * Do gnunet dns init here + */ + dns_handle = GNUNET_DNS_connect(c, + GNUNET_DNS_FLAG_PRE_RESOLUTION, + &handle_dns_request, /* rh */ + NULL); /* Closure */ + + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number(c, "gns", + "DEFAULT_LOOKUP_TIMEOUT", + &default_lookup_timeout_secs)) + { + default_lookup_timeout = GNUNET_TIME_relative_multiply( + GNUNET_TIME_UNIT_SECONDS, + default_lookup_timeout_secs); + } + + if (NULL == dns_handle) + { + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, + "Failed to connect to the dnsservice!\n"); + return GNUNET_SYSERR; + } + + return GNUNET_YES; +} + +/** + * Disconnect from interceptor + */ +void +gns_interceptor_stop(void) +{ + if (dns_handle) + GNUNET_DNS_disconnect(dns_handle); +} + +/* end of gns_interceptor.c */ |