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