Merge branch 'master' of ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / gns / gnunet-service-gns_interceptor.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-2013 GNUnet e.V.
4
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.
9
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.
14     
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/>.
17 */
18 /**
19  * @file gns/gnunet-service-gns_interceptor.c
20  * @brief GNUnet GNS interceptor logic
21  * @author Martin Schanzenbach
22  * @author Christian Grothoff
23  */
24 #include "platform.h"
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"
31 #include "gns.h"
32
33
34 /**
35  * Handle to a DNS intercepted
36  * reslution request
37  */
38 struct InterceptLookupHandle
39 {
40
41   /**
42    * We keep these in a DLL.
43    */
44   struct InterceptLookupHandle *next;
45
46   /**
47    * We keep these in a DLL.
48    */
49   struct InterceptLookupHandle *prev;
50
51   /**
52    * the request handle to reply to
53    */
54   struct GNUNET_DNS_RequestHandle *request_handle;
55
56   /**
57    * the dns parser packet received
58    */
59   struct GNUNET_DNSPARSER_Packet *packet;
60
61   /**
62    * Handle for the lookup operation.
63    */
64   struct GNS_ResolverHandle *lookup;
65
66 };
67
68
69 /**
70  * Our handle to the DNS handler library
71  */
72 static struct GNUNET_DNS_Handle *dns_handle;
73
74 /**
75  * Head of the DLL.
76  */
77 static struct InterceptLookupHandle *ilh_head;
78
79 /**
80  * Tail of the DLL.
81  */
82 static struct InterceptLookupHandle *ilh_tail;
83
84
85 /**
86  * Reply to dns request with the result from our lookup.
87  *
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
91  */
92 static void
93 reply_to_dns (void *cls, uint32_t rd_count,
94               const struct GNUNET_GNSRECORD_Data *rd)
95 {
96   struct InterceptLookupHandle *ilh = cls;
97   struct GNUNET_DNSPARSER_Packet *packet = ilh->packet;
98   struct GNUNET_DNSPARSER_Query *query = &packet->queries[0];
99   uint32_t i;
100   size_t len;
101   int ret;
102   char *buf;
103   unsigned int num_answers;
104   unsigned int skip_answers;
105   unsigned int skip_additional;
106   size_t off = 0;
107
108   /* Put records in the DNS packet */
109   num_answers = 0;
110   for (i=0; i < rd_count; i++)
111     if (rd[i].record_type == query->type)
112       num_answers++;
113   skip_answers = 0;
114   skip_additional = 0;
115
116   {
117     struct GNUNET_DNSPARSER_Record answer_records[num_answers];
118     struct GNUNET_DNSPARSER_Record additional_records[rd_count - num_answers];
119
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++)
126     {
127       if (rd[i].record_type == query->type)
128       {
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)
132         {
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,
138                                            rd[i].data_size,
139                                            &off);
140           if ( (off != rd[i].data_size) ||
141                (NULL == answer_records[i].data.hostname) )
142           {
143             GNUNET_break_op (0);
144             skip_answers++;
145           }
146           break;
147         case GNUNET_DNSPARSER_TYPE_SOA:
148           answer_records[i - skip_answers].data.soa
149             = GNUNET_DNSPARSER_parse_soa (rd[i].data,
150                                           rd[i].data_size,
151                                           &off);
152           if ( (off != rd[i].data_size) ||
153                (NULL == answer_records[i].data.soa) )
154           {
155             GNUNET_break_op (0);
156             skip_answers++;
157           }
158           break;
159         case GNUNET_DNSPARSER_TYPE_SRV:
160           /* FIXME: SRV is not yet supported */
161           skip_answers++;
162           break;
163         case GNUNET_DNSPARSER_TYPE_MX:
164           answer_records[i - skip_answers].data.mx
165             = GNUNET_DNSPARSER_parse_mx (rd[i].data,
166                                          rd[i].data_size,
167                                          &off);
168           if ( (off != rd[i].data_size) ||
169                (NULL == answer_records[i].data.hostname) )
170           {
171             GNUNET_break_op (0);
172             skip_answers++;
173           }
174           break;
175         default:
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;
178           break;
179         }
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;
183       }
184       else
185       {
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)
189         {
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,
195                                            rd[i].data_size,
196                                            &off);
197           if ( (off != rd[i].data_size) ||
198                (NULL == additional_records[i].data.hostname) )
199           {
200             GNUNET_break_op (0);
201             skip_additional++;
202           }
203           break;
204         case GNUNET_DNSPARSER_TYPE_SOA:
205           additional_records[i - skip_additional].data.soa
206             = GNUNET_DNSPARSER_parse_soa (rd[i].data,
207                                           rd[i].data_size,
208                                           &off);
209           if ( (off != rd[i].data_size) ||
210                (NULL == additional_records[i].data.hostname) )
211           {
212             GNUNET_break_op (0);
213             skip_additional++;
214           }
215           break;
216         case GNUNET_DNSPARSER_TYPE_MX:
217           additional_records[i - skip_additional].data.mx
218             = GNUNET_DNSPARSER_parse_mx (rd[i].data,
219                                          rd[i].data_size,
220                                          &off);
221           if ( (off != rd[i].data_size) ||
222                (NULL == additional_records[i].data.hostname) )
223           {
224             GNUNET_break_op (0);
225             skip_additional++;
226           }
227           break;
228         case GNUNET_DNSPARSER_TYPE_SRV:
229           /* FIXME: SRV is not yet supported */
230           skip_answers++;
231           break;
232         default:
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;
235           break;
236         }
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;
240       }
241     }
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;
245     if (NULL == rd)
246       packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NAME_ERROR;
247     else
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 */
252                                  &buf,
253                                  &len);
254     if (GNUNET_OK != ret)
255     {
256       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
257                   _("Error converting GNS response to DNS response!\n"));
258       if (GNUNET_NO == ret)
259         GNUNET_free (buf);
260     }
261     else
262     {
263       GNUNET_DNS_request_answer (ilh->request_handle,
264                                  len,
265                                  buf);
266       GNUNET_free (buf);
267     }
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);
273   }
274   GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
275   GNUNET_free (ilh);
276 }
277
278
279 /**
280  * The DNS request handler.  Called for every incoming DNS request.
281  *
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
286  */
287 static void
288 handle_dns_request (void *cls,
289                     struct GNUNET_DNS_RequestHandle *rh,
290                     size_t request_length,
291                     const char *request)
292 {
293   struct GNUNET_DNSPARSER_Packet *p;
294   struct InterceptLookupHandle *ilh;
295   struct GNUNET_CRYPTO_EcdsaPublicKey zone;
296
297   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
298               "Hijacked a DNS request. Processing.\n");
299   if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
300   {
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);
305     return;
306   }
307
308   /* Check TLD and decide if we or legacy dns is responsible */
309   if (1 != p->num_queries)
310   {
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);
315     return;
316   }
317
318   /* Check for GNS TLDs. */
319   if (GNUNET_YES ==
320       GNS_find_tld (GNS_get_tld (p->queries[0].name),
321                     &zone))
322   {
323     /* Start resolution in GNS */
324     ilh = GNUNET_new (struct InterceptLookupHandle);
325     GNUNET_CONTAINER_DLL_insert (ilh_head,
326                                  ilh_tail,
327                                  ilh);
328     ilh->packet = p;
329     ilh->request_handle = rh;
330     ilh->lookup = GNS_resolver_lookup (&zone,
331                                        p->queries[0].type,
332                                        p->queries[0].name,
333                                        GNUNET_NO,
334                                        &reply_to_dns, ilh);
335     return;
336   }
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",
340               p->queries[0].name);
341   GNUNET_DNS_request_forward (rh);
342   GNUNET_DNSPARSER_free_packet (p);
343 }
344
345
346 /**
347  * Initialized the interceptor
348  *
349  * @param c the configuration
350  * @return #GNUNET_OK on success
351  */
352 int
353 GNS_interceptor_init (const struct GNUNET_CONFIGURATION_Handle *c)
354 {
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,
359                                    &handle_dns_request,
360                                    NULL);
361   if (NULL == dns_handle)
362   {
363     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
364                 _("Failed to connect to the DNS service!\n"));
365     return GNUNET_SYSERR;
366   }
367   return GNUNET_YES;
368 }
369
370
371 /**
372  * Disconnect from interceptor
373  */
374 void
375 GNS_interceptor_done ()
376 {
377   struct InterceptLookupHandle *ilh;
378
379   while (NULL != (ilh = ilh_head))
380   {
381     GNUNET_CONTAINER_DLL_remove (ilh_head,
382                                  ilh_tail,
383                                  ilh);
384     GNS_resolver_lookup_cancel (ilh->lookup);
385     GNUNET_DNS_request_drop (ilh->request_handle);
386     GNUNET_DNSPARSER_free_packet (ilh->packet);
387     GNUNET_free (ilh);
388   }
389   if (NULL != dns_handle)
390   {
391     GNUNET_DNS_disconnect (dns_handle);
392     dns_handle = NULL;
393   }
394 }
395
396 /* end of gnunet-service-gns_interceptor.c */