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