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