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