-towards a DV test
[oweals/gnunet.git] / src / dv / dv_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009--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 dv/dv_api.c
23  * @brief library to access the DV service
24  * @author Christian Grothoff
25  * @author Nathan Evans
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_dv_service.h"
30 #include "gnunet_protocols.h"
31 #include "dv.h"
32 #include "gnunet_transport_plugin.h"
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "dv-api",__VA_ARGS__)
35
36
37 /**
38  * Handle for a send operation.
39  */
40 struct GNUNET_DV_TransmitHandle
41 {
42   /**
43    * Kept in a DLL.
44    */
45   struct GNUNET_DV_TransmitHandle *next;
46
47   /**
48    * Kept in a DLL.
49    */
50   struct GNUNET_DV_TransmitHandle *prev;
51
52   /**
53    * Handle to the service.
54    */
55   struct GNUNET_DV_ServiceHandle *sh;
56
57   /**
58    * Function to call upon completion.
59    */
60   GNUNET_DV_MessageSentCallback cb;
61
62   /**
63    * Closure for 'cb'.
64    */
65   void *cb_cls;
66   
67   /**
68    * The actual message (allocated at the end of this struct).
69    */
70   const struct GNUNET_MessageHeader *msg;
71
72   /**
73    * Destination for the message.
74    */
75   struct GNUNET_PeerIdentity target;
76
77   /**
78    * UID of our message, if any.
79    */
80   uint32_t uid;
81   
82 };
83
84
85 /**
86  * Handle to the DV service.
87  */
88 struct GNUNET_DV_ServiceHandle
89 {
90
91   /**
92    * Connection to DV service.
93    */
94   struct GNUNET_CLIENT_Connection *client;
95
96   /**
97    * Active request for transmission to DV service.
98    */
99   struct GNUNET_CLIENT_TransmitHandle *th;
100
101   /**
102    * Our configuration.
103    */
104   const struct GNUNET_CONFIGURATION_Handle *cfg;
105
106   /**
107    * Closure for the callbacks.
108    */
109   void *cls;
110   
111   /**
112    * Function to call on connect events.
113    */
114   GNUNET_DV_ConnectCallback connect_cb;
115
116   /**
117    * Function to call on distance change events.
118    */
119   GNUNET_DV_DistanceChangedCallback distance_cb;
120
121   /**
122    * Function to call on disconnect events.
123    */
124   GNUNET_DV_DisconnectCallback disconnect_cb;
125
126   /**
127    * Function to call on receiving messages events.
128    */
129   GNUNET_DV_MessageReceivedCallback message_cb;
130
131   /**
132    * Head of messages to transmit.
133    */
134   struct GNUNET_DV_TransmitHandle *th_head;
135
136   /**
137    * Tail of messages to transmit.
138    */
139   struct GNUNET_DV_TransmitHandle *th_tail;
140
141   /**
142    * Mapping of peer identities to TransmitHandles to invoke
143    * upon successful transmission.  The respective
144    * transmissions have already been done.
145    */
146   struct GNUNET_CONTAINER_MultiHashMap *send_callbacks;
147
148   /**
149    * Current unique ID
150    */
151   uint32_t uid_gen;
152
153 };
154
155
156 /**
157  * Disconnect and then reconnect to the DV service.
158  *
159  * @param sh service handle
160  */
161 static void
162 reconnect (struct GNUNET_DV_ServiceHandle *sh);
163
164
165 /**
166  * Gives a message from our queue to the DV service.
167  *
168  * @param cls handle to the dv service (struct GNUNET_DV_ServiceHandle)
169  * @param size how many bytes can we send
170  * @param buf where to copy the message to send
171  * @return how many bytes we copied to buf
172  */
173 static size_t
174 transmit_pending (void *cls, size_t size, void *buf)
175 {
176   struct GNUNET_DV_ServiceHandle *sh = cls;
177   char *cbuf = buf;
178   struct GNUNET_DV_TransmitHandle *th;
179   size_t ret;
180   size_t tsize;
181
182   sh->th = NULL;
183   if (NULL == buf)
184   {
185     reconnect (sh);
186     return 0;
187   }
188   ret = 0;
189   while ( (NULL != (th = sh->th_head)) &&
190           (size - ret >= (tsize = ntohs (th->msg->size)) ))
191   {
192     GNUNET_CONTAINER_DLL_remove (sh->th_head,
193                                  sh->th_tail,
194                                  th);
195     memcpy (&cbuf[ret], th->msg, tsize);
196     ret += tsize;
197     if (NULL != th->cb)
198     {
199       (void) GNUNET_CONTAINER_multihashmap_put (sh->send_callbacks,
200                                                 &th->target.hashPubKey,
201                                                 th,
202                                                 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
203     }
204     else
205     {
206       GNUNET_free (th);
207     }
208   }
209   return ret;
210 }
211
212
213 /**
214  * Start sending messages from our queue to the service.
215  *
216  * @param sh service handle
217  */
218 static void
219 start_transmit (struct GNUNET_DV_ServiceHandle *sh)
220 {
221   if (NULL != sh->th)
222     return;
223   if (NULL == sh->th_head)
224     return; 
225   sh->th =
226     GNUNET_CLIENT_notify_transmit_ready (sh->client,
227                                          ntohs (sh->th_head->msg->size),
228                                          GNUNET_TIME_UNIT_FOREVER_REL,
229                                          GNUNET_NO, 
230                                          &transmit_pending, sh);
231 }
232
233
234 /**
235  * Closure for 'process_ack'.
236  */
237 struct AckContext
238 {
239   /**
240    * The ACK message.
241    */
242   const struct GNUNET_DV_AckMessage *ack;
243
244   /**
245    * Our service handle.
246    */
247   struct GNUNET_DV_ServiceHandle *sh;
248 };
249
250
251 /**
252  * We got an ACK.  Check if it matches the given transmit handle, and if
253  * so call the continuation.
254  *
255  * @param cls the 'struct AckContext'
256  * @param key peer identity
257  * @param value the 'struct GNUNET_DV_TransmitHandle'
258  * @return GNUNET_OK if the ACK did not match (continue to iterate)
259  */
260 static int
261 process_ack (void *cls,
262              const struct GNUNET_HashCode *key,
263              void *value)
264 {
265   struct AckContext *ctx = cls;
266   struct GNUNET_DV_TransmitHandle *th = value;
267
268   if (th->uid != ntohl (ctx->ack->uid))
269     return GNUNET_OK;
270   LOG (GNUNET_ERROR_TYPE_DEBUG,
271        "Matchedk ACK for message to peer %s\n",
272        GNUNET_h2s (key));
273   GNUNET_assert (GNUNET_YES ==
274                  GNUNET_CONTAINER_multihashmap_remove (ctx->sh->send_callbacks,
275                                                        key,
276                                                        th));
277   th->cb (th->cb_cls,
278           (ntohs (ctx->ack->header.type) == GNUNET_MESSAGE_TYPE_DV_SEND_ACK)
279           ? GNUNET_OK
280           : GNUNET_SYSERR);
281   GNUNET_free (th);
282   return GNUNET_NO;
283 }
284
285
286 /**
287  * Handles a message sent from the DV service to us.
288  * Parse it out and give it to the plugin.
289  *
290  * @param cls the handle to the DV API
291  * @param msg the message that was received
292  */
293 static void
294 handle_message_receipt (void *cls, 
295                         const struct GNUNET_MessageHeader *msg)
296 {
297   struct GNUNET_DV_ServiceHandle *sh = cls;
298   const struct GNUNET_DV_ConnectMessage *cm;
299   const struct GNUNET_DV_DistanceUpdateMessage *dum;
300   const struct GNUNET_DV_DisconnectMessage *dm;
301   const struct GNUNET_DV_ReceivedMessage *rm;
302   const struct GNUNET_MessageHeader *payload;
303   const struct GNUNET_DV_AckMessage *ack;
304   struct AckContext ctx;
305   
306   if (NULL == msg)
307   {
308     /* Connection closed */
309     reconnect (sh);
310     return;
311   }
312   LOG (GNUNET_ERROR_TYPE_DEBUG,
313        "Received message of type %u from DV service\n",
314        (unsigned int) msg->type);
315   switch (ntohs (msg->type))
316   {
317   case GNUNET_MESSAGE_TYPE_DV_CONNECT:
318     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ConnectMessage))
319     {
320       GNUNET_break (0);
321       reconnect (sh);
322       return;
323     }
324     cm = (const struct GNUNET_DV_ConnectMessage *) msg;
325     sh->connect_cb (sh->cls,
326                     &cm->peer,
327                     ntohl (cm->distance));
328     break;
329   case GNUNET_MESSAGE_TYPE_DV_DISTANCE_CHANGED:
330     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DistanceUpdateMessage))
331     {
332       GNUNET_break (0);
333       reconnect (sh);
334       return;
335     }
336     dum = (const struct GNUNET_DV_DistanceUpdateMessage *) msg;
337     sh->distance_cb (sh->cls,
338                      &dum->peer,
339                      ntohl (dum->distance));
340     break;
341   case GNUNET_MESSAGE_TYPE_DV_DISCONNECT:
342     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DisconnectMessage))
343     {
344       GNUNET_break (0);
345       reconnect (sh);
346       return;
347     }
348     dm = (const struct GNUNET_DV_DisconnectMessage *) msg;
349     sh->disconnect_cb (sh->cls,
350                        &dm->peer);
351     break;
352   case GNUNET_MESSAGE_TYPE_DV_RECV:
353     if (ntohs (msg->size) < sizeof (struct GNUNET_DV_ReceivedMessage) + sizeof (struct GNUNET_MessageHeader))
354     {
355       GNUNET_break (0);
356       reconnect (sh);
357       return;
358     }
359     rm = (const struct GNUNET_DV_ReceivedMessage *) msg;
360     payload = (const struct GNUNET_MessageHeader *) &rm[1];
361     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ReceivedMessage) + ntohs (payload->size))
362     {
363       GNUNET_break (0);
364       reconnect (sh);
365       return;
366     }
367     sh->message_cb (sh->cls,
368                     &rm->sender,
369                     ntohl (rm->distance),
370                     payload);
371     break;
372   case GNUNET_MESSAGE_TYPE_DV_SEND_ACK:
373   case GNUNET_MESSAGE_TYPE_DV_SEND_NACK:
374     if (ntohs (msg->size) != sizeof (struct GNUNET_DV_AckMessage))
375     {
376       GNUNET_break (0);
377       reconnect (sh);
378       return;
379     }
380     ack = (const struct GNUNET_DV_AckMessage *) msg;
381     ctx.ack = ack;
382     ctx.sh = sh;
383     GNUNET_CONTAINER_multihashmap_get_multiple (sh->send_callbacks,
384                                                 &ack->target.hashPubKey,
385                                                 &process_ack,
386                                                 &ctx);
387     break;
388   default:
389     reconnect (sh);
390     break;
391   }
392   GNUNET_CLIENT_receive (sh->client, 
393                          &handle_message_receipt, sh,
394                          GNUNET_TIME_UNIT_FOREVER_REL);
395 }
396
397
398 /**
399  * Transmit the start message to the DV service.
400  *
401  * @param cls the 'struct GNUNET_DV_ServiceHandle'
402  * @param size number of bytes available in buf
403  * @param buf where to copy the message
404  * @return number of bytes written to buf
405  */ 
406 static size_t
407 transmit_start (void *cls,
408                 size_t size,
409                 void *buf)
410 {
411   struct GNUNET_DV_ServiceHandle *sh = cls;
412   struct GNUNET_MessageHeader start_message;
413
414   sh->th = NULL;
415   if (NULL == buf)
416   {
417     GNUNET_break (0);
418     reconnect (sh);
419     return 0;
420   }
421   GNUNET_assert (size >= sizeof (start_message));
422   start_message.size = htons (sizeof (struct GNUNET_MessageHeader));
423   start_message.type = htons (GNUNET_MESSAGE_TYPE_DV_START);
424   memcpy (buf, &start_message, sizeof (start_message));
425   GNUNET_CLIENT_receive (sh->client,
426                          &handle_message_receipt, sh,
427                          GNUNET_TIME_UNIT_FOREVER_REL);
428   start_transmit (sh);
429   return sizeof (start_message);
430 }
431
432
433 /**
434  * We got disconnected from the service and thus all of the
435  * pending send callbacks will never be confirmed.  Clean up.
436  *
437  * @param cls the 'struct GNUNET_DV_ServiceHandle'
438  * @param key a peer identity
439  * @param value a 'struct GNUNET_DV_TransmitHandle' to clean up
440  * @return GNUNET_OK (continue to iterate)
441  */
442 static int
443 cleanup_send_cb (void *cls,
444                  const struct GNUNET_HashCode *key,
445                  void *value)
446 {
447   struct GNUNET_DV_ServiceHandle *sh = cls;
448   struct GNUNET_DV_TransmitHandle *th = value;
449
450   GNUNET_assert (GNUNET_YES ==
451                  GNUNET_CONTAINER_multihashmap_remove (sh->send_callbacks,
452                                                        key,
453                                                        th));
454   th->cb (th->cb_cls, GNUNET_SYSERR);
455   GNUNET_free (th);
456   return GNUNET_OK;
457 }
458
459
460 /**
461  * Disconnect and then reconnect to the DV service.
462  *
463  * @param sh service handle
464  */
465 static void
466 reconnect (struct GNUNET_DV_ServiceHandle *sh)
467
468   if (NULL != sh->th)
469   {
470     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
471     sh->th = NULL;
472   }
473   if (NULL != sh->client)
474   {
475     GNUNET_CLIENT_disconnect (sh->client);
476     sh->client = NULL;
477   }
478   GNUNET_CONTAINER_multihashmap_iterate (sh->send_callbacks,
479                                          &cleanup_send_cb,
480                                          sh);
481   LOG (GNUNET_ERROR_TYPE_DEBUG,
482        "Connecting to DV service\n");
483   sh->client = GNUNET_CLIENT_connect ("dv", sh->cfg);
484   if (NULL == sh->client)
485   {
486     GNUNET_break (0);
487     return;
488   }
489   sh->th = GNUNET_CLIENT_notify_transmit_ready (sh->client,
490                                                 sizeof (struct GNUNET_MessageHeader),
491                                                 GNUNET_TIME_UNIT_FOREVER_REL,
492                                                 GNUNET_YES,
493                                                 &transmit_start,
494                                                 sh);
495 }
496
497
498 /**
499  * Connect to the DV service.
500  *
501  * @param cfg configuration
502  * @param cls closure for callbacks
503  * @param connect_cb function to call on connects
504  * @param distance_cb function to call if distances change
505  * @param disconnect_cb function to call on disconnects
506  * @param message_cb function to call if we receive messages
507  * @return handle to access the service
508  */
509 struct GNUNET_DV_ServiceHandle *
510 GNUNET_DV_service_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
511                            void *cls,
512                            GNUNET_DV_ConnectCallback connect_cb,
513                            GNUNET_DV_DistanceChangedCallback distance_cb,
514                            GNUNET_DV_DisconnectCallback disconnect_cb,
515                            GNUNET_DV_MessageReceivedCallback message_cb)
516 {
517   struct GNUNET_DV_ServiceHandle *sh;
518
519   sh = GNUNET_malloc (sizeof (struct GNUNET_DV_ServiceHandle));
520   sh->cfg = cfg;
521   sh->cls = cls;
522   sh->connect_cb = connect_cb;
523   sh->distance_cb = distance_cb;
524   sh->disconnect_cb = disconnect_cb;
525   sh->message_cb = message_cb;
526   sh->send_callbacks = GNUNET_CONTAINER_multihashmap_create (128, GNUNET_YES);
527   reconnect (sh);
528   return sh;
529 }
530
531
532 /**
533  * Disconnect from DV service.
534  *
535  * @param sh service handle
536  */
537 void
538 GNUNET_DV_service_disconnect (struct GNUNET_DV_ServiceHandle *sh)
539 {
540   struct GNUNET_DV_TransmitHandle *pos;
541   
542   if (NULL == sh)
543     return;
544   if (NULL != sh->th)
545   {
546     GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
547     sh->th = NULL;
548   }
549   while (NULL != (pos = sh->th_head))
550   {
551     GNUNET_CONTAINER_DLL_remove (sh->th_head,
552                                  sh->th_tail,
553                                  pos);
554     GNUNET_free (pos);
555   }
556   if (NULL != sh->client) 
557   {
558     GNUNET_CLIENT_disconnect (sh->client);
559     sh->client = NULL;
560   }
561   GNUNET_CONTAINER_multihashmap_iterate (sh->send_callbacks,
562                                          &cleanup_send_cb,
563                                          sh);
564   GNUNET_CONTAINER_multihashmap_destroy (sh->send_callbacks);
565   GNUNET_free (sh);
566 }
567
568
569 /**
570  * Send a message via DV service.
571  *
572  * @param sh service handle
573  * @param target intended recpient
574  * @param msg message payload
575  * @param cb function to invoke when done
576  * @param cb_cls closure for 'cb'
577  * @return handle to cancel the operation
578  */
579 struct GNUNET_DV_TransmitHandle *
580 GNUNET_DV_send (struct GNUNET_DV_ServiceHandle *sh,
581                 const struct GNUNET_PeerIdentity *target,
582                 const struct GNUNET_MessageHeader *msg,
583                 GNUNET_DV_MessageSentCallback cb,
584                 void *cb_cls)
585 {
586   struct GNUNET_DV_TransmitHandle *th;
587   struct GNUNET_DV_SendMessage *sm;
588
589   if (ntohs (msg->size) + sizeof (struct GNUNET_DV_SendMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
590   {
591     GNUNET_break (0);
592     return NULL;
593   }
594   LOG (GNUNET_ERROR_TYPE_DEBUG,
595        "Asked to send %u bytes of type %u to %s\n",
596        (unsigned int) msg->size,
597        (unsigned int) msg->type,
598        GNUNET_i2s (target));
599
600   th = GNUNET_malloc (sizeof (struct GNUNET_DV_TransmitHandle) +
601                       sizeof (struct GNUNET_DV_SendMessage) +
602                       ntohs (msg->size));
603   th->sh = sh;
604   th->target = *target;
605   th->cb = cb;
606   th->cb_cls = cb_cls;
607   th->msg = (const struct GNUNET_MessageHeader *) &th[1];
608   sm = (struct GNUNET_DV_SendMessage *) &th[1];
609   sm->header.type = htons (GNUNET_MESSAGE_TYPE_DV_SEND);
610   sm->header.size = htons (sizeof (struct GNUNET_DV_SendMessage) + 
611                            ntohs (msg->size));
612   if (0 == sh->uid_gen)
613     sh->uid_gen = 1;
614   th->uid = sh->uid_gen;
615   sm->uid = htonl (sh->uid_gen++);
616   /* use memcpy here as 'target' may not be sufficiently aligned */
617   memcpy (&sm->target, target, sizeof (struct GNUNET_PeerIdentity));
618   memcpy (&sm[1], msg, ntohs (msg->size));
619   GNUNET_CONTAINER_DLL_insert (sh->th_head,
620                                sh->th_tail,
621                                th);
622   start_transmit (sh);
623   return th;
624 }
625
626
627 /**
628  * Abort send operation (naturally, the message may have
629  * already been transmitted; this only stops the 'cb'
630  * from being called again).
631  *
632  * @param th send operation to cancel
633  */
634 void
635 GNUNET_DV_send_cancel (struct GNUNET_DV_TransmitHandle *th)
636 {
637   struct GNUNET_DV_ServiceHandle *sh = th->sh;
638   int ret;
639
640   ret = GNUNET_CONTAINER_multihashmap_remove (sh->send_callbacks,
641                                               &th->target.hashPubKey,
642                                               th);
643   if (GNUNET_YES != ret)
644     GNUNET_CONTAINER_DLL_remove (sh->th_head,
645                                  sh->th_tail,
646                                  th);
647   GNUNET_free (th);
648 }
649
650
651 /* end of dv_api.c */