fix uninit variable causing crash on 0 value
[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_namestore_service.h"
33 #include "gnunet_identity_service.h"
34 #include "gnunet_gns_service.h"
35 #include "gnunet_statistics_service.h"
36 #include "gns.h"
37 #include "gnunet-service-gns_resolver.h"
38 #include "gnunet-service-gns_reverser.h"
39 #include "gnunet-service-gns_shorten.h"
40 #include "gnunet-service-gns_interceptor.h"
41 #include "gnunet_protocols.h"
42
43
44 /**
45  * GnsClient prototype
46  */
47 struct GnsClient;
48
49 /**
50  * Handle to a lookup operation from api
51  */
52 struct ClientLookupHandle
53 {
54
55   /**
56    * We keep these in a DLL.
57    */
58   struct ClientLookupHandle *next;
59
60   /**
61    * We keep these in a DLL.
62    */
63   struct ClientLookupHandle *prev;
64   
65   /**
66    * Client handle
67    */
68   struct GnsClient *gc;
69
70   /**
71    * Active handle for the lookup.
72    */
73   struct GNS_ResolverHandle *lookup;
74
75   /**
76    * Active handle for a reverse lookup
77    */
78   struct GNS_ReverserHandle *rev_lookup;
79
80   /**
81    * request id
82    */
83   uint32_t request_id;
84
85 };
86
87 struct GnsClient
88 {
89   /**
90    * The client
91    */
92   struct GNUNET_SERVICE_Client *client;
93
94   /**
95    * The MQ
96    */
97   struct GNUNET_MQ_Handle *mq;
98
99   /**
100    * Head of the DLL.
101    */
102   struct ClientLookupHandle *clh_head;
103
104   /**
105    * Tail of the DLL.
106    */
107   struct ClientLookupHandle *clh_tail;
108 };
109
110
111 /**
112  * Our handle to the DHT
113  */
114 static struct GNUNET_DHT_Handle *dht_handle;
115
116 /**
117  * Our handle to the namecache service
118  */
119 static struct GNUNET_NAMECACHE_Handle *namecache_handle;
120
121 /**
122  * Our handle to the namestore service
123  */
124 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
125
126 /**
127  * Our handle to the identity service
128  */
129 static struct GNUNET_IDENTITY_Handle *identity_handle;
130
131 /**
132  * Our handle to the identity operation to find the master zone
133  * for intercepted queries.
134  */
135 static struct GNUNET_IDENTITY_Operation *identity_op;
136
137 /**
138  * #GNUNET_YES if ipv6 is supported
139  */
140 static int v6_enabled;
141
142 /**
143  * #GNUNET_YES if ipv4 is supported
144  */
145 static int v4_enabled;
146
147 /**
148  * Handle to the statistics service
149  */
150 static struct GNUNET_STATISTICS_Handle *statistics;
151
152
153 /**
154  * Task run during shutdown.
155  *
156  * @param cls unused
157  * @param tc unused
158  */
159 static void
160 shutdown_task (void *cls)
161 {
162   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
163               "Shutting down!\n");
164   GNS_interceptor_done ();
165   if (NULL != identity_op)
166   {
167     GNUNET_IDENTITY_cancel (identity_op);
168     identity_op = NULL;
169   }
170   if (NULL != identity_handle)
171   {
172     GNUNET_IDENTITY_disconnect (identity_handle);
173     identity_handle = NULL;
174   }
175   GNS_resolver_done ();
176   GNS_reverse_done ();
177   GNS_shorten_done ();
178   if (NULL != statistics)
179   {
180     GNUNET_STATISTICS_destroy (statistics,
181                                GNUNET_NO);
182     statistics = NULL;
183   }
184   if (NULL != namestore_handle)
185   {
186     GNUNET_NAMESTORE_disconnect (namestore_handle);
187     namestore_handle = NULL;
188   }
189   if (NULL != namecache_handle)
190   {
191     GNUNET_NAMECACHE_disconnect (namecache_handle);
192     namecache_handle = NULL;
193   }
194   if (NULL != dht_handle)
195   {
196     GNUNET_DHT_disconnect (dht_handle);
197     dht_handle = NULL;
198   }
199 }
200
201
202 /**
203  * Called whenever a client is disconnected.
204  *
205  * @param cls closure
206  * @param client identification of the client
207  * @param app_ctx @a client
208  */
209 static void
210 client_disconnect_cb (void *cls,
211                       struct GNUNET_SERVICE_Client *client,
212                       void *app_ctx)
213 {
214   struct ClientLookupHandle *clh;
215   struct GnsClient *gc = app_ctx;
216
217   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
218               "Client %p disconnected\n",
219               client);
220   while (NULL != (clh = gc->clh_head))
221   {
222     if (NULL != clh->lookup)
223       GNS_resolver_lookup_cancel (clh->lookup);
224     if (NULL != clh->rev_lookup)
225       GNS_reverse_lookup_cancel (clh->rev_lookup);
226     GNUNET_CONTAINER_DLL_remove (gc->clh_head,
227                                  gc->clh_tail,
228                                  clh);
229     GNUNET_free (clh);
230   }
231
232   GNUNET_free (gc); 
233 }
234
235
236 /**
237  * Add a client to our list of active clients.
238  *
239  * @param cls NULL
240  * @param client client to add
241  * @param mq message queue for @a client
242  * @return internal namestore client structure for this client
243  */
244 static void *
245 client_connect_cb (void *cls,
246                    struct GNUNET_SERVICE_Client *client,
247                    struct GNUNET_MQ_Handle *mq)
248 {
249   struct GnsClient *gc;
250   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
251               "Client %p connected\n",
252               client);
253   gc = GNUNET_new (struct GnsClient);
254   gc->client = client;
255   gc->mq = mq;
256   return gc;
257 }
258
259
260 /**
261  * Reply to client with the result from our lookup.
262  *
263  * @param cls the closure (our client lookup handle)
264  * @param rd_count the number of records in @a rd
265  * @param rd the record data
266  */
267 static void
268 send_lookup_response (void* cls,
269                       uint32_t rd_count,
270                       const struct GNUNET_GNSRECORD_Data *rd)
271 {
272   struct ClientLookupHandle *clh = cls;
273   struct GNUNET_MQ_Envelope *env;
274   struct LookupResultMessage *rmsg;
275   size_t len;
276
277   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
278               "Sending LOOKUP_RESULT message with %u results\n",
279               (unsigned int) rd_count);
280
281   len = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
282   env = GNUNET_MQ_msg_extra (rmsg,
283                              len,
284                              GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
285   rmsg->id = clh->request_id;
286   rmsg->rd_count = htonl (rd_count);
287   GNUNET_GNSRECORD_records_serialize (rd_count, rd, len,
288                                       (char*) &rmsg[1]);
289   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(clh->gc->client),
290                   env);
291   GNUNET_CONTAINER_DLL_remove (clh->gc->clh_head, clh->gc->clh_tail, clh);
292   GNUNET_free (clh);
293   GNUNET_STATISTICS_update (statistics,
294                             "Completed lookups", 1,
295                             GNUNET_NO);
296   GNUNET_STATISTICS_update (statistics,
297                             "Records resolved",
298                             rd_count,
299                             GNUNET_NO);
300 }
301
302 /**
303  * Reply to client with the result from our reverse lookup.
304  *
305  * @param cls the closure (our client lookup handle)
306  * @param rd_count the number of records in @a rd
307  * @param rd the record data
308  */
309 static void
310 send_reverse_lookup_response (void* cls,
311                               const char *name)
312 {
313   struct ClientLookupHandle *clh = cls;
314   struct GNUNET_MQ_Envelope *env;
315   struct ReverseLookupResultMessage *rmsg;
316   size_t len;
317
318   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
319               "Sending LOOKUP_RESULT message with %s\n",
320               name);
321
322   if (NULL == name)
323     len = 1;
324   else
325     len = strlen (name) + 1;
326   env = GNUNET_MQ_msg_extra (rmsg,
327                              len,
328                              GNUNET_MESSAGE_TYPE_GNS_REVERSE_LOOKUP_RESULT);
329   rmsg->id = clh->request_id;
330   if (1 < len)
331     GNUNET_memcpy ((char*) &rmsg[1],
332                    name,
333                    strlen (name));
334   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(clh->gc->client),
335                   env);
336   GNUNET_CONTAINER_DLL_remove (clh->gc->clh_head, clh->gc->clh_tail, clh);
337   GNUNET_free (clh);
338   GNUNET_STATISTICS_update (statistics,
339                             "Completed reverse lookups", 1,
340                             GNUNET_NO);
341 }
342
343
344 /**
345  * Checks a #GNUNET_MESSAGE_TYPE_GNS_LOOKUP message
346  *
347  * @param cls client sending the message
348  * @param l_msg message of type `struct LookupMessage`
349  * @return #GNUNET_OK if @a l_msg is well-formed
350  */
351 static int
352 check_lookup (void *cls,
353               const struct LookupMessage *l_msg)
354 {
355   size_t msg_size;
356   const char* name;
357
358   msg_size = ntohs (l_msg->header.size);
359   if (msg_size < sizeof (struct LookupMessage))
360   {
361     GNUNET_break (0);
362     return GNUNET_SYSERR;
363   }
364   name = (const char *) &l_msg[1];
365   if ( ('\0' != name[msg_size - sizeof (struct LookupMessage) - 1]) ||
366        (strlen (name) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) )
367   {
368     GNUNET_break (0);
369     return GNUNET_SYSERR;
370   }
371   return GNUNET_OK;
372 }
373
374 /**
375  * Handle lookup requests from client
376  *
377  * @param cls the closure
378  * @param client the client
379  * @param message the message
380  */
381 static void
382 handle_lookup (void *cls,
383                const struct LookupMessage *sh_msg)
384 {
385   struct GnsClient *gc = cls;
386   char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
387   struct ClientLookupHandle *clh;
388   char *nameptr = name;
389   const char *utf_in;
390   const struct GNUNET_CRYPTO_EcdsaPrivateKey *key;
391
392   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
393               "Received LOOKUP message\n");
394   GNUNET_SERVICE_client_continue (gc->client);
395   if (GNUNET_YES == ntohs (sh_msg->have_key))
396     key = &sh_msg->shorten_key;
397   else
398     key = NULL;
399   utf_in = (const char *) &sh_msg[1];
400   GNUNET_STRINGS_utf8_tolower (utf_in, nameptr);
401
402   clh = GNUNET_new (struct ClientLookupHandle);
403   GNUNET_CONTAINER_DLL_insert (gc->clh_head, gc->clh_tail, clh);
404   clh->gc = gc;
405   clh->request_id = sh_msg->id;
406   if ( (GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
407        (GNUNET_OK != v4_enabled) )
408   {
409     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
410                 "LOOKUP: Query for A record but AF_INET not supported!");
411     send_lookup_response (clh, 0, NULL);
412     return;
413   }
414   if ( (GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
415        (GNUNET_OK != v6_enabled) )
416   {
417     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
418                 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
419     send_lookup_response (clh, 0, NULL);
420     return;
421   }
422   clh->lookup = GNS_resolver_lookup (&sh_msg->zone,
423                                      ntohl (sh_msg->type),
424                                      name,
425                                      key,
426                                      (enum GNUNET_GNS_LocalOptions) ntohs (sh_msg->options),
427                                      &send_lookup_response, clh);
428   GNUNET_STATISTICS_update (statistics,
429                             "Lookup attempts",
430                             1, GNUNET_NO);
431 }
432
433 /**
434  * Handle reverse lookup requests from client
435  *
436  * @param cls the closure
437  * @param client the client
438  * @param message the message
439  */
440 static void
441 handle_rev_lookup (void *cls,
442                    const struct ReverseLookupMessage *sh_msg)
443 {
444   struct GnsClient *gc = cls;
445   struct ClientLookupHandle *clh;
446
447   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448               "Received REVERSE_LOOKUP message\n");
449   GNUNET_SERVICE_client_continue (gc->client);
450
451   clh = GNUNET_new (struct ClientLookupHandle);
452   GNUNET_CONTAINER_DLL_insert (gc->clh_head, gc->clh_tail, clh);
453   clh->gc = gc;
454   clh->request_id = sh_msg->id;
455   clh->rev_lookup = GNS_reverse_lookup (&sh_msg->zone_pkey,
456                                         &sh_msg->root_pkey,
457                                         &send_reverse_lookup_response,
458                                         clh);
459   GNUNET_STATISTICS_update (statistics,
460                             "Reverse lookup attempts",
461                             1, GNUNET_NO);
462 }
463
464
465 /**
466  * Method called to inform about the ego to be used for the master zone
467  * for DNS interceptions.
468  *
469  * This function is only called ONCE, and 'NULL' being passed in
470  * @a ego does indicate that interception is not configured.
471  * If @a ego is non-NULL, we should start to intercept DNS queries
472  * and resolve ".gnu" queries using the given ego as the master zone.
473  *
474  * @param cls closure, our `const struct GNUNET_CONFIGURATION_Handle *c`
475  * @param ego ego handle
476  * @param ctx context for application to store data for this ego
477  *                 (during the lifetime of this process, initially NULL)
478  * @param name name assigned by the user for this ego,
479  *                   NULL if the user just deleted the ego and it
480  *                   must thus no longer be used
481  */
482 static void
483 identity_reverse_cb (void *cls,
484                        struct GNUNET_IDENTITY_Ego *ego,
485                        void **ctx,
486                        const char *name)
487 {
488   identity_op = NULL;
489
490   if (NULL == ego)
491   {
492     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
493                 _("No ego configured for `%s`\n"),
494                 "gns-master");
495
496     return;
497   }
498   if (GNUNET_SYSERR ==
499       GNS_reverse_init (namestore_handle,
500                         GNUNET_IDENTITY_ego_get_private_key (ego),
501                         name))
502   {
503     GNUNET_break (0);
504     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
505     return;
506   }
507 }
508
509
510 /**
511  * Method called to inform about the ego to be used for the master zone
512  * for DNS interceptions.
513  *
514  * This function is only called ONCE, and 'NULL' being passed in
515  * @a ego does indicate that interception is not configured.
516  * If @a ego is non-NULL, we should start to intercept DNS queries
517  * and resolve ".gnu" queries using the given ego as the master zone.
518  *
519  * @param cls closure, our `const struct GNUNET_CONFIGURATION_Handle *c`
520  * @param ego ego handle
521  * @param ctx context for application to store data for this ego
522  *                 (during the lifetime of this process, initially NULL)
523  * @param name name assigned by the user for this ego,
524  *                   NULL if the user just deleted the ego and it
525  *                   must thus no longer be used
526  */
527 static void
528 identity_intercept_cb (void *cls,
529                        struct GNUNET_IDENTITY_Ego *ego,
530                        void **ctx,
531                        const char *name)
532 {
533   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
534   struct GNUNET_CRYPTO_EcdsaPublicKey dns_root;
535   identity_op = NULL;
536
537   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
538               "Looking for gns-intercept ego\n");
539   identity_op = GNUNET_IDENTITY_get (identity_handle,
540                                      "gns-reverse",
541                                      &identity_reverse_cb,
542                                      (void*)cfg);
543
544
545   if (NULL == ego)
546   {
547     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
548                 _("No ego configured for `%s`\n"),
549                 "gns-intercept");
550
551     return;
552   }
553   GNUNET_IDENTITY_ego_get_public_key (ego,
554                                       &dns_root);
555   if (GNUNET_SYSERR ==
556       GNS_interceptor_init (&dns_root, cfg))
557   {
558     GNUNET_break (0);
559     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
560     return;
561   }
562 }
563
564
565 /**
566  * Process GNS requests.
567  *
568  * @param cls closure
569  * @param server the initialized server
570  * @param c configuration to use
571  */
572 static void
573 run (void *cls,
574      const struct GNUNET_CONFIGURATION_Handle *c,
575      struct GNUNET_SERVICE_Handle *service)
576 {
577   unsigned long long max_parallel_bg_queries = 16;
578
579   v6_enabled = GNUNET_NETWORK_test_pf (PF_INET6);
580   v4_enabled = GNUNET_NETWORK_test_pf (PF_INET); 
581   namestore_handle = GNUNET_NAMESTORE_connect (c);
582   if (NULL == namestore_handle)
583   {
584     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
585                 _("Failed to connect to the namestore!\n"));
586     GNUNET_SCHEDULER_shutdown ();
587     return;
588   }
589  namecache_handle = GNUNET_NAMECACHE_connect (c);
590   if (NULL == namecache_handle)
591   {
592     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
593                 _("Failed to connect to the namecache!\n"));
594     GNUNET_SCHEDULER_shutdown ();
595     return;
596   }
597   if (GNUNET_OK ==
598       GNUNET_CONFIGURATION_get_value_number (c,
599                                              "gns",
600                                              "MAX_PARALLEL_BACKGROUND_QUERIES",
601                                              &max_parallel_bg_queries))
602   {
603     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
604                 "Number of allowed parallel background queries: %llu\n",
605                 max_parallel_bg_queries);
606   }
607   dht_handle = GNUNET_DHT_connect (c,
608                                    (unsigned int) max_parallel_bg_queries);
609   if (NULL == dht_handle)
610   {
611     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
612                 _("Could not connect to DHT!\n"));
613     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
614     return;
615   }
616
617   identity_handle = GNUNET_IDENTITY_connect (c,
618                                              NULL,
619                                              NULL);
620   if (NULL == identity_handle)
621   {
622     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
623                 "Could not connect to identity service!\n");
624   }
625   else
626   {
627     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
628                 "Looking for gns-intercept ego\n");
629     identity_op = GNUNET_IDENTITY_get (identity_handle,
630                                        "gns-intercept",
631                                        &identity_intercept_cb,
632                                        (void *) c);
633   }
634   GNS_resolver_init (namecache_handle,
635                      dht_handle,
636                      c,
637                      max_parallel_bg_queries);
638   GNS_shorten_init (namestore_handle,
639                     namecache_handle,
640                     dht_handle);
641   statistics = GNUNET_STATISTICS_create ("gns", c);
642   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
643 }
644
645
646 /**
647  * Define "main" method using service macro.
648  */
649 GNUNET_SERVICE_MAIN
650 ("gns",
651  GNUNET_SERVICE_OPTION_NONE,
652  &run,
653  &client_connect_cb,
654  &client_disconnect_cb,
655  NULL,
656  GNUNET_MQ_hd_var_size (lookup,
657                         GNUNET_MESSAGE_TYPE_GNS_LOOKUP,
658                         struct LookupMessage,
659                         NULL),
660  GNUNET_MQ_hd_fixed_size (rev_lookup,
661                           GNUNET_MESSAGE_TYPE_GNS_REVERSE_LOOKUP,
662                           struct ReverseLookupMessage,
663                           NULL),
664  GNUNET_MQ_handler_end());
665
666
667 /* end of gnunet-service-gns.c */