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