a1b40129c875adf7af7f39b1b1379ff7c7ec6956
[oweals/gnunet.git] / src / core / gnunet-service-core_sessions.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010, 2011 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 core/gnunet-service-core_sessions.c
23  * @brief code for managing of 'encrypted' sessions (key exchange done) 
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet-service-core.h"
28 #include "gnunet-service-core_neighbours.h"
29 #include "gnunet-service-core_kx.h"
30 #include "gnunet-service-core_typemap.h"
31 #include "gnunet-service-core_sessions.h"
32 #include "gnunet-service-core_clients.h"
33 #include "gnunet_constants.h"
34
35
36 /**
37  * Message ready for encryption.  This struct is followed by the
38  * actual content of the message.
39  */
40 struct SessionMessageEntry
41 {
42
43   /**
44    * We keep messages in a doubly linked list.
45    */
46   struct SessionMessageEntry *next;
47
48   /**
49    * We keep messages in a doubly linked list.
50    */
51   struct SessionMessageEntry *prev;
52
53   /**
54    * Deadline for transmission, 1s after we received it (if we
55    * are not corking), otherwise "now".  Note that this message
56    * does NOT expire past its deadline.
57    */
58   struct GNUNET_TIME_Absolute deadline;
59
60   /**
61    * How long is the message? (number of bytes following the "struct
62    * MessageEntry", but not including the size of "struct
63    * MessageEntry" itself!)
64    */
65   size_t size;
66
67 };
68
69
70 /**
71  * Data kept per session.
72  */
73 struct Session
74 {
75   /**
76    * Identity of the other peer.
77    */
78   struct GNUNET_PeerIdentity peer;
79
80   /**
81    * Head of list of requests from clients for transmission to
82    * this peer.
83    */
84   struct GSC_ClientActiveRequest *active_client_request_head;
85
86   /**
87    * Tail of list of requests from clients for transmission to
88    * this peer.
89    */
90   struct GSC_ClientActiveRequest *active_client_request_tail;
91
92   /**
93    * Head of list of messages ready for encryption.
94    */
95   struct SessionMessageEntry *sme_head;
96
97   /**
98    * Tail of list of messages ready for encryption.
99    */
100   struct SessionMessageEntry *sme_tail;
101
102   /**
103    * Information about the key exchange with the other peer.
104    */
105   struct GSC_KeyExchangeInfo *kxinfo;
106
107   /**
108    * Current type map for this peer.
109    */
110   struct GSC_TypeMap *tmap;
111
112   /**
113    * At what time did we initially establish this session?
114    * (currently unused, should be integrated with ATS in the
115    * future...).
116    */
117   struct GNUNET_TIME_Absolute time_established;
118
119   /**
120    * Task to transmit corked messages with a delay.
121    */
122   GNUNET_SCHEDULER_TaskIdentifier cork_task;
123
124   /**
125    * Is the neighbour queue empty and thus ready for us
126    * to transmit an encrypted message?  
127    */
128   int ready_to_transmit;
129
130 };
131
132
133 /**
134  * Map of peer identities to 'struct Session'.
135  */
136 static struct GNUNET_CONTAINER_MultiHashMap *sessions;
137
138
139 /**
140  * Find the session for the given peer.
141  *
142  * @param peer identity of the peer
143  * @return NULL if we are not connected, otherwise the
144  *         session handle
145  */
146 static struct Session *
147 find_session (const struct GNUNET_PeerIdentity *peer)
148 {
149   return GNUNET_CONTAINER_multihashmap_get (sessions, &peer->hashPubKey);
150 }
151
152
153 /**
154  * End the session with the given peer (we are no longer
155  * connected). 
156  *
157  * @param pid identity of peer to kill session with
158  */
159 void
160 GSC_SESSIONS_end (const struct GNUNET_PeerIdentity *pid)
161 {
162   struct Session *session;
163   struct GSC_ClientActiveRequest *car;
164
165   session = find_session (pid);
166   if (NULL == session)
167     return;
168 #if DEBUG_CORE
169   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
170               "Destroying session for peer `%4s'\n",
171               GNUNET_i2s (&session->peer));
172 #endif
173   if (GNUNET_SCHEDULER_NO_TASK != session->cork_task)
174   {
175     GNUNET_SCHEDULER_cancel (session->cork_task);
176     session->cork_task = GNUNET_SCHEDULER_NO_TASK;
177   }
178   GNUNET_assert (GNUNET_YES ==
179                  GNUNET_CONTAINER_multihashmap_remove (sessions,
180                                                        &session->peer.hashPubKey, session));
181   while (NULL != (car = session->active_client_request_head))
182   {
183     GNUNET_CONTAINER_DLL_remove (session->active_client_request_head,
184                                  session->active_client_request_tail,
185                                  car);
186     GSC_CLIENTS_reject_request (car);
187   }
188   GNUNET_STATISTICS_set (GSC_stats, 
189                          gettext_noop ("# established sessions"),
190                          GNUNET_CONTAINER_multihashmap_size (sessions), 
191                          GNUNET_NO);
192   GNUNET_free (session);
193 }
194
195
196 /**
197  * Create a session, a key exchange was just completed.
198  *
199  * @param peer peer that is now connected
200  * @param kx key exchange that completed
201  */
202 void
203 GSC_SESSIONS_create (const struct GNUNET_PeerIdentity *peer,
204                      struct GSC_KeyExchangeInfo *kx)
205 {
206   struct GNUNET_MessageHeader *hdr;
207   struct Session *session;
208
209 #if DEBUG_CORE
210   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
211               "Creating session for peer `%4s'\n", GNUNET_i2s (pid));
212 #endif
213   session = GNUNET_malloc (sizeof (struct Session));
214   session->peer = *peer;
215   session->kxinfo = kx;
216   session->time_established = GNUNET_TIME_absolute_get ();
217   GNUNET_assert (GNUNET_OK ==
218                  GNUNET_CONTAINER_multihashmap_put (sessions,
219                                                     &peer->hashPubKey, session,
220                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
221   GNUNET_STATISTICS_update (GSC_stats, 
222                             gettext_noop ("# established sessions"),
223                             GNUNET_CONTAINER_multihashmap_size (sessions), 
224                             GNUNET_NO);
225   /* FIXME: we should probably do this periodically (in case
226      type map message is lost...) */
227   hdr = GSC_TYPEMAP_compute_type_map_message ();
228   GSC_KX_encrypt_and_transmit (kx, 
229                                hdr,
230                                ntohs (hdr->size));
231   GNUNET_free (hdr);
232 }
233
234
235 /**
236  * Notify the given client about the session (client is new).
237  *
238  * @param cls the 'struct GSC_Client'
239  * @param key peer identity 
240  * @param value the 'struct Session'
241  * @return GNUNET_OK (continue to iterate)
242  */
243 static int
244 notify_client_about_session (void *cls,
245                              const GNUNET_HashCode *key,
246                              void *value)
247 {
248   struct GSC_Client *client = cls;
249   struct Session *session = value;
250
251   GDS_CLIENTS_notify_client_about_neighbour (client,
252                                              &session->peer,
253                                              NULL, 0, /* FIXME: ATS!? */
254                                              NULL, /* old TMAP: none */
255                                              session->tmap);
256   return GNUNET_OK;
257 }
258
259
260 /**
261  * We have a new client, notify it about all current sessions.
262  *
263  * @param client the new client
264  */
265 void
266 GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client)
267 {
268   /* notify new client about existing sessions */
269   GNUNET_CONTAINER_multihashmap_iterate (sessions,
270                                          &notify_client_about_session, client);
271 }
272
273
274 /**
275  * Try to perform a transmission on the given session.  Will solicit
276  * additional messages if the 'sme' queue is not full enough.
277  *
278  * @param session session to transmit messages from
279  */
280 static void
281 try_transmission (struct Session *session);
282
283
284 /**
285  * Queue a request from a client for transmission to a particular peer.
286  *
287  * @param car request to queue; this handle is then shared between
288  *         the caller (CLIENTS subsystem) and SESSIONS and must not
289  *         be released by either until either 'GNUNET_SESSIONS_dequeue',
290  *         'GNUNET_SESSIONS_transmit' or 'GNUNET_CLIENTS_failed'
291  *         have been invoked on it
292  */
293 void
294 GSC_SESSIONS_queue_request (struct GSC_ClientActiveRequest *car)
295 {
296   struct Session *session;
297
298   session = find_session (&car->target);
299   if (session == NULL)
300   {
301     /* neighbour must have disconnected since request was issued,
302      * ignore (client will realize it once it processes the
303      * disconnect notification) */
304 #if DEBUG_CORE_CLIENT
305     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
306                 "Dropped client request for transmission (am disconnected)\n");
307 #endif
308     GNUNET_STATISTICS_update (GSC_stats,
309                               gettext_noop
310                               ("# send requests dropped (disconnected)"), 1,
311                               GNUNET_NO);
312     GSC_CLIENTS_reject_request (car);
313     return;
314   }
315   if (car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
316   {
317     GNUNET_break (0);
318     GSC_CLIENTS_reject_request (car);
319     return;
320   }
321 #if DEBUG_CORE_CLIENT
322   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
323               "Received client transmission request. queueing\n");
324 #endif
325   GNUNET_CONTAINER_DLL_insert (session->active_client_request_head,
326                                session->active_client_request_tail, car);
327   try_transmission (session);
328 }
329
330
331 /**
332  * Dequeue a request from a client from transmission to a particular peer.
333  *
334  * @param car request to dequeue; this handle will then be 'owned' by
335  *        the caller (CLIENTS sysbsystem)
336  */
337 void
338 GSC_SESSIONS_dequeue_request (struct GSC_ClientActiveRequest *car)
339 {
340   struct Session *s;
341
342   s = find_session (&car->target);
343   GNUNET_CONTAINER_DLL_remove (s->active_client_request_head,
344                                s->active_client_request_tail, car);
345 }
346
347
348 /**
349  * Discard all expired active transmission requests from clients.
350  *
351  * @param session session to clean up
352  */
353 static void
354 discard_expired_requests (struct Session *session)
355 {
356   struct GSC_ClientActiveRequest *pos;
357   struct GSC_ClientActiveRequest *nxt;
358   struct GNUNET_TIME_Absolute now;
359   
360   now = GNUNET_TIME_absolute_get ();
361   pos = NULL;
362   nxt = session->active_client_request_head;
363   while (NULL != nxt)
364   {
365     pos = nxt;
366     nxt = pos->next;
367     if ( (pos->deadline.abs_value < now.abs_value) &&
368          (GNUNET_YES != pos->was_solicited) )
369     {
370       GNUNET_STATISTICS_update (GSC_stats,
371                                 gettext_noop
372                                 ("# messages discarded (expired prior to transmission)"),
373                                 1, GNUNET_NO);
374       GNUNET_CONTAINER_DLL_remove (session->active_client_request_head,
375                                    session->active_client_request_tail,
376                                    pos);
377       GSC_CLIENTS_reject_request (pos);
378     }
379   }
380 }
381
382
383 /**
384  * Solicit messages for transmission.
385  *
386  * @param session session to solict messages for
387  */
388 static void
389 solicit_messages (struct Session *session)
390 {
391   struct GSC_ClientActiveRequest *car;
392   size_t so_size;
393
394   discard_expired_requests (session); 
395   so_size = 0;
396   for (car = session->active_client_request_head; NULL != car; car = car->next)
397   {
398     if (so_size + car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
399       break;
400     so_size += car->msize;
401     if (car->was_solicited == GNUNET_YES)
402       continue;
403     car->was_solicited = GNUNET_YES;
404     GSC_CLIENTS_solicit_request (car);
405   }
406 }
407
408
409 /**
410  * Some messages were delayed (corked), but the timeout has now expired.  
411  * Send them now.
412  *
413  * @param cls 'struct Session' with the messages to transmit now
414  * @param tc scheduler context (unused)
415  */
416 static void
417 pop_cork_task (void *cls,
418                const struct GNUNET_SCHEDULER_TaskContext *tc)
419 {
420   struct Session *session = session;
421
422   session->cork_task = GNUNET_SCHEDULER_NO_TASK;
423   try_transmission (session);
424 }
425
426
427 /**
428  * Try to perform a transmission on the given session. Will solicit
429  * additional messages if the 'sme' queue is not full enough.
430  *
431  * @param session session to transmit messages from
432  */
433 static void
434 try_transmission (struct Session *session)
435 {
436   struct SessionMessageEntry *pos;
437   size_t msize;
438   struct GNUNET_TIME_Absolute now;
439   struct GNUNET_TIME_Absolute min_deadline;
440
441   if (GNUNET_YES != session->ready_to_transmit)
442     return;
443   msize = 0;
444   min_deadline = GNUNET_TIME_UNIT_FOREVER_ABS;
445   /* check 'ready' messages */
446   pos = session->sme_head;
447   while ( (NULL != pos) &&
448           (msize + pos->size <= GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE) )
449   {
450     GNUNET_assert (pos->size < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE);
451     msize += pos->size;
452     min_deadline = GNUNET_TIME_absolute_min (min_deadline,
453                                              pos->deadline);
454     pos = pos->next;
455   }
456   now = GNUNET_TIME_absolute_get ();
457   if ( (msize == 0) ||
458        ( (msize < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE / 2) &&
459          (min_deadline.abs_value > now.abs_value) ) )
460   {
461     /* not enough ready yet, try to solicit more */
462     solicit_messages (session);
463     if (msize > 0)
464     {
465       /* if there is data to send, just not yet, make sure we do transmit
466          it once the deadline is reached */
467       if (session->cork_task != GNUNET_SCHEDULER_NO_TASK)
468         GNUNET_SCHEDULER_cancel (session->cork_task);
469       session->cork_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining (min_deadline),
470                                                          &pop_cork_task,
471                                                          session);
472     }
473     return;
474   }
475   /* create plaintext buffer of all messages, encrypt and transmit */
476   {
477     static unsigned long long total_bytes;
478     static unsigned int total_msgs;
479     char pbuf[msize];    /* plaintext */
480     size_t used;
481
482     used = 0;
483     pos = session->sme_head;
484     while ( (NULL != pos) &&
485             (used + pos->size <= msize) )
486     {
487       memcpy (&pbuf[used], &pos[1], pos->size);
488       used += pos->size;
489     }
490     /* compute average payload size */
491     total_bytes += used;
492     total_msgs++;
493     if (0 == total_msgs)
494     {
495       /* 2^32 messages, wrap around... */
496       total_msgs = 1;
497       total_bytes = used;
498     }
499     GNUNET_STATISTICS_set (GSC_stats, 
500                            "# avg payload per encrypted message",
501                            total_bytes / total_msgs,
502                            GNUNET_NO);
503     /* now actually transmit... */
504     session->ready_to_transmit = GNUNET_NO;
505     GSC_KX_encrypt_and_transmit (session->kxinfo,
506                                  pbuf,
507                                  used);
508   }
509 }
510
511
512 /**
513  * Send a message to the neighbour now.
514  *
515  * @param cls the message
516  * @param key neighbour's identity
517  * @param value 'struct Neighbour' of the target
518  * @return always GNUNET_OK
519  */
520 static int
521 do_send_message (void *cls, const GNUNET_HashCode * key, void *value)
522 {
523   const struct GNUNET_MessageHeader *hdr = cls;
524   struct Session *session = value;
525   struct SessionMessageEntry *m;
526   uint16_t size;
527
528   size = ntohs (hdr->size);
529   m = GNUNET_malloc (sizeof (struct SessionMessageEntry) + size);
530   memcpy (&m[1], hdr, size);
531   m->size = size;
532   GNUNET_CONTAINER_DLL_insert (session->sme_head,
533                                session->sme_tail,
534                                m);
535   try_transmission (session);
536   return GNUNET_OK;
537 }
538
539
540 /**
541  * Broadcast a message to all neighbours.
542  *
543  * @param msg message to transmit
544  */
545 void
546 GSC_SESSIONS_broadcast (const struct GNUNET_MessageHeader *msg)
547 {
548   if (NULL == sessions)
549     return;
550   GNUNET_CONTAINER_multihashmap_iterate (sessions,
551                                          &do_send_message, (void*) msg);
552 }
553
554
555 /**
556  * Traffic is being solicited for the given peer.  This means that the
557  * message queue on the transport-level (NEIGHBOURS subsystem) is now
558  * empty and it is now OK to transmit another (non-control) message.
559  *
560  * @param pid identity of peer ready to receive data
561  */
562 void
563 GSC_SESSIONS_solicit (const struct GNUNET_PeerIdentity *pid)
564 {
565   struct Session *session;
566
567   session = find_session (pid);
568   if (NULL == session)
569     return;
570   session->ready_to_transmit = GNUNET_YES;
571   try_transmission (session);
572 }
573
574
575 /**
576  * Transmit a message to a particular peer.
577  *
578  * @param car original request that was queued and then solicited;
579  *            this handle will now be 'owned' by the SESSIONS subsystem
580  * @param msg message to transmit
581  * @param cork is corking allowed?
582  */
583 void
584 GSC_SESSIONS_transmit (struct GSC_ClientActiveRequest *car,
585                        const struct GNUNET_MessageHeader *msg,
586                        int cork)
587 {
588   struct Session *session;
589   struct SessionMessageEntry *sme;
590   size_t msize;
591
592   session = find_session (&car->target);
593   msize = ntohs (msg->size);
594   sme = GNUNET_malloc (sizeof (struct SessionMessageEntry) + msize);
595   memcpy (&sme[1], msg, msize);
596   sme->size = msize;
597   if (GNUNET_YES == cork)
598     sme->deadline = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_MAX_CORK_DELAY);
599   GNUNET_CONTAINER_DLL_insert_tail (session->sme_head,
600                                     session->sme_tail,
601                                     sme);
602   try_transmission (session);
603 }
604
605
606 /**
607  * Helper function for GSC_SESSIONS_handle_client_iterate_peers.
608  *
609  * @param cls the 'struct GNUNET_SERVER_TransmitContext' to queue replies
610  * @param key identity of the connected peer
611  * @param value the 'struct Neighbour' for the peer
612  * @return GNUNET_OK (continue to iterate)
613  */
614 #include "core.h"
615 static int
616 queue_connect_message (void *cls, const GNUNET_HashCode * key, void *value)
617 {
618   struct GNUNET_SERVER_TransmitContext *tc = cls;
619   struct Session *session = value;
620   struct ConnectNotifyMessage cnm;
621   struct GNUNET_TRANSPORT_ATS_Information *a;
622  
623   /* FIXME: code duplication with clients... */
624   cnm.header.size = htons (sizeof (struct ConnectNotifyMessage));
625   cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
626   cnm.ats_count = htonl (0);
627   cnm.peer = session->peer;
628   a = &cnm.ats;
629   // FIXME: full ats...
630   a[0].type = htonl (GNUNET_TRANSPORT_ATS_ARRAY_TERMINATOR);
631   a[0].value = htonl (0);
632   GNUNET_SERVER_transmit_context_append_message (tc, &cnm.header);
633   return GNUNET_OK;
634 }
635
636
637 /**
638  * Handle CORE_ITERATE_PEERS request. For this request type, the client
639  * does not have to have transmitted an INIT request.  All current peers
640  * are returned, regardless of which message types they accept. 
641  *
642  * @param cls unused
643  * @param client client sending the iteration request
644  * @param message iteration request message
645  */
646 void
647 GSC_SESSIONS_handle_client_iterate_peers (void *cls, struct GNUNET_SERVER_Client *client,
648                                           const struct GNUNET_MessageHeader *message)
649 {
650   struct GNUNET_MessageHeader done_msg;
651   struct GNUNET_SERVER_TransmitContext *tc;
652
653   tc = GNUNET_SERVER_transmit_context_create (client);
654   GNUNET_CONTAINER_multihashmap_iterate (sessions, 
655                                          &queue_connect_message,
656                                          tc);
657   done_msg.size = htons (sizeof (struct GNUNET_MessageHeader));
658   done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END);
659   GNUNET_SERVER_transmit_context_append_message (tc, &done_msg);
660   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
661 }
662
663
664 /**
665  * Handle CORE_PEER_CONNECTED request.   Notify client about connection
666  * to the given neighbour.  For this request type, the client does not
667  * have to have transmitted an INIT request.  All current peers are
668  * returned, regardless of which message types they accept.
669  *
670  * @param cls unused
671  * @param client client sending the iteration request
672  * @param message iteration request message
673  */
674 void
675 GSC_SESSIONS_handle_client_have_peer (void *cls, struct GNUNET_SERVER_Client *client,
676                                       const struct GNUNET_MessageHeader *message)
677 {
678   struct GNUNET_MessageHeader done_msg;
679   struct GNUNET_SERVER_TransmitContext *tc;
680   const struct GNUNET_PeerIdentity *peer;
681
682   peer = (const struct GNUNET_PeerIdentity *) &message[1]; // YUCK!
683   tc = GNUNET_SERVER_transmit_context_create (client);
684   GNUNET_CONTAINER_multihashmap_get_multiple (sessions, &peer->hashPubKey,
685                                               &queue_connect_message, tc);
686   done_msg.size = htons (sizeof (struct GNUNET_MessageHeader));
687   done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END);
688   GNUNET_SERVER_transmit_context_append_message (tc, &done_msg);
689   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
690 }
691
692
693 /**
694  * Initialize sessions subsystem.
695  */
696 void
697 GSC_SESSIONS_init ()
698 {
699   sessions = GNUNET_CONTAINER_multihashmap_create (128);
700 }
701
702
703 /**
704  * Helper function for GSC_SESSIONS_handle_client_iterate_peers.
705  *
706  * @param cls NULL
707  * @param key identity of the connected peer
708  * @param value the 'struct Session' for the peer
709  * @return GNUNET_OK (continue to iterate)
710  */
711 static int
712 free_session_helper (void *cls, const GNUNET_HashCode * key, void *value)
713 {
714   struct Session *session = value;
715
716   GSC_SESSIONS_end (&session->peer);
717   return GNUNET_OK;
718 }
719
720
721 /**
722  * Shutdown sessions subsystem.
723  */
724 void
725 GSC_SESSIONS_done ()
726 {
727   GNUNET_CONTAINER_multihashmap_iterate (sessions,
728                                          &free_session_helper,
729                                          NULL);
730   GNUNET_CONTAINER_multihashmap_destroy (sessions);
731   sessions = NULL;
732   GNUNET_STATISTICS_set (GSC_stats, 
733                          gettext_noop ("# established sessions"),
734                          0, GNUNET_NO);
735 }
736
737 /* end of gnunet-service-core_sessions.c */
738