more stats
[oweals/gnunet.git] / src / core / core_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 2, 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_core_service.h"
29 #include "core.h"
30
31
32 /**
33  * Context for the core service connection.
34  */
35 struct GNUNET_CORE_Handle
36 {
37
38   /**
39    * Our scheduler.
40    */
41   struct GNUNET_SCHEDULER_Handle *sched;
42
43   /**
44    * Configuration we're using.
45    */
46   const struct GNUNET_CONFIGURATION_Handle *cfg;
47
48   /**
49    * Closure for the various callbacks.
50    */
51   void *cls;
52
53   /**
54    * Function to call once we've handshaked with the core service.
55    */
56   GNUNET_CORE_StartupCallback init;
57
58   /**
59    * Function to call whenever we're notified about a peer connecting
60    * (pre-connects, no session key exchange yet).
61    */
62   GNUNET_CORE_ConnectEventHandler pre_connects;
63
64   /**
65    * Function to call whenever we're notified about a peer connecting.
66    */
67   GNUNET_CORE_ConnectEventHandler connects;
68
69   /**
70    * Function to call whenever we're notified about a peer disconnecting.
71    */
72   GNUNET_CORE_DisconnectEventHandler disconnects;
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 *th;
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 (h->client_notifications != NULL)
260     GNUNET_CLIENT_disconnect (h->client_notifications, GNUNET_NO);
261   h->currently_down = GNUNET_YES;
262   h->client_notifications = GNUNET_CLIENT_connect (h->sched, "core", h->cfg);
263   if (h->client_notifications == NULL)
264     h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->sched,
265                                                       GNUNET_TIME_UNIT_SECONDS,
266                                                       &reconnect_task,
267                                                       h);
268   else
269     h->th = GNUNET_CLIENT_notify_transmit_ready (h->client_notifications,
270                                                  sizeof (struct InitMessage) +
271                                                  sizeof (uint16_t) * h->hcnt,
272                                                  GNUNET_TIME_UNIT_SECONDS,
273                                                  GNUNET_NO,
274                                                  &transmit_start, h);
275 }
276
277
278 /**
279  * The given request hit its timeout.  Remove from the
280  * doubly-linked list and call the respective continuation.
281  *
282  * @param cls the transmit handle of the request that timed out
283  * @param tc context, can be NULL (!)
284  */
285 static void
286 timeout_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
287 {
288   struct GNUNET_CORE_TransmitHandle *th = cls;
289
290   th->timeout_task = GNUNET_SCHEDULER_NO_TASK;
291   GNUNET_assert (0 == th->get_message (th->get_message_cls, 0, NULL));
292 }
293
294
295 /**
296  * Function called when we are ready to transmit a request from our
297  * request list (or when this operation timed out).
298  *
299  * @param cls closure
300  * @param size number of bytes available in buf
301  * @param buf where the callee should write the message
302  * @return number of bytes written to buf
303  */
304 static size_t
305 request_start (void *cls, size_t size, void *buf)
306 {
307   struct GNUNET_CORE_Handle *h = cls;
308   struct GNUNET_CORE_TransmitHandle *th;
309   size_t ret;
310
311   h->th = NULL;
312   th = h->pending_head;
313   if (buf == NULL)
314     {
315       timeout_request (th, NULL);
316       return 0;
317     }
318   /* create new timeout task (in case core takes too long to respond!) */
319   th->timeout_task = GNUNET_SCHEDULER_add_delayed (h->sched,
320                                                    GNUNET_TIME_absolute_get_remaining
321                                                    (th->timeout),
322                                                    &timeout_request, th);
323   /* remove th from doubly-linked pending list, move to submitted */
324   GNUNET_assert (th->prev == NULL);
325   h->pending_head = th->next;
326   if (th->next == NULL)
327     h->pending_tail = NULL;
328   else
329     th->next->prev = NULL;
330   GNUNET_assert (h->submitted == NULL);
331   h->submitted = th;
332   GNUNET_assert (size >= th->msize);
333   ret = th->get_message (th->get_message_cls, size, buf);
334   GNUNET_assert (ret <= size);
335   return ret;
336 }
337
338
339 /**
340  * Check the list of pending requests, send the next
341  * one to the core.
342  */
343 static void
344 trigger_next_request (struct GNUNET_CORE_Handle *h)
345 {
346   struct GNUNET_CORE_TransmitHandle *th;
347
348   if (h->currently_down)
349     {
350 #if DEBUG_CORE
351       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
352                   "In trigger_next_request, connection currently down...\n");
353 #endif
354       return;                     /* connection temporarily down */
355     }
356   if (NULL == (th = h->pending_head))
357     return;                     /* no requests pending */
358   GNUNET_assert (NULL == h->th);
359   if (GNUNET_SCHEDULER_NO_TASK != th->timeout_task)
360     {
361       GNUNET_SCHEDULER_cancel (h->sched, th->timeout_task);
362       th->timeout_task = GNUNET_SCHEDULER_NO_TASK;
363     }
364   h->th = GNUNET_CLIENT_notify_transmit_ready (h->client_notifications,
365                                                th->msize,
366                                                GNUNET_TIME_absolute_get_remaining
367                                                (th->timeout), 
368                                                GNUNET_NO,
369                                                &request_start,
370                                                h);
371 }
372
373
374 /**
375  * Handler for notification messages received from the core.
376  *
377  * @param cls our "struct GNUNET_CORE_Handle"
378  * @param msg the message received from the core service
379  */
380 static void
381 main_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg)
382 {
383   struct GNUNET_CORE_Handle *h = cls;
384   unsigned int hpos;
385   const struct ConnectNotifyMessage *cnm;
386   const struct DisconnectNotifyMessage *dnm;
387   const struct NotifyTrafficMessage *ntm;
388   const struct GNUNET_MessageHeader *em;
389   uint16_t msize;
390   uint16_t et;
391   const struct GNUNET_CORE_MessageHandler *mh;
392
393   if (msg == NULL)
394     {
395       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
396                   _
397                   ("Client was disconnected from core service, trying to reconnect.\n"));
398       reconnect (h);
399       return;
400     }
401   msize = ntohs (msg->size);
402 #if DEBUG_CORE
403   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
404               "Processing message of type %u and size %u from core service\n",
405               ntohs (msg->type), msize);
406 #endif
407   switch (ntohs (msg->type))
408     {
409     case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_PRE_CONNECT:
410       if (NULL == h->pre_connects)
411         {
412           GNUNET_break (0);
413           break;
414         }
415       if (msize != sizeof (struct ConnectNotifyMessage))
416         {
417           GNUNET_break (0);
418           break;
419         }
420       cnm = (const struct ConnectNotifyMessage *) msg;
421       h->pre_connects (h->cls,
422                        &cnm->peer,
423                        GNUNET_TIME_relative_ntoh (cnm->latency),
424                        ntohl (cnm->distance));
425       break;
426     case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_CONNECT:
427       if (NULL == h->connects)
428         {
429           GNUNET_break (0);
430           break;
431         }
432       if (msize != sizeof (struct ConnectNotifyMessage))
433         {
434           GNUNET_break (0);
435           break;
436         }
437       cnm = (const struct ConnectNotifyMessage *) msg;
438       h->connects (h->cls,
439                    &cnm->peer,
440                    GNUNET_TIME_relative_ntoh (cnm->latency),
441                    ntohl (cnm->distance));
442       break;
443     case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_DISCONNECT:
444       if (NULL == h->disconnects)
445         {
446           GNUNET_break (0);
447           break;
448         }
449       if (msize != sizeof (struct DisconnectNotifyMessage))
450         {
451           GNUNET_break (0);
452           break;
453         }
454       dnm = (const struct DisconnectNotifyMessage *) msg;
455       h->disconnects (h->cls,
456                       &dnm->peer);
457       break;
458     case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_INBOUND:
459       if (msize <
460           sizeof (struct NotifyTrafficMessage) +
461           sizeof (struct GNUNET_MessageHeader))
462         {
463           GNUNET_break (0);
464           break;
465         }
466       ntm = (const struct NotifyTrafficMessage *) msg;
467       em = (const struct GNUNET_MessageHeader *) &ntm[1];
468 #if DEBUG_CORE
469       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
470                   "Received message of type %u from peer `%4s'\n",
471                   ntohs (em->type), GNUNET_i2s (&ntm->peer));
472 #endif
473       if ((GNUNET_NO == h->inbound_hdr_only) &&
474           (msize != ntohs (em->size) + sizeof (struct NotifyTrafficMessage)))
475         {
476           GNUNET_break (0);
477           break;
478         }
479       et = ntohs (em->type);
480       for (hpos = 0; hpos < h->hcnt; hpos++)
481         {
482           mh = &h->handlers[hpos];
483           if (mh->type != et)
484             continue;
485           if ((mh->expected_size != ntohs (em->size)) &&
486               (mh->expected_size != 0))
487             {
488               GNUNET_break (0);
489               continue;
490             }
491           if (GNUNET_OK !=
492               h->handlers[hpos].callback (h->cls, &ntm->peer, em,
493                                           GNUNET_TIME_relative_ntoh (ntm->latency),
494                                           ntohl (ntm->distance)))
495             {
496               /* error in processing, disconnect ! */
497               reconnect (h);
498               return;
499             }
500         }
501       if (NULL != h->inbound_notify)
502         h->inbound_notify (h->cls, &ntm->peer, em,
503                            GNUNET_TIME_relative_ntoh (ntm->latency),
504                            ntohl (ntm->distance));
505       break;
506     case GNUNET_MESSAGE_TYPE_CORE_NOTIFY_OUTBOUND:
507       if (msize <
508           sizeof (struct NotifyTrafficMessage) +
509           sizeof (struct GNUNET_MessageHeader))
510         {
511           GNUNET_break (0);
512           break;
513         }
514       ntm = (const struct NotifyTrafficMessage *) msg;
515       em = (const struct GNUNET_MessageHeader *) &ntm[1];
516       if ((GNUNET_NO == h->outbound_hdr_only) &&
517           (msize != ntohs (em->size) + sizeof (struct NotifyTrafficMessage)))
518         {
519           GNUNET_break (0);
520           break;
521         }
522       if (NULL == h->outbound_notify)
523         {
524           GNUNET_break (0);
525           break;
526         }
527       h->outbound_notify (h->cls, &ntm->peer, em,
528                           GNUNET_TIME_relative_ntoh (ntm->latency),
529                           ntohl (ntm->distance));
530       break;
531     default:
532       GNUNET_break (0);
533       break;
534     }
535   GNUNET_CLIENT_receive (h->client_notifications,
536                          &main_notify_handler, h, GNUNET_TIME_UNIT_FOREVER_REL);
537 }
538
539
540 /**
541  * Function called when we are ready to transmit our
542  * "START" message (or when this operation timed out).
543  *
544  * @param cls closure
545  * @param size number of bytes available in buf
546  * @param buf where the callee should write the message
547  * @return number of bytes written to buf
548  */
549 static size_t transmit_start (void *cls, size_t size, void *buf);
550
551
552 /**
553  * Function called on the first message received from
554  * the service (contains our public key, etc.).
555  * Should trigger calling the init callback
556  * and then start our regular message processing.
557  *
558  * @param cls closure
559  * @param msg message received, NULL on timeout or fatal error
560  */
561 static void
562 init_reply_handler (void *cls, const struct GNUNET_MessageHeader *msg)
563 {
564   struct GNUNET_CORE_Handle *h = cls;
565   const struct InitReplyMessage *m;
566   GNUNET_CORE_StartupCallback init;
567   struct GNUNET_PeerIdentity my_identity;
568
569   if ((msg == NULL) ||
570       (ntohs (msg->size) != sizeof (struct InitReplyMessage)) ||
571       (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_CORE_INIT_REPLY))
572     {
573       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
574                   _
575                   ("Error connecting to core service (failed to receive `%s' message).\n"),
576                   "INIT_REPLY");
577       GNUNET_break (msg == NULL);
578       transmit_start (h, 0, NULL);
579       return;
580     }
581   m = (const struct InitReplyMessage *) msg;
582   /* start our message processing loop */
583 #if DEBUG_CORE
584   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
585               "Successfully connected to core service, starting processing loop.\n");
586 #endif
587   h->currently_down = GNUNET_NO;
588   trigger_next_request (h);
589   GNUNET_CLIENT_receive (h->client_notifications,
590                          &main_notify_handler, h, GNUNET_TIME_UNIT_FOREVER_REL);
591   if (NULL != (init = h->init))
592     {
593       /* mark so we don't call init on reconnect */
594       h->init = NULL;
595       GNUNET_CRYPTO_hash (&m->publicKey,
596                           sizeof (struct
597                                   GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
598                           &my_identity.hashPubKey);
599       init (h->cls, h, &my_identity, &m->publicKey);
600     }
601 }
602
603
604 static void
605 reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
606 {
607   struct GNUNET_CORE_Handle *h = cls;
608   h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
609   reconnect (h);
610 }
611
612
613 /**
614  * Function called when we are ready to transmit our
615  * "START" message (or when this operation timed out).
616  *
617  * @param cls closure
618  * @param size number of bytes available in buf
619  * @param buf where the callee should write the message
620  * @return number of bytes written to buf
621  */
622 static size_t
623 transmit_start (void *cls, size_t size, void *buf)
624 {
625   struct GNUNET_CORE_Handle *h = cls;
626   struct InitMessage *init;
627   uint16_t *ts;
628   uint16_t msize;
629   uint32_t opt;
630   unsigned int hpos;
631   struct GNUNET_TIME_Relative delay;
632
633   h->th = NULL;
634   if (size == 0)
635     {
636       if ((h->init == NULL) ||
637           (GNUNET_TIME_absolute_get ().value < h->startup_timeout.value))
638         {
639           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
640                       _("Failed to connect to core service, retrying.\n"));
641           delay = GNUNET_TIME_absolute_get_remaining (h->startup_timeout);
642           if ((h->init == NULL) || (delay.value > 1000))
643             delay = GNUNET_TIME_UNIT_SECONDS;
644           if (h->init == NULL)
645             h->startup_timeout =
646               GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
647           h->reconnect_task =
648             GNUNET_SCHEDULER_add_delayed (h->sched, 
649                                           delay, &reconnect_task, h);
650           return 0;
651         }
652       /* timeout on initial connect */
653       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
654                   _("Failed to connect to core service, giving up.\n"));
655       h->init (h->cls, NULL, NULL, NULL);
656       GNUNET_CORE_disconnect (h);
657       return 0;
658     }
659   msize = h->hcnt * sizeof (uint16_t) + sizeof (struct InitMessage);
660   GNUNET_assert (size >= msize);
661   init = buf;
662   init->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_INIT);
663   init->header.size = htons (msize);
664   opt = GNUNET_CORE_OPTION_NOTHING;
665   if (h->pre_connects != NULL)
666     opt |= GNUNET_CORE_OPTION_SEND_PRE_CONNECT;
667   if (h->connects != NULL)
668     opt |= GNUNET_CORE_OPTION_SEND_CONNECT;
669   if (h->disconnects != NULL)
670     opt |= GNUNET_CORE_OPTION_SEND_DISCONNECT;
671   if (h->inbound_notify != NULL)
672     {
673       if (h->inbound_hdr_only)
674         opt |= GNUNET_CORE_OPTION_SEND_HDR_INBOUND;
675       else
676         opt |= GNUNET_CORE_OPTION_SEND_FULL_INBOUND;
677     }
678   if (h->outbound_notify != NULL)
679     {
680       if (h->outbound_hdr_only)
681         opt |= GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND;
682       else
683         opt |= GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND;
684     }
685   init->options = htonl (opt);
686   ts = (uint16_t *) & init[1];
687   for (hpos = 0; hpos < h->hcnt; hpos++)
688     ts[hpos] = htons (h->handlers[hpos].type);
689   GNUNET_CLIENT_receive (h->client_notifications,
690                          &init_reply_handler,
691                          h,
692                          GNUNET_TIME_absolute_get_remaining
693                          (h->startup_timeout));
694   return sizeof (struct InitMessage) + h->hcnt * sizeof (uint16_t);
695 }
696
697
698 /**
699  * Connect to the core service.  Note that the connection may
700  * complete (or fail) asynchronously.
701  *
702  * @param sched scheduler to use
703  * @param cfg configuration to use
704  * @param timeout after how long should we give up trying to connect to the core service?
705  * @param cls closure for the various callbacks that follow (including handlers in the handlers array)
706  * @param init callback to call on timeout or once we have successfully
707  *        connected to the core service; note that timeout is only meaningful if init is not NULL
708  * @param pre_connects function to call on peer pre-connect (no session key yet), can be NULL
709  * @param connects function to call on peer connect, can be NULL
710  * @param disconnects function to call on peer disconnect / timeout, can be NULL
711  * @param inbound_notify function to call for all inbound messages, can be NULL
712  * @param inbound_hdr_only set to GNUNET_YES if inbound_notify will only read the
713  *                GNUNET_MessageHeader and hence we do not need to give it the full message;
714  *                can be used to improve efficiency, ignored if inbound_notify is NULLL
715  * @param outbound_notify function to call for all outbound messages, can be NULL
716  * @param outbound_hdr_only set to GNUNET_YES if outbound_notify will only read the
717  *                GNUNET_MessageHeader and hence we do not need to give it the full message
718  *                can be used to improve efficiency, ignored if outbound_notify is NULLL
719  * @param handlers callbacks for messages we care about, NULL-terminated
720  * @return handle to the core service (only useful for disconnect until 'init' is called);
721  *                NULL on error (in this case, init is never called)
722  */
723 struct GNUNET_CORE_Handle *
724 GNUNET_CORE_connect (struct GNUNET_SCHEDULER_Handle *sched,
725                      const struct GNUNET_CONFIGURATION_Handle *cfg,
726                      struct GNUNET_TIME_Relative timeout,
727                      void *cls,
728                      GNUNET_CORE_StartupCallback init,
729                      GNUNET_CORE_ConnectEventHandler pre_connects,
730                      GNUNET_CORE_ConnectEventHandler connects,
731                      GNUNET_CORE_DisconnectEventHandler disconnects,
732                      GNUNET_CORE_MessageCallback inbound_notify,
733                      int inbound_hdr_only,
734                      GNUNET_CORE_MessageCallback outbound_notify,
735                      int outbound_hdr_only,
736                      const struct GNUNET_CORE_MessageHandler *handlers)
737 {
738   struct GNUNET_CORE_Handle *h;
739
740   h = GNUNET_malloc (sizeof (struct GNUNET_CORE_Handle));
741   h->sched = sched;
742   h->cfg = cfg;
743   h->cls = cls;
744   h->init = init;
745   h->pre_connects = pre_connects;
746   h->connects = connects;
747   h->disconnects = disconnects;
748   h->inbound_notify = inbound_notify;
749   h->outbound_notify = outbound_notify;
750   h->inbound_hdr_only = inbound_hdr_only;
751   h->outbound_hdr_only = outbound_hdr_only;
752   h->handlers = handlers;
753   h->client_notifications = GNUNET_CLIENT_connect (sched, "core", cfg);
754   if (h->client_notifications == NULL)
755     {
756       GNUNET_free (h);
757       return NULL;
758     }
759   h->startup_timeout = GNUNET_TIME_relative_to_absolute (timeout);
760   h->hcnt = 0;
761   while (handlers[h->hcnt].callback != NULL)
762     h->hcnt++;
763   GNUNET_assert (h->hcnt <
764                  (GNUNET_SERVER_MAX_MESSAGE_SIZE -
765                   sizeof (struct InitMessage)) / sizeof (uint16_t));
766 #if DEBUG_CORE
767   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
768               "Trying to connect to core service in next %llu ms.\n",
769               timeout.value);
770 #endif
771   h->th =
772     GNUNET_CLIENT_notify_transmit_ready (h->client_notifications,
773                                          sizeof (struct InitMessage) +
774                                          sizeof (uint16_t) * h->hcnt, timeout,
775                                          GNUNET_YES,
776                                          &transmit_start, h);
777   return h;
778 }
779
780
781 /**
782  * Disconnect from the core service.
783  *
784  * @param handle connection to core to disconnect
785  */
786 void
787 GNUNET_CORE_disconnect (struct GNUNET_CORE_Handle *handle)
788 {
789   if (handle->th != NULL)
790     GNUNET_CLIENT_notify_transmit_ready_cancel (handle->th);
791   if (handle->solicit_transmit_req != NULL)
792     GNUNET_CORE_notify_transmit_ready_cancel (handle->solicit_transmit_req);
793   if (handle->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
794     GNUNET_SCHEDULER_cancel (handle->sched, handle->reconnect_task);
795   if (handle->client_notifications != NULL)
796     GNUNET_CLIENT_disconnect (handle->client_notifications, GNUNET_NO);
797   GNUNET_free_non_null (handle->solicit_buffer);
798   GNUNET_free (handle);
799 }
800
801
802 /**
803  * Build the message requesting data transmission.
804  */
805 static size_t
806 produce_send (void *cls, size_t size, void *buf)
807 {
808   struct GNUNET_CORE_TransmitHandle *th = cls;
809   struct GNUNET_CORE_Handle *h;
810   struct SendMessage *sm;
811   size_t dt;
812   GNUNET_CONNECTION_TransmitReadyNotify notify;
813   void *notify_cls;
814
815   h = th->ch;
816   if (buf == NULL)
817     {
818       /* timeout or error */
819 #if DEBUG_CORE
820       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
821                   "P2P transmission request for `%4s' timed out.\n",
822                   GNUNET_i2s(&th->peer));
823 #endif
824       GNUNET_assert (0 == th->notify (th->notify_cls, 0, NULL));
825       GNUNET_CORE_notify_transmit_ready_cancel (th);
826       trigger_next_request (h);
827       return 0;
828     }
829 #if DEBUG_CORE
830   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
831               "Preparing for P2P transmission to `%4s'.\n",
832               GNUNET_i2s(&th->peer));
833 #endif
834   GNUNET_assert (th->timeout_task != GNUNET_SCHEDULER_NO_TASK);
835   sm = (struct SendMessage *) buf;
836   sm->header.type = htons (GNUNET_MESSAGE_TYPE_CORE_SEND);
837   sm->priority = htonl (th->priority);
838   sm->deadline = GNUNET_TIME_absolute_hton (th->timeout);
839   sm->peer = th->peer;
840   notify = th->notify;
841   notify_cls = th->notify_cls;
842   GNUNET_CORE_notify_transmit_ready_cancel (th);
843   trigger_next_request (h);
844   GNUNET_assert (size >= sizeof (struct SendMessage));
845   dt = notify (notify_cls, size - sizeof (struct SendMessage), &sm[1]);
846   if (0 == dt)
847     {
848 #if DEBUG_CORE
849   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
850               "Size of clients message to peer %s is 0!\n",
851               GNUNET_i2s(&th->peer));
852 #endif
853       /* client decided to send nothing! */
854       return 0;
855     }
856   GNUNET_assert (dt >= sizeof (struct GNUNET_MessageHeader));
857   sm->header.size = htons (dt + sizeof (struct SendMessage));
858   GNUNET_assert (dt + sizeof (struct SendMessage) <= size);
859   return dt + sizeof (struct SendMessage);
860 }
861
862
863 /**
864  * Ask the core to call "notify" once it is ready to transmit the
865  * given number of bytes to the specified "target".  If we are not yet
866  * connected to the specified peer, a call to this function will cause
867  * us to try to establish a connection.
868  *
869  * @param handle connection to core service
870  * @param priority how important is the message?
871  * @param maxdelay how long can the message wait?
872  * @param target who should receive the message,
873  *        use NULL for this peer (loopback)
874  * @param notify_size how many bytes of buffer space does notify want?
875  * @param notify function to call when buffer space is available
876  * @param notify_cls closure for notify
877  * @return non-NULL if the notify callback was queued,
878  *         NULL if we can not even queue the request (insufficient
879  *         memory); if NULL is returned, "notify" will NOT be called.
880  */
881 struct GNUNET_CORE_TransmitHandle *
882 GNUNET_CORE_notify_transmit_ready (struct GNUNET_CORE_Handle *handle,
883                                    unsigned int priority,
884                                    struct GNUNET_TIME_Relative maxdelay,
885                                    const struct GNUNET_PeerIdentity *target,
886                                    size_t notify_size,
887                                    GNUNET_CONNECTION_TransmitReadyNotify notify,
888                                    void *notify_cls)
889 {
890   struct GNUNET_CORE_TransmitHandle *th;
891
892   GNUNET_assert (notify_size + sizeof (struct SendMessage) <
893                  GNUNET_SERVER_MAX_MESSAGE_SIZE);
894   th = GNUNET_malloc (sizeof (struct GNUNET_CORE_TransmitHandle));
895   th->ch = handle;
896   /* append to list */
897   th->prev = handle->pending_tail;
898   if (handle->pending_tail == NULL)
899     handle->pending_head = th;
900   else
901     handle->pending_tail->next = th;
902   th->get_message = &produce_send;
903   th->get_message_cls = th;
904   th->notify = notify;
905   th->notify_cls = notify_cls;
906   th->peer = *target;
907   th->timeout = GNUNET_TIME_relative_to_absolute (maxdelay);
908   th->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->sched,
909                                                    maxdelay,
910                                                    &timeout_request, th);
911   th->priority = priority;
912   th->msize = sizeof (struct SendMessage) + notify_size;
913   /* was the request queue previously empty? */
914   if ( (handle->pending_head == th) &&
915        (handle->th == NULL) )
916     trigger_next_request (handle);
917   return th;
918 }
919
920
921 /**
922  * Cancel the specified transmission-ready notification.
923  *
924  * @param h handle that was returned by "notify_transmit_ready".
925  */
926 void
927 GNUNET_CORE_notify_transmit_ready_cancel (struct GNUNET_CORE_TransmitHandle
928                                           *h)
929 {
930   struct GNUNET_CORE_Handle *handle = h->ch;
931
932   if (handle->submitted == h)
933     {
934       handle->submitted = NULL;
935     }
936   else
937     {
938       if (h->prev == NULL)
939         handle->pending_head = h->next;
940       else
941         h->prev->next = h->next;
942       if (h->next == NULL)
943         handle->pending_tail = h->prev;
944       else
945         h->next->prev = h->prev;
946     }
947   if (h->timeout_task != GNUNET_SCHEDULER_NO_TASK)
948     GNUNET_SCHEDULER_cancel (handle->sched, h->timeout_task);
949   GNUNET_free (h);
950 }
951
952
953 /* end of core_api.c */