123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- /*
- This file is part of GNUnet.
- Copyright (C) 2009-2013 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- 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
- Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file gns/gnunet-service-gns_interceptor.c
- * @brief GNUnet GNS interceptor logic
- * @author Martin Schanzenbach
- * @author Christian Grothoff
- */
- #include "platform.h"
- #include "gnunet_util_lib.h"
- #include "gnunet_dns_service.h"
- #include "gnunet_dnsparser_lib.h"
- #include "gnunet-service-gns.h"
- #include "gnunet-service-gns_resolver.h"
- #include "gnunet-service-gns_interceptor.h"
- #include "gns.h"
- /**
- * How deep do we allow recursions to go before we abort?
- */
- #define MAX_RECURSION 256
- /**
- * Handle to a DNS intercepted
- * reslution request
- */
- struct InterceptLookupHandle
- {
- /**
- * We keep these in a DLL.
- */
- struct InterceptLookupHandle *next;
- /**
- * We keep these in a DLL.
- */
- struct InterceptLookupHandle *prev;
- /**
- * the request handle to reply to
- */
- struct GNUNET_DNS_RequestHandle *request_handle;
- /**
- * the dns parser packet received
- */
- struct GNUNET_DNSPARSER_Packet *packet;
- /**
- * Handle for the lookup operation.
- */
- struct GNS_ResolverHandle *lookup;
- };
- /**
- * Our handle to the DNS handler library
- */
- static struct GNUNET_DNS_Handle *dns_handle;
- /**
- * Head of the DLL.
- */
- static struct InterceptLookupHandle *ilh_head;
- /**
- * Tail of the DLL.
- */
- static struct InterceptLookupHandle *ilh_tail;
- /**
- * 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 = 0;
- /* 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"));
- if (GNUNET_NO == ret)
- GNUNET_free (buf);
- }
- 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);
- }
- /**
- * The DNS request handler. Called for every incoming DNS request.
- *
- * @param cls closure, unused
- * @param rh request handle to user for reply
- * @param request_length number of bytes in @a 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;
- struct InterceptLookupHandle *ilh;
- struct GNUNET_CRYPTO_EcdsaPublicKey zone;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Hijacked a DNS request. Processing.\n");
- if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
- {
- 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 */
- if (1 != p->num_queries)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Not exactly one query in DNS packet. Forwarding untouched.\n");
- GNUNET_DNS_request_forward (rh);
- GNUNET_DNSPARSER_free_packet (p);
- return;
- }
- /* Check for GNS TLDs. */
- if (GNUNET_YES ==
- GNS_find_tld (GNS_get_tld (p->queries[0].name),
- &zone))
- {
- /* Start resolution in GNS */
- ilh = GNUNET_new (struct InterceptLookupHandle);
- GNUNET_CONTAINER_DLL_insert (ilh_head,
- ilh_tail,
- ilh);
- ilh->packet = p;
- ilh->request_handle = rh;
- ilh->lookup = GNS_resolver_lookup (&zone,
- p->queries[0].type,
- p->queries[0].name,
- GNUNET_GNS_LO_DEFAULT,
- MAX_RECURSION,
- &reply_to_dns, ilh);
- return;
- }
- /* This request does not concern us. Forward to real DNS. */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Request for `%s' is forwarded to DNS untouched.\n",
- p->queries[0].name);
- GNUNET_DNS_request_forward (rh);
- GNUNET_DNSPARSER_free_packet (p);
- }
- /**
- * Initialized the interceptor
- *
- * @param c the configuration
- * @return #GNUNET_OK on success
- */
- int
- GNS_interceptor_init (const struct GNUNET_CONFIGURATION_Handle *c)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "DNS hijacking enabled. Connecting to DNS service.\n");
- dns_handle = GNUNET_DNS_connect (c,
- GNUNET_DNS_FLAG_PRE_RESOLUTION,
- &handle_dns_request,
- NULL);
- if (NULL == dns_handle)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _ ("Failed to connect to the DNS service!\n"));
- return GNUNET_SYSERR;
- }
- return GNUNET_YES;
- }
- /**
- * Disconnect from interceptor
- */
- void
- GNS_interceptor_done ()
- {
- struct InterceptLookupHandle *ilh;
- while (NULL != (ilh = ilh_head))
- {
- GNUNET_CONTAINER_DLL_remove (ilh_head,
- ilh_tail,
- ilh);
- GNS_resolver_lookup_cancel (ilh->lookup);
- GNUNET_DNS_request_drop (ilh->request_handle);
- GNUNET_DNSPARSER_free_packet (ilh->packet);
- GNUNET_free (ilh);
- }
- if (NULL != dns_handle)
- {
- GNUNET_DNS_disconnect (dns_handle);
- dns_handle = NULL;
- }
- }
- /* end of gnunet-service-gns_interceptor.c */
|