move GNUNET_TRANSPORT_ATS_ to GNUNET_ATS_
[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   return GNUNET_OK;
261 }
262
263
264 /**
265  * We have a new client, notify it about all current sessions.
266  *
267  * @param client the new client
268  */
269 void
270 GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client)
271 {
272   /* notify new client about existing sessions */
273   GNUNET_CONTAINER_multihashmap_iterate (sessions,
274                                          &notify_client_about_session, client);
275 }
276
277
278 /**
279  * Try to perform a transmission on the given session.  Will solicit
280  * additional messages if the 'sme' queue is not full enough.
281  *
282  * @param session session to transmit messages from
283  */
284 static void
285 try_transmission (struct Session *session);
286
287
288 /**
289  * Queue a request from a client for transmission to a particular peer.
290  *
291  * @param car request to queue; this handle is then shared between
292  *         the caller (CLIENTS subsystem) and SESSIONS and must not
293  *         be released by either until either 'GNUNET_SESSIONS_dequeue',
294  *         'GNUNET_SESSIONS_transmit' or 'GNUNET_CLIENTS_failed'
295  *         have been invoked on it
296  */
297 void
298 GSC_SESSIONS_queue_request (struct GSC_ClientActiveRequest *car)
299 {
300   struct Session *session;
301
302   session = find_session (&car->target);
303   if (session == NULL)
304   {
305     /* neighbour must have disconnected since request was issued,
306      * ignore (client will realize it once it processes the
307      * disconnect notification) */
308 #if DEBUG_CORE
309     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
310                 "Dropped client request for transmission (am disconnected)\n");
311 #endif
312     GNUNET_STATISTICS_update (GSC_stats,
313                               gettext_noop
314                               ("# send requests dropped (disconnected)"), 1,
315                               GNUNET_NO);
316     GSC_CLIENTS_reject_request (car);
317     return;
318   }
319   if (car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
320   {
321     GNUNET_break (0);
322     GSC_CLIENTS_reject_request (car);
323     return;
324   }
325 #if DEBUG_CORE
326   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
327               "Received client transmission request. queueing\n");
328 #endif
329   GNUNET_CONTAINER_DLL_insert (session->active_client_request_head,
330                                session->active_client_request_tail, car);
331   try_transmission (session);
332 }
333
334
335 /**
336  * Dequeue a request from a client from transmission to a particular peer.
337  *
338  * @param car request to dequeue; this handle will then be 'owned' by
339  *        the caller (CLIENTS sysbsystem)
340  */
341 void
342 GSC_SESSIONS_dequeue_request (struct GSC_ClientActiveRequest *car)
343 {
344   struct Session *s;
345
346   if (0 == memcmp (&car->target,
347                    &GSC_my_identity,
348                    sizeof (struct GNUNET_PeerIdentity)))
349     return;
350   s = find_session (&car->target);  
351   GNUNET_assert (NULL != s);
352   GNUNET_CONTAINER_DLL_remove (s->active_client_request_head,
353                                s->active_client_request_tail, car);
354 }
355
356
357 /**
358  * Discard all expired active transmission requests from clients.
359  *
360  * @param session session to clean up
361  */
362 static void
363 discard_expired_requests (struct Session *session)
364 {
365   struct GSC_ClientActiveRequest *pos;
366   struct GSC_ClientActiveRequest *nxt;
367   struct GNUNET_TIME_Absolute now;
368   
369   now = GNUNET_TIME_absolute_get ();
370   pos = NULL;
371   nxt = session->active_client_request_head;
372   while (NULL != nxt)
373   {
374     pos = nxt;
375     nxt = pos->next;
376     if ( (pos->deadline.abs_value < now.abs_value) &&
377          (GNUNET_YES != pos->was_solicited) )
378     {
379       GNUNET_STATISTICS_update (GSC_stats,
380                                 gettext_noop
381                                 ("# messages discarded (expired prior to transmission)"),
382                                 1, GNUNET_NO);
383       GNUNET_CONTAINER_DLL_remove (session->active_client_request_head,
384                                    session->active_client_request_tail,
385                                    pos);
386       GSC_CLIENTS_reject_request (pos);
387     }
388   }
389 }
390
391
392 /**
393  * Solicit messages for transmission.
394  *
395  * @param session session to solict messages for
396  */
397 static void
398 solicit_messages (struct Session *session)
399 {
400   struct GSC_ClientActiveRequest *car;
401   size_t so_size;
402
403   discard_expired_requests (session); 
404   so_size = 0;
405   for (car = session->active_client_request_head; NULL != car; car = car->next)
406   {
407     if (so_size + car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
408       break;
409     so_size += car->msize;
410     if (car->was_solicited == GNUNET_YES)
411       continue;
412     car->was_solicited = GNUNET_YES;
413     GSC_CLIENTS_solicit_request (car);
414   }
415 }
416
417
418 /**
419  * Some messages were delayed (corked), but the timeout has now expired.  
420  * Send them now.
421  *
422  * @param cls 'struct Session' with the messages to transmit now
423  * @param tc scheduler context (unused)
424  */
425 static void
426 pop_cork_task (void *cls,
427                const struct GNUNET_SCHEDULER_TaskContext *tc)
428 {
429   struct Session *session = cls;
430
431   session->cork_task = GNUNET_SCHEDULER_NO_TASK;
432   try_transmission (session);
433 }
434
435
436 /**
437  * Try to perform a transmission on the given session. Will solicit
438  * additional messages if the 'sme' queue is not full enough.
439  *
440  * @param session session to transmit messages from
441  */
442 static void
443 try_transmission (struct Session *session)
444 {
445   struct SessionMessageEntry *pos;
446   size_t msize;
447   struct GNUNET_TIME_Absolute now;
448   struct GNUNET_TIME_Absolute min_deadline;
449
450   if (GNUNET_YES != session->ready_to_transmit)
451     return;
452   msize = 0;
453   min_deadline = GNUNET_TIME_UNIT_FOREVER_ABS;
454   /* check 'ready' messages */
455   pos = session->sme_head;
456   while ( (NULL != pos) &&
457           (msize + pos->size <= GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE) )
458   {
459     GNUNET_assert (pos->size < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE);
460     msize += pos->size;
461     min_deadline = GNUNET_TIME_absolute_min (min_deadline,
462                                              pos->deadline);
463     pos = pos->next;
464   }
465   now = GNUNET_TIME_absolute_get ();
466   if ( (msize == 0) ||
467        ( (msize < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE / 2) &&
468          (min_deadline.abs_value > now.abs_value) ) )
469   {
470     /* not enough ready yet, try to solicit more */
471     solicit_messages (session);
472     if (msize > 0)
473     {
474       /* if there is data to send, just not yet, make sure we do transmit
475          it once the deadline is reached */
476       if (session->cork_task != GNUNET_SCHEDULER_NO_TASK)
477         GNUNET_SCHEDULER_cancel (session->cork_task);
478       session->cork_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining (min_deadline),
479                                                          &pop_cork_task,
480                                                          session);
481     }
482     return;
483   }
484   /* create plaintext buffer of all messages, encrypt and transmit */
485   {
486     static unsigned long long total_bytes;
487     static unsigned int total_msgs;
488     char pbuf[msize];    /* plaintext */
489     size_t used;
490
491     used = 0;
492     while ( (NULL != (pos = session->sme_head)) &&
493             (used + pos->size <= msize) )
494     {
495       memcpy (&pbuf[used], &pos[1], pos->size);
496       used += pos->size;
497       GNUNET_CONTAINER_DLL_remove (session->sme_head,
498                                    session->sme_tail,
499                                    pos);      
500       GNUNET_free (pos);
501     }
502     /* compute average payload size */
503     total_bytes += used;
504     total_msgs++;
505     if (0 == total_msgs)
506     {
507       /* 2^32 messages, wrap around... */
508       total_msgs = 1;
509       total_bytes = used;
510     }
511     GNUNET_STATISTICS_set (GSC_stats, 
512                            "# avg payload per encrypted message",
513                            total_bytes / total_msgs,
514                            GNUNET_NO);
515     /* now actually transmit... */
516     session->ready_to_transmit = GNUNET_NO;
517     GSC_KX_encrypt_and_transmit (session->kxinfo,
518                                  pbuf,
519                                  used);
520   }
521 }
522
523
524 /**
525  * Send a message to the neighbour now.
526  *
527  * @param cls the message
528  * @param key neighbour's identity
529  * @param value 'struct Neighbour' of the target
530  * @return always GNUNET_OK
531  */
532 static int
533 do_send_message (void *cls, const GNUNET_HashCode * key, void *value)
534 {
535   const struct GNUNET_MessageHeader *hdr = cls;
536   struct Session *session = value;
537   struct SessionMessageEntry *m;
538   uint16_t size;
539
540   size = ntohs (hdr->size);
541   m = GNUNET_malloc (sizeof (struct SessionMessageEntry) + size);
542   memcpy (&m[1], hdr, size);
543   m->size = size;
544   GNUNET_CONTAINER_DLL_insert (session->sme_head,
545                                session->sme_tail,
546                                m);
547   try_transmission (session);
548   return GNUNET_OK;
549 }
550
551
552 /**
553  * Broadcast a message to all neighbours.
554  *
555  * @param msg message to transmit
556  */
557 void
558 GSC_SESSIONS_broadcast (const struct GNUNET_MessageHeader *msg)
559 {
560   if (NULL == sessions)
561     return;
562   GNUNET_CONTAINER_multihashmap_iterate (sessions,
563                                          &do_send_message, (void*) msg);
564 }
565
566
567 /**
568  * Traffic is being solicited for the given peer.  This means that the
569  * message queue on the transport-level (NEIGHBOURS subsystem) is now
570  * empty and it is now OK to transmit another (non-control) message.
571  *
572  * @param pid identity of peer ready to receive data
573  */
574 void
575 GSC_SESSIONS_solicit (const struct GNUNET_PeerIdentity *pid)
576 {
577   struct Session *session;
578
579   session = find_session (pid);
580   if (NULL == session)
581     return;
582   session->ready_to_transmit = GNUNET_YES;
583   try_transmission (session);
584 }
585
586
587 /**
588  * Transmit a message to a particular peer.
589  *
590  * @param car original request that was queued and then solicited;
591  *            this handle will now be 'owned' by the SESSIONS subsystem
592  * @param msg message to transmit
593  * @param cork is corking allowed?
594  */
595 void
596 GSC_SESSIONS_transmit (struct GSC_ClientActiveRequest *car,
597                        const struct GNUNET_MessageHeader *msg,
598                        int cork)
599 {
600   struct Session *session;
601   struct SessionMessageEntry *sme;
602   size_t msize;
603
604   session = find_session (&car->target);
605   if (NULL == session)
606     return;
607   msize = ntohs (msg->size);
608   sme = GNUNET_malloc (sizeof (struct SessionMessageEntry) + msize);
609   memcpy (&sme[1], msg, msize);
610   sme->size = msize;
611   if (GNUNET_YES == cork)
612     sme->deadline = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_MAX_CORK_DELAY);
613   GNUNET_CONTAINER_DLL_insert_tail (session->sme_head,
614                                     session->sme_tail,
615                                     sme);
616   try_transmission (session);
617 }
618
619
620 /**
621  * Helper function for GSC_SESSIONS_handle_client_iterate_peers.
622  *
623  * @param cls the 'struct GNUNET_SERVER_TransmitContext' to queue replies
624  * @param key identity of the connected peer
625  * @param value the 'struct Neighbour' for the peer
626  * @return GNUNET_OK (continue to iterate)
627  */
628 #include "core.h"
629 static int
630 queue_connect_message (void *cls, const GNUNET_HashCode * key, void *value)
631 {
632   struct GNUNET_SERVER_TransmitContext *tc = cls;
633   struct Session *session = value;
634   struct ConnectNotifyMessage cnm;
635   struct GNUNET_ATS_Information *a;
636  
637   /* FIXME: code duplication with clients... */
638   cnm.header.size = htons (sizeof (struct ConnectNotifyMessage));
639   cnm.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT);
640   cnm.ats_count = htonl (0);
641   cnm.peer = session->peer;
642   a = &cnm.ats;
643   // FIXME: full ats...
644   a[0].type = htonl (GNUNET_ATS_ARRAY_TERMINATOR);
645   a[0].value = htonl (0);
646   GNUNET_SERVER_transmit_context_append_message (tc, &cnm.header);
647   return GNUNET_OK;
648 }
649
650
651 /**
652  * Handle CORE_ITERATE_PEERS request. For this request type, the client
653  * does not have to have transmitted an INIT request.  All current peers
654  * are returned, regardless of which message types they accept. 
655  *
656  * @param cls unused
657  * @param client client sending the iteration request
658  * @param message iteration request message
659  */
660 void
661 GSC_SESSIONS_handle_client_iterate_peers (void *cls, struct GNUNET_SERVER_Client *client,
662                                           const struct GNUNET_MessageHeader *message)
663 {
664   struct GNUNET_MessageHeader done_msg;
665   struct GNUNET_SERVER_TransmitContext *tc;
666
667   tc = GNUNET_SERVER_transmit_context_create (client);
668   GNUNET_CONTAINER_multihashmap_iterate (sessions, 
669                                          &queue_connect_message,
670                                          tc);
671   done_msg.size = htons (sizeof (struct GNUNET_MessageHeader));
672   done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END);
673   GNUNET_SERVER_transmit_context_append_message (tc, &done_msg);
674   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
675 }
676
677
678 /**
679  * Handle CORE_PEER_CONNECTED request.   Notify client about connection
680  * to the given neighbour.  For this request type, the client does not
681  * have to have transmitted an INIT request.  All current peers are
682  * returned, regardless of which message types they accept.
683  *
684  * @param cls unused
685  * @param client client sending the iteration request
686  * @param message iteration request message
687  */
688 void
689 GSC_SESSIONS_handle_client_have_peer (void *cls, struct GNUNET_SERVER_Client *client,
690                                       const struct GNUNET_MessageHeader *message)
691 {
692   struct GNUNET_MessageHeader done_msg;
693   struct GNUNET_SERVER_TransmitContext *tc;
694   const struct GNUNET_PeerIdentity *peer;
695
696   peer = (const struct GNUNET_PeerIdentity *) &message[1]; // YUCK!
697   tc = GNUNET_SERVER_transmit_context_create (client);
698   GNUNET_CONTAINER_multihashmap_get_multiple (sessions, &peer->hashPubKey,
699                                               &queue_connect_message, tc);
700   done_msg.size = htons (sizeof (struct GNUNET_MessageHeader));
701   done_msg.type = htons (GNUNET_MESSAGE_TYPE_CORE_ITERATE_PEERS_END);
702   GNUNET_SERVER_transmit_context_append_message (tc, &done_msg);
703   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
704 }
705
706
707 /**
708  * We've received a typemap message from a peer, update ours.
709  * Notifies clients about the session.
710  *
711  * @param peer peer this is about
712  * @param msg typemap update message
713  */
714 void
715 GSC_SESSIONS_set_typemap (const struct GNUNET_PeerIdentity *peer,
716                           const struct GNUNET_MessageHeader *msg)
717 {
718   struct Session *session;
719   struct GSC_TypeMap *nmap;
720
721   nmap = GSC_TYPEMAP_get_from_message (msg);
722   if (NULL == nmap)
723     return; /* malformed */
724   session = find_session (peer);
725   if (NULL == session)
726   {
727     GNUNET_break (0);
728     return;
729   }
730   GSC_CLIENTS_notify_clients_about_neighbour (peer,
731                                               NULL, 0, /* FIXME: ATS */
732                                               session->tmap,
733                                               nmap);
734   if (NULL != session->tmap)
735     GSC_TYPEMAP_destroy (session->tmap);
736   session->tmap = nmap;
737 }
738
739
740 /**
741  * The given peer send a message of the specified type.  Make sure the
742  * respective bit is set in its type-map and that clients are notified
743  * about the session.
744  *
745  * @param peer peer this is about
746  * @param type type of the message
747  */
748 void
749 GSC_SESSIONS_add_to_typemap (const struct GNUNET_PeerIdentity *peer,
750                              uint16_t type)
751 {
752   struct Session *session;
753   struct GSC_TypeMap *nmap;
754
755   if (0 == memcmp (peer, &GSC_my_identity, sizeof (struct GNUNET_PeerIdentity)))
756     return;
757   session = find_session (peer);
758   GNUNET_assert (NULL != session);
759   if (GNUNET_YES ==
760       GSC_TYPEMAP_test_match (session->tmap,
761                               &type, 1))
762     return; /* already in it */
763   nmap = GSC_TYPEMAP_extend (session->tmap,
764                              &type, 1);
765   GSC_CLIENTS_notify_clients_about_neighbour (peer,
766                                               NULL, 0, /* FIXME: ATS */
767                                               session->tmap,
768                                               nmap);
769   if (NULL != session->tmap)
770     GSC_TYPEMAP_destroy (session->tmap);
771   session->tmap = nmap;
772 }
773
774
775 /**
776  * Initialize sessions subsystem.
777  */
778 void
779 GSC_SESSIONS_init ()
780 {
781   sessions = GNUNET_CONTAINER_multihashmap_create (128);
782 }
783
784
785 /**
786  * Helper function for GSC_SESSIONS_handle_client_iterate_peers.
787  *
788  * @param cls NULL
789  * @param key identity of the connected peer
790  * @param value the 'struct Session' for the peer
791  * @return GNUNET_OK (continue to iterate)
792  */
793 static int
794 free_session_helper (void *cls, const GNUNET_HashCode * key, void *value)
795 {
796   struct Session *session = value;
797
798   GSC_SESSIONS_end (&session->peer);
799   return GNUNET_OK;
800 }
801
802
803 /**
804  * Shutdown sessions subsystem.
805  */
806 void
807 GSC_SESSIONS_done ()
808 {
809   GNUNET_CONTAINER_multihashmap_iterate (sessions,
810                                          &free_session_helper,
811                                          NULL);
812   GNUNET_CONTAINER_multihashmap_destroy (sessions);
813   sessions = NULL;
814   GNUNET_STATISTICS_set (GSC_stats, 
815                          gettext_noop ("# established sessions"),
816                          0, GNUNET_NO);
817 }
818
819 /* end of gnunet-service-core_sessions.c */
820