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