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 * Information we track for each peer.
44 * Handle for a send operation.
46 struct GNUNET_DV_TransmitHandle
51 struct GNUNET_DV_TransmitHandle *next;
56 struct GNUNET_DV_TransmitHandle *prev;
59 * Handle to the service.
61 struct GNUNET_DV_ServiceHandle *sh;
64 * Function to call upon completion.
66 GNUNET_DV_MessageSentCallback cb;
74 * The actual message (allocated at the end of this struct).
76 const struct GNUNET_MessageHeader *msg;
79 * Destination for the message.
81 struct ConnectedPeer *target;
84 * UID of our message, if any.
92 * Information we track for each peer.
98 * Identity of the peer.
100 struct GNUNET_PeerIdentity pid;
103 * Head of DLL of transmission handles where we need
104 * to invoke a continuation when we are informed about
105 * successful transmission. The respective request
106 * has already been sent to the DV service.
108 struct GNUNET_DV_TransmitHandle *head;
111 * Tail of DLL of transmission handles where we need
112 * to invoke a continuation when we are informed about
113 * successful transmission. The respective request
114 * has already been sent to the DV service.
116 struct GNUNET_DV_TransmitHandle *tail;
122 * Handle to the DV service.
124 struct GNUNET_DV_ServiceHandle
128 * Connection to DV service.
130 struct GNUNET_CLIENT_Connection *client;
133 * Active request for transmission to DV service.
135 struct GNUNET_CLIENT_TransmitHandle *th;
140 const struct GNUNET_CONFIGURATION_Handle *cfg;
143 * Closure for the callbacks.
148 * Function to call on connect events.
150 GNUNET_DV_ConnectCallback connect_cb;
153 * Function to call on distance change events.
155 GNUNET_DV_DistanceChangedCallback distance_cb;
158 * Function to call on disconnect events.
160 GNUNET_DV_DisconnectCallback disconnect_cb;
163 * Function to call on receiving messages events.
165 GNUNET_DV_MessageReceivedCallback message_cb;
168 * Head of messages to transmit.
170 struct GNUNET_DV_TransmitHandle *th_head;
173 * Tail of messages to transmit.
175 struct GNUNET_DV_TransmitHandle *th_tail;
178 * Information tracked per connected peer. Maps peer
179 * identities to `struct ConnectedPeer` entries.
181 struct GNUNET_CONTAINER_MultiPeerMap *peers;
192 * Disconnect and then reconnect to the DV service.
194 * @param sh service handle
197 reconnect (struct GNUNET_DV_ServiceHandle *sh);
201 * Start sending messages from our queue to the service.
203 * @param sh service handle
206 start_transmit (struct GNUNET_DV_ServiceHandle *sh);
210 * Gives a message from our queue to the DV service.
212 * @param cls handle to the dv service (`struct GNUNET_DV_ServiceHandle`)
213 * @param size how many bytes can we send
214 * @param buf where to copy the message to send
215 * @return how many bytes we copied to @a buf
218 transmit_pending (void *cls, size_t size, void *buf)
220 struct GNUNET_DV_ServiceHandle *sh = cls;
222 struct GNUNET_DV_TransmitHandle *th;
233 while ( (NULL != (th = sh->th_head)) &&
234 (size - ret >= (tsize = ntohs (th->msg->size)) ))
236 GNUNET_CONTAINER_DLL_remove (sh->th_head,
239 memcpy (&cbuf[ret], th->msg, tsize);
240 LOG (GNUNET_ERROR_TYPE_DEBUG,
241 "Passing %u bytes of type %u to DV service\n",
243 ntohs (th->msg->type));
248 GNUNET_CONTAINER_DLL_insert_tail (th->target->head,
257 if (NULL != sh->th_head)
264 * Start sending messages from our queue to the service.
266 * @param sh service handle
269 start_transmit (struct GNUNET_DV_ServiceHandle *sh)
273 if (NULL == sh->th_head)
276 GNUNET_CLIENT_notify_transmit_ready (sh->client,
277 ntohs (sh->th_head->msg->size),
278 GNUNET_TIME_UNIT_FOREVER_REL,
280 &transmit_pending, sh);
285 * We got disconnected from the service and thus all of the
286 * pending send callbacks will never be confirmed. Clean up.
288 * @param cls the 'struct GNUNET_DV_ServiceHandle'
289 * @param key a peer identity
290 * @param value a `struct ConnectedPeer` to clean up
291 * @return #GNUNET_OK (continue to iterate)
294 cleanup_send_cb (void *cls,
295 const struct GNUNET_PeerIdentity *key,
298 struct GNUNET_DV_ServiceHandle *sh = cls;
299 struct ConnectedPeer *peer = value;
300 struct GNUNET_DV_TransmitHandle *th;
302 GNUNET_assert (GNUNET_YES ==
303 GNUNET_CONTAINER_multipeermap_remove (sh->peers,
306 sh->disconnect_cb (sh->cls,
308 while (NULL != (th = peer->head))
310 GNUNET_CONTAINER_DLL_remove (peer->head, peer->tail, th);
311 th->cb (th->cb_cls, GNUNET_SYSERR);
320 * Handles a message sent from the DV service to us.
321 * Parse it out and give it to the plugin.
323 * @param cls the handle to the DV API
324 * @param msg the message that was received
327 handle_message_receipt (void *cls,
328 const struct GNUNET_MessageHeader *msg)
330 struct GNUNET_DV_ServiceHandle *sh = cls;
331 const struct GNUNET_DV_ConnectMessage *cm;
332 const struct GNUNET_DV_DistanceUpdateMessage *dum;
333 const struct GNUNET_DV_DisconnectMessage *dm;
334 const struct GNUNET_DV_ReceivedMessage *rm;
335 const struct GNUNET_MessageHeader *payload;
336 const struct GNUNET_DV_AckMessage *ack;
337 struct GNUNET_DV_TransmitHandle *th;
338 struct GNUNET_DV_TransmitHandle *tn;
339 struct ConnectedPeer *peer;
343 /* Connection closed */
347 LOG (GNUNET_ERROR_TYPE_DEBUG,
348 "Received message of type %u with %u bytes from DV service\n",
349 (unsigned int) ntohs (msg->type),
350 (unsigned int) ntohs (msg->size));
351 switch (ntohs (msg->type))
353 case GNUNET_MESSAGE_TYPE_DV_CONNECT:
354 if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ConnectMessage))
360 cm = (const struct GNUNET_DV_ConnectMessage *) msg;
361 peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
369 peer = GNUNET_new (struct ConnectedPeer);
370 peer->pid = cm->peer;
371 GNUNET_assert (GNUNET_OK ==
372 GNUNET_CONTAINER_multipeermap_put (sh->peers,
375 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
376 sh->connect_cb (sh->cls,
378 ntohl (cm->distance),
379 (enum GNUNET_ATS_Network_Type) ntohl (cm->network));
381 case GNUNET_MESSAGE_TYPE_DV_DISTANCE_CHANGED:
382 if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DistanceUpdateMessage))
388 dum = (const struct GNUNET_DV_DistanceUpdateMessage *) msg;
389 sh->distance_cb (sh->cls,
391 ntohl (dum->distance),
392 (enum GNUNET_ATS_Network_Type) ntohl (dum->network));
394 case GNUNET_MESSAGE_TYPE_DV_DISCONNECT:
395 if (ntohs (msg->size) != sizeof (struct GNUNET_DV_DisconnectMessage))
401 dm = (const struct GNUNET_DV_DisconnectMessage *) msg;
402 peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
411 while (NULL != (th = tn))
414 if (peer == th->target)
416 GNUNET_CONTAINER_DLL_remove (sh->th_head,
419 th->cb (th->cb_cls, GNUNET_SYSERR);
423 cleanup_send_cb (sh, &dm->peer, peer);
425 case GNUNET_MESSAGE_TYPE_DV_RECV:
426 if (ntohs (msg->size) < sizeof (struct GNUNET_DV_ReceivedMessage) + sizeof (struct GNUNET_MessageHeader))
432 rm = (const struct GNUNET_DV_ReceivedMessage *) msg;
433 payload = (const struct GNUNET_MessageHeader *) &rm[1];
434 if (ntohs (msg->size) != sizeof (struct GNUNET_DV_ReceivedMessage) + ntohs (payload->size))
441 GNUNET_CONTAINER_multipeermap_get (sh->peers,
448 sh->message_cb (sh->cls,
450 ntohl (rm->distance),
453 case GNUNET_MESSAGE_TYPE_DV_SEND_ACK:
454 case GNUNET_MESSAGE_TYPE_DV_SEND_NACK:
455 if (ntohs (msg->size) != sizeof (struct GNUNET_DV_AckMessage))
461 ack = (const struct GNUNET_DV_AckMessage *) msg;
462 peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
465 break; /* this happens, just ignore */
466 for (th = peer->head; NULL != th; th = th->next)
468 if (th->uid != ntohl (ack->uid))
470 LOG (GNUNET_ERROR_TYPE_DEBUG,
471 "Matched ACK for message to peer %s\n",
472 GNUNET_i2s (&ack->target));
473 GNUNET_CONTAINER_DLL_remove (peer->head,
477 (ntohs (ack->header.type) == GNUNET_MESSAGE_TYPE_DV_SEND_ACK)
488 LOG (GNUNET_ERROR_TYPE_DEBUG,
489 "Received message, continuing receive loop for %p\n",
491 GNUNET_CLIENT_receive (sh->client,
492 &handle_message_receipt, sh,
493 GNUNET_TIME_UNIT_FOREVER_REL);
498 * Transmit the start message to the DV service.
500 * @param cls the `struct GNUNET_DV_ServiceHandle *`
501 * @param size number of bytes available in buf
502 * @param buf where to copy the message
503 * @return number of bytes written to buf
506 transmit_start (void *cls,
510 struct GNUNET_DV_ServiceHandle *sh = cls;
511 struct GNUNET_MessageHeader start_message;
520 GNUNET_assert (size >= sizeof (start_message));
521 start_message.size = htons (sizeof (struct GNUNET_MessageHeader));
522 start_message.type = htons (GNUNET_MESSAGE_TYPE_DV_START);
523 memcpy (buf, &start_message, sizeof (start_message));
524 LOG (GNUNET_ERROR_TYPE_DEBUG,
525 "Transmitting START request, starting receive loop for %p\n",
527 GNUNET_CLIENT_receive (sh->client,
528 &handle_message_receipt, sh,
529 GNUNET_TIME_UNIT_FOREVER_REL);
531 return sizeof (start_message);
536 * Disconnect and then reconnect to the DV service.
538 * @param sh service handle
541 reconnect (struct GNUNET_DV_ServiceHandle *sh)
545 GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
548 LOG (GNUNET_ERROR_TYPE_DEBUG,
549 "Disconnecting from DV service at %p\n",
551 if (NULL != sh->client)
553 GNUNET_CLIENT_disconnect (sh->client);
556 GNUNET_CONTAINER_multipeermap_iterate (sh->peers,
559 LOG (GNUNET_ERROR_TYPE_DEBUG,
560 "Connecting to DV service\n");
561 sh->client = GNUNET_CLIENT_connect ("dv", sh->cfg);
562 if (NULL == sh->client)
567 sh->th = GNUNET_CLIENT_notify_transmit_ready (sh->client,
568 sizeof (struct GNUNET_MessageHeader),
569 GNUNET_TIME_UNIT_FOREVER_REL,
577 * Connect to the DV service.
579 * @param cfg configuration
580 * @param cls closure for callbacks
581 * @param connect_cb function to call on connects
582 * @param distance_cb function to call if distances change
583 * @param disconnect_cb function to call on disconnects
584 * @param message_cb function to call if we receive messages
585 * @return handle to access the service
587 struct GNUNET_DV_ServiceHandle *
588 GNUNET_DV_service_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
590 GNUNET_DV_ConnectCallback connect_cb,
591 GNUNET_DV_DistanceChangedCallback distance_cb,
592 GNUNET_DV_DisconnectCallback disconnect_cb,
593 GNUNET_DV_MessageReceivedCallback message_cb)
595 struct GNUNET_DV_ServiceHandle *sh;
597 sh = GNUNET_new (struct GNUNET_DV_ServiceHandle);
600 sh->connect_cb = connect_cb;
601 sh->distance_cb = distance_cb;
602 sh->disconnect_cb = disconnect_cb;
603 sh->message_cb = message_cb;
604 sh->peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES);
611 * Disconnect from DV service.
613 * @param sh service handle
616 GNUNET_DV_service_disconnect (struct GNUNET_DV_ServiceHandle *sh)
618 struct GNUNET_DV_TransmitHandle *pos;
624 GNUNET_CLIENT_notify_transmit_ready_cancel (sh->th);
627 while (NULL != (pos = sh->th_head))
629 GNUNET_CONTAINER_DLL_remove (sh->th_head,
634 if (NULL != sh->client)
636 GNUNET_CLIENT_disconnect (sh->client);
639 GNUNET_CONTAINER_multipeermap_iterate (sh->peers,
642 GNUNET_CONTAINER_multipeermap_destroy (sh->peers);
648 * Send a message via DV service.
650 * @param sh service handle
651 * @param target intended recpient
652 * @param msg message payload
653 * @param cb function to invoke when done
654 * @param cb_cls closure for @a cb
655 * @return handle to cancel the operation
657 struct GNUNET_DV_TransmitHandle *
658 GNUNET_DV_send (struct GNUNET_DV_ServiceHandle *sh,
659 const struct GNUNET_PeerIdentity *target,
660 const struct GNUNET_MessageHeader *msg,
661 GNUNET_DV_MessageSentCallback cb,
664 struct GNUNET_DV_TransmitHandle *th;
665 struct GNUNET_DV_SendMessage *sm;
666 struct ConnectedPeer *peer;
668 if (ntohs (msg->size) + sizeof (struct GNUNET_DV_SendMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
673 LOG (GNUNET_ERROR_TYPE_DEBUG,
674 "Asked to send %u bytes of type %u to %s via %p\n",
675 (unsigned int) ntohs (msg->size),
676 (unsigned int) ntohs (msg->type),
679 peer = GNUNET_CONTAINER_multipeermap_get (sh->peers,
686 th = GNUNET_malloc (sizeof (struct GNUNET_DV_TransmitHandle) +
687 sizeof (struct GNUNET_DV_SendMessage) +
693 th->msg = (const struct GNUNET_MessageHeader *) &th[1];
694 sm = (struct GNUNET_DV_SendMessage *) &th[1];
695 sm->header.type = htons (GNUNET_MESSAGE_TYPE_DV_SEND);
696 sm->header.size = htons (sizeof (struct GNUNET_DV_SendMessage) +
698 if (0 == sh->uid_gen)
700 th->uid = sh->uid_gen;
701 sm->uid = htonl (sh->uid_gen++);
702 /* use memcpy here as 'target' may not be sufficiently aligned */
703 memcpy (&sm->target, target, sizeof (struct GNUNET_PeerIdentity));
704 memcpy (&sm[1], msg, ntohs (msg->size));
705 GNUNET_CONTAINER_DLL_insert_tail (sh->th_head,
714 * Abort send operation (naturally, the message may have
715 * already been transmitted; this only stops the 'cb'
716 * from being called again).
718 * @param th send operation to cancel
721 GNUNET_DV_send_cancel (struct GNUNET_DV_TransmitHandle *th)
723 struct GNUNET_DV_ServiceHandle *sh = th->sh;
726 GNUNET_CONTAINER_DLL_remove (th->target->head,
730 GNUNET_CONTAINER_DLL_remove (sh->th_head,
737 /* end of dv_api.c */