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