69f1ca64025b24fe7e4a9ee059e012e891829264
[oweals/gnunet.git] / src / gns / gnunet-service-gns.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011-2013 GNUnet e.V.
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
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_identity_service.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 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 struct GnsClient
80 {
81   /**
82    * The client
83    */
84   struct GNUNET_SERVICE_Client *client;
85
86   /**
87    * The MQ
88    */
89   struct GNUNET_MQ_Handle *mq;
90
91   /**
92    * Head of the DLL.
93    */
94   struct ClientLookupHandle *clh_head;
95
96   /**
97    * Tail of the DLL.
98    */
99   struct ClientLookupHandle *clh_tail;
100 };
101
102
103 /**
104  * Representation of a TLD, mapping the respective TLD string
105  * (i.e. ".gnu") to the respective public key of the zone.
106  */
107 struct GNS_TopLevelDomain
108 {
109
110   /**
111    * Kept in a DLL, as there are unlikely enough of these to
112    * warrant a hash map.
113    */
114   struct GNS_TopLevelDomain *next;
115
116   /**
117    * Kept in a DLL, as there are unlikely enough of these to
118    * warrant a hash map.
119    */
120   struct GNS_TopLevelDomain *prev;
121
122   /**
123    * Public key associated with the @a tld.
124    */
125   struct GNUNET_CRYPTO_EddsaPublicKey pkey;
126
127   /**
128    * Top-level domain as a string, including leading ".".
129    */
130   char *tld;
131
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  * Our handle to the identity service
147  */
148 static struct GNUNET_IDENTITY_Handle *identity_handle;
149
150 /**
151  * Our handle to the identity operation to find the master zone
152  * for intercepted queries.
153  */
154 static struct GNUNET_IDENTITY_Operation *identity_op;
155
156 /**
157  * #GNUNET_YES if ipv6 is supported
158  */
159 static int v6_enabled;
160
161 /**
162  * #GNUNET_YES if ipv4 is supported
163  */
164 static int v4_enabled;
165
166 /**
167  * Handle to the statistics service
168  */
169 static struct GNUNET_STATISTICS_Handle *statistics;
170
171 /**
172  * Head of DLL of TLDs we map to GNS zones.
173  */
174 static struct GNS_TopLevelDomain *tld_head;
175
176 /**
177  * Tail of DLL of TLDs we map to GNS zones.
178  */
179 static struct GNS_TopLevelDomain *tld_tail;
180
181
182 /**
183  * Find GNS zone belonging to TLD @a tld.
184  *
185  * @param tld_str top-level domain to look up
186  * @param[out] pkey public key to set
187  * @return #GNUNET_YES if @a tld was found #GNUNET_NO if not
188  */
189 int
190 GNS_find_tld (const char *tld_str,
191               struct GNUNET_CRYPTO_EddsaPublicKey *pkey)
192 {
193   if ('\0' == *tld_str)
194     return GNUNET_NO;
195   for (struct GNS_TopLevelDomain *tld = tld_head;
196        NULL != tld;
197        tld = tld->next)
198   {
199     if (0 == strcasecmp (tld_str,
200                          tld->tld))
201     {
202       *pkey = tld->pkey;
203       return GNUNET_YES;
204     }
205   }
206   if (GNUNET_OK ==
207       GNUNET_STRINGS_string_to_data (tld_str + 1,
208                                      strlen (tld_str + 1),
209                                      pkey,
210                                      sizeof (*pkey)))
211     return GNUNET_YES; /* TLD string *was* the public key */
212   return GNUNET_NO;
213 }
214
215
216 /**
217  * Task run during shutdown.
218  *
219  * @param cls unused
220  * @param tc unused
221  */
222 static void
223 shutdown_task (void *cls)
224 {
225   struct GNS_TopLevelDomain *tld;
226
227   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
228               "Shutting down!\n");
229   GNS_interceptor_done ();
230   if (NULL != identity_op)
231   {
232     GNUNET_IDENTITY_cancel (identity_op);
233     identity_op = NULL;
234   }
235   if (NULL != identity_handle)
236   {
237     GNUNET_IDENTITY_disconnect (identity_handle);
238     identity_handle = NULL;
239   }
240   GNS_resolver_done ();
241   if (NULL != statistics)
242   {
243     GNUNET_STATISTICS_destroy (statistics,
244                                GNUNET_NO);
245     statistics = NULL;
246   }
247   if (NULL != namecache_handle)
248   {
249     GNUNET_NAMECACHE_disconnect (namecache_handle);
250     namecache_handle = NULL;
251   }
252   if (NULL != dht_handle)
253   {
254     GNUNET_DHT_disconnect (dht_handle);
255     dht_handle = NULL;
256   }
257   while (NULL != (tld = tld_head))
258   {
259     GNUNET_CONTAINER_DLL_remove (tld_head,
260                                  tld_tail,
261                                  tld);
262     GNUNET_free (tld->tld);
263     GNUNET_free (tld);
264   }
265 }
266
267
268 /**
269  * Called whenever a client is disconnected.
270  *
271  * @param cls closure
272  * @param client identification of the client
273  * @param app_ctx @a client
274  */
275 static void
276 client_disconnect_cb (void *cls,
277                       struct GNUNET_SERVICE_Client *client,
278                       void *app_ctx)
279 {
280   struct ClientLookupHandle *clh;
281   struct GnsClient *gc = app_ctx;
282
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
296   GNUNET_free (gc);
297 }
298
299
300 /**
301  * Add a client to our list of active clients.
302  *
303  * @param cls NULL
304  * @param client client to add
305  * @param mq message queue for @a client
306  * @return internal namestore client structure for this client
307  */
308 static void *
309 client_connect_cb (void *cls,
310                    struct GNUNET_SERVICE_Client *client,
311                    struct GNUNET_MQ_Handle *mq)
312 {
313   struct GnsClient *gc;
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 GNUNET_MQ_Envelope *env;
338   struct LookupResultMessage *rmsg;
339   size_t len;
340
341   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
342               "Sending LOOKUP_RESULT message with %u results\n",
343               (unsigned int) rd_count);
344
345   len = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
346   env = GNUNET_MQ_msg_extra (rmsg,
347                              len,
348                              GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
349   rmsg->id = clh->request_id;
350   rmsg->rd_count = htonl (rd_count);
351   GNUNET_GNSRECORD_records_serialize (rd_count, rd, len,
352                                       (char*) &rmsg[1]);
353   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(clh->gc->client),
354                   env);
355   GNUNET_CONTAINER_DLL_remove (clh->gc->clh_head,
356                                clh->gc->clh_tail,
357                                clh);
358   GNUNET_free (clh);
359   GNUNET_STATISTICS_update (statistics,
360                             "Completed lookups", 1,
361                             GNUNET_NO);
362   GNUNET_STATISTICS_update (statistics,
363                             "Records resolved",
364                             rd_count,
365                             GNUNET_NO);
366 }
367
368
369 /**
370  * Checks a #GNUNET_MESSAGE_TYPE_GNS_LOOKUP message
371  *
372  * @param cls client sending the message
373  * @param l_msg message of type `struct LookupMessage`
374  * @return #GNUNET_OK if @a l_msg is well-formed
375  */
376 static int
377 check_lookup (void *cls,
378               const struct LookupMessage *l_msg)
379 {
380   size_t msg_size;
381   const char* name;
382
383   msg_size = ntohs (l_msg->header.size);
384   if (msg_size < sizeof (struct LookupMessage))
385   {
386     GNUNET_break (0);
387     return GNUNET_SYSERR;
388   }
389   name = (const char *) &l_msg[1];
390   if ( ('\0' != name[msg_size - sizeof (struct LookupMessage) - 1]) ||
391        (strlen (name) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) )
392   {
393     GNUNET_break (0);
394     return GNUNET_SYSERR;
395   }
396   return GNUNET_OK;
397 }
398
399
400 /**
401  * Handle lookup requests from client
402  *
403  * @param cls the closure
404  * @param client the client
405  * @param message the message
406  */
407 static void
408 handle_lookup (void *cls,
409                const struct LookupMessage *sh_msg)
410 {
411   struct GnsClient *gc = cls;
412   char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
413   struct ClientLookupHandle *clh;
414   char *nameptr = name;
415   const char *utf_in;
416
417   GNUNET_SERVICE_client_continue (gc->client);
418   utf_in = (const char *) &sh_msg[1];
419   GNUNET_STRINGS_utf8_tolower (utf_in, nameptr);
420   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421               "Received LOOKUP `%s' message\n",
422               name);
423
424   clh = GNUNET_new (struct ClientLookupHandle);
425   GNUNET_CONTAINER_DLL_insert (gc->clh_head,
426                                gc->clh_tail,
427                                clh);
428   clh->gc = gc;
429   clh->request_id = sh_msg->id;
430   if ( (GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
431        (GNUNET_OK != v4_enabled) )
432   {
433     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434                 "LOOKUP: Query for A record but AF_INET not supported!");
435     send_lookup_response (clh, 0, NULL);
436     return;
437   }
438   if ( (GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
439        (GNUNET_OK != v6_enabled) )
440   {
441     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442                 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
443     send_lookup_response (clh, 0, NULL);
444     return;
445   }
446   clh->lookup = GNS_resolver_lookup (&sh_msg->zone,
447                                      ntohl (sh_msg->type),
448                                      name,
449                                      (enum GNUNET_GNS_LocalOptions) ntohs (sh_msg->options),
450                                      &send_lookup_response, clh);
451   GNUNET_STATISTICS_update (statistics,
452                             "Lookup attempts",
453                             1, GNUNET_NO);
454 }
455
456
457 /**
458  * Method called to inform about the ego to be used for the master zone
459  * for DNS interceptions.
460  *
461  * This function is only called ONCE, and 'NULL' being passed in
462  * @a ego does indicate that interception is not configured.
463  * If @a ego is non-NULL, we should start to intercept DNS queries
464  * and resolve ".gnu" queries using the given ego as the master zone.
465  *
466  * @param cls closure, our `const struct GNUNET_CONFIGURATION_Handle *c`
467  * @param ego ego handle
468  * @param ctx context for application to store data for this ego
469  *                 (during the lifetime of this process, initially NULL)
470  * @param name name assigned by the user for this ego,
471  *                   NULL if the user just deleted the ego and it
472  *                   must thus no longer be used
473  */
474 static void
475 identity_intercept_cb (void *cls,
476                        struct GNUNET_IDENTITY_Ego *ego,
477                        void **ctx,
478                        const char *name)
479 {
480   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
481   struct GNUNET_CRYPTO_EcdsaPublicKey dns_root;
482
483   identity_op = NULL;
484   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485               "Looking for gns-intercept ego\n");
486   if (NULL == ego)
487   {
488     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
489                 _("No ego configured for `%s`\n"),
490                 "gns-intercept");
491
492     return;
493   }
494   GNUNET_IDENTITY_ego_get_public_key (ego,
495                                       &dns_root);
496   if (GNUNET_SYSERR ==
497       GNS_interceptor_init (&dns_root,
498                             cfg))
499   {
500     GNUNET_break (0);
501     GNUNET_SCHEDULER_add_now (&shutdown_task,
502                               NULL);
503     return;
504   }
505 }
506
507
508 /**
509  * Reads the configuration and populates TLDs
510  *
511  * @param cls unused
512  * @param section name of section in config, always "gns"
513  * @param option name of the option, TLDs start with "."
514  * @param value value for the option, public key for TLDs
515  */
516 static void
517 read_service_conf (void *cls,
518                    const char *section,
519                    const char *option,
520                    const char *value)
521 {
522   struct GNUNET_CRYPTO_EddsaPublicKey pk;
523   struct GNS_TopLevelDomain *tld;
524
525   if (option[0] != '.')
526     return;
527   if (GNUNET_OK !=
528       GNUNET_STRINGS_string_to_data (value,
529                                      strlen (value),
530                                      &pk,
531                                      sizeof (pk)))
532   {
533     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
534                                section,
535                                option,
536                                _("Properly base32-encoded public key required"));
537     return;
538   }
539   tld = GNUNET_new (struct GNS_TopLevelDomain);
540   tld->tld = GNUNET_strdup (&option[1]);
541   tld->pkey = pk;
542   GNUNET_CONTAINER_DLL_insert (tld_head,
543                                tld_tail,
544                                tld);
545 }
546
547
548
549 /**
550  * Process GNS requests.
551  *
552  * @param cls closure
553  * @param server the initialized server
554  * @param c configuration to use
555  */
556 static void
557 run (void *cls,
558      const struct GNUNET_CONFIGURATION_Handle *c,
559      struct GNUNET_SERVICE_Handle *service)
560 {
561   unsigned long long max_parallel_bg_queries = 16;
562
563   GNUNET_CONFIGURATION_iterate_section_values (c,
564                                                "gns",
565                                                &read_service_conf,
566                                                NULL);
567   v6_enabled = GNUNET_NETWORK_test_pf (PF_INET6);
568   v4_enabled = GNUNET_NETWORK_test_pf (PF_INET);
569   namecache_handle = GNUNET_NAMECACHE_connect (c);
570   if (NULL == namecache_handle)
571   {
572     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
573                 _("Failed to connect to the namecache!\n"));
574     GNUNET_SCHEDULER_shutdown ();
575     return;
576   }
577   if (GNUNET_OK ==
578       GNUNET_CONFIGURATION_get_value_number (c,
579                                              "gns",
580                                              "MAX_PARALLEL_BACKGROUND_QUERIES",
581                                              &max_parallel_bg_queries))
582   {
583     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
584                 "Number of allowed parallel background queries: %llu\n",
585                 max_parallel_bg_queries);
586   }
587   dht_handle = GNUNET_DHT_connect (c,
588                                    (unsigned int) max_parallel_bg_queries);
589   if (NULL == dht_handle)
590   {
591     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
592                 _("Could not connect to DHT!\n"));
593     GNUNET_SCHEDULER_add_now (&shutdown_task,
594                               NULL);
595     return;
596   }
597
598   identity_handle = GNUNET_IDENTITY_connect (c,
599                                              NULL,
600                                              NULL);
601   if (NULL == identity_handle)
602   {
603     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
604                 "Could not connect to identity service!\n");
605   }
606   else
607   {
608     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
609                 "Looking for gns-intercept ego\n");
610     identity_op = GNUNET_IDENTITY_get (identity_handle,
611                                        "gns-intercept",
612                                        &identity_intercept_cb,
613                                        (void *) c);
614   }
615   GNS_resolver_init (namecache_handle,
616                      dht_handle,
617                      c,
618                      max_parallel_bg_queries);
619   statistics = GNUNET_STATISTICS_create ("gns", c);
620   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
621                                  NULL);
622 }
623
624
625 /**
626  * Define "main" method using service macro.
627  */
628 GNUNET_SERVICE_MAIN
629 ("gns",
630  GNUNET_SERVICE_OPTION_NONE,
631  &run,
632  &client_connect_cb,
633  &client_disconnect_cb,
634  NULL,
635  GNUNET_MQ_hd_var_size (lookup,
636                         GNUNET_MESSAGE_TYPE_GNS_LOOKUP,
637                         struct LookupMessage,
638                         NULL),
639  GNUNET_MQ_handler_end());
640
641
642 /* end of gnunet-service-gns.c */