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