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