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