2 This file is part of GNUnet.
3 (C) 2012, 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.
22 * @file fs/gnunet-service-fs_mesh_client.c
23 * @brief non-anonymous file-transfer
24 * @author Christian Grothoff
27 * - PORT is set to old application type, unsure if we should keep
28 * it that way (fine for now)
31 #include "gnunet_constants.h"
32 #include "gnunet_util_lib.h"
33 #include "gnunet_mesh_service.h"
34 #include "gnunet_protocols.h"
35 #include "gnunet_applications.h"
36 #include "gnunet-service-fs.h"
37 #include "gnunet-service-fs_indexing.h"
38 #include "gnunet-service-fs_mesh.h"
42 * After how long do we reset connections without replies?
44 #define CLIENT_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
48 * Handle for a mesh to another peer.
54 * Handle for a request that is going out via mesh API.
56 struct GSF_MeshRequest
62 struct GSF_MeshRequest *next;
67 struct GSF_MeshRequest *prev;
70 * Which mesh is this request associated with?
72 struct MeshHandle *mh;
75 * Function to call with the result.
77 GSF_MeshReplyProcessor proc;
85 * Query to transmit to the other peer.
87 struct GNUNET_HashCode query;
90 * Desired type for the reply.
92 enum GNUNET_BLOCK_Type type;
95 * Did we transmit this request already? YES if we are
96 * in the 'waiting_map', NO if we are in the 'pending' DLL.
103 * Handle for a mesh to another peer.
108 * Head of DLL of pending requests on this mesh.
110 struct GSF_MeshRequest *pending_head;
113 * Tail of DLL of pending requests on this mesh.
115 struct GSF_MeshRequest *pending_tail;
118 * Map from query to 'struct GSF_MeshRequest's waiting for
121 struct GNUNET_CONTAINER_MultiHashMap *waiting_map;
124 * Tunnel to the other peer.
126 struct GNUNET_MESH_Tunnel *tunnel;
129 * Handle for active write operation, or NULL.
131 struct GNUNET_MESH_TransmitHandle *wh;
134 * Which peer does this mesh go to?
136 struct GNUNET_PeerIdentity target;
139 * Task to kill inactive meshs (we keep them around for
140 * a few seconds to give the application a chance to give
143 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
146 * Task to reset meshs that had errors (asynchronously,
147 * as we may not be able to do it immediately during a
148 * callback from the mesh API).
150 GNUNET_SCHEDULER_TaskIdentifier reset_task;
156 * Mesh tunnel for creating outbound tunnels.
158 static struct GNUNET_MESH_Handle *mesh_tunnel;
161 * Map from peer identities to 'struct MeshHandles' with mesh
162 * tunnels to those peers.
164 static struct GNUNET_CONTAINER_MultiHashMap *mesh_map;
167 /* ********************* client-side code ************************* */
171 * Transmit pending requests via the mesh.
173 * @param mh mesh to process
176 transmit_pending (struct MeshHandle *mh);
180 * Iterator called on each entry in a waiting map to
181 * move it back to the pending list.
183 * @param cls the 'struct MeshHandle'
184 * @param key the key of the entry in the map (the query)
185 * @param value the 'struct GSF_MeshRequest' to move to pending
186 * @return GNUNET_YES (continue to iterate)
189 move_to_pending (void *cls,
190 const struct GNUNET_HashCode *key,
193 struct MeshHandle *mh = cls;
194 struct GSF_MeshRequest *sr = value;
196 GNUNET_assert (GNUNET_YES ==
197 GNUNET_CONTAINER_multihashmap_remove (mh->waiting_map,
200 GNUNET_CONTAINER_DLL_insert (mh->pending_head,
203 sr->was_transmitted = GNUNET_NO;
209 * We had a serious error, tear down and re-create mesh from scratch.
211 * @param mh mesh to reset
214 reset_mesh (struct MeshHandle *mh)
216 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
217 "Resetting mesh tunnel to %s\n",
218 GNUNET_i2s (&mh->target));
219 GNUNET_MESH_tunnel_destroy (mh->tunnel);
220 GNUNET_CONTAINER_multihashmap_iterate (mh->waiting_map,
223 mh->tunnel = GNUNET_MESH_tunnel_create (mesh_tunnel,
226 GNUNET_APPLICATION_TYPE_FS_BLOCK_TRANSFER,
233 * Task called when it is time to destroy an inactive mesh tunnel.
235 * @param cls the 'struct MeshHandle' to tear down
236 * @param tc scheduler context, unused
239 mesh_timeout (void *cls,
240 const struct GNUNET_SCHEDULER_TaskContext *tc)
242 struct MeshHandle *mh = cls;
243 struct GNUNET_MESH_Tunnel *tun;
245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
246 "Timeout on mesh tunnel to %s\n",
247 GNUNET_i2s (&mh->target));
248 mh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
251 GNUNET_MESH_tunnel_destroy (tun);
256 * Task called when it is time to reset an mesh.
258 * @param cls the 'struct MeshHandle' to tear down
259 * @param tc scheduler context, unused
262 reset_mesh_task (void *cls,
263 const struct GNUNET_SCHEDULER_TaskContext *tc)
265 struct MeshHandle *mh = cls;
267 mh->reset_task = GNUNET_SCHEDULER_NO_TASK;
273 * We had a serious error, tear down and re-create mesh from scratch,
274 * but do so asynchronously.
276 * @param mh mesh to reset
279 reset_mesh_async (struct MeshHandle *mh)
281 if (GNUNET_SCHEDULER_NO_TASK != mh->reset_task)
282 GNUNET_SCHEDULER_cancel (mh->reset_task);
283 mh->reset_task = GNUNET_SCHEDULER_add_now (&reset_mesh_task,
289 * Functions of this signature are called whenever we are ready to transmit
292 * @param cls the struct MeshHandle for which we did the write call
293 * @param size the number of bytes that can be written to 'buf'
294 * @param buf where to write the message
295 * @return number of bytes written to 'buf'
298 transmit_sqm (void *cls,
302 struct MeshHandle *mh = cls;
303 struct MeshQueryMessage sqm;
304 struct GSF_MeshRequest *sr;
312 sr = mh->pending_head;
315 GNUNET_assert (size >= sizeof (struct MeshQueryMessage));
316 GNUNET_CONTAINER_DLL_remove (mh->pending_head,
319 GNUNET_assert (GNUNET_OK ==
320 GNUNET_CONTAINER_multihashmap_put (mh->waiting_map,
323 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
324 sr->was_transmitted = GNUNET_YES;
325 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
326 "Sending query for %s via mesh to %s\n",
327 GNUNET_h2s (&sr->query),
328 GNUNET_i2s (&mh->target));
329 sqm.header.size = htons (sizeof (sqm));
330 sqm.header.type = htons (GNUNET_MESSAGE_TYPE_FS_MESH_QUERY);
331 sqm.type = htonl (sr->type);
332 sqm.query = sr->query;
333 memcpy (buf, &sqm, sizeof (sqm));
334 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
335 "Successfully transmitted %u bytes via mesh to %s\n",
337 GNUNET_i2s (&mh->target));
338 transmit_pending (mh);
344 * Transmit pending requests via the mesh.
346 * @param mh mesh to process
349 transmit_pending (struct MeshHandle *mh)
353 mh->wh = GNUNET_MESH_notify_transmit_ready (mh->tunnel, GNUNET_YES /* allow cork */,
354 GNUNET_TIME_UNIT_FOREVER_REL,
355 sizeof (struct MeshQueryMessage),
361 * Closure for 'handle_reply'.
363 struct HandleReplyClosure
372 * Expiration time for the block.
374 struct GNUNET_TIME_Absolute expiration;
377 * Number of bytes in 'data'.
384 enum GNUNET_BLOCK_Type type;
387 * Did we have a matching query?
394 * Iterator called on each entry in a waiting map to
397 * @param cls the 'struct HandleReplyClosure'
398 * @param key the key of the entry in the map (the query)
399 * @param value the 'struct GSF_MeshRequest' to handle result for
400 * @return GNUNET_YES (continue to iterate)
403 handle_reply (void *cls,
404 const struct GNUNET_HashCode *key,
407 struct HandleReplyClosure *hrc = cls;
408 struct GSF_MeshRequest *sr = value;
410 sr->proc (sr->proc_cls,
415 GSF_mesh_query_cancel (sr);
416 hrc->found = GNUNET_YES;
422 * Functions with this signature are called whenever a complete reply
425 * @param cls closure with the 'struct MeshHandle'
426 * @param tunnel tunnel handle
427 * @param tunnel_ctx tunnel context
428 * @param message the actual message
429 * @return GNUNET_OK on success, GNUNET_SYSERR to stop further processing
433 struct GNUNET_MESH_Tunnel *tunnel,
435 const struct GNUNET_MessageHeader *message)
437 struct MeshHandle *mh = *tunnel_ctx;
438 const struct MeshReplyMessage *srm;
439 struct HandleReplyClosure hrc;
441 enum GNUNET_BLOCK_Type type;
442 struct GNUNET_HashCode query;
444 msize = ntohs (message->size);
445 if (sizeof (struct MeshReplyMessage) > msize)
448 reset_mesh_async (mh);
449 return GNUNET_SYSERR;
451 srm = (const struct MeshReplyMessage *) message;
452 msize -= sizeof (struct MeshReplyMessage);
453 type = (enum GNUNET_BLOCK_Type) ntohl (srm->type);
455 GNUNET_BLOCK_get_key (GSF_block_ctx,
457 &srm[1], msize, &query))
460 reset_mesh_async (mh);
461 return GNUNET_SYSERR;
463 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
464 "Received reply `%s' via mesh from peer %s\n",
466 GNUNET_i2s (&mh->target));
467 GNUNET_STATISTICS_update (GSF_stats,
468 gettext_noop ("# replies received via mesh"), 1,
471 hrc.data_size = msize;
472 hrc.expiration = GNUNET_TIME_absolute_ntoh (srm->expiration);
474 hrc.found = GNUNET_NO;
475 GNUNET_CONTAINER_multihashmap_get_multiple (mh->waiting_map,
479 if (GNUNET_NO == hrc.found)
481 GNUNET_STATISTICS_update (GSF_stats,
482 gettext_noop ("# replies received via mesh dropped"), 1,
491 * Get (or create) a mesh to talk to the given peer.
493 * @param target peer we want to communicate with
495 static struct MeshHandle *
496 get_mesh (const struct GNUNET_PeerIdentity *target)
498 struct MeshHandle *mh;
500 mh = GNUNET_CONTAINER_multihashmap_get (mesh_map,
501 &target->hashPubKey);
504 if (GNUNET_SCHEDULER_NO_TASK != mh->timeout_task)
506 GNUNET_SCHEDULER_cancel (mh->timeout_task);
507 mh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
511 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
512 "Creating mesh tunnel to %s\n",
513 GNUNET_i2s (target));
514 mh = GNUNET_new (struct MeshHandle);
515 mh->reset_task = GNUNET_SCHEDULER_add_delayed (CLIENT_RETRY_TIMEOUT,
518 mh->waiting_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_YES);
519 mh->target = *target;
520 mh->tunnel = GNUNET_MESH_tunnel_create (mesh_tunnel,
523 GNUNET_APPLICATION_TYPE_FS_BLOCK_TRANSFER,
526 GNUNET_assert (GNUNET_OK ==
527 GNUNET_CONTAINER_multihashmap_put (mesh_map,
528 &mh->target.hashPubKey,
530 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
536 * Look for a block by directly contacting a particular peer.
538 * @param target peer that should have the block
539 * @param query hash to query for the block
540 * @param type desired type for the block
541 * @param proc function to call with result
542 * @param proc_cls closure for 'proc'
543 * @return handle to cancel the operation
545 struct GSF_MeshRequest *
546 GSF_mesh_query (const struct GNUNET_PeerIdentity *target,
547 const struct GNUNET_HashCode *query,
548 enum GNUNET_BLOCK_Type type,
549 GSF_MeshReplyProcessor proc, void *proc_cls)
551 struct MeshHandle *mh;
552 struct GSF_MeshRequest *sr;
554 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
555 "Preparing to send query for %s via mesh to %s\n",
557 GNUNET_i2s (target));
558 mh = get_mesh (target);
559 sr = GNUNET_new (struct GSF_MeshRequest);
562 sr->proc_cls = proc_cls;
565 GNUNET_CONTAINER_DLL_insert (mh->pending_head,
568 transmit_pending (mh);
574 * Cancel an active request; must not be called after 'proc'
577 * @param sr request to cancel
580 GSF_mesh_query_cancel (struct GSF_MeshRequest *sr)
582 struct MeshHandle *mh = sr->mh;
584 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
585 "Cancelled query for %s via mesh to %s\n",
586 GNUNET_h2s (&sr->query),
587 GNUNET_i2s (&sr->mh->target));
588 if (GNUNET_YES == sr->was_transmitted)
589 GNUNET_assert (GNUNET_OK ==
590 GNUNET_CONTAINER_multihashmap_remove (mh->waiting_map,
594 GNUNET_CONTAINER_DLL_remove (mh->pending_head,
598 if ( (0 == GNUNET_CONTAINER_multihashmap_size (mh->waiting_map)) &&
599 (NULL == mh->pending_head) )
600 mh->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
607 * Iterator called on each entry in a waiting map to
608 * call the 'proc' continuation and release associated
611 * @param cls the 'struct MeshHandle'
612 * @param key the key of the entry in the map (the query)
613 * @param value the 'struct GSF_MeshRequest' to clean up
614 * @return GNUNET_YES (continue to iterate)
617 free_waiting_entry (void *cls,
618 const struct GNUNET_HashCode *key,
621 struct GSF_MeshRequest *sr = value;
623 sr->proc (sr->proc_cls, GNUNET_BLOCK_TYPE_ANY,
624 GNUNET_TIME_UNIT_FOREVER_ABS,
626 GSF_mesh_query_cancel (sr);
632 * Function called by mesh when a client disconnects.
633 * Cleans up our 'struct MeshClient' of that tunnel.
636 * @param tunnel tunnel of the disconnecting client
637 * @param tunnel_ctx our 'struct MeshClient'
640 cleaner_cb (void *cls,
641 const struct GNUNET_MESH_Tunnel *tunnel,
644 struct MeshHandle *mh = tunnel_ctx;
645 struct GSF_MeshRequest *sr;
648 while (NULL != (sr = mh->pending_head))
650 sr->proc (sr->proc_cls, GNUNET_BLOCK_TYPE_ANY,
651 GNUNET_TIME_UNIT_FOREVER_ABS,
653 GSF_mesh_query_cancel (sr);
655 GNUNET_CONTAINER_multihashmap_iterate (mh->waiting_map,
659 GNUNET_MESH_notify_transmit_ready_cancel (mh->wh);
660 if (GNUNET_SCHEDULER_NO_TASK != mh->timeout_task)
661 GNUNET_SCHEDULER_cancel (mh->timeout_task);
662 if (GNUNET_SCHEDULER_NO_TASK != mh->reset_task)
663 GNUNET_SCHEDULER_cancel (mh->reset_task);
664 GNUNET_assert (GNUNET_OK ==
665 GNUNET_CONTAINER_multihashmap_remove (mesh_map,
666 &mh->target.hashPubKey,
668 GNUNET_CONTAINER_multihashmap_destroy (mh->waiting_map);
674 * Initialize subsystem for non-anonymous file-sharing.
677 GSF_mesh_start_client ()
679 static const struct GNUNET_MESH_MessageHandler handlers[] = {
680 { &reply_cb, GNUNET_MESSAGE_TYPE_FS_MESH_REPLY, 0 },
683 static const uint32_t ports[] = {
684 GNUNET_APPLICATION_TYPE_FS_BLOCK_TRANSFER,
688 mesh_map = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_YES);
689 mesh_tunnel = GNUNET_MESH_connect (GSF_cfg,
699 * Function called on each active meshs to shut them down.
702 * @param key target peer, unused
703 * @param value the 'struct MeshHandle' to destroy
704 * @return GNUNET_YES (continue to iterate)
707 release_meshs (void *cls,
708 const struct GNUNET_HashCode *key,
711 struct MeshHandle *mh = value;
712 struct GNUNET_MESH_Tunnel *tun;
714 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
715 "Timeout on mesh tunnel to %s\n",
716 GNUNET_i2s (&mh->target));
720 GNUNET_MESH_tunnel_destroy (tun);
726 * Shutdown subsystem for non-anonymous file-sharing.
729 GSF_mesh_stop_client ()
731 GNUNET_CONTAINER_multihashmap_iterate (mesh_map,
734 GNUNET_CONTAINER_multihashmap_destroy (mesh_map);
736 if (NULL != mesh_tunnel)
738 GNUNET_MESH_disconnect (mesh_tunnel);
744 /* end of gnunet-service-fs_mesh_client.c */