1e8d07bc54c7a9534ef285247330127876ca27b1
[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    * request id
77    */
78   uint32_t request_id;
79
80 };
81
82 struct GnsClient
83 {
84   /**
85    * The client
86    */
87   struct GNUNET_SERVICE_Client *client;
88
89   /**
90    * The MQ
91    */
92   struct GNUNET_MQ_Handle *mq;
93
94   /**
95    * Head of the DLL.
96    */
97   struct ClientLookupHandle *clh_head;
98
99   /**
100    * Tail of the DLL.
101    */
102   struct ClientLookupHandle *clh_tail;
103 };
104
105
106 /**
107  * Our handle to the DHT
108  */
109 static struct GNUNET_DHT_Handle *dht_handle;
110
111 /**
112  * Our handle to the namecache service
113  */
114 static struct GNUNET_NAMECACHE_Handle *namecache_handle;
115
116 /**
117  * Our handle to the namestore service
118  */
119 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
120
121 /**
122  * Our handle to the identity service
123  */
124 static struct GNUNET_IDENTITY_Handle *identity_handle;
125
126 /**
127  * Our handle to the identity operation to find the master zone
128  * for intercepted queries.
129  */
130 static struct GNUNET_IDENTITY_Operation *identity_op;
131
132 /**
133  * #GNUNET_YES if ipv6 is supported
134  */
135 static int v6_enabled;
136
137 /**
138  * #GNUNET_YES if ipv4 is supported
139  */
140 static int v4_enabled;
141
142 /**
143  * Handle to the statistics service
144  */
145 static struct GNUNET_STATISTICS_Handle *statistics;
146
147
148 /**
149  * Task run during shutdown.
150  *
151  * @param cls unused
152  * @param tc unused
153  */
154 static void
155 shutdown_task (void *cls)
156 {
157   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
158               "Shutting down!\n");
159   GNS_interceptor_done ();
160   if (NULL != identity_op)
161   {
162     GNUNET_IDENTITY_cancel (identity_op);
163     identity_op = NULL;
164   }
165   if (NULL != identity_handle)
166   {
167     GNUNET_IDENTITY_disconnect (identity_handle);
168     identity_handle = NULL;
169   }
170   GNS_resolver_done ();
171   if (NULL != statistics)
172   {
173     GNUNET_STATISTICS_destroy (statistics,
174                                GNUNET_NO);
175     statistics = NULL;
176   }
177   if (NULL != namestore_handle)
178   {
179     GNUNET_NAMESTORE_disconnect (namestore_handle);
180     namestore_handle = NULL;
181   }
182   if (NULL != namecache_handle)
183   {
184     GNUNET_NAMECACHE_disconnect (namecache_handle);
185     namecache_handle = NULL;
186   }
187   if (NULL != dht_handle)
188   {
189     GNUNET_DHT_disconnect (dht_handle);
190     dht_handle = NULL;
191   }
192 }
193
194
195 /**
196  * Called whenever a client is disconnected.
197  *
198  * @param cls closure
199  * @param client identification of the client
200  * @param app_ctx @a client
201  */
202 static void
203 client_disconnect_cb (void *cls,
204                       struct GNUNET_SERVICE_Client *client,
205                       void *app_ctx)
206 {
207   struct ClientLookupHandle *clh;
208   struct GnsClient *gc = app_ctx;
209
210   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
211               "Client %p disconnected\n",
212               client);
213   while (NULL != (clh = gc->clh_head))
214   {
215     if (NULL != clh->lookup)
216       GNS_resolver_lookup_cancel (clh->lookup);
217     GNUNET_CONTAINER_DLL_remove (gc->clh_head,
218                                  gc->clh_tail,
219                                  clh);
220     GNUNET_free (clh);
221   }
222
223   GNUNET_free (gc);
224 }
225
226
227 /**
228  * Add a client to our list of active clients.
229  *
230  * @param cls NULL
231  * @param client client to add
232  * @param mq message queue for @a client
233  * @return internal namestore client structure for this client
234  */
235 static void *
236 client_connect_cb (void *cls,
237                    struct GNUNET_SERVICE_Client *client,
238                    struct GNUNET_MQ_Handle *mq)
239 {
240   struct GnsClient *gc;
241   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
242               "Client %p connected\n",
243               client);
244   gc = GNUNET_new (struct GnsClient);
245   gc->client = client;
246   gc->mq = mq;
247   return gc;
248 }
249
250
251 /**
252  * Reply to client with the result from our lookup.
253  *
254  * @param cls the closure (our client lookup handle)
255  * @param rd_count the number of records in @a rd
256  * @param rd the record data
257  */
258 static void
259 send_lookup_response (void* cls,
260                       uint32_t rd_count,
261                       const struct GNUNET_GNSRECORD_Data *rd)
262 {
263   struct ClientLookupHandle *clh = cls;
264   struct GNUNET_MQ_Envelope *env;
265   struct LookupResultMessage *rmsg;
266   size_t len;
267
268   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
269               "Sending LOOKUP_RESULT message with %u results\n",
270               (unsigned int) rd_count);
271
272   len = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
273   env = GNUNET_MQ_msg_extra (rmsg,
274                              len,
275                              GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
276   rmsg->id = clh->request_id;
277   rmsg->rd_count = htonl (rd_count);
278   GNUNET_GNSRECORD_records_serialize (rd_count, rd, len,
279                                       (char*) &rmsg[1]);
280   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(clh->gc->client),
281                   env);
282   GNUNET_CONTAINER_DLL_remove (clh->gc->clh_head,
283                                clh->gc->clh_tail,
284                                clh);
285   GNUNET_free (clh);
286   GNUNET_STATISTICS_update (statistics,
287                             "Completed lookups", 1,
288                             GNUNET_NO);
289   GNUNET_STATISTICS_update (statistics,
290                             "Records resolved",
291                             rd_count,
292                             GNUNET_NO);
293 }
294
295
296 /**
297  * Checks a #GNUNET_MESSAGE_TYPE_GNS_LOOKUP message
298  *
299  * @param cls client sending the message
300  * @param l_msg message of type `struct LookupMessage`
301  * @return #GNUNET_OK if @a l_msg is well-formed
302  */
303 static int
304 check_lookup (void *cls,
305               const struct LookupMessage *l_msg)
306 {
307   size_t msg_size;
308   const char* name;
309
310   msg_size = ntohs (l_msg->header.size);
311   if (msg_size < sizeof (struct LookupMessage))
312   {
313     GNUNET_break (0);
314     return GNUNET_SYSERR;
315   }
316   name = (const char *) &l_msg[1];
317   if ( ('\0' != name[msg_size - sizeof (struct LookupMessage) - 1]) ||
318        (strlen (name) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) )
319   {
320     GNUNET_break (0);
321     return GNUNET_SYSERR;
322   }
323   return GNUNET_OK;
324 }
325
326
327 /**
328  * Handle lookup requests from client
329  *
330  * @param cls the closure
331  * @param client the client
332  * @param message the message
333  */
334 static void
335 handle_lookup (void *cls,
336                const struct LookupMessage *sh_msg)
337 {
338   struct GnsClient *gc = cls;
339   char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
340   struct ClientLookupHandle *clh;
341   char *nameptr = name;
342   const char *utf_in;
343
344   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
345               "Received LOOKUP message\n");
346   GNUNET_SERVICE_client_continue (gc->client);
347   utf_in = (const char *) &sh_msg[1];
348   GNUNET_STRINGS_utf8_tolower (utf_in, nameptr);
349
350   clh = GNUNET_new (struct ClientLookupHandle);
351   GNUNET_CONTAINER_DLL_insert (gc->clh_head,
352                                gc->clh_tail,
353                                clh);
354   clh->gc = gc;
355   clh->request_id = sh_msg->id;
356   if ( (GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
357        (GNUNET_OK != v4_enabled) )
358   {
359     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
360                 "LOOKUP: Query for A record but AF_INET not supported!");
361     send_lookup_response (clh, 0, NULL);
362     return;
363   }
364   if ( (GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
365        (GNUNET_OK != v6_enabled) )
366   {
367     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368                 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
369     send_lookup_response (clh, 0, NULL);
370     return;
371   }
372   clh->lookup = GNS_resolver_lookup (&sh_msg->zone,
373                                      ntohl (sh_msg->type),
374                                      name,
375                                      (enum GNUNET_GNS_LocalOptions) ntohs (sh_msg->options),
376                                      &send_lookup_response, clh);
377   GNUNET_STATISTICS_update (statistics,
378                             "Lookup attempts",
379                             1, GNUNET_NO);
380 }
381
382
383 /**
384  * Method called to inform about the ego to be used for the master zone
385  * for DNS interceptions.
386  *
387  * This function is only called ONCE, and 'NULL' being passed in
388  * @a ego does indicate that interception is not configured.
389  * If @a ego is non-NULL, we should start to intercept DNS queries
390  * and resolve ".gnu" queries using the given ego as the master zone.
391  *
392  * @param cls closure, our `const struct GNUNET_CONFIGURATION_Handle *c`
393  * @param ego ego handle
394  * @param ctx context for application to store data for this ego
395  *                 (during the lifetime of this process, initially NULL)
396  * @param name name assigned by the user for this ego,
397  *                   NULL if the user just deleted the ego and it
398  *                   must thus no longer be used
399  */
400 static void
401 identity_intercept_cb (void *cls,
402                        struct GNUNET_IDENTITY_Ego *ego,
403                        void **ctx,
404                        const char *name)
405 {
406   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
407   struct GNUNET_CRYPTO_EcdsaPublicKey dns_root;
408
409   identity_op = NULL;
410   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411               "Looking for gns-intercept ego\n");
412   if (NULL == ego)
413   {
414     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
415                 _("No ego configured for `%s`\n"),
416                 "gns-intercept");
417
418     return;
419   }
420   GNUNET_IDENTITY_ego_get_public_key (ego,
421                                       &dns_root);
422   if (GNUNET_SYSERR ==
423       GNS_interceptor_init (&dns_root,
424                             cfg))
425   {
426     GNUNET_break (0);
427     GNUNET_SCHEDULER_add_now (&shutdown_task,
428                               NULL);
429     return;
430   }
431 }
432
433
434 /**
435  * Process GNS requests.
436  *
437  * @param cls closure
438  * @param server the initialized server
439  * @param c configuration to use
440  */
441 static void
442 run (void *cls,
443      const struct GNUNET_CONFIGURATION_Handle *c,
444      struct GNUNET_SERVICE_Handle *service)
445 {
446   unsigned long long max_parallel_bg_queries = 16;
447
448   v6_enabled = GNUNET_NETWORK_test_pf (PF_INET6);
449   v4_enabled = GNUNET_NETWORK_test_pf (PF_INET);
450   namestore_handle = GNUNET_NAMESTORE_connect (c);
451   if (NULL == namestore_handle)
452   {
453     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
454                 _("Failed to connect to the namestore!\n"));
455     GNUNET_SCHEDULER_shutdown ();
456     return;
457   }
458  namecache_handle = GNUNET_NAMECACHE_connect (c);
459   if (NULL == namecache_handle)
460   {
461     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
462                 _("Failed to connect to the namecache!\n"));
463     GNUNET_SCHEDULER_shutdown ();
464     return;
465   }
466   if (GNUNET_OK ==
467       GNUNET_CONFIGURATION_get_value_number (c,
468                                              "gns",
469                                              "MAX_PARALLEL_BACKGROUND_QUERIES",
470                                              &max_parallel_bg_queries))
471   {
472     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
473                 "Number of allowed parallel background queries: %llu\n",
474                 max_parallel_bg_queries);
475   }
476   dht_handle = GNUNET_DHT_connect (c,
477                                    (unsigned int) max_parallel_bg_queries);
478   if (NULL == dht_handle)
479   {
480     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
481                 _("Could not connect to DHT!\n"));
482     GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
483     return;
484   }
485
486   identity_handle = GNUNET_IDENTITY_connect (c,
487                                              NULL,
488                                              NULL);
489   if (NULL == identity_handle)
490   {
491     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
492                 "Could not connect to identity service!\n");
493   }
494   else
495   {
496     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
497                 "Looking for gns-intercept ego\n");
498     identity_op = GNUNET_IDENTITY_get (identity_handle,
499                                        "gns-intercept",
500                                        &identity_intercept_cb,
501                                        (void *) c);
502   }
503   GNS_resolver_init (namecache_handle,
504                      dht_handle,
505                      c,
506                      max_parallel_bg_queries);
507   statistics = GNUNET_STATISTICS_create ("gns", c);
508   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
509                                  NULL);
510 }
511
512
513 /**
514  * Define "main" method using service macro.
515  */
516 GNUNET_SERVICE_MAIN
517 ("gns",
518  GNUNET_SERVICE_OPTION_NONE,
519  &run,
520  &client_connect_cb,
521  &client_disconnect_cb,
522  NULL,
523  GNUNET_MQ_hd_var_size (lookup,
524                         GNUNET_MESSAGE_TYPE_GNS_LOOKUP,
525                         struct LookupMessage,
526                         NULL),
527  GNUNET_MQ_handler_end());
528
529
530 /* end of gnunet-service-gns.c */