a16da8b7e08bc8a9755e2552f22326134f7e87c1
[oweals/gnunet.git] / src / core / gnunet-service-core_sessions.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-2014 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file core/gnunet-service-core_sessions.c
23  * @brief code for managing of 'encrypted' sessions (key exchange done)
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet-service-core.h"
28 #include "gnunet-service-core_neighbours.h"
29 #include "gnunet-service-core_kx.h"
30 #include "gnunet-service-core_typemap.h"
31 #include "gnunet-service-core_sessions.h"
32 #include "gnunet-service-core_clients.h"
33 #include "gnunet_constants.h"
34 #include "core.h"
35
36
37 /**
38  * Message ready for encryption.  This struct is followed by the
39  * actual content of the message.
40  */
41 struct SessionMessageEntry
42 {
43
44   /**
45    * We keep messages in a doubly linked list.
46    */
47   struct SessionMessageEntry *next;
48
49   /**
50    * We keep messages in a doubly linked list.
51    */
52   struct SessionMessageEntry *prev;
53
54   /**
55    * Deadline for transmission, 1s after we received it (if we
56    * are not corking), otherwise "now".  Note that this message
57    * does NOT expire past its deadline.
58    */
59   struct GNUNET_TIME_Absolute deadline;
60
61   /**
62    * How long is the message? (number of bytes following the `struct
63    * MessageEntry`, but not including the size of `struct
64    * MessageEntry` itself!)
65    */
66   size_t size;
67
68   /**
69    * How important is this message.
70    */
71   enum GNUNET_CORE_Priority priority;
72
73 };
74
75
76 /**
77  * Data kept per session.
78  */
79 struct Session
80 {
81   /**
82    * Identity of the other peer.
83    */
84   struct GNUNET_PeerIdentity peer;
85
86   /**
87    * Head of list of requests from clients for transmission to
88    * this peer.
89    */
90   struct GSC_ClientActiveRequest *active_client_request_head;
91
92   /**
93    * Tail of list of requests from clients for transmission to
94    * this peer.
95    */
96   struct GSC_ClientActiveRequest *active_client_request_tail;
97
98   /**
99    * Head of list of messages ready for encryption.
100    */
101   struct SessionMessageEntry *sme_head;
102
103   /**
104    * Tail of list of messages ready for encryption.
105    */
106   struct SessionMessageEntry *sme_tail;
107
108   /**
109    * Information about the key exchange with the other peer.
110    */
111   struct GSC_KeyExchangeInfo *kxinfo;
112
113   /**
114    * Current type map for this peer.
115    */
116   struct GSC_TypeMap *tmap;
117
118   /**
119    * Task to transmit corked messages with a delay.
120    */
121   struct GNUNET_SCHEDULER_Task * cork_task;
122
123   /**
124    * Task to transmit our type map.
125    */
126   struct GNUNET_SCHEDULER_Task * typemap_task;
127
128   /**
129    * Retransmission delay we currently use for the typemap
130    * transmissions (if not confirmed).
131    */
132   struct GNUNET_TIME_Relative typemap_delay;
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    * Is this the first time we're sending the typemap? If so,
142    * we want to send it a bit faster the second time.  0 if
143    * we are sending for the first time, 1 if not.
144    */
145   int first_typemap;
146 };
147
148
149 GNUNET_NETWORK_STRUCT_BEGIN
150
151 /**
152  * Message sent to confirm that a typemap was received.
153  */
154 struct TypeMapConfirmationMessage
155 {
156
157   /**
158    * Header with type #GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP.
159    */
160   struct GNUNET_MessageHeader header;
161
162   /**
163    * Reserved, always zero.
164    */
165   uint32_t reserved GNUNET_PACKED;
166
167   /**
168    * Hash of the (decompressed) type map that was received.
169    */
170   struct GNUNET_HashCode tm_hash;
171
172 };
173
174 GNUNET_NETWORK_STRUCT_END
175
176
177 /**
178  * Map of peer identities to `struct Session`.
179  */
180 static struct GNUNET_CONTAINER_MultiPeerMap *sessions;
181
182
183 /**
184  * Find the session for the given peer.
185  *
186  * @param peer identity of the peer
187  * @return NULL if we are not connected, otherwise the
188  *         session handle
189  */
190 static struct Session *
191 find_session (const struct GNUNET_PeerIdentity *peer)
192 {
193   return GNUNET_CONTAINER_multipeermap_get (sessions, peer);
194 }
195
196
197 /**
198  * End the session with the given peer (we are no longer
199  * connected).
200  *
201  * @param pid identity of peer to kill session with
202  */
203 void
204 GSC_SESSIONS_end (const struct GNUNET_PeerIdentity *pid)
205 {
206   struct Session *session;
207   struct GSC_ClientActiveRequest *car;
208   struct SessionMessageEntry *sme;
209
210   session = find_session (pid);
211   if (NULL == session)
212     return;
213   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
214               "Destroying session for peer `%4s'\n",
215               GNUNET_i2s (&session->peer));
216   if (NULL != session->cork_task)
217   {
218     GNUNET_SCHEDULER_cancel (session->cork_task);
219     session->cork_task = NULL;
220   }
221   while (NULL != (car = session->active_client_request_head))
222   {
223     GNUNET_CONTAINER_DLL_remove (session->active_client_request_head,
224                                  session->active_client_request_tail, car);
225     GSC_CLIENTS_reject_request (car);
226   }
227   while (NULL != (sme = session->sme_head))
228   {
229     GNUNET_CONTAINER_DLL_remove (session->sme_head,
230                                  session->sme_tail,
231                                  sme);
232     GNUNET_free (sme);
233   }
234   if (NULL != session->typemap_task)
235   {
236     GNUNET_SCHEDULER_cancel (session->typemap_task);
237     session->typemap_task = NULL;
238   }
239   GSC_CLIENTS_notify_clients_about_neighbour (&session->peer,
240                                               session->tmap, NULL);
241   GNUNET_assert (GNUNET_YES ==
242                  GNUNET_CONTAINER_multipeermap_remove (sessions,
243                                                        &session->peer,
244                                                        session));
245   GNUNET_STATISTICS_set (GSC_stats, gettext_noop ("# peers connected"),
246                          GNUNET_CONTAINER_multipeermap_size (sessions),
247                          GNUNET_NO);
248   GSC_TYPEMAP_destroy (session->tmap);
249   session->tmap = NULL;
250   GNUNET_free (session);
251 }
252
253
254 /**
255  * Transmit our current typemap message to the other peer.
256  * (Done periodically until the typemap is confirmed).
257  *
258  * @param cls the `struct Session *`
259  * @param tc unused
260  */
261 static void
262 transmit_typemap_task (void *cls,
263                        const struct GNUNET_SCHEDULER_TaskContext *tc)
264 {
265   struct Session *session = cls;
266   struct GNUNET_MessageHeader *hdr;
267   struct GNUNET_TIME_Relative delay;
268
269   session->typemap_delay = GNUNET_TIME_STD_BACKOFF (session->typemap_delay);
270   delay = session->typemap_delay;
271   /* randomize a bit to avoid spont. sync */
272   delay.rel_value_us +=
273       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1000 * 1000);
274   session->typemap_task =
275       GNUNET_SCHEDULER_add_delayed (delay,
276                                     &transmit_typemap_task, session);
277   GNUNET_STATISTICS_update (GSC_stats,
278                             gettext_noop ("# type map refreshes sent"),
279                             1,
280                             GNUNET_NO);
281   hdr = GSC_TYPEMAP_compute_type_map_message ();
282   GSC_KX_encrypt_and_transmit (session->kxinfo,
283                                hdr,
284                                ntohs (hdr->size));
285   GNUNET_free (hdr);
286 }
287
288
289 /**
290  * Restart the typemap task for the given session.
291  *
292  * @param session session to restart typemap transmission for
293  */
294 static void
295 start_typemap_task (struct Session *session)
296 {
297   if (NULL != session->typemap_task)
298     GNUNET_SCHEDULER_cancel (session->typemap_task);
299   session->typemap_delay = GNUNET_TIME_UNIT_SECONDS;
300   session->typemap_task =
301     GNUNET_SCHEDULER_add_delayed (session->typemap_delay,
302                                   &transmit_typemap_task,
303                                   session);
304 }
305
306
307 /**
308  * Create a session, a key exchange was just completed.
309  *
310  * @param peer peer that is now connected
311  * @param kx key exchange that completed
312  */
313 void
314 GSC_SESSIONS_create (const struct GNUNET_PeerIdentity *peer,
315                      struct GSC_KeyExchangeInfo *kx)
316 {
317   struct Session *session;
318
319   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
320               "Creating session for peer `%4s'\n",
321               GNUNET_i2s (peer));
322   session = GNUNET_new (struct Session);
323   session->tmap = GSC_TYPEMAP_create ();
324   session->peer = *peer;
325   session->kxinfo = kx;
326   GNUNET_assert (GNUNET_OK ==
327                  GNUNET_CONTAINER_multipeermap_put (sessions,
328                                                     &session->peer,
329                                                     session,
330                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
331   GNUNET_STATISTICS_set (GSC_stats, gettext_noop ("# peers connected"),
332                          GNUNET_CONTAINER_multipeermap_size (sessions),
333                          GNUNET_NO);
334   GSC_CLIENTS_notify_clients_about_neighbour (peer,
335                                               NULL,
336                                               session->tmap);
337   start_typemap_task (session);
338 }
339
340
341 /**
342  * The other peer has indicated that he 'lost' the session
343  * (KX down), reinitialize the session on our end, in particular
344  * this means to restart the typemap transmission.
345  *
346  * @param peer peer that is now connected
347  */
348 void
349 GSC_SESSIONS_reinit (const struct GNUNET_PeerIdentity *peer)
350 {
351   struct Session *session;
352
353   session = find_session (peer);
354   if (NULL == session)
355   {
356     /* KX/session is new for both sides; thus no need to restart what
357        has not yet begun */
358     return;
359   }
360   start_typemap_task (session);
361 }
362
363
364 /**
365  * The other peer has confirmed receiving our type map,
366  * check if it is current and if so, stop retransmitting it.
367  *
368  * @param peer peer that confirmed the type map
369  * @param msg confirmation message we received
370  */
371 void
372 GSC_SESSIONS_confirm_typemap (const struct GNUNET_PeerIdentity *peer,
373                               const struct GNUNET_MessageHeader *msg)
374 {
375   const struct TypeMapConfirmationMessage *cmsg;
376   struct Session *session;
377
378   session = find_session (peer);
379   if (NULL == session)
380   {
381     GNUNET_break (0);
382     return;
383   }
384   if (ntohs (msg->size) != sizeof (struct TypeMapConfirmationMessage))
385   {
386     GNUNET_break_op (0);
387     return;
388   }
389   cmsg = (const struct TypeMapConfirmationMessage *) msg;
390   if (GNUNET_YES !=
391       GSC_TYPEMAP_check_hash (&cmsg->tm_hash))
392   {
393     /* our typemap has changed in the meantime, do not
394        accept confirmation */
395     GNUNET_STATISTICS_update (GSC_stats,
396                               gettext_noop
397                               ("# outdated typemap confirmations received"),
398                               1, GNUNET_NO);
399     return;
400   }
401   if (NULL != session->typemap_task)
402   {
403     GNUNET_SCHEDULER_cancel (session->typemap_task);
404     session->typemap_task = NULL;
405   }
406   GNUNET_STATISTICS_update (GSC_stats,
407                             gettext_noop
408                             ("# valid typemap confirmations received"),
409                             1, GNUNET_NO);
410 }
411
412
413 /**
414  * Notify the given client about the session (client is new).
415  *
416  * @param cls the `struct GSC_Client`
417  * @param key peer identity
418  * @param value the `struct Session`
419  * @return #GNUNET_OK (continue to iterate)
420  */
421 static int
422 notify_client_about_session (void *cls,
423                              const struct GNUNET_PeerIdentity *key,
424                              void *value)
425 {
426   struct GSC_Client *client = cls;
427   struct Session *session = value;
428
429   GSC_CLIENTS_notify_client_about_neighbour (client,
430                                              &session->peer,
431                                              NULL,      /* old TMAP: none */
432                                              session->tmap);
433   return GNUNET_OK;
434 }
435
436
437 /**
438  * We have a new client, notify it about all current sessions.
439  *
440  * @param client the new client
441  */
442 void
443 GSC_SESSIONS_notify_client_about_sessions (struct GSC_Client *client)
444 {
445   /* notify new client about existing sessions */
446   GNUNET_CONTAINER_multipeermap_iterate (sessions,
447                                          &notify_client_about_session,
448                                          client);
449 }
450
451
452 /**
453  * Try to perform a transmission on the given session.  Will solicit
454  * additional messages if the 'sme' queue is not full enough.
455  *
456  * @param session session to transmit messages from
457  */
458 static void
459 try_transmission (struct Session *session);
460
461
462 /**
463  * Queue a request from a client for transmission to a particular peer.
464  *
465  * @param car request to queue; this handle is then shared between
466  *         the caller (CLIENTS subsystem) and SESSIONS and must not
467  *         be released by either until either #GSC_SESSIONS_dequeue(),
468  *         #GSC_SESSIONS_transmit() or #GSC_CLIENTS_failed()
469  *         have been invoked on it
470  */
471 void
472 GSC_SESSIONS_queue_request (struct GSC_ClientActiveRequest *car)
473 {
474   struct Session *session;
475
476   session = find_session (&car->target);
477   if (NULL == session)
478   {
479     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
480                 "Dropped client request for transmission (am disconnected)\n");
481     GNUNET_break (0);           /* should have been rejected earlier */
482     GSC_CLIENTS_reject_request (car);
483     return;
484   }
485   if (car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
486   {
487     GNUNET_break (0);
488     GSC_CLIENTS_reject_request (car);
489     return;
490   }
491   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
492               "Received client transmission request. queueing\n");
493   GNUNET_CONTAINER_DLL_insert (session->active_client_request_head,
494                                session->active_client_request_tail, car);
495   try_transmission (session);
496 }
497
498
499 /**
500  * Dequeue a request from a client from transmission to a particular peer.
501  *
502  * @param car request to dequeue; this handle will then be 'owned' by
503  *        the caller (CLIENTS sysbsystem)
504  */
505 void
506 GSC_SESSIONS_dequeue_request (struct GSC_ClientActiveRequest *car)
507 {
508   struct Session *session;
509
510   if (0 ==
511       memcmp (&car->target, &GSC_my_identity,
512               sizeof (struct GNUNET_PeerIdentity)))
513     return;
514   session = find_session (&car->target);
515   GNUNET_assert (NULL != session);
516   GNUNET_CONTAINER_DLL_remove (session->active_client_request_head,
517                                session->active_client_request_tail,
518                                car);
519 }
520
521
522 /**
523  * Discard all expired active transmission requests from clients.
524  *
525  * @param session session to clean up
526  */
527 static void
528 discard_expired_requests (struct Session *session)
529 {
530   struct GSC_ClientActiveRequest *pos;
531   struct GSC_ClientActiveRequest *nxt;
532   struct GNUNET_TIME_Absolute now;
533
534   now = GNUNET_TIME_absolute_get ();
535   pos = NULL;
536   nxt = session->active_client_request_head;
537   while (NULL != nxt)
538   {
539     pos = nxt;
540     nxt = pos->next;
541     if ( (pos->deadline.abs_value_us < now.abs_value_us) &&
542          (GNUNET_YES != pos->was_solicited) )
543     {
544       GNUNET_STATISTICS_update (GSC_stats,
545                                 gettext_noop
546                                 ("# messages discarded (expired prior to transmission)"),
547                                 1, GNUNET_NO);
548       GNUNET_CONTAINER_DLL_remove (session->active_client_request_head,
549                                    session->active_client_request_tail,
550                                    pos);
551       GSC_CLIENTS_reject_request (pos);
552     }
553   }
554 }
555
556
557 /**
558  * Solicit messages for transmission, starting with those of the highest
559  * priority.
560  *
561  * @param session session to solict messages for
562  * @param msize how many bytes do we have already
563  */
564 static void
565 solicit_messages (struct Session *session,
566                   size_t msize)
567 {
568   struct GSC_ClientActiveRequest *car;
569   struct GSC_ClientActiveRequest *nxt;
570   size_t so_size;
571   enum GNUNET_CORE_Priority pmax;
572
573   discard_expired_requests (session);
574   so_size = msize;
575   pmax = GNUNET_CORE_PRIO_BACKGROUND;
576   for (car = session->active_client_request_head; NULL != car; car = car->next)
577   {
578     if (GNUNET_YES == car->was_solicited)
579       continue;
580     pmax = GNUNET_MAX (pmax, car->priority);
581   }
582   nxt = session->active_client_request_head;
583   while (NULL != (car = nxt))
584   {
585     nxt = car->next;
586     if (car->priority < pmax)
587       continue;
588     if (so_size + car->msize > GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE)
589       break;
590     so_size += car->msize;
591     if (GNUNET_YES == car->was_solicited)
592       continue;
593     car->was_solicited = GNUNET_YES;
594     GSC_CLIENTS_solicit_request (car);
595   }
596 }
597
598
599 /**
600  * Some messages were delayed (corked), but the timeout has now expired.
601  * Send them now.
602  *
603  * @param cls `struct Session` with the messages to transmit now
604  * @param tc scheduler context (unused)
605  */
606 static void
607 pop_cork_task (void *cls,
608                const struct GNUNET_SCHEDULER_TaskContext *tc)
609 {
610   struct Session *session = cls;
611
612   session->cork_task = NULL;
613   try_transmission (session);
614 }
615
616
617 /**
618  * Try to perform a transmission on the given session. Will solicit
619  * additional messages if the 'sme' queue is not full enough or has
620  * only low-priority messages.
621  *
622  * @param session session to transmit messages from
623  */
624 static void
625 try_transmission (struct Session *session)
626 {
627   struct SessionMessageEntry *pos;
628   size_t msize;
629   struct GNUNET_TIME_Absolute now;
630   struct GNUNET_TIME_Absolute min_deadline;
631   enum GNUNET_CORE_Priority maxp;
632   enum GNUNET_CORE_Priority maxpc;
633   struct GSC_ClientActiveRequest *car;
634   int excess;
635
636   if (GNUNET_YES != session->ready_to_transmit)
637     return;
638   msize = 0;
639   min_deadline = GNUNET_TIME_UNIT_FOREVER_ABS;
640   /* if the peer has excess bandwidth, background traffic is allowed,
641      otherwise not */
642   excess = GSC_NEIGHBOURS_check_excess_bandwidth (&session->peer);
643   if (GNUNET_YES == excess)
644     maxp = GNUNET_CORE_PRIO_BACKGROUND;
645   else
646     maxp = GNUNET_CORE_PRIO_BEST_EFFORT;
647   /* determine highest priority of 'ready' messages we already solicited from clients */
648   pos = session->sme_head;
649   while ((NULL != pos) &&
650          (msize + pos->size <= GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE))
651   {
652     GNUNET_assert (pos->size < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE);
653     msize += pos->size;
654     maxp = GNUNET_MAX (maxp, pos->priority);
655     min_deadline = GNUNET_TIME_absolute_min (min_deadline,
656                                              pos->deadline);
657     pos = pos->next;
658   }
659   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
660               "Calculating transmission set with %u priority (%s) and %s earliest deadline\n",
661               maxp,
662               (GNUNET_YES == excess) ? "excess bandwidth" : "limited bandwidth",
663               GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (min_deadline),
664                                                       GNUNET_YES));
665
666   if (maxp < GNUNET_CORE_PRIO_CRITICAL_CONTROL)
667   {
668     /* if highest already solicited priority from clients is not critical,
669        check if there are higher-priority messages to be solicited from clients */
670     if (GNUNET_YES == excess)
671       maxpc = GNUNET_CORE_PRIO_BACKGROUND;
672     else
673       maxpc = GNUNET_CORE_PRIO_BEST_EFFORT;
674     for (car = session->active_client_request_head; NULL != car; car = car->next)
675     {
676       if (GNUNET_YES == car->was_solicited)
677         continue;
678       maxpc = GNUNET_MAX (maxpc, car->priority);
679     }
680     if (maxpc > maxp)
681     {
682       /* we have messages waiting for solicitation that have a higher
683          priority than those that we already accepted; solicit the
684          high-priority messages first */
685       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
686                   "Soliciting messages based on priority (%u > %u)\n",
687                   maxpc,
688                   maxp);
689       solicit_messages (session, 0);
690       return;
691     }
692   }
693   else
694   {
695     /* never solicit more, we have critical messages to process */
696     excess = GNUNET_NO;
697     maxpc = GNUNET_CORE_PRIO_BACKGROUND;
698   }
699   now = GNUNET_TIME_absolute_get ();
700   if ( ( (GNUNET_YES == excess) ||
701          (maxpc >= GNUNET_CORE_PRIO_BEST_EFFORT) ) &&
702        ( (0 == msize) ||
703          ( (msize < GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE / 2) &&
704            (min_deadline.abs_value_us > now.abs_value_us))) )
705   {
706     /* not enough ready yet (tiny message & cork possible), or no messages at all,
707        and either excess bandwidth or best-effort or higher message waiting at
708        client; in this case, we try to solicit more */
709     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
710                 "Soliciting messages (excess %d, maxpc %d, message size %u, deadline %s)\n",
711                 excess,
712                 maxpc,
713                 msize,
714                 GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (min_deadline),
715                                                         GNUNET_YES));
716     solicit_messages (session,
717                       msize);
718     if (msize > 0)
719     {
720       /* if there is data to send, just not yet, make sure we do transmit
721        * it once the deadline is reached */
722       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
723                   "Corking until %s\n",
724                   GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (min_deadline),
725                                                           GNUNET_YES));
726       if (NULL != session->cork_task)
727         GNUNET_SCHEDULER_cancel (session->cork_task);
728       session->cork_task =
729           GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining (min_deadline),
730                                         &pop_cork_task,
731                                         session);
732     }
733     return;
734   }
735   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
736               "Building combined plaintext buffer to transmit message!\n");
737   /* create plaintext buffer of all messages (that fit), encrypt and
738      transmit */
739   {
740     static unsigned long long total_bytes;
741     static unsigned int total_msgs;
742     char pbuf[msize];           /* plaintext */
743     size_t used;
744
745     used = 0;
746     while ( (NULL != (pos = session->sme_head)) &&
747             (used + pos->size <= msize) )
748     {
749       memcpy (&pbuf[used], &pos[1], pos->size);
750       used += pos->size;
751       GNUNET_CONTAINER_DLL_remove (session->sme_head,
752                                    session->sme_tail,
753                                    pos);
754       GNUNET_free (pos);
755     }
756     /* compute average payload size */
757     total_bytes += used;
758     total_msgs++;
759     if (0 == total_msgs)
760     {
761       /* 2^32 messages, wrap around... */
762       total_msgs = 1;
763       total_bytes = used;
764     }
765     GNUNET_STATISTICS_set (GSC_stats,
766                            "# avg payload per encrypted message",
767                            total_bytes / total_msgs,
768                            GNUNET_NO);
769     /* now actually transmit... */
770     session->ready_to_transmit = GNUNET_NO;
771     GSC_KX_encrypt_and_transmit (session->kxinfo,
772                                  pbuf,
773                                  used);
774   }
775 }
776
777
778 /**
779  * Send an updated typemap message to the neighbour now,
780  * and restart typemap transmissions.
781  *
782  * @param cls the message
783  * @param key neighbour's identity
784  * @param value `struct Neighbour` of the target
785  * @return always #GNUNET_OK
786  */
787 static int
788 do_restart_typemap_message (void *cls,
789                             const struct GNUNET_PeerIdentity *key,
790                             void *value)
791 {
792   const struct GNUNET_MessageHeader *hdr = cls;
793   struct Session *session = value;
794   struct SessionMessageEntry *sme;
795   uint16_t size;
796
797   size = ntohs (hdr->size);
798   sme = GNUNET_malloc (sizeof (struct SessionMessageEntry) + size);
799   memcpy (&sme[1], hdr, size);
800   sme->size = size;
801   sme->priority = GNUNET_CORE_PRIO_CRITICAL_CONTROL;
802   GNUNET_CONTAINER_DLL_insert (session->sme_head,
803                                session->sme_tail,
804                                sme);
805   try_transmission (session);
806   start_typemap_task (session);
807   return GNUNET_OK;
808 }
809
810
811 /**
812  * Broadcast an updated typemap message to all neighbours.
813  * Restarts the retransmissions until the typemaps are confirmed.
814  *
815  * @param msg message to transmit
816  */
817 void
818 GSC_SESSIONS_broadcast_typemap (const struct GNUNET_MessageHeader *msg)
819 {
820   if (NULL == sessions)
821     return;
822   GNUNET_CONTAINER_multipeermap_iterate (sessions,
823                                          &do_restart_typemap_message,
824                                          (void *) msg);
825 }
826
827
828 /**
829  * Traffic is being solicited for the given peer.  This means that the
830  * message queue on the transport-level (NEIGHBOURS subsystem) is now
831  * empty and it is now OK to transmit another (non-control) message.
832  *
833  * @param pid identity of peer ready to receive data
834  */
835 void
836 GSC_SESSIONS_solicit (const struct GNUNET_PeerIdentity *pid)
837 {
838   struct Session *session;
839
840   session = find_session (pid);
841   if (NULL == session)
842     return;
843   session->ready_to_transmit = GNUNET_YES;
844   try_transmission (session);
845 }
846
847
848 /**
849  * Transmit a message to a particular peer.
850  *
851  * @param car original request that was queued and then solicited;
852  *            this handle will now be 'owned' by the SESSIONS subsystem
853  * @param msg message to transmit
854  * @param cork is corking allowed?
855  * @param priority how important is this message
856  */
857 void
858 GSC_SESSIONS_transmit (struct GSC_ClientActiveRequest *car,
859                        const struct GNUNET_MessageHeader *msg,
860                        int cork,
861                        enum GNUNET_CORE_Priority priority)
862 {
863   struct Session *session;
864   struct SessionMessageEntry *sme;
865   struct SessionMessageEntry *pos;
866   size_t msize;
867
868   session = find_session (&car->target);
869   if (NULL == session)
870     return;
871   msize = ntohs (msg->size);
872   sme = GNUNET_malloc (sizeof (struct SessionMessageEntry) + msize);
873   memcpy (&sme[1], msg, msize);
874   sme->size = msize;
875   sme->priority = priority;
876   if (GNUNET_YES == cork)
877     sme->deadline =
878         GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_MAX_CORK_DELAY);
879   pos = session->sme_head;
880   while ( (NULL != pos) &&
881           (pos->priority >= sme->priority) )
882     pos = pos->next;
883   if (NULL == pos)
884     GNUNET_CONTAINER_DLL_insert_tail (session->sme_head,
885                                       session->sme_tail,
886                                       sme);
887   else
888     GNUNET_CONTAINER_DLL_insert_after (session->sme_head,
889                                        session->sme_tail,
890                                        pos->prev,
891                                        sme);
892   try_transmission (session);
893 }
894
895
896 /**
897  * We have received a typemap message from a peer, update ours.
898  * Notifies clients about the session.
899  *
900  * @param peer peer this is about
901  * @param msg typemap update message
902  */
903 void
904 GSC_SESSIONS_set_typemap (const struct GNUNET_PeerIdentity *peer,
905                           const struct GNUNET_MessageHeader *msg)
906 {
907   struct Session *session;
908   struct GSC_TypeMap *nmap;
909   struct SessionMessageEntry *sme;
910   struct TypeMapConfirmationMessage *tmc;
911
912   nmap = GSC_TYPEMAP_get_from_message (msg);
913   if (NULL == nmap)
914     return;                     /* malformed */
915   session = find_session (peer);
916   if (NULL == session)
917   {
918     GNUNET_break (0);
919     return;
920   }
921   sme = GNUNET_malloc (sizeof (struct SessionMessageEntry) +
922                        sizeof (struct TypeMapConfirmationMessage));
923   sme->deadline = GNUNET_TIME_absolute_get ();
924   sme->size = sizeof (struct TypeMapConfirmationMessage);
925   sme->priority = GNUNET_CORE_PRIO_CRITICAL_CONTROL;
926   tmc = (struct TypeMapConfirmationMessage *) &sme[1];
927   tmc->header.size = htons (sizeof (struct TypeMapConfirmationMessage));
928   tmc->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP);
929   tmc->reserved = htonl (0);
930   GSC_TYPEMAP_hash (nmap,
931                     &tmc->tm_hash);
932   GNUNET_CONTAINER_DLL_insert (session->sme_head,
933                                session->sme_tail,
934                                sme);
935   try_transmission (session);
936   GSC_CLIENTS_notify_clients_about_neighbour (peer,
937                                               session->tmap,
938                                               nmap);
939   GSC_TYPEMAP_destroy (session->tmap);
940   session->tmap = nmap;
941 }
942
943
944 /**
945  * The given peer send a message of the specified type.  Make sure the
946  * respective bit is set in its type-map and that clients are notified
947  * about the session.
948  *
949  * @param peer peer this is about
950  * @param type type of the message
951  */
952 void
953 GSC_SESSIONS_add_to_typemap (const struct GNUNET_PeerIdentity *peer,
954                              uint16_t type)
955 {
956   struct Session *session;
957   struct GSC_TypeMap *nmap;
958
959   if (0 == memcmp (peer,
960                    &GSC_my_identity,
961                    sizeof (struct GNUNET_PeerIdentity)))
962     return;
963   session = find_session (peer);
964   GNUNET_assert (NULL != session);
965   if (GNUNET_YES == GSC_TYPEMAP_test_match (session->tmap, &type, 1))
966     return;                     /* already in it */
967   nmap = GSC_TYPEMAP_extend (session->tmap, &type, 1);
968   GSC_CLIENTS_notify_clients_about_neighbour (peer,
969                                               session->tmap, nmap);
970   GSC_TYPEMAP_destroy (session->tmap);
971   session->tmap = nmap;
972 }
973
974
975 /**
976  * Initialize sessions subsystem.
977  */
978 void
979 GSC_SESSIONS_init ()
980 {
981   sessions = GNUNET_CONTAINER_multipeermap_create (128,
982                                                    GNUNET_YES);
983 }
984
985
986 /**
987  * Helper function for #GSC_SESSIONS_done() to free all
988  * active sessions.
989  *
990  * @param cls NULL
991  * @param key identity of the connected peer
992  * @param value the `struct Session` for the peer
993  * @return #GNUNET_OK (continue to iterate)
994  */
995 static int
996 free_session_helper (void *cls,
997                      const struct GNUNET_PeerIdentity *key,
998                      void *value)
999 {
1000   struct Session *session = value;
1001
1002   GSC_SESSIONS_end (&session->peer);
1003   return GNUNET_OK;
1004 }
1005
1006
1007 /**
1008  * Shutdown sessions subsystem.
1009  */
1010 void
1011 GSC_SESSIONS_done ()
1012 {
1013   if (NULL != sessions)
1014   {
1015     GNUNET_CONTAINER_multipeermap_iterate (sessions,
1016                                            &free_session_helper, NULL);
1017     GNUNET_CONTAINER_multipeermap_destroy (sessions);
1018     sessions = NULL;
1019   }
1020 }
1021
1022 /* end of gnunet-service-core_sessions.c */