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