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