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_identity_service.h"
33 #include "gnunet_gns_service.h"
34 #include "gnunet_statistics_service.h"
36 #include "gnunet-service-gns_resolver.h"
37 #include "gnunet-service-gns_interceptor.h"
38 #include "gnunet_protocols.h"
47 * Handle to a lookup operation from api
49 struct ClientLookupHandle
53 * We keep these in a DLL.
55 struct ClientLookupHandle *next;
58 * We keep these in a DLL.
60 struct ClientLookupHandle *prev;
68 * Active handle for the lookup.
70 struct GNS_ResolverHandle *lookup;
84 struct GNUNET_SERVICE_Client *client;
89 struct GNUNET_MQ_Handle *mq;
94 struct ClientLookupHandle *clh_head;
99 struct ClientLookupHandle *clh_tail;
104 * Representation of a TLD, mapping the respective TLD string
105 * (i.e. ".gnu") to the respective public key of the zone.
107 struct GNS_TopLevelDomain
111 * Kept in a DLL, as there are unlikely enough of these to
112 * warrant a hash map.
114 struct GNS_TopLevelDomain *next;
117 * Kept in a DLL, as there are unlikely enough of these to
118 * warrant a hash map.
120 struct GNS_TopLevelDomain *prev;
123 * Public key associated with the @a tld.
125 struct GNUNET_CRYPTO_EddsaPublicKey pkey;
128 * Top-level domain as a string, including leading ".".
136 * Our handle to the DHT
138 static struct GNUNET_DHT_Handle *dht_handle;
141 * Our handle to the namecache service
143 static struct GNUNET_NAMECACHE_Handle *namecache_handle;
146 * Our handle to the identity service
148 static struct GNUNET_IDENTITY_Handle *identity_handle;
151 * Our handle to the identity operation to find the master zone
152 * for intercepted queries.
154 static struct GNUNET_IDENTITY_Operation *identity_op;
157 * #GNUNET_YES if ipv6 is supported
159 static int v6_enabled;
162 * #GNUNET_YES if ipv4 is supported
164 static int v4_enabled;
167 * Handle to the statistics service
169 static struct GNUNET_STATISTICS_Handle *statistics;
172 * Head of DLL of TLDs we map to GNS zones.
174 static struct GNS_TopLevelDomain *tld_head;
177 * Tail of DLL of TLDs we map to GNS zones.
179 static struct GNS_TopLevelDomain *tld_tail;
183 * Find GNS zone belonging to TLD @a tld.
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
190 GNS_find_tld (const char *tld_str,
191 struct GNUNET_CRYPTO_EddsaPublicKey *pkey)
193 if ('\0' == *tld_str)
195 for (struct GNS_TopLevelDomain *tld = tld_head;
199 if (0 == strcasecmp (tld_str,
207 GNUNET_STRINGS_string_to_data (tld_str + 1,
208 strlen (tld_str + 1),
211 return GNUNET_YES; /* TLD string *was* the public key */
217 * Task run during shutdown.
223 shutdown_task (void *cls)
225 struct GNS_TopLevelDomain *tld;
227 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
229 GNS_interceptor_done ();
230 if (NULL != identity_op)
232 GNUNET_IDENTITY_cancel (identity_op);
235 if (NULL != identity_handle)
237 GNUNET_IDENTITY_disconnect (identity_handle);
238 identity_handle = NULL;
240 GNS_resolver_done ();
241 if (NULL != statistics)
243 GNUNET_STATISTICS_destroy (statistics,
247 if (NULL != namecache_handle)
249 GNUNET_NAMECACHE_disconnect (namecache_handle);
250 namecache_handle = NULL;
252 if (NULL != dht_handle)
254 GNUNET_DHT_disconnect (dht_handle);
257 while (NULL != (tld = tld_head))
259 GNUNET_CONTAINER_DLL_remove (tld_head,
262 GNUNET_free (tld->tld);
269 * Called whenever a client is disconnected.
272 * @param client identification of the client
273 * @param app_ctx @a client
276 client_disconnect_cb (void *cls,
277 struct GNUNET_SERVICE_Client *client,
280 struct ClientLookupHandle *clh;
281 struct GnsClient *gc = app_ctx;
283 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284 "Client %p disconnected\n",
286 while (NULL != (clh = gc->clh_head))
288 if (NULL != clh->lookup)
289 GNS_resolver_lookup_cancel (clh->lookup);
290 GNUNET_CONTAINER_DLL_remove (gc->clh_head,
301 * Add a client to our list of active clients.
304 * @param client client to add
305 * @param mq message queue for @a client
306 * @return internal namestore client structure for this client
309 client_connect_cb (void *cls,
310 struct GNUNET_SERVICE_Client *client,
311 struct GNUNET_MQ_Handle *mq)
313 struct GnsClient *gc;
314 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315 "Client %p connected\n",
317 gc = GNUNET_new (struct GnsClient);
325 * Reply to client with the result from our lookup.
327 * @param cls the closure (our client lookup handle)
328 * @param rd_count the number of records in @a rd
329 * @param rd the record data
332 send_lookup_response (void* cls,
334 const struct GNUNET_GNSRECORD_Data *rd)
336 struct ClientLookupHandle *clh = cls;
337 struct GNUNET_MQ_Envelope *env;
338 struct LookupResultMessage *rmsg;
341 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
342 "Sending LOOKUP_RESULT message with %u results\n",
343 (unsigned int) rd_count);
345 len = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
346 env = GNUNET_MQ_msg_extra (rmsg,
348 GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
349 rmsg->id = clh->request_id;
350 rmsg->rd_count = htonl (rd_count);
351 GNUNET_GNSRECORD_records_serialize (rd_count, rd, len,
353 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq(clh->gc->client),
355 GNUNET_CONTAINER_DLL_remove (clh->gc->clh_head,
359 GNUNET_STATISTICS_update (statistics,
360 "Completed lookups", 1,
362 GNUNET_STATISTICS_update (statistics,
370 * Checks a #GNUNET_MESSAGE_TYPE_GNS_LOOKUP message
372 * @param cls client sending the message
373 * @param l_msg message of type `struct LookupMessage`
374 * @return #GNUNET_OK if @a l_msg is well-formed
377 check_lookup (void *cls,
378 const struct LookupMessage *l_msg)
383 msg_size = ntohs (l_msg->header.size);
384 if (msg_size < sizeof (struct LookupMessage))
387 return GNUNET_SYSERR;
389 name = (const char *) &l_msg[1];
390 if ( ('\0' != name[msg_size - sizeof (struct LookupMessage) - 1]) ||
391 (strlen (name) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) )
394 return GNUNET_SYSERR;
401 * Handle lookup requests from client
403 * @param cls the closure
404 * @param client the client
405 * @param message the message
408 handle_lookup (void *cls,
409 const struct LookupMessage *sh_msg)
411 struct GnsClient *gc = cls;
412 char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
413 struct ClientLookupHandle *clh;
414 char *nameptr = name;
417 GNUNET_SERVICE_client_continue (gc->client);
418 utf_in = (const char *) &sh_msg[1];
419 GNUNET_STRINGS_utf8_tolower (utf_in, nameptr);
420 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421 "Received LOOKUP `%s' message\n",
424 clh = GNUNET_new (struct ClientLookupHandle);
425 GNUNET_CONTAINER_DLL_insert (gc->clh_head,
429 clh->request_id = sh_msg->id;
430 if ( (GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
431 (GNUNET_OK != v4_enabled) )
433 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434 "LOOKUP: Query for A record but AF_INET not supported!");
435 send_lookup_response (clh, 0, NULL);
438 if ( (GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
439 (GNUNET_OK != v6_enabled) )
441 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
443 send_lookup_response (clh, 0, NULL);
446 clh->lookup = GNS_resolver_lookup (&sh_msg->zone,
447 ntohl (sh_msg->type),
449 (enum GNUNET_GNS_LocalOptions) ntohs (sh_msg->options),
450 &send_lookup_response, clh);
451 GNUNET_STATISTICS_update (statistics,
458 * Method called to inform about the ego to be used for the master zone
459 * for DNS interceptions.
461 * This function is only called ONCE, and 'NULL' being passed in
462 * @a ego does indicate that interception is not configured.
463 * If @a ego is non-NULL, we should start to intercept DNS queries
464 * and resolve ".gnu" queries using the given ego as the master zone.
466 * @param cls closure, our `const struct GNUNET_CONFIGURATION_Handle *c`
467 * @param ego ego handle
468 * @param ctx context for application to store data for this ego
469 * (during the lifetime of this process, initially NULL)
470 * @param name name assigned by the user for this ego,
471 * NULL if the user just deleted the ego and it
472 * must thus no longer be used
475 identity_intercept_cb (void *cls,
476 struct GNUNET_IDENTITY_Ego *ego,
480 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
481 struct GNUNET_CRYPTO_EcdsaPublicKey dns_root;
484 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485 "Looking for gns-intercept ego\n");
488 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
489 _("No ego configured for `%s`\n"),
494 GNUNET_IDENTITY_ego_get_public_key (ego,
497 GNS_interceptor_init (&dns_root,
501 GNUNET_SCHEDULER_add_now (&shutdown_task,
509 * Reads the configuration and populates TLDs
512 * @param section name of section in config, always "gns"
513 * @param option name of the option, TLDs start with "."
514 * @param value value for the option, public key for TLDs
517 read_service_conf (void *cls,
522 struct GNUNET_CRYPTO_EddsaPublicKey pk;
523 struct GNS_TopLevelDomain *tld;
525 if (option[0] != '.')
528 GNUNET_STRINGS_string_to_data (value,
533 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
536 _("Properly base32-encoded public key required"));
539 tld = GNUNET_new (struct GNS_TopLevelDomain);
540 tld->tld = GNUNET_strdup (&option[1]);
542 GNUNET_CONTAINER_DLL_insert (tld_head,
550 * Process GNS requests.
553 * @param server the initialized server
554 * @param c configuration to use
558 const struct GNUNET_CONFIGURATION_Handle *c,
559 struct GNUNET_SERVICE_Handle *service)
561 unsigned long long max_parallel_bg_queries = 16;
563 GNUNET_CONFIGURATION_iterate_section_values (c,
567 v6_enabled = GNUNET_NETWORK_test_pf (PF_INET6);
568 v4_enabled = GNUNET_NETWORK_test_pf (PF_INET);
569 namecache_handle = GNUNET_NAMECACHE_connect (c);
570 if (NULL == namecache_handle)
572 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
573 _("Failed to connect to the namecache!\n"));
574 GNUNET_SCHEDULER_shutdown ();
578 GNUNET_CONFIGURATION_get_value_number (c,
580 "MAX_PARALLEL_BACKGROUND_QUERIES",
581 &max_parallel_bg_queries))
583 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
584 "Number of allowed parallel background queries: %llu\n",
585 max_parallel_bg_queries);
587 dht_handle = GNUNET_DHT_connect (c,
588 (unsigned int) max_parallel_bg_queries);
589 if (NULL == dht_handle)
591 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
592 _("Could not connect to DHT!\n"));
593 GNUNET_SCHEDULER_add_now (&shutdown_task,
598 identity_handle = GNUNET_IDENTITY_connect (c,
601 if (NULL == identity_handle)
603 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
604 "Could not connect to identity service!\n");
608 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
609 "Looking for gns-intercept ego\n");
610 identity_op = GNUNET_IDENTITY_get (identity_handle,
612 &identity_intercept_cb,
615 GNS_resolver_init (namecache_handle,
618 max_parallel_bg_queries);
619 statistics = GNUNET_STATISTICS_create ("gns", c);
620 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
626 * Define "main" method using service macro.
630 GNUNET_SERVICE_OPTION_NONE,
633 &client_disconnect_cb,
635 GNUNET_MQ_hd_var_size (lookup,
636 GNUNET_MESSAGE_TYPE_GNS_LOOKUP,
637 struct LookupMessage,
639 GNUNET_MQ_handler_end());
642 /* end of gnunet-service-gns.c */