eded50efcde080001fc6fb5d76faf30f86220e5d
[oweals/gnunet.git] / src / dht / dht_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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 dht/dht_api.c
23  * @brief library to access the DHT service
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  */
27
28 #include "platform.h"
29 #include "gnunet_bandwidth_lib.h"
30 #include "gnunet_client_lib.h"
31 #include "gnunet_constants.h"
32 #include "gnunet_container_lib.h"
33 #include "gnunet_arm_service.h"
34 #include "gnunet_hello_lib.h"
35 #include "gnunet_protocols.h"
36 #include "gnunet_server_lib.h"
37 #include "gnunet_time_lib.h"
38 #include "gnunet_dht_service.h"
39 #include "dht.h"
40
41 #define DEBUG_DHT_API GNUNET_NO
42
43 /**
44  * Entry in our list of messages to be (re-)transmitted.
45  */
46 struct PendingMessage
47 {
48   /**
49    * This is a doubly-linked list.
50    */
51   struct PendingMessage *prev;
52
53   /**
54    * This is a doubly-linked list.
55    */
56   struct PendingMessage *next;
57
58   /**
59    * Message that is pending, allocated at the end
60    * of this struct.
61    */
62   const struct GNUNET_MessageHeader *msg;
63
64   /**
65    * Handle to the DHT API context.
66    */
67   struct GNUNET_DHT_Handle *handle;
68
69   /**
70    * Continuation to call when the request has been
71    * transmitted (for the first time) to the service; can be NULL.
72    */
73   GNUNET_SCHEDULER_Task cont;
74
75   /**
76    * Closure for 'cont'.
77    */
78   void *cont_cls;
79
80   /**
81    * Timeout task for this message
82    */
83   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
84
85   /**
86    * Unique ID for this request
87    */
88   uint64_t unique_id;
89
90   /**
91    * Free the saved message once sent, set to GNUNET_YES for messages
92    * that do not receive responses; GNUNET_NO if this pending message
93    * is aliased from a 'struct GNUNET_DHT_RouteHandle' and will be freed
94    * from there.
95    */
96   int free_on_send;
97
98   /**
99    * GNUNET_YES if this message is in our pending queue right now.
100    */
101   int in_pending_queue;
102
103 };
104
105
106 /**
107  * Handle to a route request
108  */
109 struct GNUNET_DHT_RouteHandle
110 {
111
112   /**
113    * Iterator to call on data receipt
114    */
115   GNUNET_DHT_ReplyProcessor iter;
116
117   /**
118    * Closure for the iterator callback
119    */
120   void *iter_cls;
121
122   /**
123    * Main handle to this DHT api
124    */
125   struct GNUNET_DHT_Handle *dht_handle;
126
127   /**
128    * The actual message sent for this request,
129    * used for retransmitting requests on service
130    * failure/reconnect.  Freed on route_stop.
131    */
132   struct PendingMessage *message;
133
134   /**
135    * Key that this get request is for
136    */
137   GNUNET_HashCode key;
138
139   /**
140    * Unique identifier for this request (for key collisions). FIXME: redundant!?
141    */
142   uint64_t uid;
143
144 };
145
146
147 /**
148  * Connection to the DHT service.
149  */
150 struct GNUNET_DHT_Handle
151 {
152
153   /**
154    * Configuration to use.
155    */
156   const struct GNUNET_CONFIGURATION_Handle *cfg;
157
158   /**
159    * Socket (if available).
160    */
161   struct GNUNET_CLIENT_Connection *client;
162
163   /**
164    * Currently pending transmission request (or NULL).
165    */
166   struct GNUNET_CLIENT_TransmitHandle *th;
167
168   /**
169    * Head of linked list of messages we would like to transmit.
170    */
171   struct PendingMessage *pending_head;
172
173   /**
174    * Tail of linked list of messages we would like to transmit.
175    */
176   struct PendingMessage *pending_tail;
177
178   /**
179    * Hash map containing the current outstanding unique requests
180    * (values are of type 'struct GNUNET_DHT_RouteHandle').
181    */
182   struct GNUNET_CONTAINER_MultiHashMap *active_requests;
183
184   /**
185    * Task for trying to reconnect.
186    */
187   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
188
189   /**
190    * How quickly should we retry?  Used for exponential back-off on
191    * connect-errors.
192    */
193   struct GNUNET_TIME_Relative retry_time;
194
195   /**
196    * Generator for unique ids.
197    */
198   uint64_t uid_gen;
199
200 };
201
202
203 /**
204  * Transmit the next pending message, called by notify_transmit_ready
205  */
206 static size_t transmit_pending (void *cls, size_t size, void *buf);
207
208
209 /**
210  * Handler for messages received from the DHT service
211  * a demultiplexer which handles numerous message types
212  *
213  */
214 static void service_message_handler (void *cls,
215                                      const struct GNUNET_MessageHeader *msg);
216
217
218
219
220 /**
221  * Try to (re)connect to the DHT service.
222  *
223  * @return GNUNET_YES on success, GNUNET_NO on failure.
224  */
225 static int
226 try_connect (struct GNUNET_DHT_Handle *handle)
227 {
228   if (handle->client != NULL)
229     return GNUNET_OK;
230   handle->client = GNUNET_CLIENT_connect ("dht", handle->cfg);
231   if (handle->client == NULL)
232   {
233     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
234                 _("Failed to connect to the DHT service!\n"));
235     return GNUNET_NO;
236   }
237 #if DEBUG_DHT
238   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
239               "Starting to process replies from DHT\n");
240 #endif
241   GNUNET_CLIENT_receive (handle->client, &service_message_handler, handle,
242                          GNUNET_TIME_UNIT_FOREVER_REL);
243   return GNUNET_YES;
244 }
245
246
247 /**
248  * Add the request corresponding to the given route handle
249  * to the pending queue (if it is not already in there).
250  *
251  * @param cls the 'struct GNUNET_DHT_Handle*'
252  * @param key key for the request (not used)
253  * @param value the 'struct GNUNET_DHT_RouteHandle*'
254  * @return GNUNET_YES (always)
255  */
256 static int
257 add_request_to_pending (void *cls, const GNUNET_HashCode * key, void *value)
258 {
259   struct GNUNET_DHT_Handle *handle = cls;
260   struct GNUNET_DHT_RouteHandle *rh = value;
261
262   if (GNUNET_NO == rh->message->in_pending_queue)
263   {
264     GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
265                                  rh->message);
266     rh->message->in_pending_queue = GNUNET_YES;
267   }
268   return GNUNET_YES;
269 }
270
271
272 /**
273  * Try to send messages from list of messages to send
274  * @param handle DHT_Handle
275  */
276 static void process_pending_messages (struct GNUNET_DHT_Handle *handle);
277
278
279 /**
280  * Try reconnecting to the dht service.
281  *
282  * @param cls GNUNET_DHT_Handle
283  * @param tc scheduler context
284  */
285 static void
286 try_reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
287 {
288   struct GNUNET_DHT_Handle *handle = cls;
289
290   handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
291   if (handle->retry_time.rel_value < GNUNET_CONSTANTS_SERVICE_RETRY.rel_value)
292     handle->retry_time = GNUNET_CONSTANTS_SERVICE_RETRY;
293   else
294     handle->retry_time = GNUNET_TIME_relative_multiply (handle->retry_time, 2);
295   if (handle->retry_time.rel_value > GNUNET_CONSTANTS_SERVICE_TIMEOUT.rel_value)
296     handle->retry_time = GNUNET_CONSTANTS_SERVICE_TIMEOUT;
297   handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
298   handle->client = GNUNET_CLIENT_connect ("dht", handle->cfg);
299   if (handle->client == NULL)
300   {
301     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "dht reconnect failed(!)\n");
302     return;
303   }
304   GNUNET_CONTAINER_multihashmap_iterate (handle->active_requests,
305                                          &add_request_to_pending, handle);
306   process_pending_messages (handle);
307 }
308
309
310 /**
311  * Try reconnecting to the DHT service.
312  *
313  * @param handle handle to dht to (possibly) disconnect and reconnect
314  */
315 static void
316 do_disconnect (struct GNUNET_DHT_Handle *handle)
317 {
318   if (handle->client == NULL)
319     return;
320   GNUNET_assert (handle->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
321   GNUNET_CLIENT_disconnect (handle->client, GNUNET_NO);
322   handle->client = NULL;
323   handle->reconnect_task =
324       GNUNET_SCHEDULER_add_delayed (handle->retry_time, &try_reconnect, handle);
325 }
326
327
328 /**
329  * Try to send messages from list of messages to send
330  */
331 static void
332 process_pending_messages (struct GNUNET_DHT_Handle *handle)
333 {
334   struct PendingMessage *head;
335
336   if (handle->client == NULL)
337   {
338     do_disconnect (handle);
339     return;
340   }
341   if (handle->th != NULL)
342     return;
343   if (NULL == (head = handle->pending_head))
344     return;
345   handle->th =
346       GNUNET_CLIENT_notify_transmit_ready (handle->client,
347                                            ntohs (head->msg->size),
348                                            GNUNET_TIME_UNIT_FOREVER_REL,
349                                            GNUNET_YES, &transmit_pending,
350                                            handle);
351   if (NULL == handle->th)
352   {
353     do_disconnect (handle);
354     return;
355   }
356 }
357
358
359 /**
360  * Transmit the next pending message, called by notify_transmit_ready
361  */
362 static size_t
363 transmit_pending (void *cls, size_t size, void *buf)
364 {
365   struct GNUNET_DHT_Handle *handle = cls;
366   struct PendingMessage *head;
367   size_t tsize;
368
369   handle->th = NULL;
370   if (buf == NULL)
371   {
372     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
373                 "Transmission to DHT service failed!  Reconnecting!\n");
374     do_disconnect (handle);
375     return 0;
376   }
377   if (NULL == (head = handle->pending_head))
378     return 0;
379
380   tsize = ntohs (head->msg->size);
381   if (size < tsize)
382   {
383     process_pending_messages (handle);
384     return 0;
385   }
386   memcpy (buf, head->msg, tsize);
387   GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail,
388                                head);
389   if (head->timeout_task != GNUNET_SCHEDULER_NO_TASK)
390   {
391     GNUNET_SCHEDULER_cancel (head->timeout_task);
392     head->timeout_task = GNUNET_SCHEDULER_NO_TASK;
393   }
394   if (NULL != head->cont)
395   {
396     GNUNET_SCHEDULER_add_continuation (head->cont, head->cont_cls,
397                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
398     head->cont = NULL;
399     head->cont_cls = NULL;
400   }
401   head->in_pending_queue = GNUNET_NO;
402   if (GNUNET_YES == head->free_on_send)
403     GNUNET_free (head);
404   process_pending_messages (handle);
405   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
406               "Forwarded request of %u bytes to DHT service\n",
407               (unsigned int) tsize);
408   return tsize;
409 }
410
411
412 /**
413  * Process a given reply that might match the given
414  * request.
415  */
416 static int
417 process_reply (void *cls, const GNUNET_HashCode * key, void *value)
418 {
419   const struct GNUNET_DHT_RouteResultMessage *dht_msg = cls;
420   struct GNUNET_DHT_RouteHandle *rh = value;
421   const struct GNUNET_MessageHeader *enc_msg;
422   size_t enc_size;
423   uint64_t uid;
424   const struct GNUNET_PeerIdentity **outgoing_path;
425   const struct GNUNET_PeerIdentity *pos;
426   uint32_t outgoing_path_length;
427   unsigned int i;
428   char *path_offset;
429
430   uid = GNUNET_ntohll (dht_msg->unique_id);
431   if (uid != rh->uid)
432   {
433     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434                 "Reply UID did not match request UID\n");
435     return GNUNET_YES;
436   }
437   enc_msg = (const struct GNUNET_MessageHeader *) &dht_msg[1];
438   enc_size = ntohs (enc_msg->size);
439   if (enc_size < sizeof (struct GNUNET_MessageHeader))
440   {
441     GNUNET_break (0);
442     return GNUNET_NO;
443   }
444   path_offset = (char *) &dht_msg[1];
445   path_offset += enc_size;
446   pos = (const struct GNUNET_PeerIdentity *) path_offset;
447   outgoing_path_length = ntohl (dht_msg->outgoing_path_length);
448   if (outgoing_path_length * sizeof (struct GNUNET_PeerIdentity) >
449       ntohs (dht_msg->header.size) - enc_size)
450   {
451     GNUNET_break (0);
452     return GNUNET_NO;
453   }
454
455   if (outgoing_path_length > 0)
456   {
457     outgoing_path =
458         GNUNET_malloc ((outgoing_path_length +
459                         1) * sizeof (struct GNUNET_PeerIdentity *));
460     for (i = 0; i < outgoing_path_length; i++)
461     {
462       outgoing_path[i] = pos;
463       pos++;
464     }
465     outgoing_path[outgoing_path_length] = NULL;
466   }
467   else
468     outgoing_path = NULL;
469
470   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing reply.\n");
471   rh->iter (rh->iter_cls, &rh->key, outgoing_path, enc_msg);
472   GNUNET_free_non_null (outgoing_path);
473   return GNUNET_YES;
474 }
475
476
477 /**
478  * Handler for messages received from the DHT service
479  * a demultiplexer which handles numerous message types
480  *
481  * @param cls the 'struct GNUNET_DHT_Handle'
482  * @param msg the incoming message
483  */
484 static void
485 service_message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
486 {
487   struct GNUNET_DHT_Handle *handle = cls;
488   const struct GNUNET_DHT_RouteResultMessage *dht_msg;
489
490   if (msg == NULL)
491   {
492     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
493                 "Error receiving data from DHT service, reconnecting\n");
494     do_disconnect (handle);
495     return;
496   }
497   if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_DHT_LOCAL_ROUTE_RESULT)
498   {
499     GNUNET_break (0);
500     do_disconnect (handle);
501     return;
502   }
503   if (ntohs (msg->size) < sizeof (struct GNUNET_DHT_RouteResultMessage))
504   {
505     GNUNET_break (0);
506     do_disconnect (handle);
507     return;
508   }
509   dht_msg = (const struct GNUNET_DHT_RouteResultMessage *) msg;
510   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
511               "Comparing reply `%s' against %u pending requests.\n",
512               GNUNET_h2s (&dht_msg->key),
513               GNUNET_CONTAINER_multihashmap_size (handle->active_requests));
514   GNUNET_CONTAINER_multihashmap_get_multiple (handle->active_requests,
515                                               &dht_msg->key, &process_reply,
516                                               (void *) dht_msg);
517   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
518               "Continuing to process replies from DHT\n");
519   GNUNET_CLIENT_receive (handle->client, &service_message_handler, handle,
520                          GNUNET_TIME_UNIT_FOREVER_REL);
521
522 }
523
524
525 /**
526  * Initialize the connection with the DHT service.
527  *
528  * @param cfg configuration to use
529  * @param ht_len size of the internal hash table to use for
530  *               processing multiple GET/FIND requests in parallel
531  *
532  * @return handle to the DHT service, or NULL on error
533  */
534 struct GNUNET_DHT_Handle *
535 GNUNET_DHT_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
536                     unsigned int ht_len)
537 {
538   struct GNUNET_DHT_Handle *handle;
539
540   handle = GNUNET_malloc (sizeof (struct GNUNET_DHT_Handle));
541   handle->cfg = cfg;
542   handle->uid_gen =
543       GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
544   handle->active_requests = GNUNET_CONTAINER_multihashmap_create (ht_len);
545   if (GNUNET_NO == try_connect (handle))
546   {
547     GNUNET_DHT_disconnect (handle);
548     return NULL;
549   }
550   return handle;
551 }
552
553
554 /**
555  * Shutdown connection with the DHT service.
556  *
557  * @param handle handle of the DHT connection to stop
558  */
559 void
560 GNUNET_DHT_disconnect (struct GNUNET_DHT_Handle *handle)
561 {
562   struct PendingMessage *pm;
563
564   GNUNET_assert (handle != NULL);
565   GNUNET_assert (0 ==
566                  GNUNET_CONTAINER_multihashmap_size (handle->active_requests));
567   if (handle->th != NULL)
568   {
569     GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
570     handle->th = NULL;
571   }
572   while (NULL != (pm = handle->pending_head))
573   {
574     GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail,
575                                  pm);
576     GNUNET_assert (GNUNET_YES == pm->free_on_send);
577     if (GNUNET_SCHEDULER_NO_TASK != pm->timeout_task)
578       GNUNET_SCHEDULER_cancel (pm->timeout_task);
579     if (NULL != pm->cont)
580       GNUNET_SCHEDULER_add_continuation (pm->cont, pm->cont_cls,
581                                          GNUNET_SCHEDULER_REASON_TIMEOUT);
582     pm->in_pending_queue = GNUNET_NO;
583     GNUNET_free (pm);
584   }
585   if (handle->client != NULL)
586   {
587     GNUNET_CLIENT_disconnect (handle->client, GNUNET_YES);
588     handle->client = NULL;
589   }
590   if (handle->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
591     GNUNET_SCHEDULER_cancel (handle->reconnect_task);
592   GNUNET_CONTAINER_multihashmap_destroy (handle->active_requests);
593   GNUNET_free (handle);
594 }
595
596
597
598
599 /* ***** Special low-level API providing generic routing abstraction ***** */
600
601
602 /**
603  * Timeout for the transmission of a fire&forget-request.  Clean it up.
604  *
605  * @param cls the 'struct PendingMessage'
606  * @param tc scheduler context
607  */
608 static void
609 timeout_route_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
610 {
611   struct PendingMessage *pending = cls;
612   struct GNUNET_DHT_Handle *handle;
613
614   if (pending->free_on_send != GNUNET_YES)
615   {
616     /* timeouts should only apply to fire & forget requests! */
617     GNUNET_break (0);
618     return;
619   }
620   handle = pending->handle;
621   GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail,
622                                pending);
623   if (pending->cont != NULL)
624     pending->cont (pending->cont_cls, tc);
625   GNUNET_free (pending);
626 }
627
628
629 /**
630  * Initiate a generic DHT route operation.
631  *
632  * @param handle handle to the DHT service
633  * @param key the key to look up
634  * @param desired_replication_level how many peers should ultimately receive
635  *                this message (advisory only, target may be too high for the
636  *                given DHT or not hit exactly).
637  * @param options options for routing
638  * @param enc send the encapsulated message to a peer close to the key
639  * @param iter function to call on each result, NULL if no replies are expected
640  * @param iter_cls closure for iter
641  * @param timeout when to abort with an error if we fail to get
642  *                a confirmation for the request (when necessary) or how long
643  *                to wait for tramission to the service; only applies
644  *                if 'iter' is NULL
645  * @param cont continuation to call when the request has been transmitted
646  *             the first time to the service
647  * @param cont_cls closure for cont
648  * @return handle to stop the request, NULL if the request is "fire and forget"
649  */
650 struct GNUNET_DHT_RouteHandle *
651 GNUNET_DHT_route_start (struct GNUNET_DHT_Handle *handle,
652                         const GNUNET_HashCode * key,
653                         uint32_t desired_replication_level,
654                         enum GNUNET_DHT_RouteOption options,
655                         const struct GNUNET_MessageHeader *enc,
656                         struct GNUNET_TIME_Relative timeout,
657                         GNUNET_DHT_ReplyProcessor iter, void *iter_cls,
658                         GNUNET_SCHEDULER_Task cont, void *cont_cls)
659 {
660   struct PendingMessage *pending;
661   struct GNUNET_DHT_RouteMessage *message;
662   struct GNUNET_DHT_RouteHandle *route_handle;
663   uint16_t msize;
664   uint16_t esize;
665
666   esize = ntohs (enc->size);
667   if (sizeof (struct GNUNET_DHT_RouteMessage) + esize >=
668       GNUNET_SERVER_MAX_MESSAGE_SIZE)
669   {
670     GNUNET_break (0);
671     return NULL;
672   }
673   msize = sizeof (struct GNUNET_DHT_RouteMessage) + esize;
674   pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
675   message = (struct GNUNET_DHT_RouteMessage *) &pending[1];
676   pending->msg = &message->header;
677   pending->handle = handle;
678   pending->cont = cont;
679   pending->cont_cls = cont_cls;
680
681   message->header.size = htons (msize);
682   message->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_LOCAL_ROUTE);
683   message->options = htonl ((uint32_t) options);
684   message->desired_replication_level = htonl (desired_replication_level);
685   message->reserved = 0;
686   message->key = *key;
687   handle->uid_gen++;
688   message->unique_id = GNUNET_htonll (handle->uid_gen);
689   memcpy (&message[1], enc, esize);
690
691   if (iter != NULL)
692   {
693     route_handle = GNUNET_malloc (sizeof (struct GNUNET_DHT_RouteHandle));
694     route_handle->key = *key;
695     route_handle->iter = iter;
696     route_handle->iter_cls = iter_cls;
697     route_handle->dht_handle = handle;
698     route_handle->uid = handle->uid_gen;
699     route_handle->message = pending;
700     GNUNET_CONTAINER_multihashmap_put (handle->active_requests, key,
701                                        route_handle,
702                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
703   }
704   else
705   {
706     route_handle = NULL;
707     pending->free_on_send = GNUNET_YES;
708     pending->timeout_task =
709         GNUNET_SCHEDULER_add_delayed (timeout, &timeout_route_request, pending);
710   }
711   GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
712                                pending);
713   pending->in_pending_queue = GNUNET_YES;
714   process_pending_messages (handle);
715   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
716               "DHT route start request processed, returning %p\n",
717               route_handle);
718   return route_handle;
719 }
720
721
722 /**
723  * Stop a previously issued routing request
724  *
725  * @param route_handle handle to the request to stop
726  */
727 void
728 GNUNET_DHT_route_stop (struct GNUNET_DHT_RouteHandle *route_handle)
729 {
730   struct GNUNET_DHT_Handle *handle;
731   struct PendingMessage *pending;
732   struct GNUNET_DHT_StopMessage *message;
733   size_t msize;
734
735   handle = route_handle->dht_handle;
736   if (GNUNET_NO == route_handle->message->in_pending_queue)
737   {
738     /* need to send stop message */
739     msize = sizeof (struct GNUNET_DHT_StopMessage);
740     pending = GNUNET_malloc (sizeof (struct PendingMessage) + msize);
741     message = (struct GNUNET_DHT_StopMessage *) &pending[1];
742     pending->msg = &message->header;
743     message->header.size = htons (msize);
744     message->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_LOCAL_ROUTE_STOP);
745     message->reserved = 0;
746     message->unique_id = GNUNET_htonll (route_handle->uid);
747     message->key = route_handle->key;
748     pending->handle = handle;
749     pending->free_on_send = GNUNET_YES;
750     pending->in_pending_queue = GNUNET_YES;
751     GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
752                                  pending);
753     process_pending_messages (handle);
754   }
755   else
756   {
757     /* simply remove pending request from message queue before
758      * transmission, no need to transmit STOP request! */
759     GNUNET_CONTAINER_DLL_remove (handle->pending_head, handle->pending_tail,
760                                  route_handle->message);
761   }
762   GNUNET_assert (GNUNET_YES ==
763                  GNUNET_CONTAINER_multihashmap_remove (route_handle->
764                                                        dht_handle->
765                                                        active_requests,
766                                                        &route_handle->key,
767                                                        route_handle));
768   GNUNET_free (route_handle->message);
769   GNUNET_free (route_handle);
770   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "DHT route stop request processed\n");
771 }
772
773
774
775 /* ***** Special API for controlling DHT routing maintenance ******* */
776
777
778 /**
779  * Send a control message to the DHT.
780  *
781  * @param handle handle to the DHT service
782  * @param command command
783  * @param variable variable to the command
784  * @param cont continuation to call when done (transmitting request to service)
785  * @param cont_cls closure for cont
786  */
787 static void
788 send_control_message (struct GNUNET_DHT_Handle *handle, uint16_t command,
789                       uint16_t variable, GNUNET_SCHEDULER_Task cont,
790                       void *cont_cls)
791 {
792   struct GNUNET_DHT_ControlMessage *msg;
793   struct PendingMessage *pending;
794
795   pending =
796       GNUNET_malloc (sizeof (struct PendingMessage) +
797                      sizeof (struct GNUNET_DHT_ControlMessage));
798   msg = (struct GNUNET_DHT_ControlMessage *) &pending[1];
799   pending->msg = &msg->header;
800   msg->header.size = htons (sizeof (struct GNUNET_DHT_ControlMessage));
801   msg->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_CONTROL);
802   msg->command = htons (command);
803   msg->variable = htons (variable);
804   pending->free_on_send = GNUNET_YES;
805   pending->cont = cont;
806   pending->cont_cls = cont_cls;
807   pending->in_pending_queue = GNUNET_YES;
808   GNUNET_CONTAINER_DLL_insert (handle->pending_head, handle->pending_tail,
809                                pending);
810   process_pending_messages (handle);
811 }
812
813
814 /**
815  * Send a message to the DHT telling it to issue a single find
816  * peer request using the peers unique identifier as key.  This
817  * is used to fill the routing table, and is normally controlled
818  * by the DHT itself.  However, for testing and perhaps more
819  * close control over the DHT, this can be explicitly managed.
820  *
821  * @param handle handle to the DHT service
822  * @param cont continuation to call when done (transmitting request to service)
823  * @param cont_cls closure for cont
824  */
825 void
826 GNUNET_DHT_find_peers (struct GNUNET_DHT_Handle *handle,
827                        GNUNET_SCHEDULER_Task cont, void *cont_cls)
828 {
829   send_control_message (handle, GNUNET_MESSAGE_TYPE_DHT_FIND_PEER, 0, cont,
830                         cont_cls);
831 }
832
833
834
835 #if HAVE_MALICIOUS
836
837 /**
838  * Send a message to the DHT telling it to start issuing random GET
839  * requests every 'frequency' milliseconds.
840  *
841  * @param handle handle to the DHT service
842  * @param frequency delay between sending malicious messages
843  * @param cont continuation to call when done (transmitting request to service)
844  * @param cont_cls closure for cont
845  */
846 void
847 GNUNET_DHT_set_malicious_getter (struct GNUNET_DHT_Handle *handle,
848                                  struct GNUNET_TIME_Relative frequency,
849                                  GNUNET_SCHEDULER_Task cont, void *cont_cls)
850 {
851   if (frequency.rel_value > UINT16_MAX)
852   {
853     GNUNET_break (0);
854     return;
855   }
856   send_control_message (handle, GNUNET_MESSAGE_TYPE_DHT_MALICIOUS_GET,
857                         frequency.rel_value, cont, cont_cls);
858 }
859
860 /**
861  * Send a message to the DHT telling it to start issuing random PUT
862  * requests every 'frequency' milliseconds.
863  *
864  * @param handle handle to the DHT service
865  * @param frequency delay between sending malicious messages
866  * @param cont continuation to call when done (transmitting request to service)
867  * @param cont_cls closure for cont
868  */
869 void
870 GNUNET_DHT_set_malicious_putter (struct GNUNET_DHT_Handle *handle,
871                                  struct GNUNET_TIME_Relative frequency,
872                                  GNUNET_SCHEDULER_Task cont, void *cont_cls)
873 {
874   if (frequency.rel_value > UINT16_MAX)
875   {
876     GNUNET_break (0);
877     return;
878   }
879
880   send_control_message (handle, GNUNET_MESSAGE_TYPE_DHT_MALICIOUS_PUT,
881                         frequency.rel_value, cont, cont_cls);
882 }
883
884
885 /**
886  * Send a message to the DHT telling it to start dropping
887  * all requests received.
888  *
889  * @param handle handle to the DHT service
890  * @param cont continuation to call when done (transmitting request to service)
891  * @param cont_cls closure for cont
892  *
893  */
894 void
895 GNUNET_DHT_set_malicious_dropper (struct GNUNET_DHT_Handle *handle,
896                                   GNUNET_SCHEDULER_Task cont, void *cont_cls)
897 {
898   send_control_message (handle, GNUNET_MESSAGE_TYPE_DHT_MALICIOUS_DROP, 0, cont,
899                         cont_cls);
900 }
901
902 #endif
903
904 /* end of dht_api.c */