2 This file is part of GNUnet.
3 (C) 2009--2013 Christian Grothoff (and other contributing authors)
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.
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.
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.
23 * @brief library to access the DV service
24 * @author Christian Grothoff
25 * @author Nathan Evans
28 #include "gnunet_util_lib.h"
29 #include "gnunet_dv_service.h"
30 #include "gnunet_protocols.h"
32 #include "gnunet_transport_plugin.h"
34 #define LOG(kind,...) GNUNET_log_from (kind, "dv-api",__VA_ARGS__)
38 * Handle for a send operation.
40 struct GNUNET_DV_TransmitHandle
45 struct GNUNET_DV_TransmitHandle *next;
50 struct GNUNET_DV_TransmitHandle *prev;
53 * Handle to the service.
55 struct GNUNET_DV_ServiceHandle *sh;
58 * Function to call upon completion.
60 GNUNET_DV_MessageSentCallback cb;
68 * The actual message (allocated at the end of this struct).
70 const struct GNUNET_MessageHeader *msg;
73 * Destination for the message.
75 struct GNUNET_PeerIdentity target;
78 * UID of our message, if any.
86 * Handle to the DV service.
88 struct GNUNET_DV_ServiceHandle
92 * Connection to DV service.
94 struct GNUNET_CLIENT_Connection *client;
97 * Active request for transmission to DV service.
99 struct GNUNET_CLIENT_TransmitHandle *th;
104 const struct GNUNET_CONFIGURATION_Handle *cfg;
107 * Closure for the callbacks.
112 * Function to call on connect events.
114 GNUNET_DV_ConnectCallback connect_cb;
117 * Function to call on distance change events.
119 GNUNET_DV_DistanceChangedCallback distance_cb;
122 * Function to call on disconnect events.
124 GNUNET_DV_DisconnectCallback disconnect_cb;
127 * Function to call on receiving messages events.
129 GNUNET_DV_MessageReceivedCallback message_cb;
132 * Head of messages to transmit.
134 struct GNUNET_DV_TransmitHandle *th_head;
137 * Tail of messages to transmit.
139 struct GNUNET_DV_TransmitHandle *th_tail;
142 * Mapping of peer identities to TransmitHandles to invoke
143 * upon successful transmission. The respective
144 * transmissions have already been done.
146 struct GNUNET_CONTAINER_MultiPeerMap *send_callbacks;
157 * Disconnect and then reconnect to the DV service.
159 * @param sh service handle
162 reconnect (struct GNUNET_DV_ServiceHandle *sh);
166 * Gives a message from our queue to the DV service.
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
174 transmit_pending (void *cls, size_t size, void *buf)
176 struct GNUNET_DV_ServiceHandle *sh = cls;
178 struct GNUNET_DV_TransmitHandle *th;
189 while ( (NULL != (th = sh->th_head)) &&
190 (size - ret >= (tsize = ntohs (th->msg->size)) ))
192 GNUNET_CONTAINER_DLL_remove (sh->th_head,
195 memcpy (&cbuf[ret], th->msg, tsize);
199 (void) GNUNET_CONTAINER_multipeermap_put (sh->send_callbacks,
202 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
214 * Start sending messages from our queue to the service.
216 * @param sh service handle
219 start_transmit (struct GNUNET_DV_ServiceHandle *sh)
223 if (NULL == sh->th_head)
226 GNUNET_CLIENT_notify_transmit_ready (sh->client,
227 ntohs (sh->th_head->msg->size),
228 GNUNET_TIME_UNIT_FOREVER_REL,
230 &transmit_pending, sh);
235 * Closure for 'process_ack'.
242 const struct GNUNET_DV_AckMessage *ack;
245 * Our service handle.
247 struct GNUNET_DV_ServiceHandle *sh;
252 * We got an ACK. Check if it matches the given transmit handle, and if
253 * so call the continuation.
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)
261 process_ack (void *cls,
262 const struct GNUNET_PeerIdentity *key,
265 struct AckContext *ctx = cls;
266 struct GNUNET_DV_TransmitHandle *th = value;
268 if (th->uid != ntohl (ctx->ack->uid))
270 LOG (GNUNET_ERROR_TYPE_DEBUG,
271 "Matchedk ACK for message to peer %s\n",
273 GNUNET_assert (GNUNET_YES ==
274 GNUNET_CONTAINER_multipeermap_remove (ctx->sh->send_callbacks,
278 (ntohs (ctx->ack->header.type) == GNUNET_MESSAGE_TYPE_DV_SEND_ACK)
287 * Handles a message sent from the DV service to us.
288 * Parse it out and give it to the plugin.
290 * @param cls the handle to the DV API
291 * @param msg the message that was received
294 handle_message_receipt (void *cls,
295 const struct GNUNET_MessageHeader *msg)
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;
308 /* Connection closed */
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))
317 case GNUNET_MESSAGE_TYPE_DV_CONNECT:
318 if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ConnectMessage))
324 cm = (const struct GNUNET_DV_ConnectMessage *) msg;
325 sh->connect_cb (sh->cls,
327 ntohl (cm->distance));
329 case GNUNET_MESSAGE_TYPE_DV_DISTANCE_CHANGED:
330 if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DistanceUpdateMessage))
336 dum = (const struct GNUNET_DV_DistanceUpdateMessage *) msg;
337 sh->distance_cb (sh->cls,
339 ntohl (dum->distance));
341 case GNUNET_MESSAGE_TYPE_DV_DISCONNECT:
342 if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DisconnectMessage))
348 dm = (const struct GNUNET_DV_DisconnectMessage *) msg;
349 sh->disconnect_cb (sh->cls,
352 case GNUNET_MESSAGE_TYPE_DV_RECV:
353 if (ntohs (msg->size) < sizeof (struct GNUNET_DV_ReceivedMessage) + sizeof (struct GNUNET_MessageHeader))
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))
367 sh->message_cb (sh->cls,
369 ntohl (rm->distance),
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))
380 ack = (const struct GNUNET_DV_AckMessage *) msg;
383 GNUNET_CONTAINER_multipeermap_get_multiple (sh->send_callbacks,
392 GNUNET_CLIENT_receive (sh->client,
393 &handle_message_receipt, sh,
394 GNUNET_TIME_UNIT_FOREVER_REL);
399 * Transmit the start message to the DV service.
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
407 transmit_start (void *cls,
411 struct GNUNET_DV_ServiceHandle *sh = cls;
412 struct GNUNET_MessageHeader start_message;
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);
429 return sizeof (start_message);
434 * We got disconnected from the service and thus all of the
435 * pending send callbacks will never be confirmed. Clean up.
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)
443 cleanup_send_cb (void *cls,
444 const struct GNUNET_PeerIdentity *key,
447 struct GNUNET_DV_ServiceHandle *sh = cls;
448 struct GNUNET_DV_TransmitHandle *th = value;
450 GNUNET_assert (GNUNET_YES ==
451 GNUNET_CONTAINER_multipeermap_remove (sh->send_callbacks,
454 th->cb (th->cb_cls, GNUNET_SYSERR);
461 * Disconnect and then reconnect to the DV service.
463 * @param sh service handle
466 reconnect (struct GNUNET_DV_ServiceHandle *sh)
470 GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
473 if (NULL != sh->client)
475 GNUNET_CLIENT_disconnect (sh->client);
478 GNUNET_CONTAINER_multipeermap_iterate (sh->send_callbacks,
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)
489 sh->th = GNUNET_CLIENT_notify_transmit_ready (sh->client,
490 sizeof (struct GNUNET_MessageHeader),
491 GNUNET_TIME_UNIT_FOREVER_REL,
499 * Connect to the DV service.
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
509 struct GNUNET_DV_ServiceHandle *
510 GNUNET_DV_service_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
512 GNUNET_DV_ConnectCallback connect_cb,
513 GNUNET_DV_DistanceChangedCallback distance_cb,
514 GNUNET_DV_DisconnectCallback disconnect_cb,
515 GNUNET_DV_MessageReceivedCallback message_cb)
517 struct GNUNET_DV_ServiceHandle *sh;
519 sh = GNUNET_new (struct GNUNET_DV_ServiceHandle);
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_multipeermap_create (128, GNUNET_YES);
533 * Disconnect from DV service.
535 * @param sh service handle
538 GNUNET_DV_service_disconnect (struct GNUNET_DV_ServiceHandle *sh)
540 struct GNUNET_DV_TransmitHandle *pos;
546 GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
549 while (NULL != (pos = sh->th_head))
551 GNUNET_CONTAINER_DLL_remove (sh->th_head,
556 if (NULL != sh->client)
558 GNUNET_CLIENT_disconnect (sh->client);
561 GNUNET_CONTAINER_multipeermap_iterate (sh->send_callbacks,
564 GNUNET_CONTAINER_multipeermap_destroy (sh->send_callbacks);
570 * Send a message via DV service.
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
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,
586 struct GNUNET_DV_TransmitHandle *th;
587 struct GNUNET_DV_SendMessage *sm;
589 if (ntohs (msg->size) + sizeof (struct GNUNET_DV_SendMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
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));
600 th = GNUNET_malloc (sizeof (struct GNUNET_DV_TransmitHandle) +
601 sizeof (struct GNUNET_DV_SendMessage) +
604 th->target = *target;
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) +
612 if (0 == sh->uid_gen)
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,
628 * Abort send operation (naturally, the message may have
629 * already been transmitted; this only stops the 'cb'
630 * from being called again).
632 * @param th send operation to cancel
635 GNUNET_DV_send_cancel (struct GNUNET_DV_TransmitHandle *th)
637 struct GNUNET_DV_ServiceHandle *sh = th->sh;
640 ret = GNUNET_CONTAINER_multipeermap_remove (sh->send_callbacks,
643 if (GNUNET_YES != ret)
644 GNUNET_CONTAINER_DLL_remove (sh->th_head,
651 /* end of dv_api.c */