2 This file is part of GNUnet.
3 (C) 2011-2013 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file gns/gnunet-service-gns_shorten.c
23 * @brief GNUnet GNS shortening logic
24 * @author Martin Schanzenbach
25 * @author Christian Grothoff
28 #include "gnunet_util_lib.h"
29 #include "gnunet_dht_service.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "gnunet_namestore_service.h"
32 #include "gnunet_resolver_service.h"
33 #include "gnunet_gns_service.h"
35 #include "gnunet-service-gns_shorten.h"
36 #include "gnunet_vpn_service.h"
40 * Default DHT timeout for lookups.
42 #define DHT_LOOKUP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
45 * DHT replication level
47 #define DHT_GNS_REPLICATION_LEVEL 5
51 * Handle for a PSEU lookup used to shorten names.
53 struct GetPseuAuthorityHandle
58 struct GetPseuAuthorityHandle *next;
63 struct GetPseuAuthorityHandle *prev;
66 * Private key of the (shorten) zone to store the resulting
69 struct GNUNET_CRYPTO_EcdsaPrivateKey shorten_zone_key;
72 * Original label (used if no PSEU record is found).
74 char label[GNUNET_DNSPARSER_MAX_LABEL_LENGTH + 1];
77 * Label we are currently trying out (during #perform_pseu_lookup).
82 * The zone for which we are trying to find the PSEU record.
84 struct GNUNET_CRYPTO_EcdsaPublicKey target_zone;
87 * Handle for DHT lookups. Should be NULL if no lookups are in progress
89 struct GNUNET_DHT_GetHandle *get_handle;
92 * Handle to namestore request
94 struct GNUNET_NAMESTORE_QueueEntry *namestore_task;
97 * Handle to namecache request
99 struct GNUNET_NAMECACHE_QueueEntry *namecache_task;
102 * Task to abort DHT lookup operation.
104 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
110 * Head of PSEU/shorten operations list.
112 static struct GetPseuAuthorityHandle *gph_head;
115 * Tail of PSEU/shorten operations list.
117 static struct GetPseuAuthorityHandle *gph_tail;
120 * Our handle to the namestore service
122 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
125 * Our handle to the namecache service
127 static struct GNUNET_NAMECACHE_Handle *namecache_handle;
130 * Resolver handle to the dht
132 static struct GNUNET_DHT_Handle *dht_handle;
135 * Cleanup a 'struct GetPseuAuthorityHandle', terminating all
136 * pending activities.
138 * @param gph handle to terminate
141 free_get_pseu_authority_handle (struct GetPseuAuthorityHandle *gph)
143 if (NULL != gph->get_handle)
145 GNUNET_DHT_get_stop (gph->get_handle);
146 gph->get_handle = NULL;
148 if (NULL != gph->namestore_task)
150 GNUNET_NAMESTORE_cancel (gph->namestore_task);
151 gph->namestore_task = NULL;
153 if (NULL != gph->namecache_task)
155 GNUNET_NAMECACHE_cancel (gph->namecache_task);
156 gph->namecache_task = NULL;
158 if (GNUNET_SCHEDULER_NO_TASK != gph->timeout_task)
160 GNUNET_SCHEDULER_cancel (gph->timeout_task);
161 gph->timeout_task = GNUNET_SCHEDULER_NO_TASK;
163 GNUNET_CONTAINER_DLL_remove (gph_head, gph_tail, gph);
164 GNUNET_free_non_null (gph->current_label);
170 * Continuation for pkey record creation (shorten)
172 * @param cls a GetPseuAuthorityHandle
173 * @param success unused
177 create_pkey_cont (void* cls,
181 struct GetPseuAuthorityHandle* gph = cls;
183 gph->namestore_task = NULL;
184 free_get_pseu_authority_handle (gph);
189 * Namestore calls this function if we have record for this name.
190 * (or with rd_count=0 to indicate no matches).
192 * @param cls the pending query
193 * @param rd_count the number of records with 'name'
194 * @param rd the record data
197 process_pseu_lookup_ns (void *cls,
198 unsigned int rd_count,
199 const struct GNUNET_GNSRECORD_Data *rd);
203 * We obtained a result for our query to the shorten zone from
204 * the namestore. Try to decrypt.
206 * @param cls the handle to our shorten operation
207 * @param block resulting encrypted block
210 process_pseu_block_ns (void *cls,
211 const struct GNUNET_GNSRECORD_Block *block)
213 struct GetPseuAuthorityHandle *gph = cls;
214 struct GNUNET_CRYPTO_EcdsaPublicKey pub;
216 gph->namecache_task = NULL;
219 process_pseu_lookup_ns (gph, 0, NULL);
222 GNUNET_CRYPTO_ecdsa_key_get_public (&gph->shorten_zone_key,
225 GNUNET_GNSRECORD_block_decrypt (block,
228 &process_pseu_lookup_ns,
232 free_get_pseu_authority_handle (gph);
239 * Lookup in the namestore for the shorten zone the given label.
241 * @param gph the handle to our shorten operation
242 * @param label the label to lookup
245 perform_pseu_lookup (struct GetPseuAuthorityHandle *gph,
248 struct GNUNET_CRYPTO_EcdsaPublicKey pub;
249 struct GNUNET_HashCode query;
251 GNUNET_CRYPTO_ecdsa_key_get_public (&gph->shorten_zone_key,
253 GNUNET_free_non_null (gph->current_label);
254 gph->current_label = GNUNET_strdup (label);
255 GNUNET_GNSRECORD_query_from_public_key (&pub,
258 gph->namecache_task = GNUNET_NAMECACHE_lookup_block (namecache_handle,
260 &process_pseu_block_ns,
266 * Namestore calls this function if we have record for this name.
267 * (or with rd_count=0 to indicate no matches).
269 * @param cls the pending query
270 * @param rd_count the number of records with 'name'
271 * @param rd the record data
274 process_pseu_lookup_ns (void *cls,
275 unsigned int rd_count,
276 const struct GNUNET_GNSRECORD_Data *rd)
278 struct GetPseuAuthorityHandle *gph = cls;
279 struct GNUNET_GNSRECORD_Data new_pkey;
281 gph->namestore_task = NULL;
284 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
285 "Name `%s' already taken, cannot shorten.\n",
287 /* if this was not yet the original label, try one more
288 time, this time not using PSEU but the original label */
289 if (0 == strcmp (gph->current_label,
292 free_get_pseu_authority_handle (gph);
296 perform_pseu_lookup (gph, gph->label);
300 /* name is available */
301 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
302 "Shortening `%s' to `%s'\n",
303 GNUNET_GNSRECORD_z2s (&gph->target_zone),
305 new_pkey.expiration_time = UINT64_MAX;
306 new_pkey.data_size = sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
307 new_pkey.data = &gph->target_zone;
308 new_pkey.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
309 new_pkey.flags = GNUNET_GNSRECORD_RF_NONE
310 | GNUNET_GNSRECORD_RF_PRIVATE
311 | GNUNET_GNSRECORD_RF_PENDING;
313 = GNUNET_NAMESTORE_records_store (namestore_handle,
314 &gph->shorten_zone_key,
317 &create_pkey_cont, gph);
322 * Process result of a DHT lookup for a PSEU record.
324 * @param gph the handle to our shorten operation
325 * @param pseu the pseu result or NULL
328 process_pseu_result (struct GetPseuAuthorityHandle* gph,
333 /* no PSEU found, try original label */
334 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
335 "No PSEU found, trying original label `%s' instead.\n",
337 perform_pseu_lookup (gph, gph->label);
340 /* check if 'pseu' is taken */
341 perform_pseu_lookup (gph, pseu);
346 * Handle timeout for DHT request during shortening.
348 * @param cls the request handle as closure
349 * @param tc the task context
352 handle_auth_discovery_timeout (void *cls,
353 const struct GNUNET_SCHEDULER_TaskContext *tc)
355 struct GetPseuAuthorityHandle *gph = cls;
357 gph->timeout_task = GNUNET_SCHEDULER_NO_TASK;
358 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
359 "DHT lookup for PSEU query in zone `%s' timed out.\n",
360 GNUNET_GNSRECORD_z2s (&gph->target_zone));
361 GNUNET_DHT_get_stop (gph->get_handle);
362 gph->get_handle = NULL;
363 process_pseu_result (gph, NULL);
368 * Handle decrypted records from DHT result.
370 * @param cls closure with our 'struct GetPseuAuthorityHandle'
371 * @param rd_count number of entries in 'rd' array
372 * @param rd array of records with data to store
375 process_auth_records (void *cls,
376 unsigned int rd_count,
377 const struct GNUNET_GNSRECORD_Data *rd)
379 struct GetPseuAuthorityHandle *gph = cls;
382 for (i=0; i < rd_count; i++)
384 if (GNUNET_GNSRECORD_TYPE_PSEU == rd[i].record_type)
386 char pseu[rd[i].data_size + 1];
392 pseu[rd[i].data_size] = '\0';
393 process_pseu_result (gph,
398 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
399 "No PSEU record found in DHT reply with %u records.\n",
401 process_pseu_result (gph, NULL);
406 * Function called when we find a PSEU entry in the DHT
408 * @param cls the request handle
409 * @param exp lifetime
410 * @param key the key the record was stored under
411 * @param get_path get path
412 * @param get_path_length @a get_path length
413 * @param put_path put path
414 * @param put_path_length @a put_path length
415 * @param type the block type
416 * @param size number of bytes in @a data
417 * @param data the record data
420 process_auth_discovery_dht_result (void* cls,
421 struct GNUNET_TIME_Absolute exp,
422 const struct GNUNET_HashCode *key,
423 const struct GNUNET_PeerIdentity *get_path,
424 unsigned int get_path_length,
425 const struct GNUNET_PeerIdentity *put_path,
426 unsigned int put_path_length,
427 enum GNUNET_BLOCK_Type type,
431 struct GetPseuAuthorityHandle *gph = cls;
432 const struct GNUNET_GNSRECORD_Block *block;
434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
435 "Got DHT result for PSEU request\n");
436 GNUNET_DHT_get_stop (gph->get_handle);
437 gph->get_handle = NULL;
438 GNUNET_SCHEDULER_cancel (gph->timeout_task);
439 gph->timeout_task = GNUNET_SCHEDULER_NO_TASK;
443 /* is this allowed!? */
445 process_pseu_result (gph, NULL);
448 if (size < sizeof (struct GNUNET_GNSRECORD_Block))
450 /* how did this pass DHT block validation!? */
452 process_pseu_result (gph, NULL);
457 ntohl (block->purpose.size) +
458 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
459 sizeof (struct GNUNET_CRYPTO_EcdsaSignature))
461 /* how did this pass DHT block validation!? */
463 process_pseu_result (gph, NULL);
467 GNUNET_GNSRECORD_block_decrypt (block,
470 &process_auth_records,
473 /* other peer encrypted invalid block, complain */
475 process_pseu_result (gph, NULL);
482 * Callback called by namestore for a zone to name result. We're
483 * trying to see if a short name for a given zone already exists.
485 * @param cls the closure
486 * @param zone_key the zone we queried
487 * @param name the name found or NULL
488 * @param rd_len number of records for the name
489 * @param rd the record data (PKEY) for the name
492 process_zone_to_name_discover (void *cls,
493 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
496 const struct GNUNET_GNSRECORD_Data *rd)
498 struct GetPseuAuthorityHandle* gph = cls;
499 struct GNUNET_HashCode lookup_key;
501 gph->namestore_task = NULL;
504 /* we found a match in our own zone */
505 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
506 "Shortening aborted, name `%s' already reserved for the zone\n",
508 free_get_pseu_authority_handle (gph);
511 /* record does not yet exist, go into DHT to find PSEU record */
512 GNUNET_GNSRECORD_query_from_public_key (&gph->target_zone,
515 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
516 "Shortening searches in DHT for PSEU record under `%s' in zone `%s'\n",
517 GNUNET_h2s (&lookup_key),
518 GNUNET_GNSRECORD_z2s (&gph->target_zone));
520 gph->timeout_task = GNUNET_SCHEDULER_add_delayed (DHT_LOOKUP_TIMEOUT,
521 &handle_auth_discovery_timeout,
523 gph->get_handle = GNUNET_DHT_get_start (dht_handle,
524 GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
526 DHT_GNS_REPLICATION_LEVEL,
527 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
529 &process_auth_discovery_dht_result,
535 * Start shortening algorithm, try to allocate a nice short
536 * canonical name for @a pub in @a shorten_zone, using
537 * @a original_label as one possible suggestion.
539 * @param original_label original label for the zone
540 * @param pub public key of the zone to shorten
541 * @param shorten_zone private key of the target zone for the new record
544 GNS_shorten_start (const char *original_label,
545 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub,
546 const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_zone)
548 struct GetPseuAuthorityHandle *gph;
550 if (strlen (original_label) > GNUNET_DNSPARSER_MAX_LABEL_LENGTH)
555 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
556 "Starting shortening process for `%s' with old label `%s'\n",
557 GNUNET_GNSRECORD_z2s (pub),
559 gph = GNUNET_new (struct GetPseuAuthorityHandle);
560 gph->shorten_zone_key = *shorten_zone;
561 gph->target_zone = *pub;
562 strcpy (gph->label, original_label);
563 GNUNET_CONTAINER_DLL_insert (gph_head, gph_tail, gph);
564 /* first, check if we *already* have a record for this zone */
565 gph->namestore_task = GNUNET_NAMESTORE_zone_to_name (namestore_handle,
568 &process_zone_to_name_discover,
574 * Initialize the shortening subsystem
576 * @param nh the namestore handle
577 * @param nc the namecache handle
578 * @param dht the dht handle
581 GNS_shorten_init (struct GNUNET_NAMESTORE_Handle *nh,
582 struct GNUNET_NAMECACHE_Handle *nc,
583 struct GNUNET_DHT_Handle *dht)
585 namestore_handle = nh;
586 namecache_handle = nc;
592 * Shutdown shortening.
597 /* abort active shorten operations */
598 while (NULL != gph_head)
599 free_get_pseu_authority_handle (gph_head);
601 namestore_handle = NULL;
602 namecache_handle = NULL;
605 /* end of gnunet-service-gns_shorten.c */