core_api possible fix for timeout case
[oweals/gnunet.git] / src / core / core_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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/core_api.c
23  * @brief core service; this is the main API for encrypted P2P
24  *        communications
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_core_service.h"
30 #include "core.h"
31
32
33 /**
34  * Context for the core service connection.
35  */
36 struct GNUNET_CORE_Handle
37 {
38
39   /**
40    * Our scheduler.
41    */
42   struct GNUNET_SCHEDULER_Handle *sched;
43
44   /**
45    * Configuration we're using.
46    */
47   const struct GNUNET_CONFIGURATION_Handle *cfg;
48
49   /**
50    * Closure for the various callbacks.
51    */
52   void *cls;
53
54   /**
55    * Function to call once we've handshaked with the core service.
56    */
57   GNUNET_CORE_StartupCallback init;
58
59   /**
60    * Function to call whenever we're notified about a peer connecting.
61    */
62   GNUNET_CORE_ConnectEventHandler connects;
63
64   /**
65    * Function to call whenever we're notified about a peer disconnecting.
66    */
67   GNUNET_CORE_DisconnectEventHandler disconnects;
68
69   /**
70    * Function to call whenever we're notified about a peer changing status.
71    */  
72   GNUNET_CORE_PeerStatusEventHandler status_events;
73   
74   /**
75    * Function to call whenever we receive an inbound message.
76    */
77   GNUNET_CORE_MessageCallback inbound_notify;
78
79   /**
80    * Function to call whenever we receive an outbound message.
81    */
82   GNUNET_CORE_MessageCallback outbound_notify;
83
84   /**
85    * Function handlers for messages of particular type.
86    */
87   const struct GNUNET_CORE_MessageHandler *handlers;
88
89   /**
90    * Our connection to the service for notifications.
91    */
92   struct GNUNET_CLIENT_Connection *client_notifications;
93
94   /**
95    * Handle for our current transmission request.
96    */
97   struct GNUNET_CLIENT_TransmitHandle *cth;
98
99   /**
100    * Head of doubly-linked list of pending requests.
101    */
102   struct GNUNET_CORE_TransmitHandle *pending_head;
103
104   /**
105    * Tail of doubly-linked list of pending requests.
106    */
107   struct GNUNET_CORE_TransmitHandle *pending_tail;
108
109   /**
110    * Currently submitted request (or NULL)
111    */
112   struct GNUNET_CORE_TransmitHandle *submitted;
113
114   /**
115    * Currently submitted request based on solicitation (or NULL)
116    */
117   struct GNUNET_CORE_TransmitHandle *solicit_transmit_req;
118
119   /**
120    * Buffer where we store a message for transmission in response
121    * to a traffic solicitation (or NULL).
122    */
123   char *solicit_buffer;
124
125   /**
126    * How long to wait until we time out the connection attempt?
127    */
128   struct GNUNET_TIME_Absolute startup_timeout;
129
130   /**
131    * ID of reconnect task (if any).
132    */
133   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
134
135   /**
136    * Number of entries in the handlers array.
137    */
138   unsigned int hcnt;
139
140   /**
141    * For inbound notifications without a specific handler, do
142    * we expect to only receive headers?
143    */
144   int inbound_hdr_only;
145
146   /**
147    * For outbound notifications without a specific handler, do
148    * we expect to only receive headers?
149    */
150   int outbound_hdr_only;
151
152   /**
153    * Are we currently disconnected and hence unable to forward
154    * requests?
155    */
156   int currently_down;
157 };
158
159
160 /**
161  * Handle for a transmission request.
162  */
163 struct GNUNET_CORE_TransmitHandle
164 {
165
166   /**
167    * We keep active transmit handles in a doubly-linked list.
168    */
169   struct GNUNET_CORE_TransmitHandle *next;
170
171   /**
172    * We keep active transmit handles in a doubly-linked list.
173    */
174   struct GNUNET_CORE_TransmitHandle *prev;
175
176   /**
177    * Corresponding core handle.
178    */
179   struct GNUNET_CORE_Handle *ch;
180
181   /**
182    * Function that will be called to get the actual request
183    * (once we are ready to transmit this request to the core).
184    * The function will be called with a NULL buffer to signal
185    * timeout.
186    */
187   GNUNET_CONNECTION_TransmitReadyNotify get_message;
188
189   /**
190    * Closure for get_message.
191    */
192   void *get_message_cls;
193
194   /**
195    * If this entry is for a transmission request, pointer
196    * to the notify callback; otherwise NULL.
197    */
198   GNUNET_CONNECTION_TransmitReadyNotify notify;
199
200   /**
201    * Closure for notify.
202    */
203   void *notify_cls;
204
205   /**
206    * Peer the request is about.
207    */
208   struct GNUNET_PeerIdentity peer;
209
210   /**
211    * Timeout for this handle.
212    */
213   struct GNUNET_TIME_Absolute timeout;
214
215   /**
216    * ID of timeout task.
217    */
218   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
219
220   /**
221    * How important is this message?
222    */
223   uint32_t priority;
224
225   /**
226    * Size of this request.
227    */
228   uint16_t msize;
229
230
231 };
232
233
234 static void
235 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
236
237
238 /**
239  * Function called when we are ready to transmit our
240  * "START" message (or when this operation timed out).
241  *
242  * @param cls closure
243  * @param size number of bytes available in buf
244  * @param buf where the callee should write the message
245  * @return number of bytes written to buf
246  */
247 static size_t transmit_start (void *cls, size_t size, void *buf);
248
249
250 /**
251  * Our current client connection went down.  Clean it up
252  * and try to reconnect!
253  *
254  * @param h our handle to the core service
255  */
256 static void
257 reconnect (struct GNUNET_CORE_Handle *h)
258 {
259 #if DEBUG_CORE
260   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
261               "Reconnecting to CORE service\n");
262 #endif
263   if (h->client_notifications != NULL)
264     GNUNET_CLIENT_disconnect (h->client_notifications, GNUNET_NO);
265   h->currently_down = GNUNET_YES;
266   h->client_notifications = GNUNET_CLIENT_connect (h->sched, "core", h->cfg);
267   if (h->client_notifications == NULL)
268     h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->sched,
269                                                       GNUNET_TIME_UNIT_SECONDS,
270                                                       &reconnect_task,
271                                                       h);
272   else
273     h->cth = GNUNET_CLIENT_notify_transmit_ready (h->client_notifications,
274                                                   sizeof (struct InitMessage) +
275                                                   sizeof (uint16_t) * h->hcnt,
276                                                   GNUNET_TIME_UNIT_SECONDS,
277                                                   GNUNET_NO,
278                                                   &transmit_start, h);
279 }
280
281
282 /**
283  * The given request hit its timeout.  Remove from the
284  * doubly-linked list and call the respective continuation.
285  *
286  * @param cls the transmit handle of the request that timed out
287  * @param tc context, can be NULL (!)
288  */
289 static void
290 timeout_request (void *cls, 
291                  const struct GNUNET_SCHEDULER_TaskContext *tc)
292 {
293   struct GNUNET_CORE_TransmitHandle *th = cls;
294
295   GNUNET_CONTAINER_DLL_remove (th->ch->pending_head,
296                                th->ch->pending_tail,
297                                th);
298   th->timeout_task = GNUNET_SCHEDULER_NO_TASK;
299 #if DEBUG_CORE
300   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
301               "Signalling timeout of request for transmission to CORE service\n");
302 #endif
303   GNUNET_assert (0 == th->get_message (th->get_message_cls, 0, NULL));
304 }
305
306
307 /**
308  * Function called when we are ready to transmit a request from our
309  * request list (or when this operation timed out).
310  *
311  * @param cls closure
312  * @param size number of bytes available in buf
313  * @param buf where the callee should write the message
314  * @return number of bytes written to buf
315  */
316 static size_t
317 request_start (void *cls, size_t size, void *buf)
318 {
319   struct GNUNET_CORE_Handle *h = cls;
320   struct GNUNET_CORE_TransmitHandle *th;
321   size_t ret;
322
323   h->cth = NULL;  
324   th = h->pending_head;
325   if (th == NULL)
326     return 0;
327   if (buf == NULL)
328     {
329       if (th->timeout_task != GNUNET_SCHEDULER_NO_TASK)
330         GNUNET_SCHEDULER_cancel(h->sched, th->timeout_task);
331       timeout_request (th, NULL);
332       return 0;
333     }
334   GNUNET_CONTAINER_DLL_remove (h->pending_head,
335                                h->pending_tail,
336                                th);
337   GNUNET_assert (h->submitted == NULL);
338   h->submitted = th;
339   GNUNET_assert (size >= th->msize);
340   ret = th->get_message (th->get_message_cls, size, buf);
341   GNUNET_assert (ret <= size);
342 #if DEBUG_CORE
343   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
344               "Transmitting %u bytes to core\n",
345               ret);
346 #endif
347   return ret;
348 }
349
350
351 /**
352  * Check the list of pending requests, send the next
353  * one to the core.
354  */
355 static void
356 trigger_next_request (struct GNUNET_CORE_Handle *h)
357 {
358   struct GNUNET_CORE_TransmitHandle *th;
359
360   if (h->currently_down)
361     {
362 #if DEBUG_CORE
363       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364                   "In trigger_next_request, connection currently down...\n");
365 #endif
366       return;                     /* connection temporarily down */
367     }
368   if (NULL == (th = h->pending_head))
369     return;                     /* no requests pending */
370   GNUNET_assert (NULL == h->cth);
371   h->cth = GNUNET_CLIENT_notify_transmit_ready (h->client_notifications,
372                                                 th->msize,
373                                                 GNUNET_TIME_absolute_get_remaining
374                                                 (th->timeout), 
375                                                 GNUNET_NO,
376                                                 &request_start,
377                                                 h);
378 }
379
380
381 /**
382  * Handler for notification messages received from the core.
383  *
384  * @param cls our "struct GNUNET_CORE_Handle"
385  * @param msg the message received from the core service
386  */
387 static void
388 main_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg)
389 {
390   struct GNUNET_CORE_Handle *h = cls;
391   unsigned int hpos;
392   const struct ConnectNotifyMessage *cnm;
393   const struct DisconnectNotifyMessage *dnm;
394   const struct NotifyTrafficMessage *ntm;
395   const struct GNUNET_MessageHeader *em;
396   const struct PeerStatusNotifyMessage *psnm;
397   uint16_t msize;
398   uint16_t et;
399   const struct GNUNET_CORE_MessageHandler *mh;
400
401   if (msg == NULL)
402     {
403       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
404                   _
405                   ("Client was disconnected from core service, trying to reconnect.\n"));
406       reconnect (h);
407       return;
408     }
409   msize = ntohs (msg->size);
410 #if DEBUG_CORE
411   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412               "Processing message of type %u and size %u from core service\n",
413               ntohs (msg->type), msize);
414 #endif
415   switch (ntohs (msg->type))
416     {
417     case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT:
418       if (NULL == h->connects)
419         {
420           GNUNET_break (0);
421           break;
422         }
423       if (msize != sizeof (struct ConnectNotifyMessage))
424         {
425           GNUNET_break (0);
426           break;
427         }
428       cnm = (const struct ConnectNotifyMessage *) msg;
429       h->connects (h->cls,
430                    &cnm->peer,
431                    GNUNET_TIME_relative_ntoh (cnm->latency),
432                    ntohl (cnm->distance));
433       break;
434     case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT:
435       if (NULL == h->disconnects)
436         {
437           GNUNET_break (0);
438           break;
439         }
440       if (msize != sizeof (struct DisconnectNotifyMessage))
441         {
442           GNUNET_break (0);
443           break;
444         }
445       dnm = (const struct DisconnectNotifyMessage *) msg;
446       h->disconnects (h->cls,
447                       &dnm->peer);
448       break;
449     case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_STATUS_CHANGE:
450       if (NULL == h->status_events)
451         {
452           GNUNET_break (0);
453           break;
454         }
455       if (msize != sizeof (struct PeerStatusNotifyMessage))
456         {
457           GNUNET_break (0);
458           break;
459         }
460       psnm = (const struct PeerStatusNotifyMessage *) msg;
461       h->status_events (h->cls,
462                         &psnm->peer,
463                         GNUNET_TIME_relative_ntoh (psnm->latency),
464                         ntohl (psnm->distance),
465                         psnm->bandwidth_in,
466                         psnm->bandwidth_out,
467                         GNUNET_TIME_absolute_ntoh (psnm->timeout));
468       break;
469     case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND:
470       if (msize <
471           sizeof (struct NotifyTrafficMessage) +
472           sizeof (struct GNUNET_MessageHeader))
473         {
474           GNUNET_break (0);
475           break;
476         }
477       ntm = (const struct NotifyTrafficMessage *) msg;
478       em = (const struct GNUNET_MessageHeader *) &ntm[1];
479 #if DEBUG_CORE
480       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
481                   "Received message of type %u and size %u from peer `%4s'\n",
482                   ntohs (em->type), 
483                   ntohs (em->size),
484                   GNUNET_i2s (&ntm->peer));
485 #endif
486       if ((GNUNET_NO == h->inbound_hdr_only) &&
487           (msize != ntohs (em->size) + sizeof (struct NotifyTrafficMessage)))
488         {
489           GNUNET_break (0);
490           break;
491         }
492       et = ntohs (em->type);
493       for (hpos = 0; hpos < h->hcnt; hpos++)
494         {
495           mh = &h->handlers[hpos];
496           if (mh->type != et)
497             continue;
498           if ((mh->expected_size != ntohs (em->size)) &&
499               (mh->expected_size != 0))
500             {
501               GNUNET_break (0);
502               continue;
503             }
504           if (GNUNET_OK !=
505               h->handlers[hpos].callback (h->cls, &ntm->peer, em,
506                                           GNUNET_TIME_relative_ntoh (ntm->latency),
507                                           ntohl (ntm->distance)))
508             {
509               /* error in processing, disconnect ! */
510               reconnect (h);
511               return;
512             }
513         }
514       if (NULL != h->inbound_notify)
515         h->inbound_notify (h->cls, &ntm->peer, em,
516                            GNUNET_TIME_relative_ntoh (ntm->latency),
517                            ntohl (ntm->distance));
518       break;
519     case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND:
520       if (msize <
521           sizeof (struct NotifyTrafficMessage) +
522           sizeof (struct GNUNET_MessageHeader))
523         {
524           GNUNET_break (0);
525           break;
526         }
527       ntm = (const struct NotifyTrafficMessage *) msg;
528       em = (const struct GNUNET_MessageHeader *) &ntm[1];
529       if ((GNUNET_NO == h->outbound_hdr_only) &&
530           (msize != ntohs (em->size) + sizeof (struct NotifyTrafficMessage)))
531         {
532           GNUNET_break (0);
533           break;
534         }
535       if (NULL == h->outbound_notify)
536         {
537           GNUNET_break (0);
538           break;
539         }
540       h->outbound_notify (h->cls, &ntm->peer, em,
541                           GNUNET_TIME_relative_ntoh (ntm->latency),
542                           ntohl (ntm->distance));
543       break;
544     default:
545       GNUNET_break (0);
546       break;
547     }
548   GNUNET_CLIENT_receive (h->client_notifications,
549                          &main_notify_handler, h, GNUNET_TIME_UNIT_FOREVER_REL);
550 }
551
552
553 /**
554  * Function called when we are ready to transmit our
555  * "START" message (or when this operation timed out).
556  *
557  * @param cls closure
558  * @param size number of bytes available in buf
559  * @param buf where the callee should write the message
560  * @return number of bytes written to buf
561  */
562 static size_t transmit_start (void *cls, size_t size, void *buf);
563
564
565 /**
566  * Function called on the first message received from
567  * the service (contains our public key, etc.).
568  * Should trigger calling the init callback
569  * and then start our regular message processing.
570  *
571  * @param cls closure
572  * @param msg message received, NULL on timeout or fatal error
573  */
574 static void
575 init_reply_handler (void *cls, const struct GNUNET_MessageHeader *msg)
576 {
577   struct GNUNET_CORE_Handle *h = cls;
578   const struct InitReplyMessage *m;
579   GNUNET_CORE_StartupCallback init;
580   struct GNUNET_PeerIdentity my_identity;
581
582   if ((msg == NULL) ||
583       (ntohs (msg->size) != sizeof (struct InitReplyMessage)) ||
584       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY))
585     {
586       if (msg != NULL)
587         {
588           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
589                       _
590                       ("Error connecting to core service (failed to receive `%s' message, got message of type %u and size %u).\n"),
591                       "INIT_REPLY",
592                       ntohs (msg->type),
593                       ntohs (msg->size));
594           GNUNET_break (0);
595         }
596       else
597         {
598 #if DEBUG_CORE
599           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
600                       _("Failed to connect to core service, will retry.\n"));
601 #endif
602         }
603       transmit_start (h, 0, NULL);
604       return;
605     }
606   m = (const struct InitReplyMessage *) msg;
607   /* start our message processing loop */
608 #if DEBUG_CORE
609   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
610               "Successfully connected to core service, starting processing loop.\n");
611 #endif
612   h->currently_down = GNUNET_NO;
613   trigger_next_request (h);
614   GNUNET_CLIENT_receive (h->client_notifications,
615                          &main_notify_handler, h, GNUNET_TIME_UNIT_FOREVER_REL);
616   if (NULL != (init = h->init))
617     {
618       /* mark so we don't call init on reconnect */
619       h->init = NULL;
620       GNUNET_CRYPTO_hash (&m->publicKey,
621                           sizeof (struct
622                                   GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
623                           &my_identity.hashPubKey);
624       init (h->cls, h, &my_identity, &m->publicKey);
625     }
626 }
627
628
629 static void
630 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
631 {
632   struct GNUNET_CORE_Handle *h = cls;
633   h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
634   reconnect (h);
635 }
636
637
638 /**
639  * Function called when we are ready to transmit our
640  * "START" message (or when this operation timed out).
641  *
642  * @param cls closure
643  * @param size number of bytes available in buf
644  * @param buf where the callee should write the message
645  * @return number of bytes written to buf
646  */
647 static size_t
648 transmit_start (void *cls, size_t size, void *buf)
649 {
650   struct GNUNET_CORE_Handle *h = cls;
651   struct InitMessage *init;
652   uint16_t *ts;
653   uint16_t msize;
654   uint32_t opt;
655   unsigned int hpos;
656   struct GNUNET_TIME_Relative delay;
657
658   h->cth = NULL;
659   if (size == 0)
660     {
661       if ((h->init == NULL) ||
662           (GNUNET_TIME_absolute_get ().value < h->startup_timeout.value))
663         {
664           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
665                       _("Failed to connect to core service, retrying.\n"));
666           delay = GNUNET_TIME_absolute_get_remaining (h->startup_timeout);
667           if ((h->init == NULL) || (delay.value > 1000))
668             delay = GNUNET_TIME_UNIT_SECONDS;
669           if (h->init == NULL)
670             h->startup_timeout =
671               GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
672           h->reconnect_task =
673             GNUNET_SCHEDULER_add_delayed (h->sched, 
674                                           delay, &reconnect_task, h);
675           return 0;
676         }
677       /* timeout on initial connect */
678       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
679                   _("Failed to connect to core service, giving up.\n"));
680       h->init (h->cls, NULL, NULL, NULL);
681       GNUNET_CORE_disconnect (h);
682       return 0;
683     }
684   msize = h->hcnt * sizeof (uint16_t) + sizeof (struct InitMessage);
685   GNUNET_assert (size >= msize);
686   init = buf;
687   init->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT);
688   init->header.size = htons (msize);
689   opt = GNUNET_CORE_OPTION_NOTHING;
690   if (h->connects != NULL)
691     opt |= GNUNET_CORE_OPTION_SEND_CONNECT;
692   if (h->disconnects != NULL)
693     opt |= GNUNET_CORE_OPTION_SEND_DISCONNECT;
694   if (h->status_events != NULL)
695     opt |= GNUNET_CORE_OPTION_SEND_STATUS_CHANGE;
696   if (h->inbound_notify != NULL)
697     {
698       if (h->inbound_hdr_only)
699         opt |= GNUNET_CORE_OPTION_SEND_HDR_INBOUND;
700       else
701         opt |= GNUNET_CORE_OPTION_SEND_FULL_INBOUND;
702     }
703   if (h->outbound_notify != NULL)
704     {
705       if (h->outbound_hdr_only)
706         opt |= GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND;
707       else
708         opt |= GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND;
709     }
710   init->options = htonl (opt);
711   ts = (uint16_t *) & init[1];
712   for (hpos = 0; hpos < h->hcnt; hpos++)
713     ts[hpos] = htons (h->handlers[hpos].type);
714   GNUNET_CLIENT_receive (h->client_notifications,
715                          &init_reply_handler,
716                          h,
717                          GNUNET_TIME_absolute_get_remaining
718                          (h->startup_timeout));
719   return sizeof (struct InitMessage) + h->hcnt * sizeof (uint16_t);
720 }
721
722
723 /**
724  * Connect to the core service.  Note that the connection may
725  * complete (or fail) asynchronously.
726  *
727  * @param sched scheduler to use
728  * @param cfg configuration to use
729  * @param timeout after how long should we give up trying to connect to the core service?
730  * @param cls closure for the various callbacks that follow (including handlers in the handlers array)
731  * @param init callback to call on timeout or once we have successfully
732  *        connected to the core service; note that timeout is only meaningful if init is not NULL
733  * @param connects function to call on peer connect, can be NULL
734  * @param disconnects function to call on peer disconnect / timeout, can be NULL
735  * @param status_events function to call on changes to peer connection status, can be NULL
736  * @param inbound_notify function to call for all inbound messages, can be NULL
737  * @param inbound_hdr_only set to GNUNET_YES if inbound_notify will only read the
738  *                GNUNET_MessageHeader and hence we do not need to give it the full message;
739  *                can be used to improve efficiency, ignored if inbound_notify is NULLL
740  * @param outbound_notify function to call for all outbound messages, can be NULL
741  * @param outbound_hdr_only set to GNUNET_YES if outbound_notify will only read the
742  *                GNUNET_MessageHeader and hence we do not need to give it the full message
743  *                can be used to improve efficiency, ignored if outbound_notify is NULLL
744  * @param handlers callbacks for messages we care about, NULL-terminated
745  * @return handle to the core service (only useful for disconnect until 'init' is called);
746  *                NULL on error (in this case, init is never called)
747  */
748 struct GNUNET_CORE_Handle *
749 GNUNET_CORE_connect (struct GNUNET_SCHEDULER_Handle *sched,
750                      const struct GNUNET_CONFIGURATION_Handle *cfg,
751                      struct GNUNET_TIME_Relative timeout,
752                      void *cls,
753                      GNUNET_CORE_StartupCallback init,
754                      GNUNET_CORE_ConnectEventHandler connects,
755                      GNUNET_CORE_DisconnectEventHandler disconnects,
756                      GNUNET_CORE_PeerStatusEventHandler status_events,
757                      GNUNET_CORE_MessageCallback inbound_notify,
758                      int inbound_hdr_only,
759                      GNUNET_CORE_MessageCallback outbound_notify,
760                      int outbound_hdr_only,
761                      const struct GNUNET_CORE_MessageHandler *handlers)
762 {
763   struct GNUNET_CORE_Handle *h;
764
765   h = GNUNET_malloc (sizeof (struct GNUNET_CORE_Handle));
766   h->sched = sched;
767   h->cfg = cfg;
768   h->cls = cls;
769   h->init = init;
770   h->connects = connects;
771   h->disconnects = disconnects;
772   h->status_events = status_events;
773   h->inbound_notify = inbound_notify;
774   h->outbound_notify = outbound_notify;
775   h->inbound_hdr_only = inbound_hdr_only;
776   h->outbound_hdr_only = outbound_hdr_only;
777   h->handlers = handlers;
778   h->client_notifications = GNUNET_CLIENT_connect (sched, "core", cfg);
779   if (h->client_notifications == NULL)
780     {
781       GNUNET_free (h);
782       return NULL;
783     }
784   h->startup_timeout = GNUNET_TIME_relative_to_absolute (timeout);
785   h->hcnt = 0;
786   while (handlers[h->hcnt].callback != NULL)
787     h->hcnt++;
788   GNUNET_assert (h->hcnt <
789                  (GNUNET_SERVER_MAX_MESSAGE_SIZE -
790                   sizeof (struct InitMessage)) / sizeof (uint16_t));
791 #if DEBUG_CORE
792   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
793               "Trying to connect to core service in next %llu ms.\n",
794               timeout.value);
795 #endif
796   h->cth =
797     GNUNET_CLIENT_notify_transmit_ready (h->client_notifications,
798                                          sizeof (struct InitMessage) +
799                                          sizeof (uint16_t) * h->hcnt, timeout,
800                                          GNUNET_YES,
801                                          &transmit_start, h);
802   return h;
803 }
804
805
806 /**
807  * Disconnect from the core service.
808  *
809  * @param handle connection to core to disconnect
810  */
811 void
812 GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle)
813 {
814   if (handle->cth != NULL)
815     GNUNET_CLIENT_notify_transmit_ready_cancel (handle->cth);
816   if (handle->solicit_transmit_req != NULL)
817     GNUNET_CORE_notify_transmit_ready_cancel (handle->solicit_transmit_req);
818   if (handle->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
819     GNUNET_SCHEDULER_cancel (handle->sched, handle->reconnect_task);
820   if (handle->client_notifications != NULL)
821     GNUNET_CLIENT_disconnect (handle->client_notifications, GNUNET_NO);
822   GNUNET_break (handle->pending_head == NULL);
823   GNUNET_free_non_null (handle->solicit_buffer);
824   GNUNET_free (handle);
825 }
826
827
828 /**
829  * Build the message requesting data transmission.
830  */
831 static size_t
832 produce_send (void *cls, size_t size, void *buf)
833 {
834   struct GNUNET_CORE_TransmitHandle *th = cls;
835   struct GNUNET_CORE_Handle *h;
836   struct SendMessage *sm;
837   size_t dt;
838   GNUNET_CONNECTION_TransmitReadyNotify notify;
839   void *notify_cls;
840
841   h = th->ch;
842   if (buf == NULL)
843     {
844       /* timeout or error */
845 #if DEBUG_CORE
846       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
847                   "P2P transmission request for `%4s' timed out.\n",
848                   GNUNET_i2s(&th->peer));
849 #endif
850       GNUNET_assert (0 == th->notify (th->notify_cls, 0, NULL));
851       GNUNET_CORE_notify_transmit_ready_cancel (th);
852       if ((h->pending_head == th) && (h->cth != NULL)) /* Request hasn't been canceled yet! */
853         {
854           GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
855           h->cth = NULL;
856           trigger_next_request (h);
857         }
858       /* Otherwise this request timed out, but another is actually queued for sending, so don't try to send another! */
859       return 0;
860     }
861   sm = (struct SendMessage *) buf;
862   sm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND);
863   sm->priority = htonl (th->priority);
864   sm->deadline = GNUNET_TIME_absolute_hton (th->timeout);
865   sm->peer = th->peer;
866   notify = th->notify;
867   notify_cls = th->notify_cls;
868   GNUNET_CORE_notify_transmit_ready_cancel (th);
869   trigger_next_request (h);
870   size = GNUNET_MIN (size,
871                      GNUNET_CONSTANTS_MAX_ENCRYPTED_MESSAGE_SIZE);
872   GNUNET_assert (size >= sizeof (struct SendMessage));
873   dt = notify (notify_cls, size - sizeof (struct SendMessage), &sm[1]);
874   if (0 == dt)
875     {
876 #if DEBUG_CORE
877   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
878               "Size of clients message to peer %s is 0!\n",
879               GNUNET_i2s(&th->peer));
880 #endif
881       /* client decided to send nothing! */
882       return 0;
883     }
884 #if DEBUG_CORE
885   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
886               "Produced SEND message to core with %u bytes payload\n",
887               dt);
888 #endif
889   GNUNET_assert (dt >= sizeof (struct GNUNET_MessageHeader));
890   if (dt + sizeof (struct SendMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
891     {
892       GNUNET_break (0);
893       return 0;
894     }
895 #if DEBUG_CORE
896   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
897               "Preparing for P2P transmission of %u bytes to `%4s'.\n",
898               dt,
899               GNUNET_i2s(&th->peer));
900 #endif
901   sm->header.size = htons (dt + sizeof (struct SendMessage));
902   GNUNET_assert (dt + sizeof (struct SendMessage) <= size);
903   return dt + sizeof (struct SendMessage);
904 }
905
906
907 /**
908  * Ask the core to call "notify" once it is ready to transmit the
909  * given number of bytes to the specified "target".  If we are not yet
910  * connected to the specified peer, a call to this function will cause
911  * us to try to establish a connection.
912  *
913  * @param handle connection to core service
914  * @param priority how important is the message?
915  * @param maxdelay how long can the message wait?
916  * @param target who should receive the message,
917  *        use NULL for this peer (loopback)
918  * @param notify_size how many bytes of buffer space does notify want?
919  * @param notify function to call when buffer space is available
920  * @param notify_cls closure for notify
921  * @return non-NULL if the notify callback was queued,
922  *         NULL if we can not even queue the request (insufficient
923  *         memory); if NULL is returned, "notify" will NOT be called.
924  */
925 struct GNUNET_CORE_TransmitHandle *
926 GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle,
927                                    unsigned int priority,
928                                    struct GNUNET_TIME_Relative maxdelay,
929                                    const struct GNUNET_PeerIdentity *target,
930                                    size_t notify_size,
931                                    GNUNET_CONNECTION_TransmitReadyNotify notify,
932                                    void *notify_cls)
933 {
934   struct GNUNET_CORE_TransmitHandle *th;
935
936   GNUNET_assert (notify_size + sizeof (struct SendMessage) <
937                  GNUNET_SERVER_MAX_MESSAGE_SIZE);
938   th = GNUNET_malloc (sizeof (struct GNUNET_CORE_TransmitHandle));
939   th->ch = handle;
940   GNUNET_CONTAINER_DLL_insert_after (handle->pending_head,
941                                      handle->pending_tail,
942                                      handle->pending_tail,
943                                      th);
944   th->get_message = &produce_send;
945   th->get_message_cls = th;
946   th->notify = notify;
947   th->notify_cls = notify_cls;
948   th->peer = *target;
949   th->timeout = GNUNET_TIME_relative_to_absolute (maxdelay);
950   th->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->sched,
951                                                    maxdelay,
952                                                    &timeout_request, th);
953   th->priority = priority;
954   th->msize = sizeof (struct SendMessage) + notify_size;
955   /* was the request queue previously empty? */
956   if ( (handle->pending_head == th) &&
957        (handle->cth == NULL) )
958     trigger_next_request (handle);
959   return th;
960 }
961
962
963 /**
964  * Cancel the specified transmission-ready notification.
965  *
966  * @param th handle that was returned by "notify_transmit_ready".
967  */
968 void
969 GNUNET_CORE_notify_transmit_ready_cancel (struct GNUNET_CORE_TransmitHandle
970                                           *th)
971 {
972   struct GNUNET_CORE_Handle *h = th->ch;
973   
974   if (h->submitted == th)
975     h->submitted = NULL;
976   else    
977     GNUNET_CONTAINER_DLL_remove (h->pending_head,
978                                  h->pending_tail,
979                                  th);    
980   if (th->timeout_task != GNUNET_SCHEDULER_NO_TASK)
981     GNUNET_SCHEDULER_cancel (h->sched, th->timeout_task);
982   GNUNET_free (th);
983 }
984
985
986 /* end of core_api.c */