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