remove 'illegal' (non-reentrant) log logic from signal handler
[oweals/gnunet.git] / src / gns / gnunet-service-gns.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011-2018 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.c
22  * @brief GNU Name System (main service)
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_dht_service.h"
31 #include "gnunet_namecache_service.h"
32 #include "gnunet_gnsrecord_lib.h"
33 #include "gnunet_gns_service.h"
34 #include "gnunet_statistics_service.h"
35 #include "gns.h"
36 #include "gnunet-service-gns_resolver.h"
37 #include "gnunet-service-gns_interceptor.h"
38 #include "gnunet_protocols.h"
39
40
41 /**
42  * GnsClient prototype
43  */
44 struct GnsClient;
45
46 /**
47  * Handle to a lookup operation from client via API.
48  */
49 struct ClientLookupHandle
50 {
51   /**
52    * We keep these in a DLL.
53    */
54   struct ClientLookupHandle *next;
55
56   /**
57    * We keep these in a DLL.
58    */
59   struct ClientLookupHandle *prev;
60
61   /**
62    * Client handle
63    */
64   struct GnsClient *gc;
65
66   /**
67    * Active handle for the lookup.
68    */
69   struct GNS_ResolverHandle *lookup;
70
71   /**
72    * request id
73    */
74   uint32_t request_id;
75 };
76
77
78 /**
79  * Information we track per connected client.
80  */
81 struct GnsClient
82 {
83   /**
84    * The client
85    */
86   struct GNUNET_SERVICE_Client *client;
87
88   /**
89    * The MQ
90    */
91   struct GNUNET_MQ_Handle *mq;
92
93   /**
94    * Head of the DLL.
95    */
96   struct ClientLookupHandle *clh_head;
97
98   /**
99    * Tail of the DLL.
100    */
101   struct ClientLookupHandle *clh_tail;
102 };
103
104
105 /**
106  * Representation of a TLD, mapping the respective TLD string
107  * (i.e. ".gnu") to the respective public key of the zone.
108  */
109 struct GNS_TopLevelDomain
110 {
111   /**
112    * Kept in a DLL, as there are unlikely enough of these to
113    * warrant a hash map.
114    */
115   struct GNS_TopLevelDomain *next;
116
117   /**
118    * Kept in a DLL, as there are unlikely enough of these to
119    * warrant a hash map.
120    */
121   struct GNS_TopLevelDomain *prev;
122
123   /**
124    * Public key associated with the @a tld.
125    */
126   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
127
128   /**
129    * Top-level domain as a string, including leading ".".
130    */
131   char *tld;
132 };
133
134
135 /**
136  * Our handle to the DHT
137  */
138 static struct GNUNET_DHT_Handle *dht_handle;
139
140 /**
141  * Our handle to the namecache service
142  */
143 static struct GNUNET_NAMECACHE_Handle *namecache_handle;
144
145 /**
146  * #GNUNET_YES if ipv6 is supported
147  */
148 static int v6_enabled;
149
150 /**
151  * #GNUNET_YES if ipv4 is supported
152  */
153 static int v4_enabled;
154
155 /**
156  * Handle to the statistics service
157  */
158 static struct GNUNET_STATISTICS_Handle *statistics;
159
160 /**
161  * Head of DLL of TLDs we map to GNS zones.
162  */
163 static struct GNS_TopLevelDomain *tld_head;
164
165 /**
166  * Tail of DLL of TLDs we map to GNS zones.
167  */
168 static struct GNS_TopLevelDomain *tld_tail;
169
170
171 /**
172  * Find GNS zone belonging to TLD @a tld.
173  *
174  * @param tld_str top-level domain to look up
175  * @param[out] pkey public key to set
176  * @return #GNUNET_YES if @a tld was found #GNUNET_NO if not
177  */
178 int
179 GNS_find_tld (const char *tld_str,
180               struct GNUNET_CRYPTO_EcdsaPublicKey *pkey)
181 {
182   if ('\0' == *tld_str)
183     return GNUNET_NO;
184   for (struct GNS_TopLevelDomain *tld = tld_head;
185        NULL != tld;
186        tld = tld->next)
187   {
188     if (0 == strcasecmp (tld_str,
189                          tld->tld))
190     {
191       *pkey = tld->pkey;
192       return GNUNET_YES;
193     }
194   }
195   if (GNUNET_OK ==
196       GNUNET_GNSRECORD_zkey_to_pkey (tld_str + 1,
197                                      pkey))
198     return GNUNET_YES; /* TLD string *was* the public key */
199   return GNUNET_NO;
200 }
201
202
203 /**
204  * Obtain the TLD of the given @a name.
205  *
206  * @param name a name
207  * @return the part of @a name after the last ".",
208  *         or @a name if @a name does not contain a "."
209  */
210 const char *
211 GNS_get_tld (const char *name)
212 {
213   const char *tld;
214
215   tld = strrchr (name,
216                  (unsigned char) '.');
217   if (NULL == tld)
218     tld = name;
219   else
220     tld++; /* skip the '.' */
221   return tld;
222 }
223
224
225 /**
226  * Task run during shutdown.
227  *
228  * @param cls unused, NULL
229  */
230 static void
231 shutdown_task (void *cls)
232 {
233   struct GNS_TopLevelDomain *tld;
234
235   (void) cls;
236   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
237               "Shutting down!\n");
238   GNS_interceptor_done ();
239   GNS_resolver_done ();
240   if (NULL != statistics)
241   {
242     GNUNET_STATISTICS_destroy (statistics,
243                                GNUNET_NO);
244     statistics = NULL;
245   }
246   if (NULL != namecache_handle)
247   {
248     GNUNET_NAMECACHE_disconnect (namecache_handle);
249     namecache_handle = NULL;
250   }
251   if (NULL != dht_handle)
252   {
253     GNUNET_DHT_disconnect (dht_handle);
254     dht_handle = NULL;
255   }
256   while (NULL != (tld = tld_head))
257   {
258     GNUNET_CONTAINER_DLL_remove (tld_head,
259                                  tld_tail,
260                                  tld);
261     GNUNET_free (tld->tld);
262     GNUNET_free (tld);
263   }
264 }
265
266
267 /**
268  * Called whenever a client is disconnected.
269  *
270  * @param cls closure
271  * @param client identification of the client
272  * @param app_ctx @a client
273  */
274 static void
275 client_disconnect_cb (void *cls,
276                       struct GNUNET_SERVICE_Client *client,
277                       void *app_ctx)
278 {
279   struct ClientLookupHandle *clh;
280   struct GnsClient *gc = app_ctx;
281
282   (void) cls;
283   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284               "Client %p disconnected\n",
285               client);
286   while (NULL != (clh = gc->clh_head))
287   {
288     if (NULL != clh->lookup)
289       GNS_resolver_lookup_cancel (clh->lookup);
290     GNUNET_CONTAINER_DLL_remove (gc->clh_head,
291                                  gc->clh_tail,
292                                  clh);
293     GNUNET_free (clh);
294   }
295   GNUNET_free (gc);
296 }
297
298
299 /**
300  * Add a client to our list of active clients.
301  *
302  * @param cls NULL
303  * @param client client to add
304  * @param mq message queue for @a client
305  * @return internal namestore client structure for this client
306  */
307 static void *
308 client_connect_cb (void *cls,
309                    struct GNUNET_SERVICE_Client *client,
310                    struct GNUNET_MQ_Handle *mq)
311 {
312   struct GnsClient *gc;
313
314   (void) cls;
315   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
316               "Client %p connected\n",
317               client);
318   gc = GNUNET_new (struct GnsClient);
319   gc->client = client;
320   gc->mq = mq;
321   return gc;
322 }
323
324
325 /**
326  * Reply to client with the result from our lookup.
327  *
328  * @param cls the closure (our client lookup handle)
329  * @param rd_count the number of records in @a rd
330  * @param rd the record data
331  */
332 static void
333 send_lookup_response (void *cls,
334                       uint32_t rd_count,
335                       const struct GNUNET_GNSRECORD_Data *rd)
336 {
337   struct ClientLookupHandle *clh = cls;
338   struct GnsClient *gc = clh->gc;
339   struct GNUNET_MQ_Envelope *env;
340   struct LookupResultMessage *rmsg;
341   ssize_t len;
342
343   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
344               "Sending LOOKUP_RESULT message with %u results\n",
345               (unsigned int) rd_count);
346   len = GNUNET_GNSRECORD_records_get_size (rd_count,
347                                            rd);
348   if (len < 0)
349   {
350     GNUNET_break (0);
351     GNUNET_SERVICE_client_drop (gc->client);
352     return;
353   }
354   if (len > UINT16_MAX - sizeof(*rmsg))
355   {
356     GNUNET_break (0);
357     GNUNET_SERVICE_client_drop (gc->client);
358     return;
359   }
360   env = GNUNET_MQ_msg_extra (rmsg,
361                              len,
362                              GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
363   rmsg->id = clh->request_id;
364   rmsg->rd_count = htonl (rd_count);
365   GNUNET_assert (len ==
366                  GNUNET_GNSRECORD_records_serialize (rd_count,
367                                                      rd,
368                                                      len,
369                                                      (char *) &rmsg[1]));
370   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (gc->client),
371                   env);
372   GNUNET_CONTAINER_DLL_remove (gc->clh_head,
373                                gc->clh_tail,
374                                clh);
375   GNUNET_free (clh);
376   GNUNET_STATISTICS_update (statistics,
377                             "Completed lookups", 1,
378                             GNUNET_NO);
379   GNUNET_STATISTICS_update (statistics,
380                             "Records resolved",
381                             rd_count,
382                             GNUNET_NO);
383 }
384
385
386 /**
387  * Checks a #GNUNET_MESSAGE_TYPE_GNS_LOOKUP message
388  *
389  * @param cls client sending the message
390  * @param l_msg message of type `struct LookupMessage`
391  * @return #GNUNET_OK if @a l_msg is well-formed
392  */
393 static int
394 check_lookup (void *cls,
395               const struct LookupMessage *l_msg)
396 {
397   size_t nlen;
398
399   (void) cls;
400   GNUNET_MQ_check_zero_termination (l_msg);
401   nlen = ntohs (l_msg->header.size) - sizeof(struct LookupMessage);
402   if (nlen > GNUNET_DNSPARSER_MAX_NAME_LENGTH)
403   {
404     GNUNET_break (0);
405     return GNUNET_SYSERR;
406   }
407   return GNUNET_OK;
408 }
409
410
411 /**
412  * Handle lookup requests from client
413  *
414  * @param cls the closure
415  * @param client the client
416  * @param message the message
417  */
418 static void
419 handle_lookup (void *cls,
420                const struct LookupMessage *sh_msg)
421 {
422   struct GnsClient *gc = cls;
423   char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
424   struct ClientLookupHandle *clh;
425   char *nameptr = name;
426   const char *utf_in;
427
428   GNUNET_SERVICE_client_continue (gc->client);
429   utf_in = (const char *) &sh_msg[1];
430   GNUNET_STRINGS_utf8_tolower (utf_in,
431                                nameptr);
432   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
433               "Received LOOKUP `%s' message\n",
434               name);
435   clh = GNUNET_new (struct ClientLookupHandle);
436   GNUNET_CONTAINER_DLL_insert (gc->clh_head,
437                                gc->clh_tail,
438                                clh);
439   clh->gc = gc;
440   clh->request_id = sh_msg->id;
441   if ((GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
442       (GNUNET_OK != v4_enabled))
443   {
444     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
445                 "LOOKUP: Query for A record but AF_INET not supported!");
446     send_lookup_response (clh,
447                           0,
448                           NULL);
449     return;
450   }
451   if ((GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
452       (GNUNET_OK != v6_enabled))
453   {
454     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
455                 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
456     send_lookup_response (clh,
457                           0,
458                           NULL);
459     return;
460   }
461   clh->lookup = GNS_resolver_lookup (&sh_msg->zone,
462                                      ntohl (sh_msg->type),
463                                      name,
464                                      (enum GNUNET_GNS_LocalOptions) ntohs (
465                                        sh_msg->options),
466                                      ntohs (sh_msg->recursion_depth_limit),
467                                      &send_lookup_response, clh);
468   GNUNET_STATISTICS_update (statistics,
469                             "Lookup attempts",
470                             1, GNUNET_NO);
471 }
472
473
474 /**
475  * Reads the configuration and populates TLDs
476  *
477  * @param cls unused
478  * @param section name of section in config, always "gns"
479  * @param option name of the option, TLDs start with "."
480  * @param value value for the option, public key for TLDs
481  */
482 static void
483 read_service_conf (void *cls,
484                    const char *section,
485                    const char *option,
486                    const char *value)
487 {
488   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
489   struct GNS_TopLevelDomain *tld;
490
491   (void) cls;
492   (void) section;
493   if (option[0] != '.')
494     return;
495   if (GNUNET_OK !=
496       GNUNET_STRINGS_string_to_data (value,
497                                      strlen (value),
498                                      &pk,
499                                      sizeof(pk)))
500   {
501     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
502                                section,
503                                option,
504                                _ (
505                                  "Properly base32-encoded public key required"));
506     return;
507   }
508   tld = GNUNET_new (struct GNS_TopLevelDomain);
509   tld->tld = GNUNET_strdup (&option[1]);
510   tld->pkey = pk;
511   GNUNET_CONTAINER_DLL_insert (tld_head,
512                                tld_tail,
513                                tld);
514 }
515
516
517 /**
518  * Process GNS requests.
519  *
520  * @param cls closure
521  * @param server the initialized server
522  * @param c configuration to use
523  */
524 static void
525 run (void *cls,
526      const struct GNUNET_CONFIGURATION_Handle *c,
527      struct GNUNET_SERVICE_Handle *service)
528 {
529   unsigned long long max_parallel_bg_queries = 16;
530
531   GNUNET_CONFIGURATION_iterate_section_values (c,
532                                                "gns",
533                                                &read_service_conf,
534                                                NULL);
535   v6_enabled = GNUNET_NETWORK_test_pf (PF_INET6);
536   v4_enabled = GNUNET_NETWORK_test_pf (PF_INET);
537   namecache_handle = GNUNET_NAMECACHE_connect (c);
538   if (NULL == namecache_handle)
539   {
540     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
541                 _ ("Failed to connect to the namecache!\n"));
542     GNUNET_SCHEDULER_shutdown ();
543     return;
544   }
545   if (GNUNET_OK ==
546       GNUNET_CONFIGURATION_get_value_number (c,
547                                              "gns",
548                                              "MAX_PARALLEL_BACKGROUND_QUERIES",
549                                              &max_parallel_bg_queries))
550   {
551     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
552                 "Number of allowed parallel background queries: %llu\n",
553                 max_parallel_bg_queries);
554   }
555   dht_handle = GNUNET_DHT_connect (c,
556                                    (unsigned int) max_parallel_bg_queries);
557   if (NULL == dht_handle)
558   {
559     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
560                 _ ("Could not connect to DHT!\n"));
561     GNUNET_SCHEDULER_add_now (&shutdown_task,
562                               NULL);
563     return;
564   }
565   GNS_resolver_init (namecache_handle,
566                      dht_handle,
567                      c,
568                      max_parallel_bg_queries);
569   if ((GNUNET_YES ==
570        GNUNET_CONFIGURATION_get_value_yesno (c,
571                                              "gns",
572                                              "INTERCEPT_DNS")) &&
573       (GNUNET_SYSERR ==
574        GNS_interceptor_init (c)))
575   {
576     GNUNET_break (0);
577     GNUNET_SCHEDULER_add_now (&shutdown_task,
578                               NULL);
579     return;
580   }
581   statistics = GNUNET_STATISTICS_create ("gns",
582                                          c);
583   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
584                                  NULL);
585 }
586
587
588 /**
589  * Define "main" method using service macro.
590  */
591 GNUNET_SERVICE_MAIN
592   ("gns",
593   GNUNET_SERVICE_OPTION_NONE,
594   &run,
595   &client_connect_cb,
596   &client_disconnect_cb,
597   NULL,
598   GNUNET_MQ_hd_var_size (lookup,
599                          GNUNET_MESSAGE_TYPE_GNS_LOOKUP,
600                          struct LookupMessage,
601                          NULL),
602   GNUNET_MQ_handler_end ());
603
604
605 /* end of gnunet-service-gns.c */