2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010 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 3, 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
25 * @author Matthias Wachs
29 #include "hostlist-client.h"
30 #include "gnunet_core_service.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet-daemon-hostlist.h"
35 #include <curl/curl.h>
36 #include "gnunet_common.h"
37 #include "gnunet_bio_lib.h"
39 #define DEBUG_HOSTLIST_CLIENT GNUNET_NO
43 * Number of connections that we must have to NOT download
46 #define MIN_CONNECTIONS 4
49 * Interval between two advertised hostlist tests
51 #define TESTING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
54 * A single hostlist obtained by hostlist advertisements
59 * previous entry, used to manage entries in a double linked list
61 struct Hostlist *prev;
64 * next entry, used to manage entries in a double linked list
66 struct Hostlist *next;
69 * URI where hostlist can be obtained
71 const char *hostlist_uri;
74 * Value describing the quality of the hostlist, the bigger the better but (should) never < 0
75 * used for deciding which hostlist is replaced if MAX_NUMBER_HOSTLISTS in data structure is reached
76 * intial value = HOSTLIST_INITIAL
77 * increased every successful download by HOSTLIST_SUCCESSFULL_DOWNLOAD
78 * increased every successful download by number of obtained HELLO messages
79 * decreased every failed download by HOSTLIST_SUCCESSFULL_DOWNLOAD
84 * Time the hostlist advertisement was recieved and the entry was created
86 struct GNUNET_TIME_Absolute time_creation;
89 * Last time the hostlist was obtained
91 struct GNUNET_TIME_Absolute time_last_usage;
94 * Number of HELLO messages obtained during last download
99 * Number of times the hostlist was successfully obtained
109 static const struct GNUNET_CONFIGURATION_Handle *cfg;
114 static struct GNUNET_STATISTICS_Handle *stats;
119 static struct GNUNET_TRANSPORT_Handle *transport;
122 * Proxy that we are using (can be NULL).
127 * Number of bytes valid in 'download_buffer'.
129 static size_t download_pos;
132 * Current URL that we are using.
134 static char *current_url;
137 * Current CURL handle.
142 * Current multi-CURL handle.
147 * How many bytes did we download from the current hostlist URL?
149 static uint32_t stat_bytes_downloaded;
152 * Amount of time we wait between hostlist downloads.
154 static struct GNUNET_TIME_Relative hostlist_delay;
157 * ID of the task, checking if hostlist download should take plate
159 static GNUNET_SCHEDULER_TaskIdentifier ti_check_download;
162 * ID of the task downloading the hostlist
164 static GNUNET_SCHEDULER_TaskIdentifier ti_download;
167 * ID of the task saving the hostlsit in a regular intervall
169 static GNUNET_SCHEDULER_TaskIdentifier ti_saving_task;
172 * ID of the task called to initiate a download
174 static GNUNET_SCHEDULER_TaskIdentifier ti_download_dispatcher_task;
177 * ID of the task controlling the locking between two hostlist tests
179 static GNUNET_SCHEDULER_TaskIdentifier ti_testing_intervall_task;
182 * At what time MUST the current hostlist request be done?
184 static struct GNUNET_TIME_Absolute end_time;
187 * Head of the linked list used to store hostlists
189 static struct Hostlist *linked_list_head;
192 * Tail of the linked list used to store hostlists
194 static struct Hostlist *linked_list_tail;
197 * Current hostlist used for downloading
199 static struct Hostlist *current_hostlist;
202 * Size of the linke list used to store hostlists
204 static unsigned int linked_list_size;
207 * Head of the linked list used to store hostlists
209 static struct Hostlist *hostlist_to_test;
212 * Set to GNUNET_YES if the current URL had some problems.
214 static int stat_bogus_url;
217 * Value controlling if a hostlist is tested at the moment
219 static int stat_testing_hostlist;
222 * Value controlling if a hostlist testing is allowed at the moment
224 static int stat_testing_allowed;
227 * Value controlling if a hostlist download is running at the moment
229 static int stat_download_in_progress;
232 * Value saying if a preconfigured bootstrap server is used
234 static unsigned int stat_use_bootstrap;
237 * Set if we are allowed to learn new hostlists and use them
239 static int stat_learning;
242 * Value saying if hostlist download was successful
244 static unsigned int stat_download_successful;
247 * Value saying how many valid HELLO messages were obtained during download
249 static unsigned int stat_hellos_obtained;
252 * Number of active connections (according to core service).
254 static unsigned int stat_connection_count;
258 * Process downloaded bits by calling callback on each HELLO.
260 * @param ptr buffer with downloaded data
261 * @param size size of a record
262 * @param nmemb number of records downloaded
264 * @return number of bytes that were processed (always size*nmemb)
267 callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
269 static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
270 const char *cbuf = ptr;
271 const struct GNUNET_MessageHeader *msg;
277 total = size * nmemb;
278 stat_bytes_downloaded += total;
279 if ((total == 0) || (stat_bogus_url))
281 return total; /* ok, no data or bogus data */
284 GNUNET_STATISTICS_update (stats,
286 ("# bytes downloaded from hostlist servers"),
287 (int64_t) total, GNUNET_NO);
289 while ((left > 0) || (download_pos > 0))
291 cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - 1 - download_pos);
292 memcpy (&download_buffer[download_pos], cbuf, cpy);
296 if (download_pos < sizeof (struct GNUNET_MessageHeader))
298 GNUNET_assert (left == 0);
301 msg = (const struct GNUNET_MessageHeader *) download_buffer;
302 msize = ntohs (msg->size);
303 if (msize < sizeof (struct GNUNET_MessageHeader))
305 GNUNET_STATISTICS_update (stats,
307 ("# invalid HELLOs downloaded from hostlist servers"),
309 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
310 _("Invalid `%s' message received from hostlist at `%s'\n"),
311 "HELLO", current_url);
312 stat_hellos_obtained++;
316 if (download_pos < msize)
318 GNUNET_assert (left == 0);
321 if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message *) msg) == msize)
323 #if DEBUG_HOSTLIST_CLIENT
324 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
325 "Received valid `%s' message from hostlist server.\n",
328 GNUNET_STATISTICS_update (stats,
330 ("# valid HELLOs downloaded from hostlist servers"),
332 stat_hellos_obtained++;
333 GNUNET_TRANSPORT_offer_hello (transport, msg, NULL, NULL);
337 GNUNET_STATISTICS_update (stats,
339 ("# invalid HELLOs downloaded from hostlist servers"),
341 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
342 _("Invalid `%s' message received from hostlist at `%s'\n"),
343 "HELLO", current_url);
344 stat_bogus_url = GNUNET_YES;
345 stat_hellos_obtained++;
348 memmove (download_buffer, &download_buffer[msize], download_pos - msize);
349 download_pos -= msize;
356 * Obtain a hostlist URL that we should use.
358 * @return NULL if there is no URL available
361 get_bootstrap_server ()
369 GNUNET_CONFIGURATION_get_value_string (cfg,
370 "HOSTLIST", "SERVERS", &servers))
372 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
374 ("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
375 "SERVERS", "HOSTLIST");
380 if (strlen (servers) > 0)
383 pos = strlen (servers) - 1;
386 if (servers[pos] == ' ')
393 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
395 ("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
396 "SERVERS", "HOSTLIST");
397 GNUNET_free (servers);
401 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
402 pos = strlen (servers) - 1;
405 if (servers[pos] == ' ')
417 ret = GNUNET_strdup (&servers[pos]);
418 GNUNET_free (servers);
423 * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
424 * @return uri to use, NULL if there is no URL available
430 unsigned int counter;
431 struct Hostlist *pos;
433 if (GNUNET_NO == stat_learning)
435 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
436 "Using preconfigured bootstrap server\n");
437 current_hostlist = NULL;
438 return get_bootstrap_server ();
441 if ((GNUNET_YES == stat_testing_hostlist) && (NULL != hostlist_to_test))
443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
444 "Testing new advertised hostlist if it is obtainable\n");
445 current_hostlist = hostlist_to_test;
446 return strdup (hostlist_to_test->hostlist_uri);
449 if ((GNUNET_YES == stat_use_bootstrap) || (linked_list_size == 0))
451 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
452 "Using preconfigured bootstrap server\n");
453 current_hostlist = NULL;
454 return get_bootstrap_server ();
456 index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
459 pos = linked_list_head;
460 while (counter < index)
465 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
466 "Using learned hostlist `%s'\n", pos->hostlist_uri);
467 current_hostlist = pos;
468 return strdup (pos->hostlist_uri);
472 #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)
476 * Method to save hostlist to a file during hostlist client shutdown
477 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
479 static void save_hostlist_file (int shutdown);
483 * add val2 to val1 with overflow check
484 * @param val1 value 1
485 * @param val2 value 2
489 checked_add (uint64_t val1, uint64_t val2)
491 static uint64_t temp;
492 static uint64_t maxv;
505 * Subtract val2 from val1 with underflow check
506 * @param val1 value 1
507 * @param val2 value 2
511 checked_sub (uint64_t val1, uint64_t val2)
516 return (val1 - val2);
520 * Method to check if a URI is in hostlist linked list
521 * @param uri uri to check
522 * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
525 linked_list_contains (const char *uri)
527 struct Hostlist *pos;
529 pos = linked_list_head;
532 if (0 == strcmp (pos->hostlist_uri, uri))
541 * Method returning the hostlist element with the lowest quality in the datastore
542 * @return hostlist with lowest quality
544 static struct Hostlist *
545 linked_list_get_lowest_quality ()
547 struct Hostlist *pos;
548 struct Hostlist *lowest;
550 if (linked_list_size == 0)
552 lowest = linked_list_head;
553 pos = linked_list_head->next;
556 if (pos->quality < lowest->quality)
565 * Method to insert a hostlist into the datastore. If datastore
566 * contains maximum number of elements, the elements with lowest
567 * quality is dismissed
572 struct Hostlist *lowest_quality;
574 if (MAX_NUMBER_HOSTLISTS <= linked_list_size)
576 /* No free entries available, replace existing entry */
577 lowest_quality = linked_list_get_lowest_quality ();
578 GNUNET_assert (lowest_quality != NULL);
579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
580 "Removing hostlist with URI `%s' which has the worst quality of all (%llu)\n",
581 lowest_quality->hostlist_uri,
582 (unsigned long long) lowest_quality->quality);
583 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail,
586 GNUNET_free (lowest_quality);
588 GNUNET_CONTAINER_DLL_insert (linked_list_head,
589 linked_list_tail, hostlist_to_test);
591 GNUNET_STATISTICS_set (stats,
592 gettext_noop ("# advertised hostlist URIs"),
593 linked_list_size, GNUNET_NO);
594 stat_testing_hostlist = GNUNET_NO;
599 * Method updating hostlist statistics
606 if (((stat_use_bootstrap == GNUNET_NO) && (NULL != current_hostlist)) ||
607 ((stat_testing_hostlist == GNUNET_YES) && (NULL != current_hostlist)))
609 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
610 "Updating hostlist statics for URI `%s'\n",
611 current_hostlist->hostlist_uri);
612 current_hostlist->hello_count = stat_hellos_obtained;
613 current_hostlist->time_last_usage = GNUNET_TIME_absolute_get ();
614 current_hostlist->quality =
615 checked_add (current_hostlist->quality,
616 (stat_hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
617 if (GNUNET_YES == stat_download_successful)
619 current_hostlist->times_used++;
620 current_hostlist->quality =
621 checked_add (current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
622 GNUNET_asprintf (&stat, gettext_noop ("# advertised URI `%s' downloaded"),
623 current_hostlist->hostlist_uri);
625 GNUNET_STATISTICS_update (stats, stat, 1, GNUNET_YES);
629 current_hostlist->quality =
630 checked_sub (current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD);
632 current_hostlist = NULL;
633 /* Alternating the usage of preconfigured and learned hostlists */
635 if (stat_testing_hostlist == GNUNET_YES)
638 if (GNUNET_YES == stat_learning)
640 if (stat_use_bootstrap == GNUNET_YES)
641 stat_use_bootstrap = GNUNET_NO;
643 stat_use_bootstrap = GNUNET_YES;
646 stat_use_bootstrap = GNUNET_YES;
650 * Clean up the state from the task that downloaded the
651 * hostlist and schedule the next task.
658 if ((stat_testing_hostlist == GNUNET_YES) &&
659 (GNUNET_NO == stat_download_successful) && (NULL != hostlist_to_test))
661 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
663 ("Advertised hostlist with URI `%s' could not be downloaded. Advertised URI gets dismissed.\n"),
664 hostlist_to_test->hostlist_uri);
667 if (stat_testing_hostlist == GNUNET_YES)
669 stat_testing_hostlist = GNUNET_NO;
671 if (NULL != hostlist_to_test)
673 GNUNET_free (hostlist_to_test);
674 hostlist_to_test = NULL;
679 mret = curl_multi_remove_handle (multi, curl);
680 if (mret != CURLM_OK)
682 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
683 _("%s failed at %s:%d: `%s'\n"),
684 "curl_multi_remove_handle", __FILE__, __LINE__,
685 curl_multi_strerror (mret));
687 mret = curl_multi_cleanup (multi);
688 if (mret != CURLM_OK)
689 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
690 _("%s failed at %s:%d: `%s'\n"),
691 "curl_multi_cleanup", __FILE__, __LINE__,
692 curl_multi_strerror (mret));
697 curl_easy_cleanup (curl);
700 GNUNET_free_non_null (current_url);
702 stat_bytes_downloaded = 0;
703 stat_download_in_progress = GNUNET_NO;
708 * Task that is run when we are ready to receive more data from the hostlist
711 * @param cls closure, unused
712 * @param tc task context, unused
715 task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
719 * Ask CURL for the select set and then schedule the
720 * receiving task with the scheduler.
730 struct GNUNET_NETWORK_FDSet *grs;
731 struct GNUNET_NETWORK_FDSet *gws;
733 struct GNUNET_TIME_Relative rtime;
739 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
740 if (mret != CURLM_OK)
742 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
743 _("%s failed at %s:%d: `%s'\n"),
744 "curl_multi_fdset", __FILE__, __LINE__,
745 curl_multi_strerror (mret));
749 mret = curl_multi_timeout (multi, &timeout);
750 if (mret != CURLM_OK)
752 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
753 _("%s failed at %s:%d: `%s'\n"),
754 "curl_multi_timeout", __FILE__, __LINE__,
755 curl_multi_strerror (mret));
760 GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
761 GNUNET_TIME_relative_multiply
762 (GNUNET_TIME_UNIT_MILLISECONDS, timeout));
763 grs = GNUNET_NETWORK_fdset_create ();
764 gws = GNUNET_NETWORK_fdset_create ();
765 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
766 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
767 #if DEBUG_HOSTLIST_CLIENT
768 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
769 "Scheduling task for hostlist download using cURL\n");
772 = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
773 GNUNET_SCHEDULER_NO_TASK,
774 rtime, grs, gws, &task_download, multi);
775 GNUNET_NETWORK_fdset_destroy (gws);
776 GNUNET_NETWORK_fdset_destroy (grs);
781 * Task that is run when we are ready to receive more data from the hostlist
784 * @param cls closure, unused
785 * @param tc task context, unused
788 task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
794 ti_download = GNUNET_SCHEDULER_NO_TASK;
795 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
797 #if DEBUG_HOSTLIST_CLIENT
798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799 "Shutdown requested while trying to download hostlist from `%s'\n",
806 if (GNUNET_TIME_absolute_get_remaining (end_time).rel_value == 0)
808 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
809 _("Timeout trying to download hostlist from `%s'\n"),
815 #if DEBUG_HOSTLIST_CLIENT
816 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
817 "Ready for processing hostlist client request\n");
823 if (stat_bytes_downloaded > MAX_BYTES_PER_HOSTLISTS)
825 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
826 _("Download limit of %u bytes exceeded, stopping download\n"),
827 MAX_BYTES_PER_HOSTLISTS);
831 mret = curl_multi_perform (multi, &running);
836 msg = curl_multi_info_read (multi, &running);
837 GNUNET_break (msg != NULL);
843 if ((msg->data.result != CURLE_OK) &&
844 (msg->data.result != CURLE_GOT_NOTHING))
845 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
846 _("%s failed for `%s' at %s:%d: `%s'\n"),
847 "curl_multi_perform",
850 __LINE__, curl_easy_strerror (msg->data.result));
853 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
854 _("Download of hostlist `%s' completed.\n"),
856 stat_download_successful = GNUNET_YES;
858 if (GNUNET_YES == stat_testing_hostlist)
860 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
862 ("Adding successfully tested hostlist `%s' datastore.\n"),
865 hostlist_to_test = NULL;
866 stat_testing_hostlist = GNUNET_NO;
876 while ((running > 0));
879 while (mret == CURLM_CALL_MULTI_PERFORM);
881 if (mret != CURLM_OK)
883 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
884 _("%s failed at %s:%d: `%s'\n"),
885 "curl_multi_perform", __FILE__, __LINE__,
886 curl_multi_strerror (mret));
894 * Main function that will download a hostlist and process its
904 current_url = download_get_url ();
905 if (current_url == NULL)
907 curl = curl_easy_init ();
915 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
916 _("Bootstrapping using hostlist at `%s'.\n"), current_url);
918 stat_download_in_progress = GNUNET_YES;
919 stat_download_successful = GNUNET_NO;
920 stat_hellos_obtained = 0;
921 stat_bytes_downloaded = 0;
923 GNUNET_STATISTICS_update (stats,
924 gettext_noop ("# hostlist downloads initiated"),
927 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
930 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &callback_download);
936 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, NULL);
942 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
943 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
944 /* no need to abort if the above failed */
945 CURL_EASY_SETOPT (curl, CURLOPT_URL, current_url);
951 CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
953 CURL_EASY_SETOPT (curl, CURLOPT_VERBOSE, 1);
955 CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
956 if (0 == strncmp (current_url, "http", 4))
957 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
958 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 60L);
959 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 60L);
961 /* this should no longer be needed; we're now single-threaded! */
962 CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
964 multi = curl_multi_init ();
971 mret = curl_multi_add_handle (multi, curl);
972 if (mret != CURLM_OK)
974 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
975 _("%s failed at %s:%d: `%s'\n"),
976 "curl_multi_add_handle", __FILE__, __LINE__,
977 curl_multi_strerror (mret));
978 mret = curl_multi_cleanup (multi);
979 if (mret != CURLM_OK)
980 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
981 _("%s failed at %s:%d: `%s'\n"),
982 "curl_multi_cleanup", __FILE__, __LINE__,
983 curl_multi_strerror (mret));
988 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
994 task_download_dispatcher (void *cls,
995 const struct GNUNET_SCHEDULER_TaskContext *tc)
997 ti_download_dispatcher_task = GNUNET_SCHEDULER_NO_TASK;
998 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1000 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download is initiated...\n");
1001 if (GNUNET_NO == stat_download_in_progress)
1003 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download can start immediately...\n");
1004 download_hostlist ();
1008 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1009 "Download in progess, have to wait...\n");
1010 ti_download_dispatcher_task =
1011 GNUNET_SCHEDULER_add_delayed (WAITING_INTERVALL,
1012 &task_download_dispatcher, NULL);
1017 * Task that checks if we should try to download a hostlist.
1018 * If so, we initiate the download, otherwise we schedule
1019 * this task again for a later time.
1022 task_check (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1025 struct GNUNET_TIME_Relative delay;
1027 ti_check_download = GNUNET_SCHEDULER_NO_TASK;
1028 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1031 if (stat_connection_count < MIN_CONNECTIONS)
1033 ti_download_dispatcher_task =
1034 GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1039 curl_global_cleanup ();
1040 return; /* in shutdown */
1042 delay = hostlist_delay;
1043 if (hostlist_delay.rel_value == 0)
1044 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1046 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1047 if (hostlist_delay.rel_value >
1048 GNUNET_TIME_UNIT_HOURS.rel_value * (1 + stat_connection_count))
1050 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1051 (1 + stat_connection_count));
1052 GNUNET_STATISTICS_set (stats,
1054 ("# milliseconds between hostlist downloads"),
1055 hostlist_delay.rel_value, GNUNET_YES);
1058 delay = GNUNET_TIME_UNIT_ZERO;
1061 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1063 ("Have %u/%u connections. Will consider downloading hostlist in %llums\n"),
1064 stat_connection_count, MIN_CONNECTIONS,
1065 (unsigned long long) delay.rel_value);
1066 ti_check_download = GNUNET_SCHEDULER_add_delayed (delay, &task_check, NULL);
1071 * This tasks sets hostlist testing to allowed after intervall between to testings is reached
1073 * @param cls closure
1074 * @param tc TaskContext
1077 task_testing_intervall_reset (void *cls,
1078 const struct GNUNET_SCHEDULER_TaskContext *tc)
1080 ti_testing_intervall_task = GNUNET_SCHEDULER_NO_TASK;
1081 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1083 stat_testing_allowed = GNUNET_OK;
1084 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1085 "Testing new hostlist advertisements is allowed again\n");
1090 * Task that writes hostlist entries to a file on a regular base
1092 * @param cls closure
1093 * @param tc TaskContext
1096 task_hostlist_saving (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1098 ti_saving_task = GNUNET_SCHEDULER_NO_TASK;
1099 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1101 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Scheduled saving of hostlists\n"));
1102 save_hostlist_file (GNUNET_NO);
1104 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1105 _("Hostlists will be saved to file again in %llums\n"),
1106 (unsigned long long) SAVING_INTERVALL.rel_value);
1107 ti_saving_task = GNUNET_SCHEDULER_add_delayed (SAVING_INTERVALL,
1108 &task_hostlist_saving, NULL);
1113 * Method called whenever a given peer connects.
1115 * @param cls closure
1116 * @param peer peer identity this notification is about
1117 * @param atsi performance data
1120 handler_connect (void *cls,
1122 GNUNET_PeerIdentity *peer,
1123 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1125 GNUNET_assert (stat_connection_count < UINT_MAX);
1126 stat_connection_count++;
1127 GNUNET_STATISTICS_update (stats,
1128 gettext_noop ("# active connections"),
1134 * Method called whenever a given peer disconnects.
1136 * @param cls closure
1137 * @param peer peer identity this notification is about
1140 handler_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
1142 GNUNET_assert (stat_connection_count > 0);
1143 stat_connection_count--;
1144 GNUNET_STATISTICS_update (stats,
1145 gettext_noop ("# active connections"),
1151 * Method called whenever an advertisement message arrives.
1153 * @param cls closure (always NULL)
1154 * @param peer the peer sending the message
1155 * @param message the actual message
1156 * @param atsi performance data
1157 * @return GNUNET_OK to keep the connection open,
1158 * GNUNET_SYSERR to close it (signal serious error)
1161 handler_advertisement (void *cls,
1162 const struct GNUNET_PeerIdentity *peer,
1163 const struct GNUNET_MessageHeader *message,
1164 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
1168 const struct GNUNET_MessageHeader *incoming;
1170 struct Hostlist *hostlist;
1172 GNUNET_assert (ntohs (message->type) ==
1173 GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1174 size = ntohs (message->size);
1175 if (size <= sizeof (struct GNUNET_MessageHeader))
1177 GNUNET_break_op (0);
1178 return GNUNET_SYSERR;
1180 incoming = (const struct GNUNET_MessageHeader *) message;
1181 uri = (const char *) &incoming[1];
1182 uri_size = size - sizeof (struct GNUNET_MessageHeader);
1183 if (uri[uri_size - 1] != '\0')
1185 GNUNET_break_op (0);
1186 return GNUNET_SYSERR;
1188 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1189 "Hostlist client recieved advertisement from `%s' containing URI `%s'\n",
1190 GNUNET_i2s (peer), uri);
1191 if (GNUNET_NO != linked_list_contains (uri))
1193 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "URI `%s' is already known\n", uri);
1197 if (GNUNET_NO == stat_testing_allowed)
1199 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1200 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1201 return GNUNET_SYSERR;
1203 if (GNUNET_YES == stat_testing_hostlist)
1205 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1206 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1207 return GNUNET_SYSERR;
1210 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1211 hostlist->hostlist_uri = (const char *) &hostlist[1];
1212 memcpy (&hostlist[1], uri, uri_size);
1213 hostlist->time_creation = GNUNET_TIME_absolute_get ();
1214 hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero ();
1215 hostlist->quality = HOSTLIST_INITIAL;
1216 hostlist_to_test = hostlist;
1218 stat_testing_hostlist = GNUNET_YES;
1219 stat_testing_allowed = GNUNET_NO;
1220 ti_testing_intervall_task = GNUNET_SCHEDULER_add_delayed (TESTING_INTERVAL,
1221 &task_testing_intervall_reset,
1224 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1225 "Testing new hostlist advertisements is locked for the next %u ms\n",
1226 TESTING_INTERVAL.rel_value);
1228 ti_download_dispatcher_task =
1229 GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1237 * Continuation called by the statistics code once
1238 * we go the stat. Initiates hostlist download scheduling.
1240 * @param cls closure
1241 * @param success GNUNET_OK if statistics were
1242 * successfully obtained, GNUNET_SYSERR if not.
1245 primary_task (void *cls, int success)
1248 return; /* in shutdown */
1249 #if DEBUG_HOSTLIST_CLIENT
1250 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1251 "Statistics request done, scheduling hostlist download\n");
1253 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1258 process_stat (void *cls,
1259 const char *subsystem,
1260 const char *name, uint64_t value, int is_persistent)
1262 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1263 _("Initial time between hostlist downloads is %llums\n"),
1264 (unsigned long long) value);
1265 hostlist_delay.rel_value = value;
1270 * Method to load persistent hostlist file during hostlist client startup
1273 load_hostlist_file ()
1278 struct Hostlist *hostlist;
1281 uint32_t times_used;
1282 uint32_t hellos_returned;
1289 GNUNET_CONFIGURATION_get_value_filename (cfg,
1291 "HOSTLISTFILE", &filename))
1293 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1295 ("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
1296 "HOSTLISTFILE", "HOSTLIST");
1300 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1301 _("Loading saved hostlist entries from file `%s' \n"), filename);
1302 if (GNUNET_NO == GNUNET_DISK_file_test (filename))
1304 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1305 _("Hostlist file `%s' is not existing\n"), filename);
1306 GNUNET_free (filename);
1310 struct GNUNET_BIO_ReadHandle *rh = GNUNET_BIO_read_open (filename);
1314 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1316 ("Could not open file `%s' for reading to load hostlists: %s\n"),
1317 filename, STRERROR (errno));
1318 GNUNET_free (filename);
1323 while ((GNUNET_OK == GNUNET_BIO_read_string (rh, "url", &uri, MAX_URL_LEN)) &&
1325 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, ×_used)) &&
1326 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1327 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1328 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1329 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)))
1331 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1332 hostlist->hello_count = hellos_returned;
1333 hostlist->hostlist_uri = (const char *) &hostlist[1];
1334 memcpy (&hostlist[1], uri, strlen (uri) + 1);
1335 hostlist->quality = quality;
1336 hostlist->time_creation.abs_value = created;
1337 hostlist->time_last_usage.abs_value = last_used;
1338 GNUNET_CONTAINER_DLL_insert (linked_list_head, linked_list_tail, hostlist);
1340 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1341 "Added hostlist entry eith URI `%s' \n",
1342 hostlist->hostlist_uri);
1346 if (counter >= MAX_NUMBER_HOSTLISTS)
1350 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1351 _("%u hostlist URIs loaded from file\n"), counter);
1352 GNUNET_STATISTICS_set (stats,
1353 gettext_noop ("# hostlist URIs read from file"),
1354 counter, GNUNET_YES);
1355 GNUNET_STATISTICS_set (stats,
1356 gettext_noop ("# advertised hostlist URIs"),
1357 linked_list_size, GNUNET_NO);
1359 GNUNET_free_non_null (uri);
1361 GNUNET_BIO_read_close (rh, &emsg);
1364 GNUNET_free (filename);
1369 * Method to save persistent hostlist file during hostlist client shutdown
1370 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1373 save_hostlist_file (int shutdown)
1376 struct Hostlist *pos;
1377 struct GNUNET_BIO_WriteHandle *wh;
1382 GNUNET_CONFIGURATION_get_value_filename (cfg,
1384 "HOSTLISTFILE", &filename))
1386 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1388 ("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
1389 "HOSTLISTFILE", "HOSTLIST");
1392 if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
1394 GNUNET_free (filename);
1397 wh = GNUNET_BIO_write_open (filename);
1400 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1402 ("Could not open file `%s' for writing to save hostlists: %s\n"),
1403 filename, STRERROR (errno));
1404 GNUNET_free (filename);
1407 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1408 _("Writing %u hostlist URIs to `%s'\n"),
1409 linked_list_size, filename);
1410 /* add code to write hostlists to file using bio */
1413 while (NULL != (pos = linked_list_head))
1415 if (GNUNET_YES == shutdown)
1417 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1420 if (GNUNET_YES == ok)
1423 GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1425 GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1427 GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1429 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.abs_value)) ||
1431 GNUNET_BIO_write_int64 (wh, pos->time_creation.abs_value)) ||
1432 (GNUNET_OK != GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1434 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1435 _("Error writing hostlist URIs to file `%s'\n"), filename);
1440 if (GNUNET_YES == shutdown)
1443 if (counter >= MAX_NUMBER_HOSTLISTS)
1446 GNUNET_STATISTICS_set (stats,
1447 gettext_noop ("# hostlist URIs written to file"),
1448 counter, GNUNET_YES);
1450 if (GNUNET_OK != GNUNET_BIO_write_close (wh))
1451 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1452 _("Error writing hostlist URIs to file `%s'\n"), filename);
1453 GNUNET_free (filename);
1457 * Start downloading hostlists from hostlist servers as necessary.
1460 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1461 struct GNUNET_STATISTICS_Handle *st,
1462 GNUNET_CORE_ConnectEventHandler *ch,
1463 GNUNET_CORE_DisconnectEventHandler *dh,
1464 GNUNET_CORE_MessageCallback *msgh, int learn)
1469 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1472 return GNUNET_SYSERR;
1474 transport = GNUNET_TRANSPORT_connect (c, NULL, NULL, NULL, NULL, NULL);
1475 if (NULL == transport)
1477 curl_global_cleanup ();
1478 return GNUNET_SYSERR;
1483 GNUNET_CONFIGURATION_get_value_string (cfg,
1484 "HOSTLIST", "HTTP-PROXY", &proxy))
1486 stat_learning = learn;
1487 *ch = &handler_connect;
1488 *dh = &handler_disconnect;
1489 linked_list_head = NULL;
1490 linked_list_tail = NULL;
1491 stat_use_bootstrap = GNUNET_YES;
1492 stat_testing_hostlist = GNUNET_NO;
1493 stat_testing_allowed = GNUNET_YES;
1495 if (GNUNET_YES == stat_learning)
1497 *msgh = &handler_advertisement;
1498 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1499 _("Learning is enabled on this peer\n"));
1500 load_hostlist_file ();
1501 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1502 _("Hostlists will be saved to file again in %llums\n"),
1503 (unsigned long long) SAVING_INTERVALL.rel_value);
1504 ti_saving_task = GNUNET_SCHEDULER_add_delayed (SAVING_INTERVALL,
1505 &task_hostlist_saving, NULL);
1509 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1510 _("Learning is not enabled on this peer\n"));
1512 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg,
1517 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
1519 result = remove (filename);
1521 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1523 ("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),
1526 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1527 _("Hostlist file `%s' could not be removed\n"), filename);
1530 GNUNET_free (filename);
1532 GNUNET_STATISTICS_get (stats,
1535 ("# milliseconds between hostlist downloads"),
1536 GNUNET_TIME_UNIT_MINUTES, &primary_task, &process_stat,
1543 * Stop downloading hostlists from hostlist servers as necessary.
1546 GNUNET_HOSTLIST_client_stop ()
1548 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostlist client shutdown\n");
1549 #if DEBUG_HOSTLIST_CLIENT
1550 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostlist client shutdown\n");
1552 if (GNUNET_YES == stat_learning)
1553 save_hostlist_file (GNUNET_YES);
1555 if (ti_saving_task != GNUNET_SCHEDULER_NO_TASK)
1557 GNUNET_SCHEDULER_cancel (ti_saving_task);
1560 if (ti_download_dispatcher_task != GNUNET_SCHEDULER_NO_TASK)
1562 GNUNET_SCHEDULER_cancel (ti_download_dispatcher_task);
1564 if (ti_testing_intervall_task != GNUNET_SCHEDULER_NO_TASK)
1566 GNUNET_SCHEDULER_cancel (ti_testing_intervall_task);
1568 if (ti_download != GNUNET_SCHEDULER_NO_TASK)
1570 GNUNET_SCHEDULER_cancel (ti_download);
1572 if (ti_check_download != GNUNET_SCHEDULER_NO_TASK)
1574 GNUNET_SCHEDULER_cancel (ti_check_download);
1575 curl_global_cleanup ();
1577 if (transport != NULL)
1579 GNUNET_TRANSPORT_disconnect (transport);
1582 GNUNET_assert (NULL == transport);
1583 GNUNET_free_non_null (proxy);
1588 /* end of hostlist-client.c */