2 This file is part of GNUnet.
3 Copyright (C) 2011-2018 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_gnsrecord_lib.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 client via 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;
81 * Information we track per connected client.
88 struct GNUNET_SERVICE_Client *client;
93 struct GNUNET_MQ_Handle *mq;
98 struct ClientLookupHandle *clh_head;
103 struct ClientLookupHandle *clh_tail;
108 * Representation of a TLD, mapping the respective TLD string
109 * (i.e. ".gnu") to the respective public key of the zone.
111 struct GNS_TopLevelDomain
115 * Kept in a DLL, as there are unlikely enough of these to
116 * warrant a hash map.
118 struct GNS_TopLevelDomain *next;
121 * Kept in a DLL, as there are unlikely enough of these to
122 * warrant a hash map.
124 struct GNS_TopLevelDomain *prev;
127 * Public key associated with the @a tld.
129 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
132 * Top-level domain as a string, including leading ".".
140 * Our handle to the DHT
142 static struct GNUNET_DHT_Handle *dht_handle;
145 * Our handle to the namecache service
147 static struct GNUNET_NAMECACHE_Handle *namecache_handle;
150 * #GNUNET_YES if ipv6 is supported
152 static int v6_enabled;
155 * #GNUNET_YES if ipv4 is supported
157 static int v4_enabled;
160 * Handle to the statistics service
162 static struct GNUNET_STATISTICS_Handle *statistics;
165 * Head of DLL of TLDs we map to GNS zones.
167 static struct GNS_TopLevelDomain *tld_head;
170 * Tail of DLL of TLDs we map to GNS zones.
172 static struct GNS_TopLevelDomain *tld_tail;
176 * Find GNS zone belonging to TLD @a tld.
178 * @param tld_str top-level domain to look up
179 * @param[out] pkey public key to set
180 * @return #GNUNET_YES if @a tld was found #GNUNET_NO if not
183 GNS_find_tld (const char *tld_str,
184 struct GNUNET_CRYPTO_EcdsaPublicKey *pkey)
186 if ('\0' == *tld_str)
188 for (struct GNS_TopLevelDomain *tld = tld_head;
192 if (0 == strcasecmp (tld_str,
200 GNUNET_GNSRECORD_zkey_to_pkey (tld_str + 1,
202 return GNUNET_YES; /* TLD string *was* the public key */
208 * Obtain the TLD of the given @a name.
211 * @return the part of @a name after the last ".",
212 * or @a name if @a name does not contain a "."
215 GNS_get_tld (const char *name)
220 (unsigned char) '.');
224 tld++; /* skip the '.' */
230 * Task run during shutdown.
232 * @param cls unused, NULL
235 shutdown_task (void *cls)
237 struct GNS_TopLevelDomain *tld;
240 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
242 GNS_interceptor_done ();
243 GNS_resolver_done ();
244 if (NULL != statistics)
246 GNUNET_STATISTICS_destroy (statistics,
250 if (NULL != namecache_handle)
252 GNUNET_NAMECACHE_disconnect (namecache_handle);
253 namecache_handle = NULL;
255 if (NULL != dht_handle)
257 GNUNET_DHT_disconnect (dht_handle);
260 while (NULL != (tld = tld_head))
262 GNUNET_CONTAINER_DLL_remove (tld_head,
265 GNUNET_free (tld->tld);
272 * Called whenever a client is disconnected.
275 * @param client identification of the client
276 * @param app_ctx @a client
279 client_disconnect_cb (void *cls,
280 struct GNUNET_SERVICE_Client *client,
283 struct ClientLookupHandle *clh;
284 struct GnsClient *gc = app_ctx;
287 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
288 "Client %p disconnected\n",
290 while (NULL != (clh = gc->clh_head))
292 if (NULL != clh->lookup)
293 GNS_resolver_lookup_cancel (clh->lookup);
294 GNUNET_CONTAINER_DLL_remove (gc->clh_head,
304 * Add a client to our list of active clients.
307 * @param client client to add
308 * @param mq message queue for @a client
309 * @return internal namestore client structure for this client
312 client_connect_cb (void *cls,
313 struct GNUNET_SERVICE_Client *client,
314 struct GNUNET_MQ_Handle *mq)
316 struct GnsClient *gc;
319 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
320 "Client %p connected\n",
322 gc = GNUNET_new (struct GnsClient);
330 * Reply to client with the result from our lookup.
332 * @param cls the closure (our client lookup handle)
333 * @param rd_count the number of records in @a rd
334 * @param rd the record data
337 send_lookup_response (void* cls,
339 const struct GNUNET_GNSRECORD_Data *rd)
341 struct ClientLookupHandle *clh = cls;
342 struct GnsClient *gc = clh->gc;
343 struct GNUNET_MQ_Envelope *env;
344 struct LookupResultMessage *rmsg;
347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
348 "Sending LOOKUP_RESULT message with %u results\n",
349 (unsigned int) rd_count);
350 len = GNUNET_GNSRECORD_records_get_size (rd_count,
352 env = GNUNET_MQ_msg_extra (rmsg,
354 GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
355 rmsg->id = clh->request_id;
356 rmsg->rd_count = htonl (rd_count);
357 GNUNET_GNSRECORD_records_serialize (rd_count,
361 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (gc->client),
363 GNUNET_CONTAINER_DLL_remove (gc->clh_head,
367 GNUNET_STATISTICS_update (statistics,
368 "Completed lookups", 1,
370 GNUNET_STATISTICS_update (statistics,
378 * Checks a #GNUNET_MESSAGE_TYPE_GNS_LOOKUP message
380 * @param cls client sending the message
381 * @param l_msg message of type `struct LookupMessage`
382 * @return #GNUNET_OK if @a l_msg is well-formed
385 check_lookup (void *cls,
386 const struct LookupMessage *l_msg)
392 msg_size = ntohs (l_msg->header.size);
393 if (msg_size < sizeof (struct LookupMessage))
396 return GNUNET_SYSERR;
398 name = (const char *) &l_msg[1];
399 if ( ('\0' != name[msg_size - sizeof (struct LookupMessage) - 1]) ||
400 (strlen (name) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) )
403 return GNUNET_SYSERR;
410 * Handle lookup requests from client
412 * @param cls the closure
413 * @param client the client
414 * @param message the message
417 handle_lookup (void *cls,
418 const struct LookupMessage *sh_msg)
420 struct GnsClient *gc = cls;
421 char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
422 struct ClientLookupHandle *clh;
423 char *nameptr = name;
426 GNUNET_SERVICE_client_continue (gc->client);
427 utf_in = (const char *) &sh_msg[1];
428 GNUNET_STRINGS_utf8_tolower (utf_in,
430 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
431 "Received LOOKUP `%s' message\n",
433 clh = GNUNET_new (struct ClientLookupHandle);
434 GNUNET_CONTAINER_DLL_insert (gc->clh_head,
438 clh->request_id = sh_msg->id;
439 if ( (GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
440 (GNUNET_OK != v4_enabled) )
442 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
443 "LOOKUP: Query for A record but AF_INET not supported!");
444 send_lookup_response (clh,
449 if ( (GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
450 (GNUNET_OK != v6_enabled) )
452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
454 send_lookup_response (clh,
459 clh->lookup = GNS_resolver_lookup (&sh_msg->zone,
460 ntohl (sh_msg->type),
462 (enum GNUNET_GNS_LocalOptions) ntohs (sh_msg->options),
463 &send_lookup_response, clh);
464 GNUNET_STATISTICS_update (statistics,
471 * Reads the configuration and populates TLDs
474 * @param section name of section in config, always "gns"
475 * @param option name of the option, TLDs start with "."
476 * @param value value for the option, public key for TLDs
479 read_service_conf (void *cls,
484 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
485 struct GNS_TopLevelDomain *tld;
489 if (option[0] != '.')
492 GNUNET_STRINGS_string_to_data (value,
497 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
500 _("Properly base32-encoded public key required"));
503 tld = GNUNET_new (struct GNS_TopLevelDomain);
504 tld->tld = GNUNET_strdup (&option[1]);
506 GNUNET_CONTAINER_DLL_insert (tld_head,
513 * Process GNS requests.
516 * @param server the initialized server
517 * @param c configuration to use
521 const struct GNUNET_CONFIGURATION_Handle *c,
522 struct GNUNET_SERVICE_Handle *service)
524 unsigned long long max_parallel_bg_queries = 16;
526 GNUNET_CONFIGURATION_iterate_section_values (c,
530 v6_enabled = GNUNET_NETWORK_test_pf (PF_INET6);
531 v4_enabled = GNUNET_NETWORK_test_pf (PF_INET);
532 namecache_handle = GNUNET_NAMECACHE_connect (c);
533 if (NULL == namecache_handle)
535 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
536 _("Failed to connect to the namecache!\n"));
537 GNUNET_SCHEDULER_shutdown ();
541 GNUNET_CONFIGURATION_get_value_number (c,
543 "MAX_PARALLEL_BACKGROUND_QUERIES",
544 &max_parallel_bg_queries))
546 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
547 "Number of allowed parallel background queries: %llu\n",
548 max_parallel_bg_queries);
550 dht_handle = GNUNET_DHT_connect (c,
551 (unsigned int) max_parallel_bg_queries);
552 if (NULL == dht_handle)
554 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
555 _("Could not connect to DHT!\n"));
556 GNUNET_SCHEDULER_add_now (&shutdown_task,
560 GNS_resolver_init (namecache_handle,
563 max_parallel_bg_queries);
565 GNUNET_CONFIGURATION_get_value_yesno (c,
569 GNS_interceptor_init (c)) )
572 GNUNET_SCHEDULER_add_now (&shutdown_task,
576 statistics = GNUNET_STATISTICS_create ("gns",
578 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
584 * Define "main" method using service macro.
588 GNUNET_SERVICE_OPTION_NONE,
591 &client_disconnect_cb,
593 GNUNET_MQ_hd_var_size (lookup,
594 GNUNET_MESSAGE_TYPE_GNS_LOOKUP,
595 struct LookupMessage,
597 GNUNET_MQ_handler_end());
600 /* end of gnunet-service-gns.c */