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 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
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_SCHEDULER_Handle *sched;
119 struct GNUNET_STATISTICS_Handle *stats;
124 struct GNUNET_TRANSPORT_Handle *transport;
127 * Proxy that we are using (can be NULL).
132 * Buffer for data downloaded via HTTP.
134 static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
137 * Number of bytes valid in 'download_buffer'.
139 static size_t download_pos;
142 * Current URL that we are using.
144 static char *current_url;
147 * Current CURL handle.
152 * Current multi-CURL handle.
159 static uint32_t stat_bytes_downloaded;
161 * Amount of time we wait between hostlist downloads.
163 static struct GNUNET_TIME_Relative hostlist_delay;
166 * ID of the task, checking if hostlist download should take plate
168 static GNUNET_SCHEDULER_TaskIdentifier ti_check_download;
171 * ID of the task downloading the hostlist
173 static GNUNET_SCHEDULER_TaskIdentifier ti_download;
176 * ID of the task saving the hostlsit in a regular intervall
178 static GNUNET_SCHEDULER_TaskIdentifier ti_saving_task;
181 * ID of the task called to initiate a download
183 static GNUNET_SCHEDULER_TaskIdentifier ti_download_dispatcher_task;
186 * ID of the task controlling the locking between two hostlist tests
188 static GNUNET_SCHEDULER_TaskIdentifier ti_testing_intervall_task;
191 * At what time MUST the current hostlist request be done?
193 static struct GNUNET_TIME_Absolute end_time;
196 * Head of the linked list used to store hostlists
198 static struct Hostlist * linked_list_head;
201 * Tail of the linked list used to store hostlists
203 static struct Hostlist * linked_list_tail;
206 * Current hostlist used for downloading
208 static struct Hostlist * current_hostlist;
211 * Size of the linke list used to store hostlists
213 static unsigned int linked_list_size;
216 * Head of the linked list used to store hostlists
218 static struct Hostlist * hostlist_to_test;
221 * Set to GNUNET_YES if the current URL had some problems.
223 static int stat_bogus_url;
226 * Value controlling if a hostlist is tested at the moment
228 static int stat_testing_hostlist;
231 * Value controlling if a hostlist testing is allowed at the moment
233 static int stat_testing_allowed;
236 * Value controlling if a hostlist download is running at the moment
238 static int stat_download_in_progress;
241 * Value saying if a preconfigured bootstrap server is used
243 static unsigned int stat_use_bootstrap;
245 * Set if we are allowed to learn new hostlists and use them
247 static int stat_learning;
250 * Value saying if hostlist download was successful
252 static unsigned int stat_download_successful;
255 * Value saying how many valid HELLO messages were obtained during download
257 static unsigned int stat_hellos_obtained;
260 * Number of active connections (according to core service).
262 static unsigned int stat_connection_count;
266 * Process downloaded bits by calling callback on each HELLO.
268 * @param ptr buffer with downloaded data
269 * @param size size of a record
270 * @param nmemb number of records downloaded
272 * @return number of bytes that were processed (always size*nmemb)
275 callback_download (void *ptr,
280 const char * cbuf = ptr;
281 const struct GNUNET_MessageHeader *msg;
287 total = size * nmemb;
288 stat_bytes_downloaded += total;
289 if ( (total == 0) || (stat_bogus_url) )
291 return total; /* ok, no data or bogus data */
294 GNUNET_STATISTICS_update (stats,
295 gettext_noop ("# bytes downloaded from hostlist servers"),
299 while ( (left > 0) ||
302 cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - download_pos);
303 memcpy (&download_buffer[download_pos],
309 if (download_pos < sizeof(struct GNUNET_MessageHeader))
311 GNUNET_assert (left == 0);
314 msg = (const struct GNUNET_MessageHeader *) download_buffer;
315 msize = ntohs(msg->size);
316 if (msize < sizeof(struct GNUNET_MessageHeader))
318 GNUNET_STATISTICS_update (stats,
319 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
322 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
323 _("Invalid `%s' message received from hostlist at `%s'\n"),
326 stat_hellos_obtained++;
330 if (download_pos < msize)
332 GNUNET_assert (left == 0);
335 if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
337 #if DEBUG_HOSTLIST_CLIENT
338 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
339 "Received valid `%s' message from hostlist server.\n",
342 GNUNET_STATISTICS_update (stats,
343 gettext_noop ("# valid HELLOs downloaded from hostlist servers"),
346 stat_hellos_obtained++;
347 GNUNET_TRANSPORT_offer_hello (transport, msg);
351 GNUNET_STATISTICS_update (stats,
352 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
355 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
356 _("Invalid `%s' message received from hostlist at `%s'\n"),
359 stat_bogus_url = GNUNET_YES;
360 stat_hellos_obtained++;
363 memmove (download_buffer,
364 &download_buffer[msize],
365 download_pos - msize);
366 download_pos -= msize;
373 * Obtain a hostlist URL that we should use.
375 * @return NULL if there is no URL available
378 get_bootstrap_server ()
386 GNUNET_CONFIGURATION_get_value_string (cfg,
391 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
392 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
393 "SERVERS", "HOSTLIST");
398 if (strlen (servers) > 0)
401 pos = strlen (servers) - 1;
404 if (servers[pos] == ' ')
411 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
412 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
413 "SERVERS", "HOSTLIST");
414 GNUNET_free (servers);
418 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
419 pos = strlen (servers) - 1;
422 if (servers[pos] == ' ')
434 ret = GNUNET_strdup (&servers[pos]);
435 GNUNET_free (servers);
440 * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
441 * @return uri to use, NULL if there is no URL available
447 unsigned int counter;
448 struct Hostlist * pos;
450 if ( GNUNET_NO == stat_learning)
452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453 "Using preconfigured bootstrap server\n");
454 current_hostlist = NULL;
455 return get_bootstrap_server();
458 if ( ( GNUNET_YES == stat_testing_hostlist) && (NULL != hostlist_to_test) )
460 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
461 "Testing new advertised hostlist if it is obtainable\n");
462 current_hostlist = hostlist_to_test;
463 return strdup(hostlist_to_test->hostlist_uri);
466 if ( (GNUNET_YES == stat_use_bootstrap) ||
467 (linked_list_size == 0) )
469 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
470 "Using preconfigured bootstrap server\n");
471 current_hostlist = NULL;
472 return get_bootstrap_server();
474 index = GNUNET_CRYPTO_random_u32 ( GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
476 pos = linked_list_head;
477 while ( counter < index )
482 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
483 "Using learned hostlist `%s'\n", pos->hostlist_uri);
484 current_hostlist = pos;
485 return strdup(pos->hostlist_uri);
489 #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)
492 * Method to save hostlist to a file during hostlist client shutdown
493 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
495 static void save_hostlist_file ( int shutdown );
498 * add val2 to val1 with overflow check
499 * @param val1 value 1
500 * @param val2 value 2
503 static uint64_t checked_add (uint64_t val1, uint64_t val2)
505 static uint64_t temp;
506 static uint64_t maxv;
519 * Subtract val2 from val1 with underflow check
520 * @param val1 value 1
521 * @param val2 value 2
524 static uint64_t checked_sub (uint64_t val1, uint64_t val2)
533 * Method to check if a URI is in hostlist linked list
534 * @param uri uri to check
535 * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
538 linked_list_contains (const char * uri)
540 struct Hostlist * pos;
542 pos = linked_list_head;
545 if (0 == strcmp(pos->hostlist_uri, uri) )
554 * Method returning the hostlist element with the lowest quality in the datastore
555 * @return hostlist with lowest quality
557 static struct Hostlist *
558 linked_list_get_lowest_quality ( )
560 struct Hostlist * pos;
561 struct Hostlist * lowest;
563 if (linked_list_size == 0)
565 lowest = linked_list_head;
566 pos = linked_list_head->next;
569 if (pos->quality < lowest->quality)
578 * Method to insert a hostlist into the datastore. If datastore contains maximum number of elements, the elements with lowest quality is dismissed
581 insert_hostlist ( void )
583 GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist_to_test);
586 GNUNET_STATISTICS_set (stats,
587 gettext_noop("# advertised hostlist URIs"),
591 if (MAX_NUMBER_HOSTLISTS >= linked_list_size)
594 /* No free entries available, replace existing entry */
595 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
596 "Removing lowest quality entry\n" );
597 struct Hostlist * lowest_quality = linked_list_get_lowest_quality();
598 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
599 "Hostlist with URI `%s' has the worst quality of all with value %llu\n",
600 lowest_quality->hostlist_uri,
601 (unsigned long long) lowest_quality->quality);
602 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, lowest_quality);
605 GNUNET_STATISTICS_set (stats,
606 gettext_noop("# advertised hostlist URIs"),
610 GNUNET_free (lowest_quality);
612 stat_testing_hostlist = GNUNET_NO;
618 * Method updating hostlist statistics
620 static void update_hostlist ( )
623 if ( ((stat_use_bootstrap == GNUNET_NO) && ( NULL != current_hostlist )) ||
624 ((stat_testing_hostlist == GNUNET_YES) && ( NULL != current_hostlist )) )
626 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
627 "Updating hostlist statics for URI `%s'\n",current_hostlist->hostlist_uri );
628 current_hostlist->hello_count = stat_hellos_obtained;
629 current_hostlist->time_last_usage = GNUNET_TIME_absolute_get();
630 current_hostlist->quality = checked_add ( current_hostlist->quality, (stat_hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
631 if ( GNUNET_YES == stat_download_successful )
633 current_hostlist->times_used++;
634 current_hostlist->quality = checked_add ( current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
635 GNUNET_asprintf (&stat,
636 gettext_noop("# advertised URI `%s' downloaded"),
637 current_hostlist->hostlist_uri);
639 GNUNET_STATISTICS_update ( stats,
646 current_hostlist->quality = checked_sub ( current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD );
648 current_hostlist = NULL;
649 /* Alternating the usage of preconfigured and learned hostlists */
651 if (stat_testing_hostlist == GNUNET_YES)
654 if ( GNUNET_YES == stat_learning)
656 if (stat_use_bootstrap == GNUNET_YES)
657 stat_use_bootstrap = GNUNET_NO;
659 stat_use_bootstrap = GNUNET_YES;
662 stat_use_bootstrap = GNUNET_YES;
666 * Clean up the state from the task that downloaded the
667 * hostlist and schedule the next task.
674 if ( ( stat_testing_hostlist == GNUNET_YES ) && ( GNUNET_NO == stat_download_successful) && (NULL != hostlist_to_test))
676 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
677 _("Advertised hostlist with URI `%s' could not be downloaded. Advertised URI gets dismissed.\n"),hostlist_to_test->hostlist_uri);
680 if ( stat_testing_hostlist == GNUNET_YES )
682 stat_testing_hostlist = GNUNET_NO;
684 if ( NULL != hostlist_to_test)
686 GNUNET_free (hostlist_to_test);
687 hostlist_to_test = NULL;
692 mret = curl_multi_remove_handle (multi, curl);
693 if (mret != CURLM_OK)
695 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
696 _("%s failed at %s:%d: `%s'\n"),
697 "curl_multi_remove_handle", __FILE__, __LINE__,
698 curl_multi_strerror (mret));
700 mret = curl_multi_cleanup (multi);
701 if (mret != CURLM_OK)
702 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
703 _("%s failed at %s:%d: `%s'\n"),
704 "curl_multi_cleanup", __FILE__, __LINE__,
705 curl_multi_strerror (mret));
710 curl_easy_cleanup (curl);
713 GNUNET_free_non_null (current_url);
715 stat_bytes_downloaded = 0;
716 stat_download_in_progress = GNUNET_NO;
720 * Task that is run when we are ready to receive more data from the hostlist
723 * @param cls closure, unused
724 * @param tc task context, unused
727 task_download (void *cls,
728 const struct GNUNET_SCHEDULER_TaskContext *tc);
731 * Ask CURL for the select set and then schedule the
732 * receiving task with the scheduler.
742 struct GNUNET_NETWORK_FDSet *grs;
743 struct GNUNET_NETWORK_FDSet *gws;
745 struct GNUNET_TIME_Relative rtime;
751 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
752 if (mret != CURLM_OK)
754 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
755 _("%s failed at %s:%d: `%s'\n"),
756 "curl_multi_fdset", __FILE__, __LINE__,
757 curl_multi_strerror (mret));
761 mret = curl_multi_timeout (multi, &timeout);
762 if (mret != CURLM_OK)
764 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
765 _("%s failed at %s:%d: `%s'\n"),
766 "curl_multi_timeout", __FILE__, __LINE__,
767 curl_multi_strerror (mret));
771 rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
772 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
774 grs = GNUNET_NETWORK_fdset_create ();
775 gws = GNUNET_NETWORK_fdset_create ();
776 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
777 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
778 #if DEBUG_HOSTLIST_CLIENT
779 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
780 "Scheduling task for hostlist download using cURL\n");
783 = GNUNET_SCHEDULER_add_select (sched,
784 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
785 GNUNET_SCHEDULER_NO_TASK,
791 GNUNET_NETWORK_fdset_destroy (gws);
792 GNUNET_NETWORK_fdset_destroy (grs);
797 * Task that is run when we are ready to receive more data from the hostlist
800 * @param cls closure, unused
801 * @param tc task context, unused
804 task_download (void *cls,
805 const struct GNUNET_SCHEDULER_TaskContext *tc)
812 ti_download = GNUNET_SCHEDULER_NO_TASK;
813 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
815 #if DEBUG_HOSTLIST_CLIENT
816 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
817 "Shutdown requested while trying to download hostlist from `%s'\n",
824 if (GNUNET_TIME_absolute_get_remaining (end_time).value == 0)
826 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
827 _("Timeout trying to download hostlist from `%s'\n"),
833 #if DEBUG_HOSTLIST_CLIENT
834 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
835 "Ready for processing hostlist client request\n");
841 if (stat_bytes_downloaded > MAX_BYTES_PER_HOSTLISTS)
843 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
844 _("Download limit of %u bytes exceeded, stopping download\n"),MAX_BYTES_PER_HOSTLISTS);
848 mret = curl_multi_perform (multi, &running);
855 msg = curl_multi_info_read (multi, &running);
856 GNUNET_break (msg != NULL);
862 if ( (msg->data.result != CURLE_OK) &&
863 (msg->data.result != CURLE_GOT_NOTHING) )
864 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
865 _("%s failed for `%s' at %s:%d: `%s'\n"),
866 "curl_multi_perform",
870 curl_easy_strerror (msg->data.result));
873 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
874 _("Download of hostlist `%s' completed.\n"),
876 stat_download_successful = GNUNET_YES;
878 if (GNUNET_YES == stat_testing_hostlist)
880 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
881 _("Adding successfully tested hostlist `%s' datastore.\n"),current_url);
883 hostlist_to_test = NULL;
884 stat_testing_hostlist = GNUNET_NO;
894 while ( (running > 0) );
897 while (mret == CURLM_CALL_MULTI_PERFORM);
899 if (mret != CURLM_OK)
901 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
902 _("%s failed at %s:%d: `%s'\n"),
903 "curl_multi_perform", __FILE__, __LINE__,
904 curl_multi_strerror (mret));
912 * Main function that will download a hostlist and process its
922 current_url = download_get_url ();
923 if (current_url == NULL)
925 curl = curl_easy_init ();
933 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
934 _("Bootstrapping using hostlist at `%s'.\n"),
937 stat_download_in_progress = GNUNET_YES;
938 stat_download_successful = GNUNET_NO;
939 stat_hellos_obtained = 0;
940 stat_bytes_downloaded = 0;
942 GNUNET_STATISTICS_update (stats,
943 gettext_noop ("# hostlist downloads initiated"),
947 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
950 CURL_EASY_SETOPT (curl,
951 CURLOPT_WRITEFUNCTION,
958 CURL_EASY_SETOPT (curl,
966 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
967 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
968 /* no need to abort if the above failed */
969 CURL_EASY_SETOPT (curl,
977 CURL_EASY_SETOPT (curl,
981 CURL_EASY_SETOPT (curl,
985 CURL_EASY_SETOPT (curl,
987 GNUNET_SERVER_MAX_MESSAGE_SIZE);
988 if (0 == strncmp (current_url, "http", 4))
989 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
990 CURL_EASY_SETOPT (curl,
991 CURLOPT_CONNECTTIMEOUT,
993 CURL_EASY_SETOPT (curl,
997 /* this should no longer be needed; we're now single-threaded! */
998 CURL_EASY_SETOPT (curl,
1002 multi = curl_multi_init ();
1009 mret = curl_multi_add_handle (multi, curl);
1010 if (mret != CURLM_OK)
1012 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1013 _("%s failed at %s:%d: `%s'\n"),
1014 "curl_multi_add_handle", __FILE__, __LINE__,
1015 curl_multi_strerror (mret));
1016 mret = curl_multi_cleanup (multi);
1017 if (mret != CURLM_OK)
1018 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1019 _("%s failed at %s:%d: `%s'\n"),
1020 "curl_multi_cleanup", __FILE__, __LINE__,
1021 curl_multi_strerror (mret));
1026 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
1027 download_prepare ();
1032 task_download_dispatcher (void *cls,
1033 const struct GNUNET_SCHEDULER_TaskContext *tc)
1035 ti_download_dispatcher_task = GNUNET_SCHEDULER_NO_TASK;
1036 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1038 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1039 "Download is initiated...\n");
1040 if ( GNUNET_NO == stat_download_in_progress )
1042 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1043 "Download can start immediately...\n");
1044 download_hostlist();
1048 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1049 "Download in progess, have to wait...\n");
1050 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_delayed (sched,
1052 &task_download_dispatcher,
1058 * Task that checks if we should try to download a hostlist.
1059 * If so, we initiate the download, otherwise we schedule
1060 * this task again for a later time.
1063 task_check (void *cls,
1064 const struct GNUNET_SCHEDULER_TaskContext *tc)
1067 struct GNUNET_TIME_Relative delay;
1069 ti_check_download = GNUNET_SCHEDULER_NO_TASK;
1070 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1073 if (stat_connection_count < MIN_CONNECTIONS)
1075 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_now ( sched,
1076 &task_download_dispatcher,
1082 curl_global_cleanup ();
1083 return; /* in shutdown */
1085 delay = hostlist_delay;
1086 if (hostlist_delay.value == 0)
1087 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1089 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1090 if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * (1 + stat_connection_count))
1091 hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1092 (1 + stat_connection_count));
1093 GNUNET_STATISTICS_set (stats,
1094 gettext_noop("# seconds between hostlist downloads"),
1095 hostlist_delay.value,
1099 delay = GNUNET_TIME_UNIT_ZERO;
1102 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1103 _("Have %u/%u connections. Will consider downloading hostlist in %llums\n"),
1104 stat_connection_count,
1106 (unsigned long long) delay.value);
1107 ti_check_download = GNUNET_SCHEDULER_add_delayed (sched,
1114 * This tasks sets hostlist testing to allowed after intervall between to testings is reached
1119 task_testing_intervall_reset (void *cls,
1120 const struct GNUNET_SCHEDULER_TaskContext *tc)
1122 ti_testing_intervall_task = GNUNET_SCHEDULER_NO_TASK;
1123 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1125 stat_testing_allowed = GNUNET_OK;
1126 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1127 "Testing new hostlist advertisements is allowed again\n");
1132 * Task that writes hostlist entries to a file on a regular base
1137 task_hostlist_saving (void *cls,
1138 const struct GNUNET_SCHEDULER_TaskContext *tc)
1140 ti_saving_task = GNUNET_SCHEDULER_NO_TASK;
1141 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1143 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1144 _("Scheduled saving of hostlists\n"));
1145 save_hostlist_file ( GNUNET_NO );
1147 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1148 _("Hostlists will be saved to file again in %llums\n"),
1149 (unsigned long long) SAVING_INTERVALL.value);
1150 ti_saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1152 &task_hostlist_saving,
1157 * Method called whenever a given peer connects.
1159 * @param cls closure
1160 * @param peer peer identity this notification is about
1161 * @param latency reported latency of the connection with 'other'
1162 * @param distance reported distance (DV) to 'other'
1165 handler_connect (void *cls,
1167 GNUNET_PeerIdentity * peer,
1168 struct GNUNET_TIME_Relative latency,
1171 unsigned int max = 0 ;
1174 if (stat_connection_count < max)
1176 stat_connection_count++;
1177 GNUNET_STATISTICS_update (stats,
1178 gettext_noop ("# active connections"),
1186 * Method called whenever a given peer disconnects.
1188 * @param cls closure
1189 * @param peer peer identity this notification is about
1192 handler_disconnect (void *cls,
1194 GNUNET_PeerIdentity * peer)
1196 GNUNET_assert (stat_connection_count > 0);
1197 stat_connection_count--;
1198 GNUNET_STATISTICS_update (stats,
1199 gettext_noop ("# active connections"),
1205 * Method called whenever an advertisement message arrives.
1207 * @param cls closure (always NULL)
1208 * @param peer the peer sending the message
1209 * @param message the actual message
1210 * @param latency latency
1211 * @param distance distance
1212 * @return GNUNET_OK to keep the connection open,
1213 * GNUNET_SYSERR to close it (signal serious error)
1216 handler_advertisement (void *cls,
1217 const struct GNUNET_PeerIdentity * peer,
1218 const struct GNUNET_MessageHeader * message,
1219 struct GNUNET_TIME_Relative latency,
1224 const struct GNUNET_MessageHeader * incoming;
1226 struct Hostlist * hostlist;
1228 GNUNET_assert (ntohs (message->type) == GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1229 size = ntohs (message->size);
1230 if (size <= sizeof(struct GNUNET_MessageHeader))
1232 GNUNET_break_op (0);
1233 return GNUNET_SYSERR;
1235 incoming = (const struct GNUNET_MessageHeader *) message;
1236 uri = (const char*) &incoming[1];
1237 uri_size = size - sizeof (struct GNUNET_MessageHeader);
1238 if (uri [uri_size - 1] != '\0')
1240 GNUNET_break_op (0);
1241 return GNUNET_SYSERR;
1243 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1244 "Hostlist client recieved advertisement from `%s' containing URI `%s'\n",
1247 if (GNUNET_NO != linked_list_contains (uri))
1249 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1250 "URI `%s' is already known\n",
1255 if ( GNUNET_NO == stat_testing_allowed )
1257 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1258 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1259 return GNUNET_SYSERR;
1261 if ( GNUNET_YES == stat_testing_hostlist )
1263 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1264 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1265 return GNUNET_SYSERR;
1268 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1269 hostlist->hostlist_uri = (const char*) &hostlist[1];
1270 memcpy (&hostlist[1], uri, uri_size);
1271 hostlist->time_creation = GNUNET_TIME_absolute_get();
1272 hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero();
1273 hostlist->quality = HOSTLIST_INITIAL;
1274 hostlist_to_test = hostlist;
1276 stat_testing_hostlist = GNUNET_YES;
1277 stat_testing_allowed = GNUNET_NO;
1278 ti_testing_intervall_task = GNUNET_SCHEDULER_add_delayed (sched,
1280 &task_testing_intervall_reset,
1283 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1284 "Testing new hostlist advertisements is locked for the next %u ms\n",
1285 TESTING_INTERVAL.value);
1287 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_now (sched,
1288 &task_download_dispatcher,
1297 * Continuation called by the statistics code once
1298 * we go the stat. Initiates hostlist download scheduling.
1300 * @param cls closure
1301 * @param success GNUNET_OK if statistics were
1302 * successfully obtained, GNUNET_SYSERR if not.
1305 primary_task (void *cls, int success)
1308 return; /* in shutdown */
1309 #if DEBUG_HOSTLIST_CLIENT
1310 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1311 "Statistics request done, scheduling hostlist download\n");
1313 ti_check_download = GNUNET_SCHEDULER_add_now (sched,
1320 process_stat (void *cls,
1321 const char *subsystem,
1326 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1327 _("Initial time between hostlist downloads is %llums\n"),
1328 (unsigned long long) value);
1329 hostlist_delay.value = value;
1334 * Method to load persistent hostlist file during hostlist client startup
1337 load_hostlist_file ()
1342 struct Hostlist * hostlist;
1344 uint32_t times_used;
1345 uint32_t hellos_returned;
1352 GNUNET_CONFIGURATION_get_value_string (cfg,
1357 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1358 _("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
1359 "HOSTLISTFILE", "HOSTLIST");
1363 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1364 _("Loading saved hostlist entries from file `%s' \n"), filename);
1365 if ( GNUNET_NO == GNUNET_DISK_file_test (filename) )
1367 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1368 _("Hostlist file `%s' is not existing\n"), filename);
1369 GNUNET_free ( filename );
1373 struct GNUNET_BIO_ReadHandle * rh = GNUNET_BIO_read_open (filename);
1376 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1377 _("Could not open file `%s' for reading to load hostlists: %s\n"),
1380 GNUNET_free (filename);
1385 while ( (GNUNET_OK == GNUNET_BIO_read_string (rh, "url" , &uri, MAX_URL_LEN)) &&
1386 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, ×_used)) &&
1387 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1388 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1389 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1390 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)) )
1392 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1393 hostlist->hello_count = hellos_returned;
1394 hostlist->hostlist_uri = (const char *) &hostlist[1];
1395 memcpy (&hostlist[1], uri, strlen(uri)+1);
1396 hostlist->quality = quality;
1397 hostlist->time_creation.value = created;
1398 hostlist->time_last_usage.value = last_used;
1399 GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist);
1401 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1402 "Added hostlist entry eith URI `%s' \n", hostlist->hostlist_uri);
1406 if ( counter >= MAX_NUMBER_HOSTLISTS ) break;
1409 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1410 _("%u hostlist URIs loaded from file\n"), counter);
1411 GNUNET_STATISTICS_set (stats,
1412 gettext_noop("# hostlist URIs read from file"),
1415 GNUNET_STATISTICS_set (stats,
1416 gettext_noop("# advertised hostlist URIs"),
1420 GNUNET_free_non_null (uri);
1422 GNUNET_BIO_read_close (rh, &emsg);
1425 GNUNET_free (filename);
1430 * Method to save persistent hostlist file during hostlist client shutdown
1431 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1433 static void save_hostlist_file ( int shutdown )
1436 struct Hostlist *pos;
1437 struct GNUNET_BIO_WriteHandle * wh;
1442 GNUNET_CONFIGURATION_get_value_string (cfg,
1447 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1448 _("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
1449 "HOSTLISTFILE", "HOSTLIST");
1452 wh = GNUNET_BIO_write_open (filename);
1455 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1456 _("Could not open file `%s' for writing to save hostlists: %s\n"),
1459 GNUNET_free (filename);
1462 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1463 _("Writing %u hostlist URIs to `%s'\n" ),
1464 linked_list_size, filename);
1466 /* add code to write hostlists to file using bio */
1469 while (NULL != (pos = linked_list_head))
1471 if ( GNUNET_YES == shutdown)
1473 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1476 if (GNUNET_YES == ok)
1479 GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1481 GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1483 GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1485 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.value)) ||
1487 GNUNET_BIO_write_int64 (wh, pos->time_creation.value)) ||
1489 GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1491 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1492 _("Error writing hostlist URIs to file `%s'\n"),
1498 if ( GNUNET_YES == shutdown)
1501 if ( counter >= MAX_NUMBER_HOSTLISTS) break;
1503 GNUNET_STATISTICS_set (stats,
1504 gettext_noop("# hostlist URIs written to file"),
1508 if ( GNUNET_OK != GNUNET_BIO_write_close ( wh ) )
1509 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1510 _("Error writing hostlist URIs to file `%s'\n"),
1512 GNUNET_free (filename);
1516 * Start downloading hostlists from hostlist servers as necessary.
1519 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1520 struct GNUNET_SCHEDULER_Handle *s,
1521 struct GNUNET_STATISTICS_Handle *st,
1522 GNUNET_CORE_ConnectEventHandler *ch,
1523 GNUNET_CORE_DisconnectEventHandler *dh,
1524 GNUNET_CORE_MessageCallback *msgh,
1530 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1533 return GNUNET_SYSERR;
1535 transport = GNUNET_TRANSPORT_connect (s, c, NULL, NULL, NULL, NULL);
1536 if (NULL == transport)
1538 curl_global_cleanup ();
1539 return GNUNET_SYSERR;
1545 GNUNET_CONFIGURATION_get_value_string (cfg,
1550 stat_learning = learn;
1551 *ch = &handler_connect;
1552 *dh = &handler_disconnect;
1553 linked_list_head = NULL;
1554 linked_list_tail = NULL;
1555 stat_use_bootstrap = GNUNET_YES;
1556 stat_testing_hostlist = GNUNET_NO;
1557 stat_testing_allowed = GNUNET_YES;
1559 if ( GNUNET_YES == stat_learning )
1561 *msgh = &handler_advertisement;
1562 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1563 _("Learning is enabled on this peer\n"));
1564 load_hostlist_file ();
1565 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1566 _("Hostlists will be saved to file again in %llums\n"),
1567 (unsigned long long) SAVING_INTERVALL.value);
1568 ti_saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1570 &task_hostlist_saving,
1575 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1576 _("Learning is not enabled on this peer\n"));
1578 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1583 if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
1585 result = remove (filename);
1587 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1588 _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),filename);
1590 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1591 _("Hostlist file `%s' could not be removed\n"),filename);
1594 GNUNET_free ( filename );
1596 GNUNET_STATISTICS_get (stats,
1598 gettext_noop("# seconds between hostlist downloads"),
1599 GNUNET_TIME_UNIT_MINUTES,
1608 * Stop downloading hostlists from hostlist servers as necessary.
1611 GNUNET_HOSTLIST_client_stop ()
1613 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1614 "Hostlist client shutdown\n");
1615 #if DEBUG_HOSTLIST_CLIENT
1616 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1617 "Hostlist client shutdown\n");
1619 if ( GNUNET_YES == stat_learning )
1620 save_hostlist_file ( GNUNET_YES );
1622 if (ti_saving_task != GNUNET_SCHEDULER_NO_TASK)
1624 GNUNET_SCHEDULER_cancel (sched,
1628 if (ti_download_dispatcher_task != GNUNET_SCHEDULER_NO_TASK)
1630 GNUNET_SCHEDULER_cancel (sched,
1631 ti_download_dispatcher_task);
1633 if (ti_testing_intervall_task != GNUNET_SCHEDULER_NO_TASK)
1635 GNUNET_SCHEDULER_cancel (sched,
1636 ti_testing_intervall_task);
1638 if (ti_download != GNUNET_SCHEDULER_NO_TASK)
1640 GNUNET_SCHEDULER_cancel (sched,
1643 if (ti_check_download != GNUNET_SCHEDULER_NO_TASK)
1645 GNUNET_SCHEDULER_cancel (sched,
1647 curl_global_cleanup ();
1649 if (transport != NULL)
1651 GNUNET_TRANSPORT_disconnect (transport);
1654 GNUNET_assert (NULL == transport);
1655 GNUNET_free_non_null (proxy);
1661 /* end of hostlist-client.c */