session end function must include address to notify address
[oweals/gnunet.git] / src / core / gnunet-service-core_sessions.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009-2014 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  * How often do we transmit our typemap?
37  */
38 #define TYPEMAP_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
39
40 /**
41  * How often do we transmit our typemap on first attempt?
42  */
43 #define TYPEMAP_FREQUENCY_FIRST GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
44
45
46 /**
47  * Message ready for encryption.  This struct is followed by the
48  * actual content of the message.
49  */
50 struct SessionMessageEntry
51 {
52
53   /**
54    * We keep messages in a doubly linked list.
55    */
56   struct SessionMessageEntry *next;
57
58   /**
59    * We keep messages in a doubly linked list.
60    */
61   struct SessionMessageEntry *prev;
62
63   /**
64    * Deadline for transmission, 1s after we received it (if we
65    * are not corking), otherwise "now".  Note that this message
66    * does NOT expire past its deadline.
67    */
68   struct GNUNET_TIME_Absolute deadline;
69
70   /**
71    * How long is the message? (number of bytes following the "struct
72    * MessageEntry", but not including the size of "struct
73    * MessageEntry" itself!)
74    */
75   size_t size;
76
77   /**
78    * How important is this message.
79    */
80   enum GNUNET_CORE_Priority priority;
81
82 };
83
84
85 /**
86  * Data kept per session.
87  */
88 struct Session
89 {
90   /**
91    * Identity of the other peer.
92    */
93   struct GNUNET_PeerIdentity peer;
94
95   /**
96    * Head of list of requests from clients for transmission to
97    * this peer.
98    */
99   struct GSC_ClientActiveRequest *active_client_request_head;
100
101   /**
102    * Tail of list of requests from clients for transmission to
103    * this peer.
104    */
105   struct GSC_ClientActiveRequest *active_client_request_tail;
106
107   /**
108    * Head of list of messages ready for encryption.
109    */
110   struct SessionMessageEntry *sme_head;
111
112   /**
113    * Tail of list of messages ready for encryption.
114    */
115   struct SessionMessageEntry *sme_tail;
116
117   /**
118    * Information about the key exchange with the other peer.
119    */
120   struct GSC_KeyExchangeInfo *kxinfo;
121
122   /**
123    * Current type map for this peer.
124    */
125   struct GSC_TypeMap *tmap;
126
127   /**
128    * Task to transmit corked messages with a delay.
129    */
130   GNUNET_SCHEDULER_TaskIdentifier cork_task;
131
132   /**
133    * Task to transmit our type map.
134    */
135   GNUNET_SCHEDULER_TaskIdentifier typemap_task;
136
137   /**
138    * Is the neighbour queue empty and thus ready for us
139    * to transmit an encrypted message?
140    */
141   int ready_to_transmit;
142
143   /**
144    * Is this the first time we're sending the typemap? If so,
145    * we want to send it a bit faster the second time.  0 if
146    * we are sending for the first time, 1 if not.
147    */
148   int first_typemap;
149 };
150
151
152 /**
153  * Map of peer identities to `struct Session`.
154  */
155 static struct GNUNET_CONTAINER_MultiPeerMap *sessions;
156
157
158 /**
159  * Find the session for the given peer.
160  *
161  * @param peer identity of the peer
162  * @return NULL if we are not connected, otherwise the
163  *         session handle
164  */
165 static struct Session *
166 find_session (const struct GNUNET_PeerIdentity *peer)
167 {
168   return GNUNET_CONTAINER_multipeermap_get (sessions, peer);
169 }
170
171
172 /**
173  * End the session with the given peer (we are no longer
174  * connected).
175  *
176  * @param pid identity of peer to kill session with
177  */
178 void
179 GSC_SESSIONS_end (const struct GNUNET_PeerIdentity *pid)
180 {
181   struct Session *session;
182   struct GSC_ClientActiveRequest *car;
183   struct SessionMessageEntry *sme;
184
185   session = find_session (pid);
186   if (NULL == session)
187     return;
188   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
189               "Destroying session for peer `%4s'\n",
190               GNUNET_i2s (&session->peer));
191   if (GNUNET_SCHEDULER_NO_TASK != session->cork_task)
192   {
193     GNUNET_SCHEDULER_cancel (session->cork_task);
194     session->cork_task = GNUNET_SCHEDULER_NO_TASK;
195   }
196   while (NULL != (car = session->active_client_request_head))
197   {
198     GNUNET_CONTAINER_DLL_remove (session->active_client_request_head,
199                                  session->active_client_request_tail, car);
200     GSC_CLIENTS_reject_request (car);
201   }
202   while (NULL != (sme = session->sme_head))
203   {
204     GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, sme);
205     GNUNET_free (sme);
206   }
207   GNUNET_SCHEDULER_cancel (session->typemap_task);
208   GSC_CLIENTS_notify_clients_about_neighbour (&session->peer,
209                                               session->tmap, NULL);
210   GNUNET_assert (GNUNET_YES ==
211                  GNUNET_CONTAINER_multipeermap_remove (sessions,
212                                                        &session->peer,
213                                                        session));
214   GNUNET_STATISTICS_set (GSC_stats, gettext_noop ("# peers connected"),
215                          GNUNET_CONTAINER_multipeermap_size (sessions),
216                          GNUNET_NO);
217   GSC_TYPEMAP_destroy (session->tmap);
218   session->tmap = NULL;
219   GNUNET_free (session);
220 }
221
222
223 /**
224  * Transmit our current typemap message to the other peer.
225  * (Done periodically in case an update got lost).
226  *
227  * @param cls the `struct Session *`
228  * @param tc unused
229  */
230 static void
231 transmit_typemap_task (void *cls,
232                        const struct GNUNET_SCHEDULER_TaskContext *tc)
233 {
234   struct Session *session = cls;
235   struct GNUNET_MessageHeader *hdr;
236   struct GNUNET_TIME_Relative delay;
237
238   if (0 == session->first_typemap)
239   {
240     delay = TYPEMAP_FREQUENCY_FIRST;
241     session->first_typemap = 1;
242   }
243   else
244   {
245     delay = TYPEMAP_FREQUENCY;
246   }
247   /* randomize a bit to avoid spont. sync */
248   delay.rel_value_us +=
249       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1000 * 1000);
250   session->typemap_task =
251       GNUNET_SCHEDULER_add_delayed (delay, &transmit_typemap_task, session);
252   GNUNET_STATISTICS_update (GSC_stats,
253                             gettext_noop ("# type map refreshes sent"), 1,
254                             GNUNET_NO);
255   hdr = GSC_TYPEMAP_compute_type_map_message ();
256   GSC_KX_encrypt_and_transmit (session->kxinfo, hdr, ntohs (hdr->size));
257   GNUNET_free (hdr);
258 }
259
260
261 /**
262  * Create a session, a key exchange was just completed.
263  *
264  * @param peer peer that is now connected
265  * @param kx key exchange that completed
266  */
267 void
268 GSC_SESSIONS_create (const struct GNUNET_PeerIdentity *peer,
269                      struct GSC_KeyExchangeInfo *kx)
270 {
271   struct Session *session;
272
273   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
274               "Creating session for peer `%4s'\n",
275               GNUNET_i2s (peer));
276   session = GNUNET_new (struct Session);
277   session->tmap = GSC_TYPEMAP_create ();
278   session->peer = *peer;
279   session->kxinfo = kx;
280   session->typemap_task =
281       GNUNET_SCHEDULER_add_now (&transmit_typemap_task, session);
282   GNUNET_assert (GNUNET_OK ==
283                  GNUNET_CONTAINER_multipeermap_put (sessions, peer,
284                                                     session,
285                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
286   GNUNET_STATISTICS_set (GSC_stats, gettext_noop ("# peers connected"),
287                          GNUNET_CONTAINER_multipeermap_size (sessions),
288                          GNUNET_NO);
289   GSC_CLIENTS_notify_clients_about_neighbour (peer,
290                                               NULL, session->tmap);
291 }
292
293
294 /**
295  * Notify the given client about the session (client is new).
296  *
297  * @param cls the `struct GSC_Client`
298  * @param key peer identity
299  * @param value the `struct Session`
300  * @return #GNUNET_OK (continue to iterate)
301  */
302 static int
303 notify_client_about_session (void *cls,
304                              const struct GNUNET_PeerIdentity *key,
305                              void *value)
306 {
307   struct GSC_Client *client = cls;
308   struct Session *session = value;
309
310   GSC_CLIENTS_notify_client_about_neighbour (client, &session->peer,
311                                              NULL,      /* old TMAP: none */
312                                              session->tmap);
313   return GNUNET_OK;
314 }
315
316
317 /**
318  * We have a new client, notify it about all current sessions.
319  *
320  * @param client the new client
321  */
322 void
323 GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client)
324 {
325   /* notify new client about existing sessions */
326   GNUNET_CONTAINER_multipeermap_iterate (sessions, &notify_client_about_session,
327                                          client);
328 }
329
330
331 /**
332  * Try to perform a transmission on the given session.  Will solicit
333  * additional messages if the 'sme' queue is not full enough.
334  *
335  * @param session session to transmit messages from
336  */
337 static void
338 try_transmission (struct Session *session);
339
340
341 /**
342  * Queue a request from a client for transmission to a particular peer.
343  *
344  * @param car request to queue; this handle is then shared between
345  *         the caller (CLIENTS subsystem) and SESSIONS and must not
346  *         be released by either until either #GSC_SESSIONS_dequeue(),
347  *         #GSC_SESSIONS_transmit() or #GSC_CLIENTS_failed()
348  *         have been invoked on it
349  */
350 void
351 GSC_SESSIONS_queue_request (struct GSC_ClientActiveRequest *car)
352 {
353   struct Session *session;
354
355   session = find_session (&car->target);
356   if (NULL == session)
357   {
358     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
359                 "Dropped client request for transmission (am disconnected)\n");
360     GNUNET_break (0);           /* should have been rejected earlier */
361     GSC_CLIENTS_reject_request (car);
362     return;
363   }
364   if (car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
365   {
366     GNUNET_break (0);
367     GSC_CLIENTS_reject_request (car);
368     return;
369   }
370   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
371               "Received client transmission request. queueing\n");
372   GNUNET_CONTAINER_DLL_insert (session->active_client_request_head,
373                                session->active_client_request_tail, car);
374   try_transmission (session);
375 }
376
377
378 /**
379  * Dequeue a request from a client from transmission to a particular peer.
380  *
381  * @param car request to dequeue; this handle will then be 'owned' by
382  *        the caller (CLIENTS sysbsystem)
383  */
384 void
385 GSC_SESSIONS_dequeue_request (struct GSC_ClientActiveRequest *car)
386 {
387   struct Session *s;
388
389   if (0 ==
390       memcmp (&car->target, &GSC_my_identity,
391               sizeof (struct GNUNET_PeerIdentity)))
392     return;
393   s = find_session (&car->target);
394   GNUNET_assert (NULL != s);
395   GNUNET_CONTAINER_DLL_remove (s->active_client_request_head,
396                                s->active_client_request_tail, car);
397 }
398
399
400 /**
401  * Discard all expired active transmission requests from clients.
402  *
403  * @param session session to clean up
404  */
405 static void
406 discard_expired_requests (struct Session *session)
407 {
408   struct GSC_ClientActiveRequest *pos;
409   struct GSC_ClientActiveRequest *nxt;
410   struct GNUNET_TIME_Absolute now;
411
412   now = GNUNET_TIME_absolute_get ();
413   pos = NULL;
414   nxt = session->active_client_request_head;
415   while (NULL != nxt)
416   {
417     pos = nxt;
418     nxt = pos->next;
419     if ((pos->deadline.abs_value_us < now.abs_value_us) &&
420         (GNUNET_YES != pos->was_solicited))
421     {
422       GNUNET_STATISTICS_update (GSC_stats,
423                                 gettext_noop
424                                 ("# messages discarded (expired prior to transmission)"),
425                                 1, GNUNET_NO);
426       GNUNET_CONTAINER_DLL_remove (session->active_client_request_head,
427                                    session->active_client_request_tail, pos);
428       GSC_CLIENTS_reject_request (pos);
429     }
430   }
431 }
432
433
434 /**
435  * Solicit messages for transmission, starting with those of the highest
436  * priority.
437  *
438  * @param session session to solict messages for
439  * @param msize how many bytes do we have already
440  */
441 static void
442 solicit_messages (struct Session *session,
443                   size_t msize)
444 {
445   struct GSC_ClientActiveRequest *car;
446   struct GSC_ClientActiveRequest *nxt;
447   size_t so_size;
448   enum GNUNET_CORE_Priority pmax;
449
450   discard_expired_requests (session);
451   so_size = msize;
452   pmax = GNUNET_CORE_PRIO_BACKGROUND;
453   for (car = session->active_client_request_head; NULL != car; car = car->next)
454   {
455     if (GNUNET_YES == car->was_solicited)
456       continue;
457     pmax = GNUNET_MAX (pmax, car->priority);
458   }
459   nxt = session->active_client_request_head;
460   while (NULL != (car = nxt))
461   {
462     nxt = car->next;
463     if (car->priority < pmax)
464       continue;
465     if (so_size + car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
466       break;
467     so_size += car->msize;
468     if (GNUNET_YES == car->was_solicited)
469       continue;
470     car->was_solicited = GNUNET_YES;
471     GSC_CLIENTS_solicit_request (car);
472   }
473 }
474
475
476 /**
477  * Some messages were delayed (corked), but the timeout has now expired.
478  * Send them now.
479  *
480  * @param cls `struct Session` with the messages to transmit now
481  * @param tc scheduler context (unused)
482  */
483 static void
484 pop_cork_task (void *cls,
485                const struct GNUNET_SCHEDULER_TaskContext *tc)
486 {
487   struct Session *session = cls;
488
489   session->cork_task = GNUNET_SCHEDULER_NO_TASK;
490   try_transmission (session);
491 }
492
493
494 /**
495  * Try to perform a transmission on the given session. Will solicit
496  * additional messages if the 'sme' queue is not full enough or has
497  * only low-priority messages.
498  *
499  * @param session session to transmit messages from
500  */
501 static void
502 try_transmission (struct Session *session)
503 {
504   struct SessionMessageEntry *pos;
505   size_t msize;
506   struct GNUNET_TIME_Absolute now;
507   struct GNUNET_TIME_Absolute min_deadline;
508   enum GNUNET_CORE_Priority maxp;
509   enum GNUNET_CORE_Priority maxpc;
510   struct GSC_ClientActiveRequest *car;
511   int excess;
512
513   if (GNUNET_YES != session->ready_to_transmit)
514     return;
515   msize = 0;
516   min_deadline = GNUNET_TIME_UNIT_FOREVER_ABS;
517   /* if the peer has excess bandwidth, background traffic is allowed,
518      otherwise not */
519   excess = GSC_NEIGHBOURS_check_excess_bandwidth (&session->peer);
520   if (GNUNET_YES == excess)
521     maxp = GNUNET_CORE_PRIO_BACKGROUND;
522   else
523     maxp = GNUNET_CORE_PRIO_BEST_EFFORT;
524   /* determine highest priority of 'ready' messages we already solicited from clients */
525   pos = session->sme_head;
526   while ((NULL != pos) &&
527          (msize + pos->size <= GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE))
528   {
529     GNUNET_assert (pos->size < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE);
530     msize += pos->size;
531     maxp = GNUNET_MAX (maxp, pos->priority);
532     min_deadline = GNUNET_TIME_absolute_min (min_deadline, pos->deadline);
533     pos = pos->next;
534   }
535   if (maxp < GNUNET_CORE_PRIO_CRITICAL_CONTROL)
536   {
537     /* if highest already solicited priority from clients is not critical,
538        check if there are higher-priority messages to be solicited from clients */
539     if (GNUNET_YES == excess)
540       maxpc = GNUNET_CORE_PRIO_BACKGROUND;
541     else
542       maxpc = GNUNET_CORE_PRIO_BEST_EFFORT;
543     for (car = session->active_client_request_head; NULL != car; car = car->next)
544     {
545       if (GNUNET_YES == car->was_solicited)
546         continue;
547       maxpc = GNUNET_MAX (maxpc, car->priority);
548     }
549     if (maxpc > maxp)
550     {
551       /* we have messages waiting for solicitation that have a higher
552          priority than those that we already accepted; solicit the
553          high-priority messages first */
554       solicit_messages (session, 0);
555       return;
556     }
557   }
558
559   now = GNUNET_TIME_absolute_get ();
560   if ( ( (GNUNET_YES == excess) ||
561          (maxpc >= GNUNET_CORE_PRIO_BEST_EFFORT) ) &&
562        ( (0 == msize) ||
563          ( (msize < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE / 2) &&
564            (min_deadline.abs_value_us > now.abs_value_us))) )
565   {
566     /* not enough ready yet (tiny message & cork possible), or no messages at all,
567        and either excess bandwidth or best-effort or higher message waiting at
568        client; in this case, we try to solicit more */
569     solicit_messages (session,
570                       msize);
571     if (msize > 0)
572     {
573       /* if there is data to send, just not yet, make sure we do transmit
574        * it once the deadline is reached */
575       if (GNUNET_SCHEDULER_NO_TASK != session->cork_task)
576         GNUNET_SCHEDULER_cancel (session->cork_task);
577       session->cork_task =
578           GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
579                                         (min_deadline), &pop_cork_task,
580                                         session);
581     }
582     return;
583   }
584   /* create plaintext buffer of all messages (that fit), encrypt and
585      transmit */
586   {
587     static unsigned long long total_bytes;
588     static unsigned int total_msgs;
589     char pbuf[msize];           /* plaintext */
590     size_t used;
591
592     used = 0;
593     while ((NULL != (pos = session->sme_head)) && (used + pos->size <= msize))
594     {
595       memcpy (&pbuf[used], &pos[1], pos->size);
596       used += pos->size;
597       GNUNET_CONTAINER_DLL_remove (session->sme_head, session->sme_tail, pos);
598       GNUNET_free (pos);
599     }
600     /* compute average payload size */
601     total_bytes += used;
602     total_msgs++;
603     if (0 == total_msgs)
604     {
605       /* 2^32 messages, wrap around... */
606       total_msgs = 1;
607       total_bytes = used;
608     }
609     GNUNET_STATISTICS_set (GSC_stats,
610                            "# avg payload per encrypted message",
611                            total_bytes / total_msgs, GNUNET_NO);
612     /* now actually transmit... */
613     session->ready_to_transmit = GNUNET_NO;
614     GSC_KX_encrypt_and_transmit (session->kxinfo, pbuf, used);
615   }
616 }
617
618
619 /**
620  * Send a message to the neighbour now.
621  *
622  * @param cls the message
623  * @param key neighbour's identity
624  * @param value `struct Neighbour` of the target
625  * @return always #GNUNET_OK
626  */
627 static int
628 do_send_message (void *cls,
629                  const struct GNUNET_PeerIdentity *key,
630                  void *value)
631 {
632   const struct GNUNET_MessageHeader *hdr = cls;
633   struct Session *session = value;
634   struct SessionMessageEntry *m;
635   uint16_t size;
636
637   size = ntohs (hdr->size);
638   m = GNUNET_malloc (sizeof (struct SessionMessageEntry) + size);
639   memcpy (&m[1], hdr, size);
640   m->size = size;
641   m->priority = GNUNET_CORE_PRIO_CRITICAL_CONTROL;
642   GNUNET_CONTAINER_DLL_insert_tail (session->sme_head, session->sme_tail, m);
643   try_transmission (session);
644   return GNUNET_OK;
645 }
646
647
648 /**
649  * Broadcast a message to all neighbours.
650  *
651  * @param msg message to transmit
652  */
653 void
654 GSC_SESSIONS_broadcast (const struct GNUNET_MessageHeader *msg)
655 {
656   if (NULL == sessions)
657     return;
658   GNUNET_CONTAINER_multipeermap_iterate (sessions,
659                                          &do_send_message,
660                                          (void *) msg);
661 }
662
663
664 /**
665  * Traffic is being solicited for the given peer.  This means that the
666  * message queue on the transport-level (NEIGHBOURS subsystem) is now
667  * empty and it is now OK to transmit another (non-control) message.
668  *
669  * @param pid identity of peer ready to receive data
670  */
671 void
672 GSC_SESSIONS_solicit (const struct GNUNET_PeerIdentity *pid)
673 {
674   struct Session *session;
675
676   session = find_session (pid);
677   if (NULL == session)
678     return;
679   session->ready_to_transmit = GNUNET_YES;
680   try_transmission (session);
681 }
682
683
684 /**
685  * Transmit a message to a particular peer.
686  *
687  * @param car original request that was queued and then solicited;
688  *            this handle will now be 'owned' by the SESSIONS subsystem
689  * @param msg message to transmit
690  * @param cork is corking allowed?
691  * @param priority how important is this message
692  */
693 void
694 GSC_SESSIONS_transmit (struct GSC_ClientActiveRequest *car,
695                        const struct GNUNET_MessageHeader *msg,
696                        int cork,
697                        enum GNUNET_CORE_Priority priority)
698 {
699   struct Session *session;
700   struct SessionMessageEntry *sme;
701   struct SessionMessageEntry *pos;
702   size_t msize;
703
704   session = find_session (&car->target);
705   if (NULL == session)
706     return;
707   msize = ntohs (msg->size);
708   sme = GNUNET_malloc (sizeof (struct SessionMessageEntry) + msize);
709   memcpy (&sme[1], msg, msize);
710   sme->size = msize;
711   sme->priority = priority;
712   if (GNUNET_YES == cork)
713     sme->deadline =
714         GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_MAX_CORK_DELAY);
715   pos = session->sme_head;
716   while ( (NULL != pos) &&
717           (pos->priority > sme->priority) )
718     pos = pos->next;
719   if (NULL == pos)
720     GNUNET_CONTAINER_DLL_insert_tail (session->sme_head,
721                                       session->sme_tail,
722                                       sme);
723   else
724     GNUNET_CONTAINER_DLL_insert_after (session->sme_head,
725                                        session->sme_tail,
726                                        pos->prev,
727                                        sme);
728   try_transmission (session);
729 }
730
731
732 /**
733  * Helper function for GSC_SESSIONS_handle_client_iterate_peers.
734  *
735  * @param cls the `struct GNUNET_SERVER_TransmitContext` to queue replies
736  * @param key identity of the connected peer
737  * @param value the `struct Neighbour` for the peer
738  * @return GNUNET_OK (continue to iterate)
739  */
740 #include "core.h"
741 static int
742 queue_connect_message (void *cls,
743                        const struct GNUNET_PeerIdentity *key,
744                        void *value)
745 {
746   struct GNUNET_SERVER_TransmitContext *tc = cls;
747   struct Session *session = value;
748   struct ConnectNotifyMessage cnm;
749
750   /* FIXME: code duplication with clients... */
751   cnm.header.size = htons (sizeof (struct ConnectNotifyMessage));
752   cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
753   cnm.reserved = htonl (0);
754   cnm.peer = session->peer;
755   GNUNET_SERVER_transmit_context_append_message (tc, &cnm.header);
756   return GNUNET_OK;
757 }
758
759
760 /**
761  * Handle CORE_ITERATE_PEERS request. For this request type, the client
762  * does not have to have transmitted an INIT request.  All current peers
763  * are returned, regardless of which message types they accept.
764  *
765  * @param cls unused
766  * @param client client sending the iteration request
767  * @param message iteration request message
768  */
769 void
770 GSC_SESSIONS_handle_client_iterate_peers (void *cls,
771                                           struct GNUNET_SERVER_Client *client,
772                                           const struct GNUNET_MessageHeader
773                                           *message)
774 {
775   struct GNUNET_MessageHeader done_msg;
776   struct GNUNET_SERVER_TransmitContext *tc;
777
778   tc = GNUNET_SERVER_transmit_context_create (client);
779   GNUNET_CONTAINER_multipeermap_iterate (sessions, &queue_connect_message, tc);
780   done_msg.size = htons (sizeof (struct GNUNET_MessageHeader));
781   done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END);
782   GNUNET_SERVER_transmit_context_append_message (tc, &done_msg);
783   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
784 }
785
786
787 /**
788  * We've received a typemap message from a peer, update ours.
789  * Notifies clients about the session.
790  *
791  * @param peer peer this is about
792  * @param msg typemap update message
793  */
794 void
795 GSC_SESSIONS_set_typemap (const struct GNUNET_PeerIdentity *peer,
796                           const struct GNUNET_MessageHeader *msg)
797 {
798   struct Session *session;
799   struct GSC_TypeMap *nmap;
800
801   nmap = GSC_TYPEMAP_get_from_message (msg);
802   if (NULL == nmap)
803     return;                     /* malformed */
804   session = find_session (peer);
805   if (NULL == session)
806   {
807     GNUNET_break (0);
808     return;
809   }
810   GSC_CLIENTS_notify_clients_about_neighbour (peer,
811                                               session->tmap, nmap);
812   GSC_TYPEMAP_destroy (session->tmap);
813   session->tmap = nmap;
814 }
815
816
817 /**
818  * The given peer send a message of the specified type.  Make sure the
819  * respective bit is set in its type-map and that clients are notified
820  * about the session.
821  *
822  * @param peer peer this is about
823  * @param type type of the message
824  */
825 void
826 GSC_SESSIONS_add_to_typemap (const struct GNUNET_PeerIdentity *peer,
827                              uint16_t type)
828 {
829   struct Session *session;
830   struct GSC_TypeMap *nmap;
831
832   if (0 == memcmp (peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
833     return;
834   session = find_session (peer);
835   GNUNET_assert (NULL != session);
836   if (GNUNET_YES == GSC_TYPEMAP_test_match (session->tmap, &type, 1))
837     return;                     /* already in it */
838   nmap = GSC_TYPEMAP_extend (session->tmap, &type, 1);
839   GSC_CLIENTS_notify_clients_about_neighbour (peer,
840                                               session->tmap, nmap);
841   GSC_TYPEMAP_destroy (session->tmap);
842   session->tmap = nmap;
843 }
844
845
846 /**
847  * Initialize sessions subsystem.
848  */
849 void
850 GSC_SESSIONS_init ()
851 {
852   sessions = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
853 }
854
855
856 /**
857  * Helper function for GSC_SESSIONS_handle_client_iterate_peers.
858  *
859  * @param cls NULL
860  * @param key identity of the connected peer
861  * @param value the `struct Session` for the peer
862  * @return #GNUNET_OK (continue to iterate)
863  */
864 static int
865 free_session_helper (void *cls,
866                      const struct GNUNET_PeerIdentity *key,
867                      void *value)
868 {
869   struct Session *session = value;
870
871   GSC_SESSIONS_end (&session->peer);
872   return GNUNET_OK;
873 }
874
875
876 /**
877  * Shutdown sessions subsystem.
878  */
879 void
880 GSC_SESSIONS_done ()
881 {
882   if (NULL != sessions)
883   {
884     GNUNET_CONTAINER_multipeermap_iterate (sessions, &free_session_helper, NULL);
885     GNUNET_CONTAINER_multipeermap_destroy (sessions);
886     sessions = NULL;
887   }
888 }
889
890 /* end of gnunet-service-core_sessions.c */