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_namestore_service.h"
31 #include "gnunet_resolver_service.h"
32 #include "gnunet_gns_service.h"
34 #include "gnunet-service-gns_shorten.h"
35 #include "gnunet_vpn_service.h"
39 * Default DHT timeout for lookups.
41 #define DHT_LOOKUP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
44 * DHT replication level
46 #define DHT_GNS_REPLICATION_LEVEL 5
50 * Handle for a PSEU lookup used to shorten names.
52 struct GetPseuAuthorityHandle
57 struct GetPseuAuthorityHandle *next;
62 struct GetPseuAuthorityHandle *prev;
65 * Private key of the (shorten) zone to store the resulting
68 struct GNUNET_CRYPTO_EccPrivateKey shorten_zone_key;
71 * Original label (used if no PSEU record is found).
73 char label[GNUNET_DNSPARSER_MAX_LABEL_LENGTH + 1];
76 * Label we are currently trying out (during #perform_pseu_lookup).
81 * The zone for which we are trying to find the PSEU record.
83 struct GNUNET_CRYPTO_EccPublicSignKey target_zone;
86 * Handle for DHT lookups. Should be NULL if no lookups are in progress
88 struct GNUNET_DHT_GetHandle *get_handle;
91 * Handle to namestore request
93 struct GNUNET_NAMESTORE_QueueEntry *namestore_task;
96 * Task to abort DHT lookup operation.
98 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
104 * Head of PSEU/shorten operations list.
106 static struct GetPseuAuthorityHandle *gph_head;
109 * Tail of PSEU/shorten operations list.
111 static struct GetPseuAuthorityHandle *gph_tail;
114 * Our handle to the namestore service
116 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
119 * Resolver handle to the dht
121 static struct GNUNET_DHT_Handle *dht_handle;
124 * Cleanup a 'struct GetPseuAuthorityHandle', terminating all
125 * pending activities.
127 * @param gph handle to terminate
130 free_get_pseu_authority_handle (struct GetPseuAuthorityHandle *gph)
132 if (NULL != gph->get_handle)
134 GNUNET_DHT_get_stop (gph->get_handle);
135 gph->get_handle = NULL;
137 if (NULL != gph->namestore_task)
139 GNUNET_NAMESTORE_cancel (gph->namestore_task);
140 gph->namestore_task = NULL;
142 if (GNUNET_SCHEDULER_NO_TASK != gph->timeout_task)
144 GNUNET_SCHEDULER_cancel (gph->timeout_task);
145 gph->timeout_task = GNUNET_SCHEDULER_NO_TASK;
147 GNUNET_CONTAINER_DLL_remove (gph_head, gph_tail, gph);
148 GNUNET_free_non_null (gph->current_label);
154 * Continuation for pkey record creation (shorten)
156 * @param cls a GetPseuAuthorityHandle
157 * @param success unused
161 create_pkey_cont (void* cls,
165 struct GetPseuAuthorityHandle* gph = cls;
167 gph->namestore_task = NULL;
168 free_get_pseu_authority_handle (gph);
173 * Namestore calls this function if we have record for this name.
174 * (or with rd_count=0 to indicate no matches).
176 * @param cls the pending query
177 * @param rd_count the number of records with 'name'
178 * @param rd the record data
181 process_pseu_lookup_ns (void *cls,
182 unsigned int rd_count,
183 const struct GNUNET_NAMESTORE_RecordData *rd);
187 * We obtained a result for our query to the shorten zone from
188 * the namestore. Try to decrypt.
190 * @param cls the handle to our shorten operation
191 * @param block resulting encrypted block
194 process_pseu_block_ns (void *cls,
195 const struct GNUNET_NAMESTORE_Block *block)
197 struct GetPseuAuthorityHandle *gph = cls;
198 struct GNUNET_CRYPTO_EccPublicSignKey pub;
200 gph->namestore_task = NULL;
203 process_pseu_lookup_ns (gph, 0, NULL);
206 GNUNET_CRYPTO_ecc_key_get_public_for_signature (&gph->shorten_zone_key,
209 GNUNET_NAMESTORE_block_decrypt (block,
212 &process_pseu_lookup_ns,
216 free_get_pseu_authority_handle (gph);
223 * Lookup in the namestore for the shorten zone the given label.
225 * @param gph the handle to our shorten operation
226 * @param label the label to lookup
229 perform_pseu_lookup (struct GetPseuAuthorityHandle *gph,
232 struct GNUNET_CRYPTO_EccPublicSignKey pub;
233 struct GNUNET_HashCode query;
235 GNUNET_CRYPTO_ecc_key_get_public_for_signature (&gph->shorten_zone_key,
237 GNUNET_free_non_null (gph->current_label);
238 gph->current_label = GNUNET_strdup (label);
239 GNUNET_NAMESTORE_query_from_public_key (&pub,
242 gph->namestore_task = GNUNET_NAMESTORE_lookup_block (namestore_handle,
244 &process_pseu_block_ns,
250 * Namestore calls this function if we have record for this name.
251 * (or with rd_count=0 to indicate no matches).
253 * @param cls the pending query
254 * @param rd_count the number of records with 'name'
255 * @param rd the record data
258 process_pseu_lookup_ns (void *cls,
259 unsigned int rd_count,
260 const struct GNUNET_NAMESTORE_RecordData *rd)
262 struct GetPseuAuthorityHandle *gph = cls;
263 struct GNUNET_NAMESTORE_RecordData new_pkey;
265 gph->namestore_task = NULL;
268 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
269 "Name `%s' already taken, cannot shorten.\n",
271 /* if this was not yet the original label, try one more
272 time, this time not using PSEU but the original label */
273 if (0 == strcmp (gph->current_label,
276 free_get_pseu_authority_handle (gph);
280 perform_pseu_lookup (gph, gph->label);
284 /* name is available */
285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
286 "Shortening `%s' to `%s'\n",
287 GNUNET_NAMESTORE_z2s (&gph->target_zone),
289 new_pkey.expiration_time = UINT64_MAX;
290 new_pkey.data_size = sizeof (struct GNUNET_CRYPTO_EccPublicSignKey);
291 new_pkey.data = &gph->target_zone;
292 new_pkey.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
293 new_pkey.flags = GNUNET_NAMESTORE_RF_NONE
294 | GNUNET_NAMESTORE_RF_PRIVATE
295 | GNUNET_NAMESTORE_RF_PENDING;
297 = GNUNET_NAMESTORE_records_store (namestore_handle,
298 &gph->shorten_zone_key,
301 &create_pkey_cont, gph);
306 * Process result of a DHT lookup for a PSEU record.
308 * @param gph the handle to our shorten operation
309 * @param pseu the pseu result or NULL
312 process_pseu_result (struct GetPseuAuthorityHandle* gph,
317 /* no PSEU found, try original label */
318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
319 "No PSEU found, trying original label `%s' instead.\n",
321 perform_pseu_lookup (gph, gph->label);
324 /* check if 'pseu' is taken */
325 perform_pseu_lookup (gph, pseu);
330 * Handle timeout for DHT request during shortening.
332 * @param cls the request handle as closure
333 * @param tc the task context
336 handle_auth_discovery_timeout (void *cls,
337 const struct GNUNET_SCHEDULER_TaskContext *tc)
339 struct GetPseuAuthorityHandle *gph = cls;
341 gph->timeout_task = GNUNET_SCHEDULER_NO_TASK;
342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
343 "DHT lookup for PSEU query timed out.\n");
344 GNUNET_DHT_get_stop (gph->get_handle);
345 gph->get_handle = NULL;
346 process_pseu_result (gph, NULL);
351 * Handle decrypted records from DHT result.
353 * @param cls closure with our 'struct GetPseuAuthorityHandle'
354 * @param rd_count number of entries in 'rd' array
355 * @param rd array of records with data to store
358 process_auth_records (void *cls,
359 unsigned int rd_count,
360 const struct GNUNET_NAMESTORE_RecordData *rd)
362 struct GetPseuAuthorityHandle *gph = cls;
365 for (i=0; i < rd_count; i++)
367 if (GNUNET_NAMESTORE_TYPE_PSEU == rd[i].record_type)
370 process_pseu_result (gph,
371 (const char *) rd[i].data);
375 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
376 "No PSEU record found in DHT reply.\n");
377 process_pseu_result (gph, NULL);
382 * Function called when we find a PSEU entry in the DHT
384 * @param cls the request handle
385 * @param exp lifetime
386 * @param key the key the record was stored under
387 * @param get_path get path
388 * @param get_path_length get path length
389 * @param put_path put path
390 * @param put_path_length put path length
391 * @param type the block type
392 * @param size the size of the record
393 * @param data the record data
396 process_auth_discovery_dht_result (void* cls,
397 struct GNUNET_TIME_Absolute exp,
398 const struct GNUNET_HashCode *key,
399 const struct GNUNET_PeerIdentity *get_path,
400 unsigned int get_path_length,
401 const struct GNUNET_PeerIdentity *put_path,
402 unsigned int put_path_length,
403 enum GNUNET_BLOCK_Type type,
407 struct GetPseuAuthorityHandle *gph = cls;
408 const struct GNUNET_NAMESTORE_Block *block;
410 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411 "Got DHT result for PSEU request\n");
412 GNUNET_DHT_get_stop (gph->get_handle);
413 gph->get_handle = NULL;
414 GNUNET_SCHEDULER_cancel (gph->timeout_task);
415 gph->timeout_task = GNUNET_SCHEDULER_NO_TASK;
419 /* is this allowed!? */
421 process_pseu_result (gph, NULL);
424 if (size < sizeof (struct GNUNET_NAMESTORE_Block))
426 /* how did this pass DHT block validation!? */
428 process_pseu_result (gph, NULL);
433 ntohs (block->purpose.size) +
434 sizeof (struct GNUNET_CRYPTO_EccPublicSignKey) +
435 sizeof (struct GNUNET_CRYPTO_EccSignature))
437 /* how did this pass DHT block validation!? */
439 process_pseu_result (gph, NULL);
443 GNUNET_NAMESTORE_block_decrypt (block,
446 &process_auth_records,
449 /* other peer encrypted invalid block, complain */
451 process_pseu_result (gph, NULL);
458 * Callback called by namestore for a zone to name result. We're
459 * trying to see if a short name for a given zone already exists.
461 * @param cls the closure
462 * @param zone_key the zone we queried
463 * @param name the name found or NULL
464 * @param rd_len number of records for the name
465 * @param rd the record data (PKEY) for the name
468 process_zone_to_name_discover (void *cls,
469 const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
472 const struct GNUNET_NAMESTORE_RecordData *rd)
474 struct GetPseuAuthorityHandle* gph = cls;
475 struct GNUNET_HashCode lookup_key;
477 gph->namestore_task = NULL;
480 /* we found a match in our own zone */
481 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482 "Shortening aborted, name `%s' already reserved for the zone\n",
484 free_get_pseu_authority_handle (gph);
487 /* record does not yet exist, go into DHT to find PSEU record */
488 GNUNET_NAMESTORE_query_from_public_key (&gph->target_zone,
491 gph->timeout_task = GNUNET_SCHEDULER_add_delayed (DHT_LOOKUP_TIMEOUT,
492 &handle_auth_discovery_timeout,
494 gph->get_handle = GNUNET_DHT_get_start (dht_handle,
495 GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
497 DHT_GNS_REPLICATION_LEVEL,
498 GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
500 &process_auth_discovery_dht_result,
506 * Start shortening algorithm, try to allocate a nice short
507 * canonical name for @a pub in @a shorten_zone, using
508 * @a original_label as one possible suggestion.
510 * @param original_label original label for the zone
511 * @param pub public key of the zone to shorten
512 * @param shorten_zone private key of the target zone for the new record
515 GNS_shorten_start (const char *original_label,
516 const struct GNUNET_CRYPTO_EccPublicSignKey *pub,
517 const struct GNUNET_CRYPTO_EccPrivateKey *shorten_zone)
519 struct GetPseuAuthorityHandle *gph;
521 if (strlen (original_label) > GNUNET_DNSPARSER_MAX_LABEL_LENGTH)
526 gph = GNUNET_new (struct GetPseuAuthorityHandle);
527 gph->shorten_zone_key = *shorten_zone;
528 gph->target_zone = *pub;
529 strcpy (gph->label, original_label);
530 GNUNET_CONTAINER_DLL_insert (gph_head, gph_tail, gph);
531 /* first, check if we *already* have a record for this zone */
532 gph->namestore_task = GNUNET_NAMESTORE_zone_to_name (namestore_handle,
535 &process_zone_to_name_discover,
541 * Initialize the shortening subsystem
543 * @param nh the namestore handle
544 * @param dht the dht handle
547 GNS_shorten_init (struct GNUNET_NAMESTORE_Handle *nh,
548 struct GNUNET_DHT_Handle *dht)
550 namestore_handle = nh;
556 * Shutdown shortening.
561 /* abort active shorten operations */
562 while (NULL != gph_head)
563 free_get_pseu_authority_handle (gph_head);
565 namestore_handle = NULL;
568 /* end of gnunet-service-gns_shorten.c */