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 it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * @file gns/gnunet-service-gns.c
20 * @brief GNU Name System (main service)
21 * @author Martin Schanzenbach
22 * @author Christian Grothoff
25 #include "gnunet_util_lib.h"
26 #include "gnunet_dns_service.h"
27 #include "gnunet_dnsparser_lib.h"
28 #include "gnunet_dht_service.h"
29 #include "gnunet_namecache_service.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "gnunet_gns_service.h"
32 #include "gnunet_statistics_service.h"
34 #include "gnunet-service-gns_resolver.h"
35 #include "gnunet-service-gns_interceptor.h"
36 #include "gnunet_protocols.h"
45 * Handle to a lookup operation from client via API.
47 struct ClientLookupHandle
51 * We keep these in a DLL.
53 struct ClientLookupHandle *next;
56 * We keep these in a DLL.
58 struct ClientLookupHandle *prev;
66 * Active handle for the lookup.
68 struct GNS_ResolverHandle *lookup;
79 * Information we track per connected client.
86 struct GNUNET_SERVICE_Client *client;
91 struct GNUNET_MQ_Handle *mq;
96 struct ClientLookupHandle *clh_head;
101 struct ClientLookupHandle *clh_tail;
106 * Representation of a TLD, mapping the respective TLD string
107 * (i.e. ".gnu") to the respective public key of the zone.
109 struct GNS_TopLevelDomain
113 * Kept in a DLL, as there are unlikely enough of these to
114 * warrant a hash map.
116 struct GNS_TopLevelDomain *next;
119 * Kept in a DLL, as there are unlikely enough of these to
120 * warrant a hash map.
122 struct GNS_TopLevelDomain *prev;
125 * Public key associated with the @a tld.
127 struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
130 * Top-level domain as a string, including leading ".".
138 * Our handle to the DHT
140 static struct GNUNET_DHT_Handle *dht_handle;
143 * Our handle to the namecache service
145 static struct GNUNET_NAMECACHE_Handle *namecache_handle;
148 * #GNUNET_YES if ipv6 is supported
150 static int v6_enabled;
153 * #GNUNET_YES if ipv4 is supported
155 static int v4_enabled;
158 * Handle to the statistics service
160 static struct GNUNET_STATISTICS_Handle *statistics;
163 * Head of DLL of TLDs we map to GNS zones.
165 static struct GNS_TopLevelDomain *tld_head;
168 * Tail of DLL of TLDs we map to GNS zones.
170 static struct GNS_TopLevelDomain *tld_tail;
174 * Find GNS zone belonging to TLD @a tld.
176 * @param tld_str top-level domain to look up
177 * @param[out] pkey public key to set
178 * @return #GNUNET_YES if @a tld was found #GNUNET_NO if not
181 GNS_find_tld (const char *tld_str,
182 struct GNUNET_CRYPTO_EcdsaPublicKey *pkey)
184 if ('\0' == *tld_str)
186 for (struct GNS_TopLevelDomain *tld = tld_head;
190 if (0 == strcasecmp (tld_str,
198 GNUNET_GNSRECORD_zkey_to_pkey (tld_str + 1,
200 return GNUNET_YES; /* TLD string *was* the public key */
206 * Obtain the TLD of the given @a name.
209 * @return the part of @a name after the last ".",
210 * or @a name if @a name does not contain a "."
213 GNS_get_tld (const char *name)
218 (unsigned char) '.');
222 tld++; /* skip the '.' */
228 * Task run during shutdown.
230 * @param cls unused, NULL
233 shutdown_task (void *cls)
235 struct GNS_TopLevelDomain *tld;
238 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
240 GNS_interceptor_done ();
241 GNS_resolver_done ();
242 if (NULL != statistics)
244 GNUNET_STATISTICS_destroy (statistics,
248 if (NULL != namecache_handle)
250 GNUNET_NAMECACHE_disconnect (namecache_handle);
251 namecache_handle = NULL;
253 if (NULL != dht_handle)
255 GNUNET_DHT_disconnect (dht_handle);
258 while (NULL != (tld = tld_head))
260 GNUNET_CONTAINER_DLL_remove (tld_head,
263 GNUNET_free (tld->tld);
270 * Called whenever a client is disconnected.
273 * @param client identification of the client
274 * @param app_ctx @a client
277 client_disconnect_cb (void *cls,
278 struct GNUNET_SERVICE_Client *client,
281 struct ClientLookupHandle *clh;
282 struct GnsClient *gc = app_ctx;
285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
286 "Client %p disconnected\n",
288 while (NULL != (clh = gc->clh_head))
290 if (NULL != clh->lookup)
291 GNS_resolver_lookup_cancel (clh->lookup);
292 GNUNET_CONTAINER_DLL_remove (gc->clh_head,
302 * Add a client to our list of active clients.
305 * @param client client to add
306 * @param mq message queue for @a client
307 * @return internal namestore client structure for this client
310 client_connect_cb (void *cls,
311 struct GNUNET_SERVICE_Client *client,
312 struct GNUNET_MQ_Handle *mq)
314 struct GnsClient *gc;
317 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
318 "Client %p connected\n",
320 gc = GNUNET_new (struct GnsClient);
328 * Reply to client with the result from our lookup.
330 * @param cls the closure (our client lookup handle)
331 * @param rd_count the number of records in @a rd
332 * @param rd the record data
335 send_lookup_response (void *cls,
337 const struct GNUNET_GNSRECORD_Data *rd)
339 struct ClientLookupHandle *clh = cls;
340 struct GnsClient *gc = clh->gc;
341 struct GNUNET_MQ_Envelope *env;
342 struct LookupResultMessage *rmsg;
345 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
346 "Sending LOOKUP_RESULT message with %u results\n",
347 (unsigned int) rd_count);
348 len = GNUNET_GNSRECORD_records_get_size (rd_count,
353 GNUNET_SERVICE_client_drop (gc->client);
356 if (len > UINT16_MAX - sizeof (*rmsg))
359 GNUNET_SERVICE_client_drop (gc->client);
362 env = GNUNET_MQ_msg_extra (rmsg,
364 GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT);
365 rmsg->id = clh->request_id;
366 rmsg->rd_count = htonl (rd_count);
367 GNUNET_assert (len ==
368 GNUNET_GNSRECORD_records_serialize (rd_count,
372 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (gc->client),
374 GNUNET_CONTAINER_DLL_remove (gc->clh_head,
378 GNUNET_STATISTICS_update (statistics,
379 "Completed lookups", 1,
381 GNUNET_STATISTICS_update (statistics,
389 * Checks a #GNUNET_MESSAGE_TYPE_GNS_LOOKUP message
391 * @param cls client sending the message
392 * @param l_msg message of type `struct LookupMessage`
393 * @return #GNUNET_OK if @a l_msg is well-formed
396 check_lookup (void *cls,
397 const struct LookupMessage *l_msg)
403 msg_size = ntohs (l_msg->header.size);
404 if (msg_size < sizeof (struct LookupMessage))
407 return GNUNET_SYSERR;
409 name = (const char *) &l_msg[1];
410 if ( ('\0' != name[msg_size - sizeof (struct LookupMessage) - 1]) ||
411 (strlen (name) > GNUNET_DNSPARSER_MAX_NAME_LENGTH) )
414 return GNUNET_SYSERR;
421 * Handle lookup requests from client
423 * @param cls the closure
424 * @param client the client
425 * @param message the message
428 handle_lookup (void *cls,
429 const struct LookupMessage *sh_msg)
431 struct GnsClient *gc = cls;
432 char name[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 1];
433 struct ClientLookupHandle *clh;
434 char *nameptr = name;
437 GNUNET_SERVICE_client_continue (gc->client);
438 utf_in = (const char *) &sh_msg[1];
439 GNUNET_STRINGS_utf8_tolower (utf_in,
441 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442 "Received LOOKUP `%s' message\n",
444 clh = GNUNET_new (struct ClientLookupHandle);
445 GNUNET_CONTAINER_DLL_insert (gc->clh_head,
449 clh->request_id = sh_msg->id;
450 if ( (GNUNET_DNSPARSER_TYPE_A == ntohl (sh_msg->type)) &&
451 (GNUNET_OK != v4_enabled) )
453 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
454 "LOOKUP: Query for A record but AF_INET not supported!");
455 send_lookup_response (clh,
460 if ( (GNUNET_DNSPARSER_TYPE_AAAA == ntohl (sh_msg->type)) &&
461 (GNUNET_OK != v6_enabled) )
463 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
464 "LOOKUP: Query for AAAA record but AF_INET6 not supported!");
465 send_lookup_response (clh,
470 clh->lookup = GNS_resolver_lookup (&sh_msg->zone,
471 ntohl (sh_msg->type),
473 (enum GNUNET_GNS_LocalOptions) ntohs (sh_msg->options),
474 &send_lookup_response, clh);
475 GNUNET_STATISTICS_update (statistics,
482 * Reads the configuration and populates TLDs
485 * @param section name of section in config, always "gns"
486 * @param option name of the option, TLDs start with "."
487 * @param value value for the option, public key for TLDs
490 read_service_conf (void *cls,
495 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
496 struct GNS_TopLevelDomain *tld;
500 if (option[0] != '.')
503 GNUNET_STRINGS_string_to_data (value,
508 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
511 _("Properly base32-encoded public key required"));
514 tld = GNUNET_new (struct GNS_TopLevelDomain);
515 tld->tld = GNUNET_strdup (&option[1]);
517 GNUNET_CONTAINER_DLL_insert (tld_head,
524 * Process GNS requests.
527 * @param server the initialized server
528 * @param c configuration to use
532 const struct GNUNET_CONFIGURATION_Handle *c,
533 struct GNUNET_SERVICE_Handle *service)
535 unsigned long long max_parallel_bg_queries = 16;
537 GNUNET_CONFIGURATION_iterate_section_values (c,
541 v6_enabled = GNUNET_NETWORK_test_pf (PF_INET6);
542 v4_enabled = GNUNET_NETWORK_test_pf (PF_INET);
543 namecache_handle = GNUNET_NAMECACHE_connect (c);
544 if (NULL == namecache_handle)
546 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
547 _("Failed to connect to the namecache!\n"));
548 GNUNET_SCHEDULER_shutdown ();
552 GNUNET_CONFIGURATION_get_value_number (c,
554 "MAX_PARALLEL_BACKGROUND_QUERIES",
555 &max_parallel_bg_queries))
557 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
558 "Number of allowed parallel background queries: %llu\n",
559 max_parallel_bg_queries);
561 dht_handle = GNUNET_DHT_connect (c,
562 (unsigned int) max_parallel_bg_queries);
563 if (NULL == dht_handle)
565 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
566 _("Could not connect to DHT!\n"));
567 GNUNET_SCHEDULER_add_now (&shutdown_task,
571 GNS_resolver_init (namecache_handle,
574 max_parallel_bg_queries);
576 GNUNET_CONFIGURATION_get_value_yesno (c,
580 GNS_interceptor_init (c)) )
583 GNUNET_SCHEDULER_add_now (&shutdown_task,
587 statistics = GNUNET_STATISTICS_create ("gns",
589 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
595 * Define "main" method using service macro.
599 GNUNET_SERVICE_OPTION_NONE,
602 &client_disconnect_cb,
604 GNUNET_MQ_hd_var_size (lookup,
605 GNUNET_MESSAGE_TYPE_GNS_LOOKUP,
606 struct LookupMessage,
608 GNUNET_MQ_handler_end());
611 /* end of gnunet-service-gns.c */