global reindent, now with uncrustify hook enabled
[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    * 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  * 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
148         case GNUNET_DNSPARSER_TYPE_SOA:
149           answer_records[i - skip_answers].data.soa
150             = GNUNET_DNSPARSER_parse_soa (rd[i].data,
151                                           rd[i].data_size,
152                                           &off);
153           if ((off != rd[i].data_size) ||
154               (NULL == answer_records[i].data.soa))
155           {
156             GNUNET_break_op (0);
157             skip_answers++;
158           }
159           break;
160
161         case GNUNET_DNSPARSER_TYPE_SRV:
162           /* FIXME: SRV is not yet supported */
163           skip_answers++;
164           break;
165
166         case GNUNET_DNSPARSER_TYPE_MX:
167           answer_records[i - skip_answers].data.mx
168             = GNUNET_DNSPARSER_parse_mx (rd[i].data,
169                                          rd[i].data_size,
170                                          &off);
171           if ((off != rd[i].data_size) ||
172               (NULL == answer_records[i].data.hostname))
173           {
174             GNUNET_break_op (0);
175             skip_answers++;
176           }
177           break;
178
179         default:
180           answer_records[i - skip_answers].data.raw.data_len = rd[i].data_size;
181           answer_records[i - skip_answers].data.raw.data = (char*) rd[i].data;
182           break;
183         }
184         GNUNET_break (0 == (rd[i - skip_answers].flags
185                             & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
186         answer_records[i - skip_answers].expiration_time.abs_value_us =
187           rd[i].expiration_time;
188         answer_records[i - skip_answers].dns_traffic_class =
189           GNUNET_TUN_DNS_CLASS_INTERNET;
190       }
191       else
192       {
193         additional_records[i - skip_additional].name = query->name;
194         additional_records[i - skip_additional].type = rd[i].record_type;
195         switch (rd[i].record_type)
196         {
197         case GNUNET_DNSPARSER_TYPE_NS:
198         case GNUNET_DNSPARSER_TYPE_CNAME:
199         case GNUNET_DNSPARSER_TYPE_PTR:
200           additional_records[i - skip_additional].data.hostname
201             = GNUNET_DNSPARSER_parse_name (rd[i].data,
202                                            rd[i].data_size,
203                                            &off);
204           if ((off != rd[i].data_size) ||
205               (NULL == additional_records[i].data.hostname))
206           {
207             GNUNET_break_op (0);
208             skip_additional++;
209           }
210           break;
211
212         case GNUNET_DNSPARSER_TYPE_SOA:
213           additional_records[i - skip_additional].data.soa
214             = GNUNET_DNSPARSER_parse_soa (rd[i].data,
215                                           rd[i].data_size,
216                                           &off);
217           if ((off != rd[i].data_size) ||
218               (NULL == additional_records[i].data.hostname))
219           {
220             GNUNET_break_op (0);
221             skip_additional++;
222           }
223           break;
224
225         case GNUNET_DNSPARSER_TYPE_MX:
226           additional_records[i - skip_additional].data.mx
227             = GNUNET_DNSPARSER_parse_mx (rd[i].data,
228                                          rd[i].data_size,
229                                          &off);
230           if ((off != rd[i].data_size) ||
231               (NULL == additional_records[i].data.hostname))
232           {
233             GNUNET_break_op (0);
234             skip_additional++;
235           }
236           break;
237
238         case GNUNET_DNSPARSER_TYPE_SRV:
239           /* FIXME: SRV is not yet supported */
240           skip_answers++;
241           break;
242
243         default:
244           additional_records[i - skip_additional].data.raw.data_len =
245             rd[i].data_size;
246           additional_records[i - skip_additional].data.raw.data =
247             (char*) rd[i].data;
248           break;
249         }
250         GNUNET_break (0 == (rd[i - skip_additional].flags
251                             & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION));
252         additional_records[i - skip_additional].expiration_time.abs_value_us =
253           rd[i].expiration_time;
254         additional_records[i - skip_additional].dns_traffic_class =
255           GNUNET_TUN_DNS_CLASS_INTERNET;
256       }
257     }
258     packet->num_answers = num_answers - skip_answers;
259     packet->num_additional_records = rd_count - num_answers - skip_additional;
260     packet->flags.authoritative_answer = 1;
261     if (NULL == rd)
262       packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NAME_ERROR;
263     else
264       packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NO_ERROR;
265     packet->flags.query_or_response = 1;
266     ret = GNUNET_DNSPARSER_pack (packet,
267                                  1024, /* maximum allowed size for DNS reply */
268                                  &buf,
269                                  &len);
270     if (GNUNET_OK != ret)
271     {
272       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
273                   _ ("Error converting GNS response to DNS response!\n"));
274       if (GNUNET_NO == ret)
275         GNUNET_free (buf);
276     }
277     else
278     {
279       GNUNET_DNS_request_answer (ilh->request_handle,
280                                  len,
281                                  buf);
282       GNUNET_free (buf);
283     }
284     packet->num_answers = 0;
285     packet->answers = NULL;
286     packet->num_additional_records = 0;
287     packet->additional_records = NULL;
288     GNUNET_DNSPARSER_free_packet (packet);
289   }
290   GNUNET_CONTAINER_DLL_remove (ilh_head, ilh_tail, ilh);
291   GNUNET_free (ilh);
292 }
293
294
295 /**
296  * The DNS request handler.  Called for every incoming DNS request.
297  *
298  * @param cls closure, unused
299  * @param rh request handle to user for reply
300  * @param request_length number of bytes in @a request
301  * @param request UDP payload of the DNS request
302  */
303 static void
304 handle_dns_request (void *cls,
305                     struct GNUNET_DNS_RequestHandle *rh,
306                     size_t request_length,
307                     const char *request)
308 {
309   struct GNUNET_DNSPARSER_Packet *p;
310   struct InterceptLookupHandle *ilh;
311   struct GNUNET_CRYPTO_EcdsaPublicKey zone;
312
313   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314               "Hijacked a DNS request. Processing.\n");
315   if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
316   {
317     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
318                 "Received malformed DNS packet, leaving it untouched.\n");
319     GNUNET_DNS_request_forward (rh);
320     GNUNET_DNSPARSER_free_packet (p);
321     return;
322   }
323
324   /* Check TLD and decide if we or legacy dns is responsible */
325   if (1 != p->num_queries)
326   {
327     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
328                 "Not exactly one query in DNS packet. Forwarding untouched.\n");
329     GNUNET_DNS_request_forward (rh);
330     GNUNET_DNSPARSER_free_packet (p);
331     return;
332   }
333
334   /* Check for GNS TLDs. */
335   if (GNUNET_YES ==
336       GNS_find_tld (GNS_get_tld (p->queries[0].name),
337                     &zone))
338   {
339     /* Start resolution in GNS */
340     ilh = GNUNET_new (struct InterceptLookupHandle);
341     GNUNET_CONTAINER_DLL_insert (ilh_head,
342                                  ilh_tail,
343                                  ilh);
344     ilh->packet = p;
345     ilh->request_handle = rh;
346     ilh->lookup = GNS_resolver_lookup (&zone,
347                                        p->queries[0].type,
348                                        p->queries[0].name,
349                                        GNUNET_NO,
350                                        &reply_to_dns, ilh);
351     return;
352   }
353   /* This request does not concern us. Forward to real DNS. */
354   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
355               "Request for `%s' is forwarded to DNS untouched.\n",
356               p->queries[0].name);
357   GNUNET_DNS_request_forward (rh);
358   GNUNET_DNSPARSER_free_packet (p);
359 }
360
361
362 /**
363  * Initialized the interceptor
364  *
365  * @param c the configuration
366  * @return #GNUNET_OK on success
367  */
368 int
369 GNS_interceptor_init (const struct GNUNET_CONFIGURATION_Handle *c)
370 {
371   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
372               "DNS hijacking enabled. Connecting to DNS service.\n");
373   dns_handle = GNUNET_DNS_connect (c,
374                                    GNUNET_DNS_FLAG_PRE_RESOLUTION,
375                                    &handle_dns_request,
376                                    NULL);
377   if (NULL == dns_handle)
378   {
379     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
380                 _ ("Failed to connect to the DNS service!\n"));
381     return GNUNET_SYSERR;
382   }
383   return GNUNET_YES;
384 }
385
386
387 /**
388  * Disconnect from interceptor
389  */
390 void
391 GNS_interceptor_done ()
392 {
393   struct InterceptLookupHandle *ilh;
394
395   while (NULL != (ilh = ilh_head))
396   {
397     GNUNET_CONTAINER_DLL_remove (ilh_head,
398                                  ilh_tail,
399                                  ilh);
400     GNS_resolver_lookup_cancel (ilh->lookup);
401     GNUNET_DNS_request_drop (ilh->request_handle);
402     GNUNET_DNSPARSER_free_packet (ilh->packet);
403     GNUNET_free (ilh);
404   }
405   if (NULL != dns_handle)
406   {
407     GNUNET_DNS_disconnect (dns_handle);
408     dns_handle = NULL;
409   }
410 }
411
412 /* end of gnunet-service-gns_interceptor.c */