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
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.
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      General Public License for more details.
14
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.
19 */
20 /**
21  * @file gns/gnunet-service-gns_interceptor.c
22  * @brief GNUnet GNS interceptor logic
23  * @author Martin Schanzenbach
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
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"
32 #include "gns.h"
33
34
35 /**
36  * Handle to a DNS intercepted
37  * reslution request
38  */
39 struct InterceptLookupHandle
40 {
41
42   /**
43    * We keep these in a DLL.
44    */
45   struct InterceptLookupHandle *next;
46
47   /**
48    * We keep these in a DLL.
49    */
50   struct InterceptLookupHandle *prev;
51
52   /**
53    * the request handle to reply to
54    */
55   struct GNUNET_DNS_RequestHandle *request_handle;
56
57   /**
58    * the dns parser packet received
59    */
60   struct GNUNET_DNSPARSER_Packet *packet;
61
62   /**
63    * Handle for the lookup operation.
64    */
65   struct GNS_ResolverHandle *lookup;
66
67 };
68
69
70 /**
71  * Our handle to the DNS handler library
72  */
73 static struct GNUNET_DNS_Handle *dns_handle;
74
75 /**
76  * Key of the zone we start lookups in.
77  */
78 static struct GNUNET_CRYPTO_EcdsaPublicKey zone;
79
80 /**
81  * Head of the DLL.
82  */
83 static struct InterceptLookupHandle *ilh_head;
84
85 /**
86  * Tail of the DLL.
87  */
88 static struct InterceptLookupHandle *ilh_tail;
89
90
91 /**
92  * Reply to dns request with the result from our lookup.
93  *
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
97  */
98 static void
99 reply_to_dns (void *cls, uint32_t rd_count,
100               const struct GNUNET_GNSRECORD_Data *rd)
101 {
102   struct InterceptLookupHandle *ilh = cls;
103   struct GNUNET_DNSPARSER_Packet *packet = ilh->packet;
104   struct GNUNET_DNSPARSER_Query *query = &packet->queries[0];
105   uint32_t i;
106   size_t len;
107   int ret;
108   char *buf;
109   unsigned int num_answers;
110   unsigned int skip_answers;
111   unsigned int skip_additional;
112   size_t off;
113
114   /* Put records in the DNS packet */
115   num_answers = 0;
116   for (i=0; i < rd_count; i++)
117     if (rd[i].record_type == query->type)
118       num_answers++;
119   skip_answers = 0;
120   skip_additional = 0;
121
122   {
123     struct GNUNET_DNSPARSER_Record answer_records[num_answers];
124     struct GNUNET_DNSPARSER_Record additional_records[rd_count - num_answers];
125
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++)
132     {
133       if (rd[i].record_type == query->type)
134       {
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)
138         {
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,
144                                            rd[i].data_size,
145                                            &off);
146           if ( (off != rd[i].data_size) ||
147                (NULL == answer_records[i].data.hostname) )
148           {
149             GNUNET_break_op (0);
150             skip_answers++;
151           }
152           break;
153         case GNUNET_DNSPARSER_TYPE_SOA:
154           answer_records[i - skip_answers].data.soa
155             = GNUNET_DNSPARSER_parse_soa (rd[i].data,
156                                           rd[i].data_size,
157                                           &off);
158           if ( (off != rd[i].data_size) ||
159                (NULL == answer_records[i].data.soa) )
160           {
161             GNUNET_break_op (0);
162             skip_answers++;
163           }
164           break;
165         case GNUNET_DNSPARSER_TYPE_SRV:
166           /* FIXME: SRV is not yet supported */
167           skip_answers++;
168           break;
169         case GNUNET_DNSPARSER_TYPE_MX:
170           answer_records[i - skip_answers].data.mx
171             = GNUNET_DNSPARSER_parse_mx (rd[i].data,
172                                          rd[i].data_size,
173                                          &off);
174           if ( (off != rd[i].data_size) ||
175                (NULL == answer_records[i].data.hostname) )
176           {
177             GNUNET_break_op (0);
178             skip_answers++;
179           }
180           break;
181         default:
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;
184           break;
185         }
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;
189       }
190       else
191       {
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)
195         {
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,
201                                            rd[i].data_size,
202                                            &off);
203           if ( (off != rd[i].data_size) ||
204                (NULL == additional_records[i].data.hostname) )
205           {
206             GNUNET_break_op (0);
207             skip_additional++;
208           }
209           break;
210         case GNUNET_DNSPARSER_TYPE_SOA:
211           additional_records[i - skip_additional].data.soa
212             = GNUNET_DNSPARSER_parse_soa (rd[i].data,
213                                           rd[i].data_size,
214                                           &off);
215           if ( (off != rd[i].data_size) ||
216                (NULL == additional_records[i].data.hostname) )
217           {
218             GNUNET_break_op (0);
219             skip_additional++;
220           }
221           break;
222         case GNUNET_DNSPARSER_TYPE_MX:
223           additional_records[i - skip_additional].data.mx
224             = GNUNET_DNSPARSER_parse_mx (rd[i].data,
225                                          rd[i].data_size,
226                                          &off);
227           if ( (off != rd[i].data_size) ||
228                (NULL == additional_records[i].data.hostname) )
229           {
230             GNUNET_break_op (0);
231             skip_additional++;
232           }
233           break;
234         case GNUNET_DNSPARSER_TYPE_SRV:
235           /* FIXME: SRV is not yet supported */
236           skip_answers++;
237           break;
238         default:
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;
241           break;
242         }
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;
246       }
247     }
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;
251     if (NULL == rd)
252       packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NAME_ERROR;
253     else
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 */
258                                  &buf,
259                                  &len);
260     if (GNUNET_OK != ret)
261     {
262       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
263                   _("Error converting GNS response to DNS response!\n"));
264       if (GNUNET_NO == ret)
265         GNUNET_free (buf);
266     }
267     else
268     {
269       GNUNET_DNS_request_answer (ilh->request_handle,
270                                  len,
271                                  buf);
272       GNUNET_free (buf);
273     }
274     packet->num_answers = 0;
275     packet->answers = NULL;
276     packet->num_additional_records = 0;
277     packet->additional_records = NULL;
278     GNUNET_DNSPARSER_free_packet (packet);
279   }
280   GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
281   GNUNET_free (ilh);
282 }
283
284
285 /**
286  * The DNS request handler.  Called for every incoming DNS request.
287  *
288  * @param cls closure, unused
289  * @param rh request handle to user for reply
290  * @param request_length number of bytes in @a request
291  * @param request UDP payload of the DNS request
292  */
293 static void
294 handle_dns_request (void *cls,
295                     struct GNUNET_DNS_RequestHandle *rh,
296                     size_t request_length,
297                     const char *request)
298 {
299   struct GNUNET_DNSPARSER_Packet *p;
300   struct InterceptLookupHandle *ilh;
301
302   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
303               "Hijacked a DNS request. Processing.\n");
304   if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
305   {
306     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
307                 "Received malformed DNS packet, leaving it untouched.\n");
308     GNUNET_DNS_request_forward (rh);
309     GNUNET_DNSPARSER_free_packet (p);
310     return;
311   }
312
313   /* Check TLD and decide if we or legacy dns is responsible */
314   if (1 != p->num_queries)
315   {
316     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317                 "Not exactly one query in DNS packet. Forwarding untouched.\n");
318     GNUNET_DNS_request_forward (rh);
319     GNUNET_DNSPARSER_free_packet(p);
320     return;
321   }
322
323   /* Check for GNS TLDs. */
324   if ( (GNUNET_YES == is_gnu_tld (p->queries[0].name)) ||
325        (GNUNET_YES == is_zkey_tld (p->queries[0].name)) ||
326        (0 == strcmp (p->queries[0].name, GNUNET_GNS_TLD)) )
327   {
328     /* Start resolution in GNS */
329     ilh = GNUNET_new (struct InterceptLookupHandle);
330     GNUNET_CONTAINER_DLL_insert (ilh_head, ilh_tail, ilh);
331     ilh->packet = p;
332     ilh->request_handle = rh;
333     ilh->lookup = GNS_resolver_lookup (&zone,
334                                        p->queries[0].type,
335                                        p->queries[0].name,
336                                        GNUNET_NO,
337                                        &reply_to_dns, ilh);
338     return;
339   }
340   /* This request does not concern us. Forward to real DNS. */
341   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
342               "Request for `%s' is forwarded to DNS untouched.\n",
343               p->queries[0].name);
344   GNUNET_DNS_request_forward (rh);
345   GNUNET_DNSPARSER_free_packet (p);
346 }
347
348
349 /**
350  * Initialized the interceptor
351  *
352  * @param gnu_zone the zone to work in
353  * @param c the configuration
354  * @return #GNUNET_OK on success
355  */
356 int
357 GNS_interceptor_init (const struct GNUNET_CRYPTO_EcdsaPublicKey *gnu_zone,
358                       const struct GNUNET_CONFIGURATION_Handle *c)
359 {
360   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
361               "DNS hijacking enabled. Connecting to DNS service.\n");
362   zone = *gnu_zone;
363   dns_handle = GNUNET_DNS_connect (c,
364                                    GNUNET_DNS_FLAG_PRE_RESOLUTION,
365                                    &handle_dns_request,
366                                    NULL);
367   if (NULL == dns_handle)
368   {
369     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
370                 _("Failed to connect to the DNS service!\n"));
371     return GNUNET_SYSERR;
372   }
373   return GNUNET_YES;
374 }
375
376
377 /**
378  * Disconnect from interceptor
379  */
380 void
381 GNS_interceptor_done ()
382 {
383   struct InterceptLookupHandle *ilh;
384
385   while (NULL != (ilh = ilh_head))
386   {
387     GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
388     GNS_resolver_lookup_cancel (ilh->lookup);
389     GNUNET_DNS_request_drop (ilh->request_handle);
390     GNUNET_DNSPARSER_free_packet (ilh->packet);
391     GNUNET_free (ilh);
392   }
393   if (NULL != dns_handle)
394   {
395     GNUNET_DNS_disconnect (dns_handle);
396     dns_handle = NULL;
397   }
398 }
399
400 /* end of gnunet-service-gns_interceptor.c */