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