2 This file is part of GNUnet.
3 Copyright (C) 2009-2013 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
16 * @file gns/gnunet-service-gns_interceptor.c
17 * @brief GNUnet GNS interceptor logic
18 * @author Martin Schanzenbach
19 * @author Christian Grothoff
22 #include "gnunet_util_lib.h"
23 #include "gnunet_dns_service.h"
24 #include "gnunet_dnsparser_lib.h"
25 #include "gnunet-service-gns.h"
26 #include "gnunet-service-gns_resolver.h"
27 #include "gnunet-service-gns_interceptor.h"
32 * Handle to a DNS intercepted
35 struct InterceptLookupHandle
39 * We keep these in a DLL.
41 struct InterceptLookupHandle *next;
44 * We keep these in a DLL.
46 struct InterceptLookupHandle *prev;
49 * the request handle to reply to
51 struct GNUNET_DNS_RequestHandle *request_handle;
54 * the dns parser packet received
56 struct GNUNET_DNSPARSER_Packet *packet;
59 * Handle for the lookup operation.
61 struct GNS_ResolverHandle *lookup;
67 * Our handle to the DNS handler library
69 static struct GNUNET_DNS_Handle *dns_handle;
74 static struct InterceptLookupHandle *ilh_head;
79 static struct InterceptLookupHandle *ilh_tail;
83 * Reply to dns request with the result from our lookup.
85 * @param cls the closure to the request (an InterceptLookupHandle)
86 * @param rd_count the number of records to return
87 * @param rd the record data
90 reply_to_dns (void *cls, uint32_t rd_count,
91 const struct GNUNET_GNSRECORD_Data *rd)
93 struct InterceptLookupHandle *ilh = cls;
94 struct GNUNET_DNSPARSER_Packet *packet = ilh->packet;
95 struct GNUNET_DNSPARSER_Query *query = &packet->queries[0];
100 unsigned int num_answers;
101 unsigned int skip_answers;
102 unsigned int skip_additional;
105 /* Put records in the DNS packet */
107 for (i=0; i < rd_count; i++)
108 if (rd[i].record_type == query->type)
114 struct GNUNET_DNSPARSER_Record answer_records[num_answers];
115 struct GNUNET_DNSPARSER_Record additional_records[rd_count - num_answers];
117 packet->answers = answer_records;
118 packet->additional_records = additional_records;
119 /* FIXME: need to handle #GNUNET_GNSRECORD_RF_SHADOW_RECORD option
120 (by ignoring records where this flag is set if there is any
121 other record of that type in the result set) */
122 for (i=0; i < rd_count; i++)
124 if (rd[i].record_type == query->type)
126 answer_records[i - skip_answers].name = query->name;
127 answer_records[i - skip_answers].type = rd[i].record_type;
128 switch(rd[i].record_type)
130 case GNUNET_DNSPARSER_TYPE_NS:
131 case GNUNET_DNSPARSER_TYPE_CNAME:
132 case GNUNET_DNSPARSER_TYPE_PTR:
133 answer_records[i - skip_answers].data.hostname
134 = GNUNET_DNSPARSER_parse_name (rd[i].data,
137 if ( (off != rd[i].data_size) ||
138 (NULL == answer_records[i].data.hostname) )
144 case GNUNET_DNSPARSER_TYPE_SOA:
145 answer_records[i - skip_answers].data.soa
146 = GNUNET_DNSPARSER_parse_soa (rd[i].data,
149 if ( (off != rd[i].data_size) ||
150 (NULL == answer_records[i].data.soa) )
156 case GNUNET_DNSPARSER_TYPE_SRV:
157 /* FIXME: SRV is not yet supported */
160 case GNUNET_DNSPARSER_TYPE_MX:
161 answer_records[i - skip_answers].data.mx
162 = GNUNET_DNSPARSER_parse_mx (rd[i].data,
165 if ( (off != rd[i].data_size) ||
166 (NULL == answer_records[i].data.hostname) )
173 answer_records[i - skip_answers].data.raw.data_len = rd[i].data_size;
174 answer_records[i - skip_answers].data.raw.data = (char*)rd[i].data;
177 GNUNET_break (0 == (rd[i - skip_answers].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
178 answer_records[i - skip_answers].expiration_time.abs_value_us = rd[i].expiration_time;
179 answer_records[i - skip_answers].dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
183 additional_records[i - skip_additional].name = query->name;
184 additional_records[i - skip_additional].type = rd[i].record_type;
185 switch(rd[i].record_type)
187 case GNUNET_DNSPARSER_TYPE_NS:
188 case GNUNET_DNSPARSER_TYPE_CNAME:
189 case GNUNET_DNSPARSER_TYPE_PTR:
190 additional_records[i - skip_additional].data.hostname
191 = GNUNET_DNSPARSER_parse_name (rd[i].data,
194 if ( (off != rd[i].data_size) ||
195 (NULL == additional_records[i].data.hostname) )
201 case GNUNET_DNSPARSER_TYPE_SOA:
202 additional_records[i - skip_additional].data.soa
203 = GNUNET_DNSPARSER_parse_soa (rd[i].data,
206 if ( (off != rd[i].data_size) ||
207 (NULL == additional_records[i].data.hostname) )
213 case GNUNET_DNSPARSER_TYPE_MX:
214 additional_records[i - skip_additional].data.mx
215 = GNUNET_DNSPARSER_parse_mx (rd[i].data,
218 if ( (off != rd[i].data_size) ||
219 (NULL == additional_records[i].data.hostname) )
225 case GNUNET_DNSPARSER_TYPE_SRV:
226 /* FIXME: SRV is not yet supported */
230 additional_records[i - skip_additional].data.raw.data_len = rd[i].data_size;
231 additional_records[i - skip_additional].data.raw.data = (char*)rd[i].data;
234 GNUNET_break (0 == (rd[i - skip_additional].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
235 additional_records[i - skip_additional].expiration_time.abs_value_us = rd[i].expiration_time;
236 additional_records[i - skip_additional].dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
239 packet->num_answers = num_answers - skip_answers;
240 packet->num_additional_records = rd_count - num_answers - skip_additional;
241 packet->flags.authoritative_answer = 1;
243 packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NAME_ERROR;
245 packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NO_ERROR;
246 packet->flags.query_or_response = 1;
247 ret = GNUNET_DNSPARSER_pack (packet,
248 1024, /* maximum allowed size for DNS reply */
251 if (GNUNET_OK != ret)
253 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
254 _("Error converting GNS response to DNS response!\n"));
255 if (GNUNET_NO == ret)
260 GNUNET_DNS_request_answer (ilh->request_handle,
265 packet->num_answers = 0;
266 packet->answers = NULL;
267 packet->num_additional_records = 0;
268 packet->additional_records = NULL;
269 GNUNET_DNSPARSER_free_packet (packet);
271 GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
277 * The DNS request handler. Called for every incoming DNS request.
279 * @param cls closure, unused
280 * @param rh request handle to user for reply
281 * @param request_length number of bytes in @a request
282 * @param request UDP payload of the DNS request
285 handle_dns_request (void *cls,
286 struct GNUNET_DNS_RequestHandle *rh,
287 size_t request_length,
290 struct GNUNET_DNSPARSER_Packet *p;
291 struct InterceptLookupHandle *ilh;
292 struct GNUNET_CRYPTO_EcdsaPublicKey zone;
294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
295 "Hijacked a DNS request. Processing.\n");
296 if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
298 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
299 "Received malformed DNS packet, leaving it untouched.\n");
300 GNUNET_DNS_request_forward (rh);
301 GNUNET_DNSPARSER_free_packet (p);
305 /* Check TLD and decide if we or legacy dns is responsible */
306 if (1 != p->num_queries)
308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
309 "Not exactly one query in DNS packet. Forwarding untouched.\n");
310 GNUNET_DNS_request_forward (rh);
311 GNUNET_DNSPARSER_free_packet(p);
315 /* Check for GNS TLDs. */
317 GNS_find_tld (GNS_get_tld (p->queries[0].name),
320 /* Start resolution in GNS */
321 ilh = GNUNET_new (struct InterceptLookupHandle);
322 GNUNET_CONTAINER_DLL_insert (ilh_head,
326 ilh->request_handle = rh;
327 ilh->lookup = GNS_resolver_lookup (&zone,
334 /* This request does not concern us. Forward to real DNS. */
335 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
336 "Request for `%s' is forwarded to DNS untouched.\n",
338 GNUNET_DNS_request_forward (rh);
339 GNUNET_DNSPARSER_free_packet (p);
344 * Initialized the interceptor
346 * @param c the configuration
347 * @return #GNUNET_OK on success
350 GNS_interceptor_init (const struct GNUNET_CONFIGURATION_Handle *c)
352 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
353 "DNS hijacking enabled. Connecting to DNS service.\n");
354 dns_handle = GNUNET_DNS_connect (c,
355 GNUNET_DNS_FLAG_PRE_RESOLUTION,
358 if (NULL == dns_handle)
360 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
361 _("Failed to connect to the DNS service!\n"));
362 return GNUNET_SYSERR;
369 * Disconnect from interceptor
372 GNS_interceptor_done ()
374 struct InterceptLookupHandle *ilh;
376 while (NULL != (ilh = ilh_head))
378 GNUNET_CONTAINER_DLL_remove (ilh_head,
381 GNS_resolver_lookup_cancel (ilh->lookup);
382 GNUNET_DNS_request_drop (ilh->request_handle);
383 GNUNET_DNSPARSER_free_packet (ilh->packet);
386 if (NULL != dns_handle)
388 GNUNET_DNS_disconnect (dns_handle);
393 /* end of gnunet-service-gns_interceptor.c */