-add 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
51 struct IteratorHandle
52 {
53   /**
54    * Records found
55    */
56   struct ReverseRecordEntry *records_head;
57
58   /**
59    * Records found
60    */
61   struct ReverseRecordEntry *records_tail;
62
63   /**
64    * Current delegation to expect
65    */
66   struct GNUNET_CRYPTO_EcdsaPublicKey target;
67
68   /**
69    * The zone target for reverse record resolution
70    */
71   struct GNUNET_CRYPTO_EcdsaPublicKey myzone;
72
73   /**
74    * The nick of our zone
75    */
76   char *mynick;
77
78 };
79
80 struct ReverseTreeNode
81 {
82   /**
83    * DLL
84    */
85   struct ReverseTreeNode *next;
86
87   /**
88    * DLL
89    */
90   struct ReverseTreeNode *prev;
91
92   /**
93    * Resolved name until now
94    */
95   char *name;
96
97   /**
98    * Depth of the resolution at this node
99    */
100   uint8_t depth;
101
102   /**
103    * The pkey of the namespace
104    */
105   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
106
107 };
108
109
110 struct GNS_ReverserHandle
111 {
112   /**
113    * GNS resolver handle
114    */
115   struct GNS_ResolverHandle *rh;
116
117   /**
118    * The authority to look for
119    */
120   struct GNUNET_CRYPTO_EcdsaPublicKey authority;
121
122   /**
123    * Resolution candidate queue
124    */
125   struct ReverseTreeNode *node_queue_head;
126
127   /**
128    * Resolution candidate queue
129    */
130   struct ReverseTreeNode *node_queue_tail;
131
132   /**
133    * Max depth for the resolution
134    */
135   uint8_t max_depth;
136
137   /**
138    * Result callback
139    */
140   GNS_ReverseResultProcessor proc;
141
142   /**
143    * Callback closure
144    */
145   void *proc_cls;
146 };
147
148 /**
149  * Reverse record collection task
150  */
151 static struct GNUNET_SCHEDULER_Task *reverse_record_check_task;
152
153 /**
154  * GNS lookup handle
155  */
156 static struct GNS_ResolverHandle *gns_lookup_reverse;
157
158 /**
159  * NS handle
160  */
161 static struct GNUNET_NAMESTORE_Handle *ns;
162
163 /**
164  * NS Iterator
165  */
166 static struct GNUNET_NAMESTORE_ZoneIterator *namestore_iter;
167
168 void
169 cleanup_handle (struct GNS_ReverserHandle *rh)
170 {
171   struct ReverseTreeNode *rtn;
172
173   for (rtn = rh->node_queue_head; NULL != rtn; rtn = rh->node_queue_head)
174   {
175     if (NULL != rtn->name)
176       GNUNET_free (rtn->name);
177     GNUNET_CONTAINER_DLL_remove (rh->node_queue_head,
178                                  rh->node_queue_tail,
179                                  rtn);
180     GNUNET_free (rtn);
181   }
182 }
183
184 void
185 handle_gns_result (void *cls,
186                    uint32_t rd_count,
187                    const struct GNUNET_GNSRECORD_Data *rd)
188 {
189   struct GNS_ReverserHandle *rh = cls;
190   const struct GNUNET_GNSRECORD_ReverseRecord *rr;
191   struct ReverseTreeNode *rtn;
192   char *result;
193   const char *name;
194   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
195               "Got result (%d)\n", rd_count);
196
197   for (int i = 0; i < rd_count; i++)
198   {
199     /**
200      * Check if we are in the delegation set
201      */
202     if (GNUNET_GNSRECORD_TYPE_REVERSE != rd[i].record_type)
203       continue;
204     rr = rd[i].data;
205     name = (const char*) &rr[1];
206     if (0 == memcmp (&rh->authority,
207                      &rr->pkey,
208                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
209     {
210       //Found!
211       GNUNET_asprintf (&result,
212                        "%s.%s.gnu",
213                        rh->node_queue_head->name,
214                        name);
215       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
216                   "Found path from %s\n", result);
217
218       rh->proc (rh->proc_cls, result);
219       cleanup_handle (rh);
220       GNUNET_free (result);
221       return;
222     } else {
223       if (rh->node_queue_head->depth >= rh->max_depth)
224         break;
225       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
226                   "Found REVERSE from %s\n", name);
227
228       rtn = GNUNET_new (struct ReverseTreeNode);
229       if (NULL == rh->node_queue_head->name)
230         rtn->name = GNUNET_strdup (name);
231       else
232         GNUNET_asprintf (&rtn->name,
233                          "%s.%s",
234                          rh->node_queue_head->name,
235                          name);
236       rtn->depth = rh->node_queue_head->depth + 1;
237       rtn->pkey = rr->pkey;
238       GNUNET_CONTAINER_DLL_insert_tail (rh->node_queue_head,
239                                         rh->node_queue_tail,
240                                         rtn);
241     }
242   }
243
244   /**
245    * Done here remove node from queue
246    */
247   rtn = rh->node_queue_head;
248   GNUNET_CONTAINER_DLL_remove (rh->node_queue_head,
249                                rh->node_queue_tail,
250                                rtn);
251   if (NULL == rh->node_queue_head)
252   {
253     //No luck
254     rh->proc (rh->proc_cls, NULL);
255     cleanup_handle (rh);
256     return;
257   }
258   rh->rh = GNS_resolver_lookup (&rh->node_queue_head->pkey,
259                                 GNUNET_GNSRECORD_TYPE_REVERSE,
260                                 "+.gnu",
261                                 NULL,
262                                 GNUNET_GNS_LO_DEFAULT,
263                                 &handle_gns_result,
264                                 rh);
265 }
266
267 struct GNS_ReverserHandle *
268 GNS_reverse_lookup (const struct GNUNET_CRYPTO_EcdsaPublicKey *target,
269                     const struct GNUNET_CRYPTO_EcdsaPublicKey *authority,
270                     GNS_ReverseResultProcessor proc,
271                     void *proc_cls)
272 {
273   struct GNS_ReverserHandle *rh;
274   struct ReverseTreeNode *rtn;
275
276   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
277               "Starting reverse resolution\n");
278   rh = GNUNET_new (struct GNS_ReverserHandle);
279   rh->proc = proc;
280   rh->proc_cls = proc_cls;
281   rtn = GNUNET_new (struct ReverseTreeNode);
282   rtn->name = NULL;
283   rtn->pkey = *target;
284   rtn->depth = 0;
285   GNUNET_CONTAINER_DLL_insert (rh->node_queue_head,
286                                rh->node_queue_tail,
287                                rtn);
288   rh->authority = *authority;
289   rh->max_depth = 3; //TODO make argument
290   rh->rh = GNS_resolver_lookup (target,
291                                 GNUNET_GNSRECORD_TYPE_REVERSE,
292                                 "+.gnu",
293                                 NULL,
294                                 GNUNET_GNS_LO_DEFAULT,
295                                 &handle_gns_result,
296                                 rh);
297   return rh;
298 }
299
300 /**
301  * Cancel active resolution (i.e. client disconnected).
302  *
303  * @param rh resolution to abort
304  */
305 void
306 GNS_reverse_lookup_cancel (struct GNS_ReverserHandle *rh)
307 {
308   cleanup_handle (rh);
309   return;
310 }
311
312 void
313 next_it (void *cls);
314
315 void
316 handle_gns_result_iter (void *cls,
317                         uint32_t rd_count,
318                         const struct GNUNET_GNSRECORD_Data *rd)
319 {
320   struct IteratorHandle *ith = cls;
321   struct ReverseRecordEntry *rr;
322   if ((rd_count != 1) ||
323       (GNUNET_GNSRECORD_TYPE_PKEY != rd->record_type))
324   {
325     gns_lookup_reverse = NULL;
326     GNUNET_SCHEDULER_add_now (&next_it, NULL);
327     return;
328   }
329
330
331   rr = GNUNET_new (struct ReverseRecordEntry);
332   rr->record = GNUNET_malloc (sizeof (struct GNUNET_GNSRECORD_ReverseRecord)
333                               + strlen (ith->mynick) + 1);
334   rr->record->pkey = ith->target;
335   rr->record->expiration.abs_value_us = rd->expiration_time;
336   GNUNET_memcpy ((char*)&rr->record[1],
337                  ith->mynick,
338                  strlen (ith->mynick));
339   GNUNET_CONTAINER_DLL_insert (ith->records_head,
340                                ith->records_tail,
341                                rr);
342   GNUNET_SCHEDULER_add_now (&next_it, NULL);
343 }
344
345 void
346 next_it (void *cls)
347 {
348   GNUNET_assert (NULL != namestore_iter);
349   GNUNET_NAMESTORE_zone_iterator_next (namestore_iter);
350 }
351
352 void
353 iterator_cb (void *cls,
354              const struct GNUNET_CRYPTO_EcdsaPrivateKey *key,
355              const char *label,
356              unsigned int rd_count,
357              const struct GNUNET_GNSRECORD_Data *rd)
358 {
359   struct IteratorHandle *ith = cls;
360   struct GNUNET_CRYPTO_EcdsaPublicKey *target;
361   struct GNUNET_CRYPTO_EcdsaPublicKey zone;
362
363   if ((rd_count != 1) ||
364       (GNUNET_GNSRECORD_TYPE_PKEY != rd->record_type))
365   {
366     GNUNET_SCHEDULER_add_now (&next_it, NULL);
367     return;
368   }
369   GNUNET_CRYPTO_ecdsa_key_get_public (key,
370                                       &zone);
371   if (0 != memcmp (&zone, &ith->myzone,
372                    sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
373   {
374     GNUNET_SCHEDULER_add_now (&next_it, NULL);
375     return;
376   }
377   target = (struct GNUNET_CRYPTO_EcdsaPublicKey *) rd->data;
378   gns_lookup_reverse = GNS_resolver_lookup (target,
379                                             GNUNET_GNSRECORD_TYPE_PKEY,
380                                             ith->mynick,
381                                             NULL,
382                                             GNUNET_GNS_LO_DEFAULT,
383                                             &handle_gns_result_iter,
384                                             ith);
385 }
386
387 void check_reverse_records (void *cls);
388
389 void
390 finished_cb (void *cls)
391 {
392   struct IteratorHandle *ith = cls;
393   struct ReverseRecordEntry *rr;
394
395   //TODO add results to namestore!
396   for (rr = ith->records_head; NULL != rr; rr = ith->records_head)
397   {
398     GNUNET_CONTAINER_DLL_remove (ith->records_head,
399                                  ith->records_tail,
400                                  rr);
401     GNUNET_free (rr->record);
402     GNUNET_free (rr);
403   }
404   reverse_record_check_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_DAYS,
405                                                             &check_reverse_records,
406                                                             NULL);
407
408 }
409
410 void
411 it_error (void *cls)
412 {
413   finished_cb (cls);
414 }
415
416 void
417 check_reverse_records (void *cls)
418 {
419   struct IteratorHandle *ith = cls;
420   namestore_iter = GNUNET_NAMESTORE_zone_iteration_start (ns,
421                                                           NULL,
422                                                           &it_error,
423                                                           ith,
424                                                           &iterator_cb,
425                                                           ith,
426                                                           &finished_cb,
427                                                           ith);
428 }
429
430 void
431 GNS_reverse_init (const struct GNUNET_CONFIGURATION_Handle *c,
432                   const struct GNUNET_NAMESTORE_Handle *nh,
433                   const struct GNUNET_CRYPTO_EcdsaPublicKey *myzone,
434                   const char *mynick)
435 {
436   struct IteratorHandle *ith;
437
438   ns = ns;
439   ith = GNUNET_new (struct IteratorHandle);
440   ith->mynick = GNUNET_strdup (mynick);
441   ith->myzone = *myzone;
442   reverse_record_check_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_DAYS,
443                                                             &check_reverse_records,
444                                                             NULL);
445 }
446