More API function tests...
[oweals/gnunet.git] / src / gns / gnunet-service-gns_reverser.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-2013 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file gns/gnunet-service-gns_reverser.c
22  * @brief GNUnet GNS service
23  * @author Martin Schanzenbach
24  */
25
26
27 #include "platform.h"
28 #include "gnunet_gns_service.h"
29 #include "gnunet-service-gns_resolver.h"
30 #include "gnunet-service-gns_reverser.h"
31
32 struct ReverseRecordEntry
33 {
34   /**
35    * DLL
36    */
37   struct ReverseRecordEntry *next;
38
39   /**
40    * DLL
41    */
42   struct ReverseRecordEntry *prev;
43
44   /**
45    * ReverseRecord
46    */
47   struct GNUNET_GNSRECORD_ReverseRecord *record;
48
49   /**
50    * Record length
51    */
52   size_t record_len;
53
54 };
55
56 struct IteratorHandle
57 {
58   /**
59    * Records found
60    */
61   struct ReverseRecordEntry *records_head;
62
63   /**
64    * Records found
65    */
66   struct ReverseRecordEntry *records_tail;
67
68   /**
69    * Record count
70    */
71   uint64_t record_count;
72
73   /**
74    * Current delegation to expect
75    */
76   struct GNUNET_CRYPTO_EcdsaPublicKey target;
77
78   /**
79    * Queue entry
80    */
81   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
82
83 };
84
85 struct ReverseTreeNode
86 {
87   /**
88    * DLL
89    */
90   struct ReverseTreeNode *next;
91
92   /**
93    * DLL
94    */
95   struct ReverseTreeNode *prev;
96
97   /**
98    * Resolved name until now
99    */
100   char *name;
101
102   /**
103    * Depth of the resolution at this node
104    */
105   uint8_t depth;
106
107   /**
108    * The pkey of the namespace
109    */
110   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
111
112 };
113
114
115 struct GNS_ReverserHandle
116 {
117   /**
118    * GNS resolver handle
119    */
120   struct GNS_ResolverHandle *rh;
121
122   /**
123    * The authority to look for
124    */
125   struct GNUNET_CRYPTO_EcdsaPublicKey authority;
126
127   /**
128    * Resolution candidate queue
129    */
130   struct ReverseTreeNode *node_queue_head;
131
132   /**
133    * Resolution candidate queue
134    */
135   struct ReverseTreeNode *node_queue_tail;
136
137   /**
138    * Max depth for the resolution
139    */
140   uint8_t max_depth;
141
142   /**
143    * Result callback
144    */
145   GNS_ReverseResultProcessor proc;
146
147   /**
148    * Callback closure
149    */
150   void *proc_cls;
151 };
152
153 /**
154  * Reverse record collection task
155  */
156 static struct GNUNET_SCHEDULER_Task *reverse_record_check_task;
157
158 /**
159  * NS iterator task
160  */
161 static struct GNUNET_SCHEDULER_Task *it_task;
162
163 /**
164  * GNS lookup handle
165  */
166 static struct GNS_ResolverHandle *gns_lookup_reverse;
167
168 /**
169  * NS handle
170  */
171 static struct GNUNET_NAMESTORE_Handle *ns;
172
173 /**
174  * NS Iterator
175  */
176 static struct GNUNET_NAMESTORE_ZoneIterator *namestore_iter;
177
178 /**
179  * The zone target for reverse record resolution
180  */
181 static struct GNUNET_CRYPTO_EcdsaPublicKey myzone;
182
183 /**
184  * The zone target for reverse record resolution
185  */
186 static struct GNUNET_CRYPTO_EcdsaPrivateKey pzone;
187
188 /**
189  * The nick of our zone
190  */
191 static char *mynick;
192
193
194 static void
195 cleanup_handle (struct GNS_ReverserHandle *rh)
196 {
197   struct ReverseTreeNode *rtn;
198
199   for (rtn = rh->node_queue_head; NULL != rtn; rtn = rh->node_queue_head)
200   {
201     if (NULL != rtn->name)
202       GNUNET_free (rtn->name);
203         GNUNET_CONTAINER_DLL_remove (rh->node_queue_head,
204                                  rh->node_queue_tail,
205                                  rtn);
206         GNUNET_free (rtn);
207   }
208   GNUNET_free (rh);
209 }
210
211 static void
212 handle_gns_result (void *cls,
213                    uint32_t rd_count,
214                    const struct GNUNET_GNSRECORD_Data *rd)
215 {
216   struct GNS_ReverserHandle *rh = cls;
217   const struct GNUNET_GNSRECORD_ReverseRecord *rr;
218   struct ReverseTreeNode *rtn;
219   char *result;
220   const char *name;
221   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
222               "Got result (%d)\n", rd_count);
223
224   for (int i = 0; i < rd_count; i++)
225   {
226     /**
227      * Check if we are in the delegation set
228      */
229     if (GNUNET_GNSRECORD_TYPE_REVERSE != rd[i].record_type)
230       continue;
231     rr = rd[i].data;
232     name = (const char*) &rr[1];
233     if (0 == memcmp (&rh->authority,
234                      &rr->pkey,
235                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
236     {
237       //Found!
238       GNUNET_asprintf (&result,
239                        "%s.%s.gnu",
240                        rh->node_queue_head->name,
241                        name);
242       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
243                   "Found path from %s\n", result);
244
245       rh->proc (rh->proc_cls, result);
246       cleanup_handle (rh);
247       GNUNET_free (result);
248       return;
249     } else {
250       if (rh->node_queue_head->depth >= rh->max_depth)
251         break;
252       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
253                   "Found REVERSE from %s\n", name);
254
255       rtn = GNUNET_new (struct ReverseTreeNode);
256       if (NULL == rh->node_queue_head->name)
257         rtn->name = GNUNET_strdup (name);
258       else
259         GNUNET_asprintf (&rtn->name,
260                          "%s.%s",
261                          rh->node_queue_head->name,
262                          name);
263       rtn->depth = rh->node_queue_head->depth + 1;
264       rtn->pkey = rr->pkey;
265       GNUNET_CONTAINER_DLL_insert_tail (rh->node_queue_head,
266                                         rh->node_queue_tail,
267                                         rtn);
268     }
269   }
270
271   /**
272    * Done here remove node from queue
273    */
274   rtn = rh->node_queue_head;
275   if (NULL != rtn)
276     GNUNET_CONTAINER_DLL_remove (rh->node_queue_head,
277                                  rh->node_queue_tail,
278                                  rtn);
279   if (NULL == rh->node_queue_head)
280   {
281     //No luck
282     rh->proc (rh->proc_cls, NULL);
283     cleanup_handle (rh);
284     return;
285   }
286   rh->rh = GNS_resolver_lookup (&rh->node_queue_head->pkey,
287                                 GNUNET_GNSRECORD_TYPE_REVERSE,
288                                 "+.gnu",
289                                 NULL,
290                                 GNUNET_GNS_LO_DEFAULT,
291                                 &handle_gns_result,
292                                 rh);
293 }
294
295 /**
296  * Reverse lookup of a specific zone
297  * calls RecordLookupProcessor on result or timeout
298  *
299  * @param target the zone to perform the lookup in
300  * @param authority the authority
301  * @param proc the processor to call
302  * @param proc_cls the closure to pass to @a proc
303  * @return handle to cancel operation
304  */
305 struct GNS_ReverserHandle *
306 GNS_reverse_lookup (const struct GNUNET_CRYPTO_EcdsaPublicKey *target,
307                     const struct GNUNET_CRYPTO_EcdsaPublicKey *authority,
308                     GNS_ReverseResultProcessor proc,
309                     void *proc_cls)
310 {
311   struct GNS_ReverserHandle *rh;
312   struct ReverseTreeNode *rtn;
313
314   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315               "Starting reverse resolution\n");
316   rh = GNUNET_new (struct GNS_ReverserHandle);
317   rh->proc = proc;
318   rh->proc_cls = proc_cls;
319   rtn = GNUNET_new (struct ReverseTreeNode);
320   rtn->name = NULL;
321   rtn->pkey = *target;
322   rtn->depth = 0;
323   GNUNET_CONTAINER_DLL_insert (rh->node_queue_head,
324                                rh->node_queue_tail,
325                                rtn);
326   rh->authority = *authority;
327   rh->max_depth = 3; //TODO make argument
328   rh->rh = GNS_resolver_lookup (target,
329                                 GNUNET_GNSRECORD_TYPE_REVERSE,
330                                 "+.gnu",
331                                 NULL,
332                                 GNUNET_GNS_LO_DEFAULT,
333                                 &handle_gns_result,
334                                 rh);
335   return rh;
336 }
337
338 /**
339  * Cancel active resolution (i.e. client disconnected).
340  *
341  * @param rh resolution to abort
342  */
343 void
344 GNS_reverse_lookup_cancel (struct GNS_ReverserHandle *rh)
345 {
346   cleanup_handle (rh);
347   return;
348 }
349
350 /********************************************
351  * Reverse iterator
352  * ******************************************/
353
354
355 static void
356 next_it (void *cls);
357
358 static void
359 handle_gns_result_iter (void *cls,
360                         uint32_t rd_count,
361                         const struct GNUNET_GNSRECORD_Data *rd)
362 {
363   struct IteratorHandle *ith = cls;
364   struct ReverseRecordEntry *rr;
365   gns_lookup_reverse = NULL;
366   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
367               "GNS for REVERSE (%s)\n", mynick);
368
369
370   if ((rd_count != 1) ||
371       (GNUNET_GNSRECORD_TYPE_PKEY != rd->record_type))
372   {
373     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
374                 "GNS invalid REVERSE (%s)\n", mynick);
375     gns_lookup_reverse = NULL;
376     it_task = GNUNET_SCHEDULER_add_now (&next_it, ith);
377     return;
378   }
379
380
381   rr = GNUNET_new (struct ReverseRecordEntry);
382   rr->record_len = sizeof (struct GNUNET_GNSRECORD_ReverseRecord)
383     + strlen (mynick) + 1;
384   rr->record = GNUNET_malloc (rr->record_len);
385   rr->record->pkey = ith->target;
386   rr->record->expiration.abs_value_us = rd->expiration_time;
387   GNUNET_memcpy ((char*)&rr->record[1],
388                  mynick,
389                  strlen (mynick));
390   GNUNET_CONTAINER_DLL_insert (ith->records_head,
391                                ith->records_tail,
392                                rr);
393   ith->record_count++;
394   it_task = GNUNET_SCHEDULER_add_now (&next_it, ith);
395 }
396
397
398 static void
399 next_it (void *cls)
400 {
401   it_task = NULL;
402   GNUNET_assert (NULL != namestore_iter);
403   GNUNET_NAMESTORE_zone_iterator_next (namestore_iter);
404 }
405
406
407 static void
408 iterator_cb (void *cls,
409              const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
410              const char *label,
411              unsigned int rd_count,
412              const struct GNUNET_GNSRECORD_Data *rd)
413 {
414   struct IteratorHandle *ith = cls;
415   struct GNUNET_CRYPTO_EcdsaPublicKey zone;
416   char *name;
417
418   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
419               "iterating for REVERSE (%s / %s)\n",
420               label,
421               mynick);
422
423
424   if ((rd_count != 1) ||
425       (GNUNET_GNSRECORD_TYPE_PKEY != rd->record_type))
426   {
427     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428                 "wrong format (%s)\n", mynick);
429
430
431     it_task = GNUNET_SCHEDULER_add_now (&next_it, ith);
432     return;
433   }
434   GNUNET_CRYPTO_ecdsa_key_get_public (key,
435                                       &zone);
436   if (0 != memcmp (&zone, &myzone,
437                    sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
438   {
439     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
440                 "wrong zone (%s)\n", mynick);
441
442
443     it_task = GNUNET_SCHEDULER_add_now (&next_it, ith);
444     return;
445   }
446   ith->target = *((struct GNUNET_CRYPTO_EcdsaPublicKey *) rd->data);
447   GNUNET_asprintf (&name,
448                   "%s.gnu",
449                   mynick);
450   gns_lookup_reverse = GNS_resolver_lookup (&ith->target,
451                                             GNUNET_GNSRECORD_TYPE_PKEY,
452                                             name,
453                                             NULL,
454                                             GNUNET_GNS_LO_DEFAULT,
455                                             &handle_gns_result_iter,
456                                             ith);
457   GNUNET_free (name);
458 }
459
460 static void check_reverse_records (void *cls);
461
462 static void
463 store_reverse (void *cls,
464                int32_t success,
465                const char *emsg)
466 {
467   struct IteratorHandle *ith = cls;
468   struct ReverseRecordEntry *rr;
469
470   if (GNUNET_SYSERR == success)
471   {
472     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
473                 "%s\n",
474                 emsg);
475   }
476   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stored records (%s)\n", mynick);
477
478   for (rr = ith->records_head; NULL != rr; rr = ith->records_head)
479   {
480     GNUNET_CONTAINER_DLL_remove (ith->records_head,
481                                  ith->records_tail,
482                                  rr);
483     GNUNET_free (rr->record);
484     GNUNET_free (rr);
485   }
486   reverse_record_check_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_DAYS,
487                                                             &check_reverse_records,
488                                                             NULL);
489   GNUNET_free (ith);
490 }
491
492
493 static void
494 finished_cb (void *cls)
495 {
496   struct IteratorHandle *ith = cls;
497   struct ReverseRecordEntry *rr;
498   struct GNUNET_GNSRECORD_Data rd[ith->record_count];
499
500   memset (rd, 0, sizeof (struct GNUNET_GNSRECORD_Data) * ith->record_count);
501
502   rr = ith->records_head;
503   for (int i = 0; i < ith->record_count; i++)
504   {
505     rd[i].data_size = rr->record_len;
506     rd[i].data = GNUNET_malloc (rr->record_len);
507     rd[i].record_type = GNUNET_GNSRECORD_TYPE_REVERSE;
508     rd[i].expiration_time = rr->record->expiration.abs_value_us;
509     GNUNET_memcpy ((char*) rd[i].data,
510                    rr->record,
511                    rr->record_len);
512     rr = rr->next;
513   }
514   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
515               "Finished iterating for REVERSE\n");
516
517   ith->ns_qe = GNUNET_NAMESTORE_records_store (ns,
518                                                &pzone,
519                                                "+",
520                                                ith->record_count,
521                                                rd,
522                                                &store_reverse,
523                                                ith);
524   namestore_iter = NULL;
525
526 }
527
528
529 static void
530 it_error (void *cls)
531 {
532   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
533               "Error iterating for REVERSE\n");
534 }
535
536
537 static void
538 check_reverse_records (void *cls)
539 {
540   struct IteratorHandle *ith;
541   ith = GNUNET_new (struct IteratorHandle);
542   ith->record_count = 0;
543   reverse_record_check_task = NULL;
544   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
545               "Start iterating for REVERSE (%s)\n", mynick);
546   namestore_iter = GNUNET_NAMESTORE_zone_iteration_start (ns,
547                                                           NULL,
548                                                           &it_error,
549                                                           ith,
550                                                           &iterator_cb,
551                                                           ith,
552                                                           &finished_cb,
553                                                           ith);
554 }
555
556
557 /**
558  * Initialize reverser
559  *
560  * @param nh handle to a namestore
561  * @param key the private key of the gns-reverse zone
562  * @param name the name of the gns-reverse zone
563  * @return GNUNET_OK
564  */
565 int
566 GNS_reverse_init (struct GNUNET_NAMESTORE_Handle *nh,
567                   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
568                   const char *nick)
569 {
570   GNUNET_asprintf (&mynick,
571                    "%s",
572                    nick);
573   GNUNET_CRYPTO_ecdsa_key_get_public (zone,
574                                       &myzone);
575   GNUNET_memcpy (&pzone,
576                  zone,
577                  sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
578   ns = nh;
579   reverse_record_check_task = GNUNET_SCHEDULER_add_now (&check_reverse_records,
580                                                         NULL);
581   return GNUNET_OK;
582 }
583
584 /**
585  * Cleanup reverser
586  */
587 void
588 GNS_reverse_done ()
589 {
590   if (NULL != mynick)
591     GNUNET_free (mynick);
592   if (NULL != it_task)
593     GNUNET_SCHEDULER_cancel (it_task);
594   if (NULL != reverse_record_check_task)
595     GNUNET_SCHEDULER_cancel (reverse_record_check_task);
596   if (NULL != gns_lookup_reverse)
597     GNS_resolver_lookup_cancel (gns_lookup_reverse);
598   if (NULL != namestore_iter)
599     GNUNET_NAMESTORE_zone_iteration_stop (namestore_iter);
600 }
601