e13beb8897d4fe9d9eb4c2a1897bc4ab293f6c78
[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   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
227               "Shutting down!\n");
228   GNS_interceptor_done ();
229   if (NULL != identity_op)
230   {
231     GNUNET_IDENTITY_cancel (identity_op);
232     identity_op = NULL;
233   }
234   if (NULL != identity_handle)
235   {
236     GNUNET_IDENTITY_disconnect (identity_handle);
237     identity_handle = NULL;
238   }
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   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
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   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314               "Client %p connected\n",
315               client);
316   gc = GNUNET_new (struct GnsClient);
317   gc->client = client;
318   gc->mq = mq;
319   return gc;
320 }
321
322
323 /**
324  * Reply to client with the result from our lookup.
325  *
326  * @param cls the closure (our client lookup handle)
327  * @param rd_count the number of records in @a rd
328  * @param rd the record data
329  */
330 static void
331 send_lookup_response (void* cls,
332                       uint32_t rd_count,
333                       const struct GNUNET_GNSRECORD_Data *rd)
334 {
335   struct ClientLookupHandle *clh = cls;
336   struct GNUNET_MQ_Envelope *env;
337   struct LookupResultMessage *rmsg;
338   size_t len;
339
340   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
341               "Sending LOOKUP_RESULT message with %u results\n",
342               (unsigned int) rd_count);
343
344   len = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
345   env = GNUNET_MQ_msg_extra (rmsg,
346                              len,
347                              GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
348   rmsg->id = clh->request_id;
349   rmsg->rd_count = htonl (rd_count);
350   GNUNET_GNSRECORD_records_serialize (rd_count, rd, len,
351                                       (char*) &rmsg[1]);
352   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(clh->gc->client),
353                   env);
354   GNUNET_CONTAINER_DLL_remove (clh->gc->clh_head,
355                                clh->gc->clh_tail,
356                                clh);
357   GNUNET_free (clh);
358   GNUNET_STATISTICS_update (statistics,
359                             "Completed lookups", 1,
360                             GNUNET_NO);
361   GNUNET_STATISTICS_update (statistics,
362                             "Records resolved",
363                             rd_count,
364                             GNUNET_NO);
365 }
366
367
368 /**
369  * Checks a #GNUNET_MESSAGE_TYPE_GNS_LOOKUP message
370  *
371  * @param cls client sending the message
372  * @param l_msg message of type `struct LookupMessage`
373  * @return #GNUNET_OK if @a l_msg is well-formed
374  */
375 static int
376 check_lookup (void *cls,
377               const struct LookupMessage *l_msg)
378 {
379   size_t msg_size;
380   const char* name;
381
382   msg_size = ntohs (l_msg->header.size);
383   if (msg_size < sizeof (struct LookupMessage))
384   {
385     GNUNET_break (0);
386     return GNUNET_SYSERR;
387   }
388   name = (const char *) &l_msg[1];
389   if ( ('\0' != name[msg_size - sizeof (struct LookupMessage) - 1]) ||
390        (strlen (name) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) )
391   {
392     GNUNET_break (0);
393     return GNUNET_SYSERR;
394   }
395   return GNUNET_OK;
396 }
397
398
399 /**
400  * Handle lookup requests from client
401  *
402  * @param cls the closure
403  * @param client the client
404  * @param message the message
405  */
406 static void
407 handle_lookup (void *cls,
408                const struct LookupMessage *sh_msg)
409 {
410   struct GnsClient *gc = cls;
411   char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
412   struct ClientLookupHandle *clh;
413   char *nameptr = name;
414   const char *utf_in;
415
416   GNUNET_SERVICE_client_continue (gc->client);
417   utf_in = (const char *) &sh_msg[1];
418   GNUNET_STRINGS_utf8_tolower (utf_in, nameptr);
419   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
420               "Received LOOKUP `%s' message\n",
421               name);
422
423   clh = GNUNET_new (struct ClientLookupHandle);
424   GNUNET_CONTAINER_DLL_insert (gc->clh_head,
425                                gc->clh_tail,
426                                clh);
427   clh->gc = gc;
428   clh->request_id = sh_msg->id;
429   if ( (GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
430        (GNUNET_OK != v4_enabled) )
431   {
432     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
433                 "LOOKUP: Query for A record but AF_INET not supported!");
434     send_lookup_response (clh, 0, NULL);
435     return;
436   }
437   if ( (GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
438        (GNUNET_OK != v6_enabled) )
439   {
440     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
441                 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
442     send_lookup_response (clh, 0, NULL);
443     return;
444   }
445   clh->lookup = GNS_resolver_lookup (&sh_msg->zone,
446                                      ntohl (sh_msg->type),
447                                      name,
448                                      (enum GNUNET_GNS_LocalOptions) ntohs (sh_msg->options),
449                                      &send_lookup_response, clh);
450   GNUNET_STATISTICS_update (statistics,
451                             "Lookup attempts",
452                             1, GNUNET_NO);
453 }
454
455
456 /**
457  * Method called to inform about the ego to be used for the master zone
458  * for DNS interceptions.
459  *
460  * This function is only called ONCE, and 'NULL' being passed in
461  * @a ego does indicate that interception is not configured.
462  * If @a ego is non-NULL, we should start to intercept DNS queries
463  * and resolve ".gnu" queries using the given ego as the master zone.
464  *
465  * @param cls closure, our `const struct GNUNET_CONFIGURATION_Handle *c`
466  * @param ego ego handle
467  * @param ctx context for application to store data for this ego
468  *                 (during the lifetime of this process, initially NULL)
469  * @param name name assigned by the user for this ego,
470  *                   NULL if the user just deleted the ego and it
471  *                   must thus no longer be used
472  */
473 static void
474 identity_intercept_cb (void *cls,
475                        struct GNUNET_IDENTITY_Ego *ego,
476                        void **ctx,
477                        const char *name)
478 {
479   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
480   struct GNUNET_CRYPTO_EcdsaPublicKey dns_root;
481
482   identity_op = NULL;
483   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
484               "Looking for gns-intercept ego\n");
485   if (NULL == ego)
486   {
487     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
488                 _("No ego configured for `%s`\n"),
489                 "gns-intercept");
490
491     return;
492   }
493   GNUNET_IDENTITY_ego_get_public_key (ego,
494                                       &dns_root);
495   if (GNUNET_SYSERR ==
496       GNS_interceptor_init (&dns_root,
497                             cfg))
498   {
499     GNUNET_break (0);
500     GNUNET_SCHEDULER_add_now (&shutdown_task,
501                               NULL);
502     return;
503   }
504 }
505
506
507 /**
508  * Reads the configuration and populates TLDs
509  *
510  * @param cls unused
511  * @param section name of section in config, always "gns"
512  * @param option name of the option, TLDs start with "."
513  * @param value value for the option, public key for TLDs
514  */
515 static void
516 read_service_conf (void *cls,
517                    const char *section,
518                    const char *option,
519                    const char *value)
520 {
521   struct GNUNET_CRYPTO_EddsaPublicKey pk;
522   struct GNS_TopLevelDomain *tld;
523
524   if (option[0] != '.')
525     return;
526   if (GNUNET_OK !=
527       GNUNET_STRINGS_string_to_data (value,
528                                      strlen (value),
529                                      &pk,
530                                      sizeof (pk)))
531   {
532     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
533                                section,
534                                option,
535                                _("Properly base32-encoded public key required"));
536     return;
537   }
538   tld = GNUNET_new (struct GNS_TopLevelDomain);
539   tld->tld = GNUNET_strdup (option);
540   tld->pkey = pk;
541   GNUNET_CONTAINER_DLL_insert (tld_head,
542                                tld_tail,
543                                tld);
544 }
545
546
547
548 /**
549  * Process GNS requests.
550  *
551  * @param cls closure
552  * @param server the initialized server
553  * @param c configuration to use
554  */
555 static void
556 run (void *cls,
557      const struct GNUNET_CONFIGURATION_Handle *c,
558      struct GNUNET_SERVICE_Handle *service)
559 {
560   unsigned long long max_parallel_bg_queries = 16;
561
562   GNUNET_CONFIGURATION_iterate_section_values (c,
563                                                "gns",
564                                                &read_service_conf,
565                                                NULL);
566   v6_enabled = GNUNET_NETWORK_test_pf (PF_INET6);
567   v4_enabled = GNUNET_NETWORK_test_pf (PF_INET);
568   namecache_handle = GNUNET_NAMECACHE_connect (c);
569   if (NULL == namecache_handle)
570   {
571     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
572                 _("Failed to connect to the namecache!\n"));
573     GNUNET_SCHEDULER_shutdown ();
574     return;
575   }
576   if (GNUNET_OK ==
577       GNUNET_CONFIGURATION_get_value_number (c,
578                                              "gns",
579                                              "MAX_PARALLEL_BACKGROUND_QUERIES",
580                                              &max_parallel_bg_queries))
581   {
582     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
583                 "Number of allowed parallel background queries: %llu\n",
584                 max_parallel_bg_queries);
585   }
586   dht_handle = GNUNET_DHT_connect (c,
587                                    (unsigned int) max_parallel_bg_queries);
588   if (NULL == dht_handle)
589   {
590     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
591                 _("Could not connect to DHT!\n"));
592     GNUNET_SCHEDULER_add_now (&shutdown_task,
593                               NULL);
594     return;
595   }
596
597   identity_handle = GNUNET_IDENTITY_connect (c,
598                                              NULL,
599                                              NULL);
600   if (NULL == identity_handle)
601   {
602     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
603                 "Could not connect to identity service!\n");
604   }
605   else
606   {
607     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
608                 "Looking for gns-intercept ego\n");
609     identity_op = GNUNET_IDENTITY_get (identity_handle,
610                                        "gns-intercept",
611                                        &identity_intercept_cb,
612                                        (void *) c);
613   }
614   GNS_resolver_init (namecache_handle,
615                      dht_handle,
616                      c,
617                      max_parallel_bg_queries);
618   statistics = GNUNET_STATISTICS_create ("gns", c);
619   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
620                                  NULL);
621 }
622
623
624 /**
625  * Define "main" method using service macro.
626  */
627 GNUNET_SERVICE_MAIN
628 ("gns",
629  GNUNET_SERVICE_OPTION_NONE,
630  &run,
631  &client_connect_cb,
632  &client_disconnect_cb,
633  NULL,
634  GNUNET_MQ_hd_var_size (lookup,
635                         GNUNET_MESSAGE_TYPE_GNS_LOOKUP,
636                         struct LookupMessage,
637                         NULL),
638  GNUNET_MQ_handler_end());
639
640
641 /* end of gnunet-service-gns.c */