2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file hostlist/hostlist-client.c
23 * @brief hostlist support. Downloads HELLOs via HTTP.
24 * @author Christian Grothoff
28 #include "hostlist-client.h"
29 #include "gnunet_core_service.h"
30 #include "gnunet_hello_lib.h"
31 #include "gnunet_transport_service.h"
32 #include <curl/curl.h>
35 * Number of connections that we must have to NOT download
38 #define MIN_CONNECTIONS 4
43 static const struct GNUNET_CONFIGURATION_Handle *cfg;
48 static struct GNUNET_SCHEDULER_Handle *sched;
53 struct GNUNET_STATISTICS_Handle *stats;
58 struct GNUNET_TRANSPORT_Handle *transport;
61 * Proxy that we are using (can be NULL).
66 * Buffer for data downloaded via HTTP.
68 static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
71 * Number of bytes valid in 'download_buffer'.
73 static size_t download_pos;
76 * Current URL that we are using.
78 static char *current_url;
81 * Current CURL handle.
86 * Current multi-CURL handle.
91 * ID of the current task scheduled.
93 static GNUNET_SCHEDULER_TaskIdentifier current_task;
96 * Amount of time we wait between hostlist downloads.
98 static struct GNUNET_TIME_Relative hostlist_delay;
101 * Set to GNUNET_YES if the current URL had some problems.
103 static int bogus_url;
106 * Number of active connections (according to core service).
108 static unsigned int connection_count;
112 * Process downloaded bits by calling callback on each HELLO.
114 * @param ptr buffer with downloaded data
115 * @param size size of a record
116 * @param nmemb number of records downloaded
118 * @return number of bytes that were processed (always size*nmemb)
121 download_hostlist_processor (void *ptr,
126 const char * cbuf = ptr;
127 const struct GNUNET_MessageHeader *msg;
133 total = size * nmemb;
134 if ( (total == 0) || (bogus_url) )
136 return total; /* ok, no data or bogus data */
141 cpy = GNUNET_MIN (total, GNUNET_SERVER_MAX_MESSAGE_SIZE - download_pos);
142 GNUNET_assert (cpy > 0);
143 memcpy (&download_buffer[download_pos],
149 if (download_pos < sizeof(struct GNUNET_MessageHeader))
151 msg = (const struct GNUNET_MessageHeader *) download_buffer;
152 msize = ntohs(msg->size);
153 if (msize < sizeof(struct GNUNET_MessageHeader))
155 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
156 _("Invalid `%s' message received from hostlist at `%s'\n"),
162 if (download_pos < msize)
164 if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
166 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
167 "Received valid `%s' message from hostlist server.\n",
169 GNUNET_TRANSPORT_offer_hello (transport, msg);
173 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
174 _("Invalid `%s' message received from hostlist at `%s'\n"),
180 memmove (download_buffer,
181 &download_buffer[msize],
182 download_pos - msize);
183 download_pos -= msize;
190 * Obtain a hostlist URL that we should use.
192 * @return NULL if there is no URL available
203 GNUNET_CONFIGURATION_get_value_string (cfg,
208 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
209 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
210 "SERVERS", "HOSTLIST");
215 if (strlen (servers) > 0)
218 pos = strlen (servers) - 1;
221 if (servers[pos] == ' ')
228 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
229 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
230 "SERVERS", "HOSTLIST");
231 GNUNET_free (servers);
235 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
236 pos = strlen (servers) - 1;
239 if (servers[pos] == ' ')
251 ret = GNUNET_strdup (&servers[pos]);
252 GNUNET_free (servers);
257 #define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_log(GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0);
261 * Schedule the background task that will (possibly)
262 * download a hostlist.
265 schedule_hostlist_task (void);
269 * Clean up the state from the task that downloaded the
270 * hostlist and schedule the next task.
279 mret = curl_multi_remove_handle (multi, curl);
280 if (mret != CURLM_OK)
282 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
283 _("%s failed at %s:%d: `%s'\n"),
284 "curl_multi_remove_handle", __FILE__, __LINE__,
285 curl_multi_strerror (mret));
287 mret = curl_multi_cleanup (multi);
288 if (mret != CURLM_OK)
289 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
290 _("%s failed at %s:%d: `%s'\n"),
291 "curl_multi_cleanup", __FILE__, __LINE__,
292 curl_multi_strerror (mret));
297 curl_easy_cleanup (curl);
300 GNUNET_free_non_null (current_url);
302 schedule_hostlist_task ();
307 * Task that is run when we are ready to receive more data from the hostlist
310 * @param cls closure, unused
311 * @param tc task context, unused
314 multi_ready (void *cls,
315 const struct GNUNET_SCHEDULER_TaskContext *tc)
321 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
326 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
328 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
329 _("Timeout trying to download hostlist from `%s'\n"),
337 mret = curl_multi_perform (multi, &running);
342 msg = curl_multi_info_read (multi, &running);
343 GNUNET_break (msg != NULL);
349 if (msg->data.result != CURLE_OK)
350 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
351 _("%s failed at %s:%d: `%s'\n"),
352 "curl_multi_perform", __FILE__,
354 curl_easy_strerror (msg->data.result));
363 while (mret == CURLM_CALL_MULTI_PERFORM);
364 if (mret != CURLM_OK)
366 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
367 _("%s failed at %s:%d: `%s'\n"),
368 "curl_multi_perform", __FILE__, __LINE__,
369 curl_multi_strerror (mret));
373 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
374 _("Download of hostlist `%s' completed.\n"),
381 * Ask CURL for the select set and then schedule the
382 * receiving task with the scheduler.
392 struct GNUNET_NETWORK_FDSet *grs;
393 struct GNUNET_NETWORK_FDSet *gws;
399 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
400 if (mret != CURLM_OK)
402 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
403 _("%s failed at %s:%d: `%s'\n"),
404 "curl_multi_fdset", __FILE__, __LINE__,
405 curl_multi_strerror (mret));
409 grs = GNUNET_NETWORK_fdset_create ();
410 gws = GNUNET_NETWORK_fdset_create ();
411 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max);
412 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max);
414 = GNUNET_SCHEDULER_add_select (sched,
415 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
416 GNUNET_SCHEDULER_NO_TASK,
417 GNUNET_TIME_UNIT_MINUTES,
422 GNUNET_NETWORK_fdset_destroy (gws);
423 GNUNET_NETWORK_fdset_destroy (grs);
428 * Main function that will download a hostlist and process its
437 curl = curl_easy_init ();
445 transport = GNUNET_TRANSPORT_connect (sched, cfg, NULL, NULL, NULL, NULL);
446 current_url = get_url ();
447 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
448 _("Bootstrapping using hostlist at `%s'.\n"),
452 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
455 CURL_EASY_SETOPT (curl,
456 CURLOPT_WRITEFUNCTION,
457 &download_hostlist_processor);
463 CURL_EASY_SETOPT (curl,
471 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
472 /* no need to abort if the above failed */
473 CURL_EASY_SETOPT (curl,
481 CURL_EASY_SETOPT (curl,
484 CURL_EASY_SETOPT (curl,
486 GNUNET_SERVER_MAX_MESSAGE_SIZE);
487 if (0 == strncmp (current_url, "http", 4))
488 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
489 CURL_EASY_SETOPT (curl,
490 CURLOPT_CONNECTTIMEOUT,
492 CURL_EASY_SETOPT (curl,
495 multi = curl_multi_init ();
502 mret = curl_multi_add_handle (multi, curl);
503 if (mret != CURLM_OK)
505 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
506 _("%s failed at %s:%d: `%s'\n"),
507 "curl_multi_add_handle", __FILE__, __LINE__,
508 curl_multi_strerror (mret));
509 mret = curl_multi_cleanup (multi);
510 if (mret != CURLM_OK)
511 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
512 _("%s failed at %s:%d: `%s'\n"),
513 "curl_multi_cleanup", __FILE__, __LINE__,
514 curl_multi_strerror (mret));
524 * Task that checks if we should try to download a hostlist.
525 * If so, we initiate the download, otherwise we schedule
526 * this task again for a later time.
529 check_task (void *cls,
530 const struct GNUNET_SCHEDULER_TaskContext *tc)
532 current_task = GNUNET_SCHEDULER_NO_TASK;
533 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
535 if (connection_count < MIN_CONNECTIONS)
536 download_hostlist ();
538 schedule_hostlist_task ();
543 * Compute when we should check the next time about downloading
544 * a hostlist; then schedule the task accordingly.
547 schedule_hostlist_task ()
549 struct GNUNET_TIME_Relative delay;
553 curl_global_cleanup ();
554 return; /* in shutdown */
556 delay = hostlist_delay;
557 if (hostlist_delay.value == 0)
558 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
560 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
561 if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * connection_count)
562 hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_DAYS,
564 GNUNET_STATISTICS_set (stats,
565 gettext_noop("Minimum time between hostlist downloads"),
566 hostlist_delay.value,
568 current_task = GNUNET_SCHEDULER_add_delayed (sched,
576 * Method called whenever a given peer connects.
579 * @param peer peer identity this notification is about
582 connect_handler (void *cls,
584 GNUNET_PeerIdentity * peer)
591 * Method called whenever a given peer connects.
594 * @param peer peer identity this notification is about
597 disconnect_handler (void *cls,
599 GNUNET_PeerIdentity * peer)
606 * Continuation called by the statistics code once
607 * we go the stat. Initiates hostlist download scheduling.
610 * @param success GNUNET_OK if statistics were
611 * successfully obtained, GNUNET_SYSERR if not.
614 primary_task (void *cls, int success)
617 return; /* in shutdown */
618 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
619 "Statistics request done, scheduling hostlist download\n");
620 schedule_hostlist_task ();
625 process_stat (void *cls,
626 const char *subsystem,
631 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
632 _("Initial time between hostlist downloads is %llums\n"),
633 (unsigned long long) value);
634 hostlist_delay.value = value;
640 * Start downloading hostlists from hostlist servers as necessary.
643 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
644 struct GNUNET_SCHEDULER_Handle *s,
645 struct GNUNET_STATISTICS_Handle *st,
646 GNUNET_CORE_ClientEventHandler *ch,
647 GNUNET_CORE_ClientEventHandler *dh)
649 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
652 return GNUNET_SYSERR;
658 GNUNET_CONFIGURATION_get_value_string (cfg,
663 *ch = &connect_handler;
664 *dh = &disconnect_handler;
665 GNUNET_STATISTICS_get (stats,
667 gettext_noop("Minimum time between hostlist downloads"),
668 GNUNET_TIME_UNIT_MINUTES,
677 * Stop downloading hostlists from hostlist servers as necessary.
680 GNUNET_HOSTLIST_client_stop ()
682 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
683 "Hostlist client shutdown\n");
684 if (current_task != GNUNET_SCHEDULER_NO_TASK)
686 GNUNET_SCHEDULER_cancel (sched,
688 if (transport != NULL)
690 GNUNET_TRANSPORT_disconnect (transport);
693 curl_global_cleanup ();
695 GNUNET_assert (NULL == transport);
696 GNUNET_free_non_null (proxy);
703 /* end of hostlist-client.c */