2 This file is part of GNUnet.
3 Copyright (C) 2009-2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 * @file gns/gnunet-service-gns_interceptor.c
22 * @brief GNUnet GNS interceptor logic
23 * @author Martin Schanzenbach
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_dns_service.h"
29 #include "gnunet_dnsparser_lib.h"
30 #include "gnunet-service-gns_resolver.h"
31 #include "gnunet-service-gns_interceptor.h"
36 * Handle to a DNS intercepted
39 struct InterceptLookupHandle
43 * We keep these in a DLL.
45 struct InterceptLookupHandle *next;
48 * We keep these in a DLL.
50 struct InterceptLookupHandle *prev;
53 * the request handle to reply to
55 struct GNUNET_DNS_RequestHandle *request_handle;
58 * the dns parser packet received
60 struct GNUNET_DNSPARSER_Packet *packet;
63 * Handle for the lookup operation.
65 struct GNS_ResolverHandle *lookup;
71 * Our handle to the DNS handler library
73 static struct GNUNET_DNS_Handle *dns_handle;
76 * Key of the zone we start lookups in.
78 static struct GNUNET_CRYPTO_EcdsaPublicKey zone;
83 static struct InterceptLookupHandle *ilh_head;
88 static struct InterceptLookupHandle *ilh_tail;
92 * Reply to dns request with the result from our lookup.
94 * @param cls the closure to the request (an InterceptLookupHandle)
95 * @param rd_count the number of records to return
96 * @param rd the record data
99 reply_to_dns (void *cls, uint32_t rd_count,
100 const struct GNUNET_GNSRECORD_Data *rd)
102 struct InterceptLookupHandle *ilh = cls;
103 struct GNUNET_DNSPARSER_Packet *packet = ilh->packet;
104 struct GNUNET_DNSPARSER_Query *query = &packet->queries[0];
109 unsigned int num_answers;
110 unsigned int skip_answers;
111 unsigned int skip_additional;
114 /* Put records in the DNS packet */
116 for (i=0; i < rd_count; i++)
117 if (rd[i].record_type == query->type)
123 struct GNUNET_DNSPARSER_Record answer_records[num_answers];
124 struct GNUNET_DNSPARSER_Record additional_records[rd_count - num_answers];
126 packet->answers = answer_records;
127 packet->additional_records = additional_records;
128 /* FIXME: need to handle #GNUNET_GNSRECORD_RF_SHADOW_RECORD option
129 (by ignoring records where this flag is set if there is any
130 other record of that type in the result set) */
131 for (i=0; i < rd_count; i++)
133 if (rd[i].record_type == query->type)
135 answer_records[i - skip_answers].name = query->name;
136 answer_records[i - skip_answers].type = rd[i].record_type;
137 switch(rd[i].record_type)
139 case GNUNET_DNSPARSER_TYPE_NS:
140 case GNUNET_DNSPARSER_TYPE_CNAME:
141 case GNUNET_DNSPARSER_TYPE_PTR:
142 answer_records[i - skip_answers].data.hostname
143 = GNUNET_DNSPARSER_parse_name (rd[i].data,
146 if ( (off != rd[i].data_size) ||
147 (NULL == answer_records[i].data.hostname) )
153 case GNUNET_DNSPARSER_TYPE_SOA:
154 answer_records[i - skip_answers].data.soa
155 = GNUNET_DNSPARSER_parse_soa (rd[i].data,
158 if ( (off != rd[i].data_size) ||
159 (NULL == answer_records[i].data.soa) )
165 case GNUNET_DNSPARSER_TYPE_SRV:
166 /* FIXME: SRV is not yet supported */
169 case GNUNET_DNSPARSER_TYPE_MX:
170 answer_records[i - skip_answers].data.mx
171 = GNUNET_DNSPARSER_parse_mx (rd[i].data,
174 if ( (off != rd[i].data_size) ||
175 (NULL == answer_records[i].data.hostname) )
182 answer_records[i - skip_answers].data.raw.data_len = rd[i].data_size;
183 answer_records[i - skip_answers].data.raw.data = (char*)rd[i].data;
186 GNUNET_break (0 == (rd[i - skip_answers].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
187 answer_records[i - skip_answers].expiration_time.abs_value_us = rd[i].expiration_time;
188 answer_records[i - skip_answers].dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
192 additional_records[i - skip_additional].name = query->name;
193 additional_records[i - skip_additional].type = rd[i].record_type;
194 switch(rd[i].record_type)
196 case GNUNET_DNSPARSER_TYPE_NS:
197 case GNUNET_DNSPARSER_TYPE_CNAME:
198 case GNUNET_DNSPARSER_TYPE_PTR:
199 additional_records[i - skip_additional].data.hostname
200 = GNUNET_DNSPARSER_parse_name (rd[i].data,
203 if ( (off != rd[i].data_size) ||
204 (NULL == additional_records[i].data.hostname) )
210 case GNUNET_DNSPARSER_TYPE_SOA:
211 additional_records[i - skip_additional].data.soa
212 = GNUNET_DNSPARSER_parse_soa (rd[i].data,
215 if ( (off != rd[i].data_size) ||
216 (NULL == additional_records[i].data.hostname) )
222 case GNUNET_DNSPARSER_TYPE_MX:
223 additional_records[i - skip_additional].data.mx
224 = GNUNET_DNSPARSER_parse_mx (rd[i].data,
227 if ( (off != rd[i].data_size) ||
228 (NULL == additional_records[i].data.hostname) )
234 case GNUNET_DNSPARSER_TYPE_SRV:
235 /* FIXME: SRV is not yet supported */
239 additional_records[i - skip_additional].data.raw.data_len = rd[i].data_size;
240 additional_records[i - skip_additional].data.raw.data = (char*)rd[i].data;
243 GNUNET_break (0 == (rd[i - skip_additional].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
244 additional_records[i - skip_additional].expiration_time.abs_value_us = rd[i].expiration_time;
245 additional_records[i - skip_additional].dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
248 packet->num_answers = num_answers - skip_answers;
249 packet->num_additional_records = rd_count - num_answers - skip_additional;
250 packet->flags.authoritative_answer = 1;
252 packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NAME_ERROR;
254 packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NO_ERROR;
255 packet->flags.query_or_response = 1;
256 ret = GNUNET_DNSPARSER_pack (packet,
257 1024, /* maximum allowed size for DNS reply */
260 if (GNUNET_OK != ret)
262 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
263 _("Error converting GNS response to DNS response!\n"));
267 GNUNET_DNS_request_answer (ilh->request_handle,
272 packet->num_answers = 0;
273 packet->answers = NULL;
274 packet->num_additional_records = 0;
275 packet->additional_records = NULL;
276 GNUNET_DNSPARSER_free_packet (packet);
278 GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
284 * The DNS request handler. Called for every incoming DNS request.
286 * @param cls closure, unused
287 * @param rh request handle to user for reply
288 * @param request_length number of bytes in @a request
289 * @param request UDP payload of the DNS request
292 handle_dns_request (void *cls,
293 struct GNUNET_DNS_RequestHandle *rh,
294 size_t request_length,
297 struct GNUNET_DNSPARSER_Packet *p;
298 struct InterceptLookupHandle *ilh;
300 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
301 "Hijacked a DNS request. Processing.\n");
302 if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
304 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
305 "Received malformed DNS packet, leaving it untouched.\n");
306 GNUNET_DNS_request_forward (rh);
307 GNUNET_DNSPARSER_free_packet (p);
311 /* Check TLD and decide if we or legacy dns is responsible */
312 if (1 != p->num_queries)
314 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315 "Not exactly one query in DNS packet. Forwarding untouched.\n");
316 GNUNET_DNS_request_forward (rh);
317 GNUNET_DNSPARSER_free_packet(p);
321 /* Check for GNS TLDs. */
322 if ( (GNUNET_YES == is_gnu_tld (p->queries[0].name)) ||
323 (GNUNET_YES == is_zkey_tld (p->queries[0].name)) ||
324 (0 == strcmp (p->queries[0].name, GNUNET_GNS_TLD)) )
326 /* Start resolution in GNS */
327 ilh = GNUNET_new (struct InterceptLookupHandle);
328 GNUNET_CONTAINER_DLL_insert (ilh_head, ilh_tail, ilh);
330 ilh->request_handle = rh;
331 ilh->lookup = GNS_resolver_lookup (&zone,
334 NULL /* FIXME: enable shorten for DNS intercepts? */,
339 /* This request does not concern us. Forward to real DNS. */
340 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
341 "Request for `%s' is forwarded to DNS untouched.\n",
343 GNUNET_DNS_request_forward (rh);
344 GNUNET_DNSPARSER_free_packet (p);
349 * Initialized the interceptor
351 * @param gnu_zone the zone to work in
352 * @param c the configuration
353 * @return #GNUNET_OK on success
356 GNS_interceptor_init (const struct GNUNET_CRYPTO_EcdsaPublicKey *gnu_zone,
357 const struct GNUNET_CONFIGURATION_Handle *c)
359 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
360 "DNS hijacking enabled. Connecting to DNS service.\n");
362 dns_handle = GNUNET_DNS_connect (c,
363 GNUNET_DNS_FLAG_PRE_RESOLUTION,
366 if (NULL == dns_handle)
368 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
369 _("Failed to connect to the DNS service!\n"));
370 return GNUNET_SYSERR;
377 * Disconnect from interceptor
380 GNS_interceptor_done ()
382 struct InterceptLookupHandle *ilh;
384 while (NULL != (ilh = ilh_head))
386 GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
387 GNS_resolver_lookup_cancel (ilh->lookup);
388 GNUNET_DNS_request_drop (ilh->request_handle);
389 GNUNET_DNSPARSER_free_packet (ilh->packet);
392 if (NULL != dns_handle)
394 GNUNET_DNS_disconnect (dns_handle);
399 /* end of gnunet-service-gns_interceptor.c */