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.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * @file gns/gnunet-service-gns_interceptor.c
20 * @brief GNUnet GNS interceptor logic
21 * @author Martin Schanzenbach
22 * @author Christian Grothoff
25 #include "gnunet_util_lib.h"
26 #include "gnunet_dns_service.h"
27 #include "gnunet_dnsparser_lib.h"
28 #include "gnunet-service-gns.h"
29 #include "gnunet-service-gns_resolver.h"
30 #include "gnunet-service-gns_interceptor.h"
35 * Handle to a DNS intercepted
38 struct InterceptLookupHandle
42 * We keep these in a DLL.
44 struct InterceptLookupHandle *next;
47 * We keep these in a DLL.
49 struct InterceptLookupHandle *prev;
52 * the request handle to reply to
54 struct GNUNET_DNS_RequestHandle *request_handle;
57 * the dns parser packet received
59 struct GNUNET_DNSPARSER_Packet *packet;
62 * Handle for the lookup operation.
64 struct GNS_ResolverHandle *lookup;
70 * Our handle to the DNS handler library
72 static struct GNUNET_DNS_Handle *dns_handle;
77 static struct InterceptLookupHandle *ilh_head;
82 static struct InterceptLookupHandle *ilh_tail;
86 * Reply to dns request with the result from our lookup.
88 * @param cls the closure to the request (an InterceptLookupHandle)
89 * @param rd_count the number of records to return
90 * @param rd the record data
93 reply_to_dns (void *cls, uint32_t rd_count,
94 const struct GNUNET_GNSRECORD_Data *rd)
96 struct InterceptLookupHandle *ilh = cls;
97 struct GNUNET_DNSPARSER_Packet *packet = ilh->packet;
98 struct GNUNET_DNSPARSER_Query *query = &packet->queries[0];
103 unsigned int num_answers;
104 unsigned int skip_answers;
105 unsigned int skip_additional;
108 /* Put records in the DNS packet */
110 for (i=0; i < rd_count; i++)
111 if (rd[i].record_type == query->type)
117 struct GNUNET_DNSPARSER_Record answer_records[num_answers];
118 struct GNUNET_DNSPARSER_Record additional_records[rd_count - num_answers];
120 packet->answers = answer_records;
121 packet->additional_records = additional_records;
122 /* FIXME: need to handle #GNUNET_GNSRECORD_RF_SHADOW_RECORD option
123 (by ignoring records where this flag is set if there is any
124 other record of that type in the result set) */
125 for (i=0; i < rd_count; i++)
127 if (rd[i].record_type == query->type)
129 answer_records[i - skip_answers].name = query->name;
130 answer_records[i - skip_answers].type = rd[i].record_type;
131 switch(rd[i].record_type)
133 case GNUNET_DNSPARSER_TYPE_NS:
134 case GNUNET_DNSPARSER_TYPE_CNAME:
135 case GNUNET_DNSPARSER_TYPE_PTR:
136 answer_records[i - skip_answers].data.hostname
137 = GNUNET_DNSPARSER_parse_name (rd[i].data,
140 if ( (off != rd[i].data_size) ||
141 (NULL == answer_records[i].data.hostname) )
147 case GNUNET_DNSPARSER_TYPE_SOA:
148 answer_records[i - skip_answers].data.soa
149 = GNUNET_DNSPARSER_parse_soa (rd[i].data,
152 if ( (off != rd[i].data_size) ||
153 (NULL == answer_records[i].data.soa) )
159 case GNUNET_DNSPARSER_TYPE_SRV:
160 /* FIXME: SRV is not yet supported */
163 case GNUNET_DNSPARSER_TYPE_MX:
164 answer_records[i - skip_answers].data.mx
165 = GNUNET_DNSPARSER_parse_mx (rd[i].data,
168 if ( (off != rd[i].data_size) ||
169 (NULL == answer_records[i].data.hostname) )
176 answer_records[i - skip_answers].data.raw.data_len = rd[i].data_size;
177 answer_records[i - skip_answers].data.raw.data = (char*)rd[i].data;
180 GNUNET_break (0 == (rd[i - skip_answers].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
181 answer_records[i - skip_answers].expiration_time.abs_value_us = rd[i].expiration_time;
182 answer_records[i - skip_answers].dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
186 additional_records[i - skip_additional].name = query->name;
187 additional_records[i - skip_additional].type = rd[i].record_type;
188 switch(rd[i].record_type)
190 case GNUNET_DNSPARSER_TYPE_NS:
191 case GNUNET_DNSPARSER_TYPE_CNAME:
192 case GNUNET_DNSPARSER_TYPE_PTR:
193 additional_records[i - skip_additional].data.hostname
194 = GNUNET_DNSPARSER_parse_name (rd[i].data,
197 if ( (off != rd[i].data_size) ||
198 (NULL == additional_records[i].data.hostname) )
204 case GNUNET_DNSPARSER_TYPE_SOA:
205 additional_records[i - skip_additional].data.soa
206 = GNUNET_DNSPARSER_parse_soa (rd[i].data,
209 if ( (off != rd[i].data_size) ||
210 (NULL == additional_records[i].data.hostname) )
216 case GNUNET_DNSPARSER_TYPE_MX:
217 additional_records[i - skip_additional].data.mx
218 = GNUNET_DNSPARSER_parse_mx (rd[i].data,
221 if ( (off != rd[i].data_size) ||
222 (NULL == additional_records[i].data.hostname) )
228 case GNUNET_DNSPARSER_TYPE_SRV:
229 /* FIXME: SRV is not yet supported */
233 additional_records[i - skip_additional].data.raw.data_len = rd[i].data_size;
234 additional_records[i - skip_additional].data.raw.data = (char*)rd[i].data;
237 GNUNET_break (0 == (rd[i - skip_additional].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
238 additional_records[i - skip_additional].expiration_time.abs_value_us = rd[i].expiration_time;
239 additional_records[i - skip_additional].dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
242 packet->num_answers = num_answers - skip_answers;
243 packet->num_additional_records = rd_count - num_answers - skip_additional;
244 packet->flags.authoritative_answer = 1;
246 packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NAME_ERROR;
248 packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NO_ERROR;
249 packet->flags.query_or_response = 1;
250 ret = GNUNET_DNSPARSER_pack (packet,
251 1024, /* maximum allowed size for DNS reply */
254 if (GNUNET_OK != ret)
256 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
257 _("Error converting GNS response to DNS response!\n"));
258 if (GNUNET_NO == ret)
263 GNUNET_DNS_request_answer (ilh->request_handle,
268 packet->num_answers = 0;
269 packet->answers = NULL;
270 packet->num_additional_records = 0;
271 packet->additional_records = NULL;
272 GNUNET_DNSPARSER_free_packet (packet);
274 GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
280 * The DNS request handler. Called for every incoming DNS request.
282 * @param cls closure, unused
283 * @param rh request handle to user for reply
284 * @param request_length number of bytes in @a request
285 * @param request UDP payload of the DNS request
288 handle_dns_request (void *cls,
289 struct GNUNET_DNS_RequestHandle *rh,
290 size_t request_length,
293 struct GNUNET_DNSPARSER_Packet *p;
294 struct InterceptLookupHandle *ilh;
295 struct GNUNET_CRYPTO_EcdsaPublicKey zone;
297 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
298 "Hijacked a DNS request. Processing.\n");
299 if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
301 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
302 "Received malformed DNS packet, leaving it untouched.\n");
303 GNUNET_DNS_request_forward (rh);
304 GNUNET_DNSPARSER_free_packet (p);
308 /* Check TLD and decide if we or legacy dns is responsible */
309 if (1 != p->num_queries)
311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
312 "Not exactly one query in DNS packet. Forwarding untouched.\n");
313 GNUNET_DNS_request_forward (rh);
314 GNUNET_DNSPARSER_free_packet(p);
318 /* Check for GNS TLDs. */
320 GNS_find_tld (GNS_get_tld (p->queries[0].name),
323 /* Start resolution in GNS */
324 ilh = GNUNET_new (struct InterceptLookupHandle);
325 GNUNET_CONTAINER_DLL_insert (ilh_head,
329 ilh->request_handle = rh;
330 ilh->lookup = GNS_resolver_lookup (&zone,
337 /* This request does not concern us. Forward to real DNS. */
338 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
339 "Request for `%s' is forwarded to DNS untouched.\n",
341 GNUNET_DNS_request_forward (rh);
342 GNUNET_DNSPARSER_free_packet (p);
347 * Initialized the interceptor
349 * @param c the configuration
350 * @return #GNUNET_OK on success
353 GNS_interceptor_init (const struct GNUNET_CONFIGURATION_Handle *c)
355 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
356 "DNS hijacking enabled. Connecting to DNS service.\n");
357 dns_handle = GNUNET_DNS_connect (c,
358 GNUNET_DNS_FLAG_PRE_RESOLUTION,
361 if (NULL == dns_handle)
363 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
364 _("Failed to connect to the DNS service!\n"));
365 return GNUNET_SYSERR;
372 * Disconnect from interceptor
375 GNS_interceptor_done ()
377 struct InterceptLookupHandle *ilh;
379 while (NULL != (ilh = ilh_head))
381 GNUNET_CONTAINER_DLL_remove (ilh_head,
384 GNS_resolver_lookup_cancel (ilh->lookup);
385 GNUNET_DNS_request_drop (ilh->request_handle);
386 GNUNET_DNSPARSER_free_packet (ilh->packet);
389 if (NULL != dns_handle)
391 GNUNET_DNS_disconnect (dns_handle);
396 /* end of gnunet-service-gns_interceptor.c */