use nick instead of PSEU lookup
[oweals/gnunet.git] / src / gns / gnunet-service-gns_shorten.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011-2013 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19 */
20
21 /**
22  * @file gns/gnunet-service-gns_shorten.c
23  * @brief GNUnet GNS shortening logic
24  * @author Martin Schanzenbach
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
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"
34 #include "gns.h"
35 #include "gnunet-service-gns_shorten.h"
36 #include "gnunet_vpn_service.h"
37
38
39 /**
40  * Default DHT timeout for lookups.
41  */
42 #define DHT_LOOKUP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
43
44 /**
45  * DHT replication level
46  */
47 #define DHT_GNS_REPLICATION_LEVEL 5
48
49
50 /**
51  * Handle for a PSEU lookup used to shorten names.
52  */
53 struct GetPseuAuthorityHandle
54 {
55   /**
56    * DLL
57    */
58   struct GetPseuAuthorityHandle *next;
59
60   /**
61    * DLL
62    */
63   struct GetPseuAuthorityHandle *prev;
64
65   /**
66    * Private key of the (shorten) zone to store the resulting
67    * pseudonym in.
68    */
69   struct GNUNET_CRYPTO_EcdsaPrivateKey shorten_zone_key;
70
71   /**
72    * Original label (used if no PSEU record is found).
73    */
74   char label[GNUNET_DNSPARSER_MAX_LABEL_LENGTH + 1];
75
76   /**
77    * Suggested label based on NICK record
78    */
79   char * suggested_label;
80
81   /**
82    * Label we are currently trying out (during #perform_pseu_lookup).
83    */
84   char *current_label;
85
86   /**
87    * The zone for which we are trying to find the PSEU record.
88    */
89   struct GNUNET_CRYPTO_EcdsaPublicKey target_zone;
90
91   /**
92    * Handle for DHT lookups. Should be NULL if no lookups are in progress
93    */
94   struct GNUNET_DHT_GetHandle *get_handle;
95
96   /**
97    * Handle to namestore request
98    */
99   struct GNUNET_NAMESTORE_QueueEntry *namestore_task;
100
101   /**
102    * Handle to namecache request
103    */
104   struct GNUNET_NAMECACHE_QueueEntry *namecache_task;
105
106   /**
107    * Task to abort DHT lookup operation.
108    */
109   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
110
111 };
112
113
114 /**
115  * Head of PSEU/shorten operations list.
116  */
117 static struct GetPseuAuthorityHandle *gph_head;
118
119 /**
120  * Tail of PSEU/shorten operations list.
121  */
122 static struct GetPseuAuthorityHandle *gph_tail;
123
124 /**
125  * Our handle to the namestore service
126  */
127 static struct GNUNET_NAMESTORE_Handle *namestore_handle;
128
129 /**
130  * Our handle to the namecache service
131  */
132 static struct GNUNET_NAMECACHE_Handle *namecache_handle;
133
134 /**
135  * Resolver handle to the dht
136  */
137 static struct GNUNET_DHT_Handle *dht_handle;
138
139 /**
140  * Cleanup a 'struct GetPseuAuthorityHandle', terminating all
141  * pending activities.
142  *
143  * @param gph handle to terminate
144  */
145 static void
146 free_get_pseu_authority_handle (struct GetPseuAuthorityHandle *gph)
147 {
148   if (NULL != gph->get_handle)
149   {
150     GNUNET_DHT_get_stop (gph->get_handle);
151     gph->get_handle = NULL;
152   }
153   if (NULL != gph->namestore_task)
154   {
155     GNUNET_NAMESTORE_cancel (gph->namestore_task);
156     gph->namestore_task = NULL;
157   }
158   if (NULL != gph->namecache_task)
159   {
160     GNUNET_NAMECACHE_cancel (gph->namecache_task);
161     gph->namecache_task = NULL;
162   }
163   if (GNUNET_SCHEDULER_NO_TASK != gph->timeout_task)
164   {
165     GNUNET_SCHEDULER_cancel (gph->timeout_task);
166     gph->timeout_task = GNUNET_SCHEDULER_NO_TASK;
167   }
168   GNUNET_CONTAINER_DLL_remove (gph_head, gph_tail, gph);
169   GNUNET_free_non_null (gph->current_label);
170   GNUNET_free (gph);
171 }
172
173
174 /**
175  * Continuation for pkey record creation (shorten)
176  *
177  * @param cls a GetPseuAuthorityHandle
178  * @param success unused
179  * @param emsg unused
180  */
181 static void
182 create_pkey_cont (void* cls,
183                   int32_t success,
184                   const char *emsg)
185 {
186   struct GetPseuAuthorityHandle* gph = cls;
187
188   gph->namestore_task = NULL;
189   free_get_pseu_authority_handle (gph);
190 }
191
192
193 /**
194  * Namestore calls this function if we have record for this name.
195  * (or with rd_count=0 to indicate no matches).
196  *
197  * @param cls the pending query
198  * @param rd_count the number of records with 'name'
199  * @param rd the record data
200  */
201 static void
202 process_pseu_lookup_ns (void *cls,
203                         unsigned int rd_count,
204                         const struct GNUNET_GNSRECORD_Data *rd);
205
206
207 /**
208  * We obtained a result for our query to the shorten zone from
209  * the namestore.  Try to decrypt.
210  *
211  * @param cls the handle to our shorten operation
212  * @param block resulting encrypted block
213  */
214 static void
215 process_pseu_block_ns (void *cls,
216                        const struct GNUNET_GNSRECORD_Block *block)
217 {
218   struct GetPseuAuthorityHandle *gph = cls;
219   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
220
221   gph->namecache_task = NULL;
222   if (NULL == block)
223   {
224     process_pseu_lookup_ns (gph, 0, NULL);
225     return;
226   }
227   GNUNET_CRYPTO_ecdsa_key_get_public (&gph->shorten_zone_key,
228                                     &pub);
229   if (GNUNET_OK !=
230       GNUNET_GNSRECORD_block_decrypt (block,
231                                       &pub,
232                                       gph->current_label,
233                                       &process_pseu_lookup_ns,
234                                       gph))
235   {
236     GNUNET_break (0);
237     free_get_pseu_authority_handle (gph);
238     return;
239   }
240 }
241
242
243 /**
244  * Lookup in the namecache for the shorten zone the given label.
245  *
246  * @param gph the handle to our shorten operation
247  * @param label the label to lookup
248  */
249 static void
250 perform_pseu_lookup (struct GetPseuAuthorityHandle *gph,
251                      const char *label)
252 {
253   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
254   struct GNUNET_HashCode query;
255
256   GNUNET_CRYPTO_ecdsa_key_get_public (&gph->shorten_zone_key,
257                                     &pub);
258   GNUNET_free_non_null (gph->current_label);
259   gph->current_label = GNUNET_strdup (label);
260   GNUNET_GNSRECORD_query_from_public_key (&pub,
261                                           label,
262                                           &query);
263   gph->namecache_task = GNUNET_NAMECACHE_lookup_block (namecache_handle,
264                                                        &query,
265                                                        &process_pseu_block_ns,
266                                                        gph);
267 }
268
269
270 /**
271  * Namestore calls this function if we have record for this name.
272  * (or with rd_count=0 to indicate no matches).
273  *
274  * @param cls the pending query
275  * @param rd_count the number of records with 'name'
276  * @param rd the record data
277  */
278 static void
279 process_pseu_lookup_ns (void *cls,
280                         unsigned int rd_count,
281                         const struct GNUNET_GNSRECORD_Data *rd)
282 {
283   struct GetPseuAuthorityHandle *gph = cls;
284   struct GNUNET_GNSRECORD_Data new_pkey;
285
286   gph->namestore_task = NULL;
287   if (rd_count > 0)
288   {
289     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
290                 "Name `%s' already taken, cannot shorten.\n",
291                 gph->current_label);
292     /* if this was not yet the original label, try one more
293        time, this time not using PSEU but the original label */
294     if (0 == strcmp (gph->current_label,
295                      gph->label))
296     {
297       free_get_pseu_authority_handle (gph);
298     }
299     else
300     {
301       perform_pseu_lookup (gph, gph->label);
302     }
303     return;
304   }
305   /* name is available */
306   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
307               "Shortening `%s' to `%s'\n",
308               GNUNET_GNSRECORD_z2s (&gph->target_zone),
309               gph->current_label);
310   new_pkey.expiration_time = UINT64_MAX;
311   new_pkey.data_size = sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
312   new_pkey.data = &gph->target_zone;
313   new_pkey.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
314   new_pkey.flags = GNUNET_GNSRECORD_RF_NONE
315                  | GNUNET_GNSRECORD_RF_PRIVATE;
316   gph->namestore_task
317     = GNUNET_NAMESTORE_records_store (namestore_handle,
318                                       &gph->shorten_zone_key,
319                                       gph->current_label,
320                                       1, &new_pkey,
321                                       &create_pkey_cont, gph);
322 }
323
324
325 /**
326  * Process result of a DHT lookup for a PSEU record.
327  *
328  * @param gph the handle to our shorten operation
329  * @param pseu the pseu result or NULL
330  */
331 static void
332 process_pseu_result (struct GetPseuAuthorityHandle* gph,
333                      const char *pseu)
334 {
335   if (NULL == pseu)
336   {
337     /* no PSEU found, try original label */
338     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
339                 "No PSEU found, trying original label `%s' instead.\n",
340                 gph->label);
341     perform_pseu_lookup (gph, gph->label);
342     return;
343   }
344   /* check if 'pseu' is taken */
345   perform_pseu_lookup (gph, pseu);
346 }
347
348
349 /**
350  * Handle timeout for DHT request during shortening.
351  *
352  * @param cls the request handle as closure
353  * @param tc the task context
354  */
355 static void
356 handle_auth_discovery_timeout (void *cls,
357                                const struct GNUNET_SCHEDULER_TaskContext *tc)
358 {
359   struct GetPseuAuthorityHandle *gph = cls;
360
361   gph->timeout_task = GNUNET_SCHEDULER_NO_TASK;
362   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
363               "DHT lookup for PSEU query in zone `%s' timed out.\n",
364               GNUNET_GNSRECORD_z2s (&gph->target_zone));
365   GNUNET_DHT_get_stop (gph->get_handle);
366   gph->get_handle = NULL;
367   process_pseu_result (gph, NULL);
368 }
369
370
371 /**
372  * Handle decrypted records from DHT result.
373  *
374  * @param cls closure with our 'struct GetPseuAuthorityHandle'
375  * @param rd_count number of entries in 'rd' array
376  * @param rd array of records with data to store
377  */
378 static void
379 process_auth_records (void *cls,
380                       unsigned int rd_count,
381                       const struct GNUNET_GNSRECORD_Data *rd)
382 {
383   struct GetPseuAuthorityHandle *gph = cls;
384   unsigned int i;
385
386   for (i=0; i < rd_count; i++)
387   {
388     if (GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type)
389     {
390       char pseu[rd[i].data_size + 1];
391
392       /* found pseu */
393       memcpy (pseu,
394               rd[i].data,
395               rd[i].data_size);
396       pseu[rd[i].data_size] = '\0';
397       process_pseu_result (gph,
398                            pseu);
399       return;
400     }
401   }
402   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
403               "No PSEU record found in DHT reply with %u records.\n",
404               rd_count);
405   process_pseu_result (gph, NULL);
406 }
407
408
409 /**
410  * Function called when we find a PSEU entry in the DHT
411  *
412  * @param cls the request handle
413  * @param exp lifetime
414  * @param key the key the record was stored under
415  * @param get_path get path
416  * @param get_path_length @a get_path length
417  * @param put_path put path
418  * @param put_path_length @a put_path length
419  * @param type the block type
420  * @param size number of bytes in @a data
421  * @param data the record data
422  */
423 static void
424 process_auth_discovery_dht_result (void* cls,
425                                    struct GNUNET_TIME_Absolute exp,
426                                    const struct GNUNET_HashCode *key,
427                                    const struct GNUNET_PeerIdentity *get_path,
428                                    unsigned int get_path_length,
429                                    const struct GNUNET_PeerIdentity *put_path,
430                                    unsigned int put_path_length,
431                                    enum GNUNET_BLOCK_Type type,
432                                    size_t size,
433                                    const void *data)
434 {
435   struct GetPseuAuthorityHandle *gph = cls;
436   const struct GNUNET_GNSRECORD_Block *block;
437
438   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
439               "Got DHT result for PSEU request\n");
440   GNUNET_DHT_get_stop (gph->get_handle);
441   gph->get_handle = NULL;
442   GNUNET_SCHEDULER_cancel (gph->timeout_task);
443   gph->timeout_task = GNUNET_SCHEDULER_NO_TASK;
444
445   if (NULL == data)
446   {
447     /* is this allowed!? */
448     GNUNET_break (0);
449     process_pseu_result (gph, NULL);
450     return;
451   }
452   if (size < sizeof (struct GNUNET_GNSRECORD_Block))
453   {
454     /* how did this pass DHT block validation!? */
455     GNUNET_break (0);
456     process_pseu_result (gph, NULL);
457     return;
458   }
459   block = data;
460   if (size !=
461       ntohl (block->purpose.size) +
462       sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey) +
463       sizeof (struct GNUNET_CRYPTO_EcdsaSignature))
464   {
465     /* how did this pass DHT block validation!? */
466     GNUNET_break (0);
467     process_pseu_result (gph, NULL);
468     return;
469   }
470   if (GNUNET_OK !=
471       GNUNET_GNSRECORD_block_decrypt (block,
472                                       &gph->target_zone,
473                                       GNUNET_GNS_TLD_PLUS,
474                                       &process_auth_records,
475                                       gph))
476   {
477     /* other peer encrypted invalid block, complain */
478     GNUNET_break_op (0);
479     process_pseu_result (gph, NULL);
480     return;
481   }
482 }
483
484 static void suggested_lookup_cb (void *cls,
485                                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
486                                 const char *label,
487                                 unsigned int rd_count,
488                                 const struct GNUNET_GNSRECORD_Data *rd)
489 {
490   struct GetPseuAuthorityHandle* gph = cls;
491   gph->namestore_task = NULL;
492   if ((0 == strcmp (label, gph->suggested_label)) && (0 == rd_count) && (NULL == rd))
493   {
494
495     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
496                 "Shortening to suggested name `%s' possible\n",
497                 gph->suggested_label);
498     process_pseu_result (gph, gph->suggested_label);
499   }
500 }
501
502
503
504 /**
505  * Callback called by namestore for a zone to name result.  We're
506  * trying to see if a short name for a given zone already exists.
507  *
508  * @param cls the closure
509  * @param zone_key the zone we queried
510  * @param name the name found or NULL
511  * @param rd_len number of records for the name
512  * @param rd the record data (PKEY) for the name
513  */
514 static void
515 process_zone_to_name_discover (void *cls,
516                                const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
517                                const char *name,
518                                unsigned int rd_len,
519                                const struct GNUNET_GNSRECORD_Data *rd)
520 {
521   struct GetPseuAuthorityHandle* gph = cls;
522 #if 0
523   struct GNUNET_HashCode lookup_key;
524 #endif
525
526   gph->namestore_task = NULL;
527   if (0 != rd_len)
528   {
529     /* we found a match in our own zone */
530     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
531                 "Shortening aborted, name `%s' already reserved for the zone\n",
532                 name);
533     free_get_pseu_authority_handle (gph);
534     return;
535   }
536   /* record does not yet exist, check if suggested label is available */
537
538   if (NULL != gph->suggested_label)
539     gph->namestore_task = GNUNET_NAMESTORE_records_lookup (namestore_handle, zone_key,
540         gph->suggested_label, &suggested_lookup_cb, gph);
541
542 #if 0
543   GNUNET_GNSRECORD_query_from_public_key (&gph->target_zone,
544                                           GNUNET_GNS_TLD_PLUS,
545                                           &lookup_key);
546   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
547               "Shortening searches in DHT for PSEU record under `%s' in zone `%s'\n",
548               GNUNET_h2s (&lookup_key),
549               GNUNET_GNSRECORD_z2s (&gph->target_zone));
550
551   gph->timeout_task = GNUNET_SCHEDULER_add_delayed (DHT_LOOKUP_TIMEOUT,
552                                                     &handle_auth_discovery_timeout,
553                                                     gph);
554   gph->get_handle = GNUNET_DHT_get_start (dht_handle,
555                                           GNUNET_BLOCK_TYPE_GNS_NAMERECORD,
556                                           &lookup_key,
557                                           DHT_GNS_REPLICATION_LEVEL,
558                                           GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
559                                           NULL, 0,
560                                           &process_auth_discovery_dht_result,
561                                           gph);
562 #endif
563 }
564
565
566 /**
567  * Start shortening algorithm, try to allocate a nice short
568  * canonical name for @a pub in @a shorten_zone, using
569  * @a original_label as one possible suggestion.
570  *
571  * @param original_label original label for the zone
572  * @param suggested_label suggested label for the zone
573  * @param pub public key of the zone to shorten
574  * @param shorten_zone private key of the target zone for the new record
575  */
576 void
577 GNS_shorten_start (const char *original_label,
578                    const char *suggested_label,
579                    const struct GNUNET_CRYPTO_EcdsaPublicKey *pub,
580                    const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_zone)
581 {
582   struct GetPseuAuthorityHandle *gph;
583
584   if (strlen (original_label) > GNUNET_DNSPARSER_MAX_LABEL_LENGTH)
585   {
586     GNUNET_break (0);
587     return;
588   }
589   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
590               "Starting shortening process for `%s' with old label `%s'\n",
591               GNUNET_GNSRECORD_z2s (pub),
592               original_label);
593   gph = GNUNET_new (struct GetPseuAuthorityHandle);
594   gph->shorten_zone_key = *shorten_zone;
595   gph->target_zone = *pub;
596   gph->suggested_label = GNUNET_strdup (suggested_label);
597   strcpy (gph->label, original_label);
598   GNUNET_CONTAINER_DLL_insert (gph_head, gph_tail, gph);
599   /* first, check if we *already* have a record for this zone */
600   gph->namestore_task = GNUNET_NAMESTORE_zone_to_name (namestore_handle,
601                                                        shorten_zone,
602                                                        pub,
603                                                        &process_zone_to_name_discover,
604                                                        gph);
605 }
606
607
608 /**
609  * Initialize the shortening subsystem
610  *
611  * @param nh the namestore handle
612  * @param nc the namecache handle
613  * @param dht the dht handle
614  */
615 void
616 GNS_shorten_init (struct GNUNET_NAMESTORE_Handle *nh,
617                   struct GNUNET_NAMECACHE_Handle *nc,
618                   struct GNUNET_DHT_Handle *dht)
619 {
620   namestore_handle = nh;
621   namecache_handle = nc;
622   dht_handle = dht;
623 }
624
625
626 /**
627  * Shutdown shortening.
628  */
629 void
630 GNS_shorten_done ()
631 {
632   /* abort active shorten operations */
633   while (NULL != gph_head)
634     free_get_pseu_authority_handle (gph_head);
635   dht_handle = NULL;
636   namestore_handle = NULL;
637   namecache_handle = NULL;
638 }
639
640 /* end of gnunet-service-gns_shorten.c */