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