2 This file is part of GNUnet.
3 (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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
21 * @file gns/gnunet-service-gns_interceptor.c
22 * @brief GNUnet GNS interceptor logic
23 * @author Martin Schanzenbach
24 * @author Christian Grothoff
27 * - implement RF_SHADOW_RECORD logic
30 #include "gnunet_util_lib.h"
31 #include "gnunet_dns_service.h"
32 #include "gnunet_dnsparser_lib.h"
33 #include "gnunet-service-gns_resolver.h"
34 #include "gnunet-service-gns_interceptor.h"
39 * Handle to a DNS intercepted
42 struct InterceptLookupHandle
46 * We keep these in a DLL.
48 struct InterceptLookupHandle *next;
51 * We keep these in a DLL.
53 struct InterceptLookupHandle *prev;
56 * the request handle to reply to
58 struct GNUNET_DNS_RequestHandle *request_handle;
61 * the dns parser packet received
63 struct GNUNET_DNSPARSER_Packet *packet;
66 * Handle for the lookup operation.
68 struct GNS_ResolverHandle *lookup;
74 * Our handle to the DNS handler library
76 static struct GNUNET_DNS_Handle *dns_handle;
79 * Key of the zone we start lookups in.
81 static struct GNUNET_CRYPTO_EccPublicKey zone;
86 static struct InterceptLookupHandle *ilh_head;
91 static struct InterceptLookupHandle *ilh_tail;
95 * Reply to dns request with the result from our lookup.
97 * @param cls the closure to the request (an InterceptLookupHandle)
98 * @param rd_count the number of records to return
99 * @param rd the record data
102 reply_to_dns (void *cls, uint32_t rd_count,
103 const struct GNUNET_NAMESTORE_RecordData *rd)
105 struct InterceptLookupHandle *ilh = cls;
106 struct GNUNET_DNSPARSER_Packet *packet = ilh->packet;
107 struct GNUNET_DNSPARSER_Query *query = &packet->queries[0];
112 unsigned int num_answers;
114 /* Put records in the DNS packet */
116 for (i=0; i < rd_count; i++)
117 if (rd[i].record_type == query->type)
121 struct GNUNET_DNSPARSER_Record answer_records[num_answers];
122 struct GNUNET_DNSPARSER_Record additional_records[rd_count - num_answers];
124 packet->answers = answer_records;
125 packet->additional_records = additional_records;
127 /* FIXME: need to handle #GNUNET_NAMESTORE_RF_SHADOW_RECORD option
128 (by ignoring records where this flag is set if there is any
129 other record of that type in the result set) */
130 for (i=0; i < rd_count; i++)
132 if (rd[i].record_type == query->type)
134 answer_records[i].name = query->name;
135 answer_records[i].type = rd[i].record_type;
136 switch(rd[i].record_type)
138 case GNUNET_DNSPARSER_TYPE_NS:
139 case GNUNET_DNSPARSER_TYPE_CNAME:
140 case GNUNET_DNSPARSER_TYPE_PTR:
141 answer_records[i].data.hostname = (char*)rd[i].data;
143 case GNUNET_DNSPARSER_TYPE_SOA:
144 answer_records[i].data.soa =
145 (struct GNUNET_DNSPARSER_SoaRecord *)rd[i].data;
147 case GNUNET_DNSPARSER_TYPE_MX:
148 answer_records[i].data.mx =
149 (struct GNUNET_DNSPARSER_MxRecord *)rd[i].data;
152 answer_records[i].data.raw.data_len = rd[i].data_size;
153 answer_records[i].data.raw.data = (char*)rd[i].data;
156 GNUNET_break (0 == (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION));
157 answer_records[i].expiration_time.abs_value_us = rd[i].expiration_time;
158 answer_records[i].class = GNUNET_TUN_DNS_CLASS_INTERNET;
162 additional_records[i].name = query->name;
163 additional_records[i].type = rd[i].record_type;
164 switch(rd[i].record_type)
166 case GNUNET_DNSPARSER_TYPE_NS:
167 case GNUNET_DNSPARSER_TYPE_CNAME:
168 case GNUNET_DNSPARSER_TYPE_PTR:
169 additional_records[i].data.hostname = (char*)rd[i].data;
171 case GNUNET_DNSPARSER_TYPE_SOA:
172 additional_records[i].data.soa =
173 (struct GNUNET_DNSPARSER_SoaRecord *)rd[i].data;
175 case GNUNET_DNSPARSER_TYPE_MX:
176 additional_records[i].data.mx =
177 (struct GNUNET_DNSPARSER_MxRecord *)rd[i].data;
180 additional_records[i].data.raw.data_len = rd[i].data_size;
181 additional_records[i].data.raw.data = (char*)rd[i].data;
184 GNUNET_break (0 == (rd[i].flags & GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION));
185 additional_records[i].expiration_time.abs_value_us = rd[i].expiration_time;
186 additional_records[i].class = GNUNET_TUN_DNS_CLASS_INTERNET;
189 packet->num_answers = num_answers;
190 packet->num_additional_records = rd_count - num_answers;
191 packet->flags.authoritative_answer = 1;
193 packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NAME_ERROR;
195 packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NO_ERROR;
196 packet->flags.query_or_response = 1;
197 ret = GNUNET_DNSPARSER_pack (packet,
198 1024, /* maximum allowed size for DNS reply */
201 if (GNUNET_OK != ret)
203 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
204 _("Error converting GNS response to DNS response!\n"));
208 GNUNET_DNS_request_answer (ilh->request_handle,
213 packet->num_answers = 0;
214 packet->answers = NULL;
215 packet->num_additional_records = 0;
216 packet->additional_records = NULL;
217 GNUNET_DNSPARSER_free_packet (packet);
219 GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
225 * The DNS request handler. Called for every incoming DNS request.
227 * @param cls closure, unused
228 * @param rh request handle to user for reply
229 * @param request_length number of bytes in @a request
230 * @param request UDP payload of the DNS request
233 handle_dns_request (void *cls,
234 struct GNUNET_DNS_RequestHandle *rh,
235 size_t request_length,
238 struct GNUNET_DNSPARSER_Packet *p;
239 struct InterceptLookupHandle *ilh;
241 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
242 "Hijacked a DNS request. Processing.\n");
243 if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
245 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
246 "Received malformed DNS packet, leaving it untouched.\n");
247 GNUNET_DNS_request_forward (rh);
248 GNUNET_DNSPARSER_free_packet (p);
252 /* Check TLD and decide if we or legacy dns is responsible */
253 if (1 != p->num_queries)
255 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
256 "Not exactly one query in DNS packet. Forwarding untouched.\n");
257 GNUNET_DNS_request_forward (rh);
258 GNUNET_DNSPARSER_free_packet(p);
262 /* Check for GNS TLDs. */
263 if ( (GNUNET_YES == is_gnu_tld (p->queries[0].name)) ||
264 (GNUNET_YES == is_zkey_tld (p->queries[0].name)) ||
265 (0 == strcmp (p->queries[0].name, GNUNET_GNS_TLD)) )
267 /* Start resolution in GNS */
268 ilh = GNUNET_new (struct InterceptLookupHandle);
269 GNUNET_CONTAINER_DLL_insert (ilh_head, ilh_tail, ilh);
271 ilh->request_handle = rh;
272 ilh->lookup = GNS_resolver_lookup (&zone,
275 NULL /* FIXME: enable shorten for DNS intercepts? */,
280 /* This request does not concern us. Forward to real DNS. */
281 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
282 "Request for `%s' is forwarded to DNS untouched.\n",
284 GNUNET_DNS_request_forward (rh);
285 GNUNET_DNSPARSER_free_packet (p);
290 * Initialized the interceptor
292 * @param gnu_zone the zone to work in
293 * @param c the configuration
294 * @return GNUNET_OK on success
297 GNS_interceptor_init (const struct GNUNET_CRYPTO_EccPublicKey *gnu_zone,
298 const struct GNUNET_CONFIGURATION_Handle *c)
300 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
301 "DNS hijacking enabled. Connecting to DNS service.\n");
303 dns_handle = GNUNET_DNS_connect (c,
304 GNUNET_DNS_FLAG_PRE_RESOLUTION,
307 if (NULL == dns_handle)
309 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
310 _("Failed to connect to the DNS service!\n"));
311 return GNUNET_SYSERR;
318 * Disconnect from interceptor
321 GNS_interceptor_done ()
323 struct InterceptLookupHandle *ilh;
325 while (NULL != (ilh = ilh_head))
327 GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
328 GNS_resolver_lookup_cancel (ilh->lookup);
329 GNUNET_DNS_request_drop (ilh->request_handle);
330 GNUNET_DNSPARSER_free_packet (ilh->packet);
333 if (NULL != dns_handle)
335 GNUNET_DNS_disconnect (dns_handle);
340 /* end of gnunet-service-gns_interceptor.c */