2 This file is part of GNUnet.
3 Copyright (C) 2011-2013 GNUnet e.V.
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.
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.
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.
21 * @file gns/gnunet-service-gns.c
22 * @brief GNU Name System (main service)
23 * @author Martin Schanzenbach
24 * @author Christian Grothoff
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"
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"
50 * Handle to a lookup operation from api
52 struct ClientLookupHandle
56 * We keep these in a DLL.
58 struct ClientLookupHandle *next;
61 * We keep these in a DLL.
63 struct ClientLookupHandle *prev;
71 * Active handle for the lookup.
73 struct GNS_ResolverHandle *lookup;
76 * Active handle for a reverse lookup
78 struct GNS_ReverserHandle *rev_lookup;
92 struct GNUNET_SERVICE_Client *client;
97 struct GNUNET_MQ_Handle *mq;
102 struct ClientLookupHandle *clh_head;
107 struct ClientLookupHandle *clh_tail;
112 * Our handle to the DHT
114 static struct GNUNET_DHT_Handle *dht_handle;
117 * Our handle to the namecache service
119 static struct GNUNET_NAMECACHE_Handle *namecache_handle;
122 * Our handle to the namestore service
124 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
127 * Our handle to the identity service
129 static struct GNUNET_IDENTITY_Handle *identity_handle;
132 * Our handle to the identity operation to find the master zone
133 * for intercepted queries.
135 static struct GNUNET_IDENTITY_Operation *identity_op;
138 * #GNUNET_YES if ipv6 is supported
140 static int v6_enabled;
143 * #GNUNET_YES if ipv4 is supported
145 static int v4_enabled;
148 * Handle to the statistics service
150 static struct GNUNET_STATISTICS_Handle *statistics;
154 * Task run during shutdown.
160 shutdown_task (void *cls)
162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
164 GNS_interceptor_done ();
165 if (NULL != identity_op)
167 GNUNET_IDENTITY_cancel (identity_op);
170 if (NULL != identity_handle)
172 GNUNET_IDENTITY_disconnect (identity_handle);
173 identity_handle = NULL;
175 GNS_resolver_done ();
178 if (NULL != statistics)
180 GNUNET_STATISTICS_destroy (statistics,
184 if (NULL != namestore_handle)
186 GNUNET_NAMESTORE_disconnect (namestore_handle);
187 namestore_handle = NULL;
189 if (NULL != namecache_handle)
191 GNUNET_NAMECACHE_disconnect (namecache_handle);
192 namecache_handle = NULL;
194 if (NULL != dht_handle)
196 GNUNET_DHT_disconnect (dht_handle);
203 * Called whenever a client is disconnected.
206 * @param client identification of the client
207 * @param app_ctx @a client
210 client_disconnect_cb (void *cls,
211 struct GNUNET_SERVICE_Client *client,
214 struct ClientLookupHandle *clh;
215 struct GnsClient *gc = app_ctx;
217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
218 "Client %p disconnected\n",
220 while (NULL != (clh = gc->clh_head))
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,
237 * Add a client to our list of active clients.
240 * @param client client to add
241 * @param mq message queue for @a client
242 * @return internal namestore client structure for this client
245 client_connect_cb (void *cls,
246 struct GNUNET_SERVICE_Client *client,
247 struct GNUNET_MQ_Handle *mq)
249 struct GnsClient *gc;
250 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
251 "Client %p connected\n",
253 gc = GNUNET_new (struct GnsClient);
261 * Reply to client with the result from our lookup.
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
268 send_lookup_response (void* cls,
270 const struct GNUNET_GNSRECORD_Data *rd)
272 struct ClientLookupHandle *clh = cls;
273 struct GNUNET_MQ_Envelope *env;
274 struct LookupResultMessage *rmsg;
277 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
278 "Sending LOOKUP_RESULT message with %u results\n",
279 (unsigned int) rd_count);
281 len = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
282 env = GNUNET_MQ_msg_extra (rmsg,
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,
289 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(clh->gc->client),
291 GNUNET_CONTAINER_DLL_remove (clh->gc->clh_head, clh->gc->clh_tail, clh);
293 GNUNET_STATISTICS_update (statistics,
294 "Completed lookups", 1,
296 GNUNET_STATISTICS_update (statistics,
303 * Reply to client with the result from our reverse lookup.
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
310 send_reverse_lookup_response (void* cls,
313 struct ClientLookupHandle *clh = cls;
314 struct GNUNET_MQ_Envelope *env;
315 struct ReverseLookupResultMessage *rmsg;
318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
319 "Sending LOOKUP_RESULT message with %s\n",
325 len = strlen (name) + 1;
326 env = GNUNET_MQ_msg_extra (rmsg,
328 GNUNET_MESSAGE_TYPE_GNS_REVERSE_LOOKUP_RESULT);
329 rmsg->id = clh->request_id;
331 GNUNET_memcpy ((char*) &rmsg[1],
334 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(clh->gc->client),
336 GNUNET_CONTAINER_DLL_remove (clh->gc->clh_head, clh->gc->clh_tail, clh);
338 GNUNET_STATISTICS_update (statistics,
339 "Completed reverse lookups", 1,
345 * Checks a #GNUNET_MESSAGE_TYPE_GNS_LOOKUP message
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
352 check_lookup (void *cls,
353 const struct LookupMessage *l_msg)
358 msg_size = ntohs (l_msg->header.size);
359 if (msg_size < sizeof (struct LookupMessage))
362 return GNUNET_SYSERR;
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) )
369 return GNUNET_SYSERR;
375 * Handle lookup requests from client
377 * @param cls the closure
378 * @param client the client
379 * @param message the message
382 handle_lookup (void *cls,
383 const struct LookupMessage *sh_msg)
385 struct GnsClient *gc = cls;
386 char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
387 struct ClientLookupHandle *clh;
388 char *nameptr = name;
390 const struct GNUNET_CRYPTO_EcdsaPrivateKey *key;
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;
399 utf_in = (const char *) &sh_msg[1];
400 GNUNET_STRINGS_utf8_tolower (utf_in, nameptr);
402 clh = GNUNET_new (struct ClientLookupHandle);
403 GNUNET_CONTAINER_DLL_insert (gc->clh_head, gc->clh_tail, clh);
405 clh->request_id = sh_msg->id;
406 if ( (GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
407 (GNUNET_OK != v4_enabled) )
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);
414 if ( (GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
415 (GNUNET_OK != v6_enabled) )
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);
422 clh->lookup = GNS_resolver_lookup (&sh_msg->zone,
423 ntohl (sh_msg->type),
426 (enum GNUNET_GNS_LocalOptions) ntohs (sh_msg->options),
427 &send_lookup_response, clh);
428 GNUNET_STATISTICS_update (statistics,
434 * Handle reverse lookup requests from client
436 * @param cls the closure
437 * @param client the client
438 * @param message the message
441 handle_rev_lookup (void *cls,
442 const struct ReverseLookupMessage *sh_msg)
444 struct GnsClient *gc = cls;
445 struct ClientLookupHandle *clh;
447 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448 "Received REVERSE_LOOKUP message\n");
449 GNUNET_SERVICE_client_continue (gc->client);
451 clh = GNUNET_new (struct ClientLookupHandle);
452 GNUNET_CONTAINER_DLL_insert (gc->clh_head, gc->clh_tail, clh);
454 clh->request_id = sh_msg->id;
455 clh->rev_lookup = GNS_reverse_lookup (&sh_msg->zone_pkey,
457 &send_reverse_lookup_response,
459 GNUNET_STATISTICS_update (statistics,
460 "Reverse lookup attempts",
466 * Method called to inform about the ego to be used for the master zone
467 * for DNS interceptions.
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.
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
483 identity_reverse_cb (void *cls,
484 struct GNUNET_IDENTITY_Ego *ego,
492 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
493 _("No ego configured for `%s`\n"),
499 GNS_reverse_init (namestore_handle,
500 GNUNET_IDENTITY_ego_get_private_key (ego),
504 GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
511 * Method called to inform about the ego to be used for the master zone
512 * for DNS interceptions.
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.
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
528 identity_intercept_cb (void *cls,
529 struct GNUNET_IDENTITY_Ego *ego,
533 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
534 struct GNUNET_CRYPTO_EcdsaPublicKey dns_root;
537 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
538 "Looking for gns-intercept ego\n");
539 identity_op = GNUNET_IDENTITY_get (identity_handle,
541 &identity_reverse_cb,
547 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
548 _("No ego configured for `%s`\n"),
553 GNUNET_IDENTITY_ego_get_public_key (ego,
556 GNS_interceptor_init (&dns_root, cfg))
559 GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
566 * Process GNS requests.
569 * @param server the initialized server
570 * @param c configuration to use
574 const struct GNUNET_CONFIGURATION_Handle *c,
575 struct GNUNET_SERVICE_Handle *service)
577 unsigned long long max_parallel_bg_queries = 16;
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)
584 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
585 _("Failed to connect to the namestore!\n"));
586 GNUNET_SCHEDULER_shutdown ();
589 namecache_handle = GNUNET_NAMECACHE_connect (c);
590 if (NULL == namecache_handle)
592 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
593 _("Failed to connect to the namecache!\n"));
594 GNUNET_SCHEDULER_shutdown ();
598 GNUNET_CONFIGURATION_get_value_number (c,
600 "MAX_PARALLEL_BACKGROUND_QUERIES",
601 &max_parallel_bg_queries))
603 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
604 "Number of allowed parallel background queries: %llu\n",
605 max_parallel_bg_queries);
607 dht_handle = GNUNET_DHT_connect (c,
608 (unsigned int) max_parallel_bg_queries);
609 if (NULL == dht_handle)
611 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
612 _("Could not connect to DHT!\n"));
613 GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
617 identity_handle = GNUNET_IDENTITY_connect (c,
620 if (NULL == identity_handle)
622 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
623 "Could not connect to identity service!\n");
627 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
628 "Looking for gns-intercept ego\n");
629 identity_op = GNUNET_IDENTITY_get (identity_handle,
631 &identity_intercept_cb,
634 GNS_resolver_init (namecache_handle,
637 max_parallel_bg_queries);
638 GNS_shorten_init (namestore_handle,
641 statistics = GNUNET_STATISTICS_create ("gns", c);
642 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
647 * Define "main" method using service macro.
651 GNUNET_SERVICE_OPTION_NONE,
654 &client_disconnect_cb,
656 GNUNET_MQ_hd_var_size (lookup,
657 GNUNET_MESSAGE_TYPE_GNS_LOOKUP,
658 struct LookupMessage,
660 GNUNET_MQ_hd_fixed_size (rev_lookup,
661 GNUNET_MESSAGE_TYPE_GNS_REVERSE_LOOKUP,
662 struct ReverseLookupMessage,
664 GNUNET_MQ_handler_end());
667 /* end of gnunet-service-gns.c */