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_YES
43 * Number of connections that we must have to NOT download
46 #define MIN_CONNECTIONS 4
49 * A single hostlist obtained by hostlist advertisements
54 * previous entry, used to manage entries in a double linked list
56 struct Hostlist * prev;
59 * next entry, used to manage entries in a double linked list
61 struct Hostlist * next;
64 * URI where hostlist can be obtained
66 const char *hostlist_uri;
69 * Value describing the quality of the hostlist, the bigger the better but (should) never < 0
70 * used for deciding which hostlist is replaced if MAX_NUMBER_HOSTLISTS in data structure is reached
71 * intial value = HOSTLIST_INITIAL
72 * increased every successful download by HOSTLIST_SUCCESSFULL_DOWNLOAD
73 * increased every successful download by number of obtained HELLO messages
74 * decreased every failed download by HOSTLIST_SUCCESSFULL_DOWNLOAD
79 * Time the hostlist advertisement was recieved and the entry was created
81 struct GNUNET_TIME_Absolute time_creation;
84 * Last time the hostlist was obtained
86 struct GNUNET_TIME_Absolute time_last_usage;
89 * Number of HELLO messages obtained during last download
94 * Number of times the hostlist was successfully obtained
104 static const struct GNUNET_CONFIGURATION_Handle *cfg;
109 static struct GNUNET_SCHEDULER_Handle *sched;
114 struct GNUNET_STATISTICS_Handle *stats;
119 struct GNUNET_TRANSPORT_Handle *transport;
122 * Proxy that we are using (can be NULL).
127 * Buffer for data downloaded via HTTP.
129 static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
132 * Number of bytes valid in 'download_buffer'.
134 static size_t download_pos;
137 * Current URL that we are using.
139 static char *current_url;
142 * Current CURL handle.
147 * Current multi-CURL handle.
152 * ID of the current task scheduled.
154 static GNUNET_SCHEDULER_TaskIdentifier ti_check_download;
157 * ID of the current task scheduled.
159 static GNUNET_SCHEDULER_TaskIdentifier ti_download;
162 * ID of the current hostlist saving task scheduled.
164 static GNUNET_SCHEDULER_TaskIdentifier ti_saving_task;
167 * ID of the current hostlist saving task scheduled.
169 static GNUNET_SCHEDULER_TaskIdentifier ti_download_dispatcher_task;
173 * ID of the task checking the intervall between to hostlist tests
175 static GNUNET_SCHEDULER_TaskIdentifier ti_testing_intervall_task;
178 * Amount of time we wait between hostlist downloads.
180 static struct GNUNET_TIME_Relative hostlist_delay;
183 * Set to GNUNET_YES if the current URL had some problems.
185 static int bogus_url;
188 * Number of active connections (according to core service).
190 static unsigned int connection_count;
193 * At what time MUST the current hostlist request be done?
195 static struct GNUNET_TIME_Absolute end_time;
198 * Head of the linked list used to store hostlists
200 static struct Hostlist * linked_list_head;
203 * Tail of the linked list used to store hostlists
205 static struct Hostlist * linked_list_tail;
208 * Current hostlist used for downloading
210 static struct Hostlist * current_hostlist;
213 * Size of the linke list used to store hostlists
215 static unsigned int linked_list_size;
218 * Head of the linked list used to store hostlists
220 static struct Hostlist * hostlist_to_test;
222 static int testing_hostlist;
224 static int testing_allowed;
226 static int download_in_progress;
229 * Value saying if preconfigured is used
231 static unsigned int use_preconfigured_list;
234 * Set if we are allowed to learn new hostlists and use them
239 * Value saying how many valid HELLO messages were obtained during download
241 static unsigned int hellos_obtained;
244 * Value saying if hostlist download was successful
246 static unsigned int download_successful;
249 * Process downloaded bits by calling callback on each HELLO.
251 * @param ptr buffer with downloaded data
252 * @param size size of a record
253 * @param nmemb number of records downloaded
255 * @return number of bytes that were processed (always size*nmemb)
258 download_hostlist_processor (void *ptr,
263 const char * cbuf = ptr;
264 const struct GNUNET_MessageHeader *msg;
270 total = size * nmemb;
271 if ( (total == 0) || (bogus_url) )
273 return total; /* ok, no data or bogus data */
275 GNUNET_STATISTICS_update (stats,
276 gettext_noop ("# bytes downloaded from hostlist servers"),
280 while ( (left > 0) ||
283 cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - download_pos);
284 memcpy (&download_buffer[download_pos],
290 if (download_pos < sizeof(struct GNUNET_MessageHeader))
292 GNUNET_assert (left == 0);
295 msg = (const struct GNUNET_MessageHeader *) download_buffer;
296 msize = ntohs(msg->size);
297 if (msize < sizeof(struct GNUNET_MessageHeader))
299 GNUNET_STATISTICS_update (stats,
300 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
303 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
304 _("Invalid `%s' message received from hostlist at `%s'\n"),
310 if (download_pos < msize)
312 GNUNET_assert (left == 0);
315 if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
317 #if DEBUG_HOSTLIST_CLIENT
318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
319 "Received valid `%s' message from hostlist server.\n",
322 GNUNET_STATISTICS_update (stats,
323 gettext_noop ("# valid HELLOs downloaded from hostlist servers"),
327 GNUNET_TRANSPORT_offer_hello (transport, msg);
331 GNUNET_STATISTICS_update (stats,
332 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
335 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
336 _("Invalid `%s' message received from hostlist at `%s'\n"),
339 bogus_url = GNUNET_YES;
342 memmove (download_buffer,
343 &download_buffer[msize],
344 download_pos - msize);
345 download_pos -= msize;
352 * Obtain a hostlist URL that we should use.
354 * @return NULL if there is no URL available
365 GNUNET_CONFIGURATION_get_value_string (cfg,
370 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
371 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
372 "SERVERS", "HOSTLIST");
377 if (strlen (servers) > 0)
380 pos = strlen (servers) - 1;
383 if (servers[pos] == ' ')
390 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
391 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
392 "SERVERS", "HOSTLIST");
393 GNUNET_free (servers);
397 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
398 pos = strlen (servers) - 1;
401 if (servers[pos] == ' ')
413 ret = GNUNET_strdup (&servers[pos]);
414 GNUNET_free (servers);
419 * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
420 * @return uri to use, NULL if there is no URL available
426 unsigned int counter;
427 struct Hostlist * pos;
429 if ( GNUNET_NO == learning)
431 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
432 "Using preconfigured bootstrap server\n");
433 current_hostlist = NULL;
434 return get_bootstrap_url();
437 if ( ( GNUNET_YES == testing_hostlist) && (NULL != hostlist_to_test) )
439 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
440 "Testing new advertised hostlist if it is obtainable\n");
441 current_hostlist = hostlist_to_test;
442 return strdup(hostlist_to_test->hostlist_uri);
445 if ( (GNUNET_YES == use_preconfigured_list) ||
446 (linked_list_size == 0) )
448 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
449 "Using preconfigured bootstrap server\n");
450 current_hostlist = NULL;
451 return get_bootstrap_url();
453 index = GNUNET_CRYPTO_random_u32 ( GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
455 pos = linked_list_head;
456 while ( counter < index )
461 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
462 "Using learned hostlist `%s'\n", pos->hostlist_uri);
463 current_hostlist = pos;
464 return strdup(pos->hostlist_uri);
468 #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);
471 * Method to load persistent hostlist file during hostlist client shutdown
472 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
474 static void save_hostlist_file ( int shutdown );
477 * add val2 to val1 with overflow check
478 * @param val1 value 1
479 * @param val2 value 2
482 static uint64_t checked_add (uint64_t val1, uint64_t val2)
484 static uint64_t temp;
485 static uint64_t maxv;
498 * Subtract val2 from val1 with underflow check
499 * @param val1 value 1
500 * @param val2 value 2
503 static uint64_t checked_sub (uint64_t val1, uint64_t val2)
512 * Method to check if URI is in hostlist linked list
513 * @param uri uri to check
514 * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
517 linked_list_contains (const char * uri)
519 struct Hostlist * pos;
521 pos = linked_list_head;
524 if (0 == strcmp(pos->hostlist_uri, uri) )
533 * Method returning the uri with the lowest quality in the datastore
534 * @return hostlist with lowest quality
536 static struct Hostlist *
537 linked_list_get_lowest_quality ( )
539 struct Hostlist * pos;
540 struct Hostlist * lowest;
542 if (linked_list_size == 0)
544 lowest = linked_list_head;
545 pos = linked_list_head->next;
548 if (pos->quality < lowest->quality)
557 * Method to insert a hostlist into the datastore. If datastore contains maximum number of elements, the elements with lowest quality is dismissed
560 insert_hostlist ( void )
562 GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist_to_test);
565 GNUNET_STATISTICS_set (stats,
566 gettext_noop("# advertised hostlist URIs"),
570 if (MAX_NUMBER_HOSTLISTS >= linked_list_size)
573 /* No free entries available, replace existing entry */
574 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
575 "Removing lowest quality entry\n" );
576 struct Hostlist * lowest_quality = linked_list_get_lowest_quality();
577 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
578 "Hostlist with URI `%s' has the worst quality of all with value %llu\n",
579 lowest_quality->hostlist_uri,
580 (unsigned long long) lowest_quality->quality);
581 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, lowest_quality);
584 GNUNET_STATISTICS_set (stats,
585 gettext_noop("# advertised hostlist URIs"),
589 GNUNET_free (lowest_quality);
591 testing_hostlist = GNUNET_NO;
597 * Method updating hostlist statistics
599 static void update_hostlist ( )
602 if ( ((use_preconfigured_list == GNUNET_NO) && ( NULL != current_hostlist )) ||
603 ((testing_hostlist == GNUNET_YES) && ( NULL != current_hostlist )) )
605 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
606 "Updating hostlist statics for URI `%s'\n",current_hostlist->hostlist_uri );
607 current_hostlist->hello_count = hellos_obtained;
608 current_hostlist->time_last_usage = GNUNET_TIME_absolute_get();
609 current_hostlist->quality = checked_add ( current_hostlist->quality, (hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
610 if ( GNUNET_YES == download_successful )
612 current_hostlist->times_used++;
613 current_hostlist->quality = checked_add ( current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
614 GNUNET_asprintf (&stat,
615 gettext_noop("# advertised URI `%s' downloaded"),
616 current_hostlist->hostlist_uri);
618 GNUNET_STATISTICS_update ( stats,
625 current_hostlist->quality = checked_sub ( current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD );
627 current_hostlist = NULL;
628 /* Alternating the usage of preconfigured and learned hostlists */
630 if (testing_hostlist == GNUNET_YES)
633 if ( GNUNET_YES == learning)
635 if (use_preconfigured_list == GNUNET_YES)
636 use_preconfigured_list = GNUNET_NO;
638 use_preconfigured_list = GNUNET_YES;
641 use_preconfigured_list = GNUNET_YES;
645 * Clean up the state from the task that downloaded the
646 * hostlist and schedule the next task.
653 if ( ( testing_hostlist == GNUNET_YES ) && ( GNUNET_NO == download_successful) && (NULL != hostlist_to_test))
655 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
656 _("Advertised hostlist with URI `%s' could not be downloaded. Advertised URI gets dismissed.\n"),hostlist_to_test->hostlist_uri);
659 if ( testing_hostlist == GNUNET_YES )
661 testing_hostlist = GNUNET_NO;
663 if ( NULL != hostlist_to_test)
665 GNUNET_free (hostlist_to_test);
666 hostlist_to_test = NULL;
672 mret = curl_multi_remove_handle (multi, curl);
673 if (mret != CURLM_OK)
675 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
676 _("%s failed at %s:%d: `%s'\n"),
677 "curl_multi_remove_handle", __FILE__, __LINE__,
678 curl_multi_strerror (mret));
680 mret = curl_multi_cleanup (multi);
681 if (mret != CURLM_OK)
682 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
683 _("%s failed at %s:%d: `%s'\n"),
684 "curl_multi_cleanup", __FILE__, __LINE__,
685 curl_multi_strerror (mret));
690 curl_easy_cleanup (curl);
693 GNUNET_free_non_null (current_url);
696 download_in_progress = GNUNET_NO;
700 * Task that is run when we are ready to receive more data from the hostlist
703 * @param cls closure, unused
704 * @param tc task context, unused
707 multi_ready (void *cls,
708 const struct GNUNET_SCHEDULER_TaskContext *tc);
711 * Ask CURL for the select set and then schedule the
712 * receiving task with the scheduler.
722 struct GNUNET_NETWORK_FDSet *grs;
723 struct GNUNET_NETWORK_FDSet *gws;
725 struct GNUNET_TIME_Relative rtime;
731 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
732 if (mret != CURLM_OK)
734 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
735 _("%s failed at %s:%d: `%s'\n"),
736 "curl_multi_fdset", __FILE__, __LINE__,
737 curl_multi_strerror (mret));
741 mret = curl_multi_timeout (multi, &timeout);
742 if (mret != CURLM_OK)
744 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
745 _("%s failed at %s:%d: `%s'\n"),
746 "curl_multi_timeout", __FILE__, __LINE__,
747 curl_multi_strerror (mret));
751 rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
752 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
754 grs = GNUNET_NETWORK_fdset_create ();
755 gws = GNUNET_NETWORK_fdset_create ();
756 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
757 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
758 #if DEBUG_HOSTLIST_CLIENT
759 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
760 "Scheduling task for hostlist download using cURL\n");
763 = GNUNET_SCHEDULER_add_select (sched,
764 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
765 GNUNET_SCHEDULER_NO_TASK,
771 GNUNET_NETWORK_fdset_destroy (gws);
772 GNUNET_NETWORK_fdset_destroy (grs);
777 * Task that is run when we are ready to receive more data from the hostlist
780 * @param cls closure, unused
781 * @param tc task context, unused
784 multi_ready (void *cls,
785 const struct GNUNET_SCHEDULER_TaskContext *tc)
787 unsigned int counter;
793 ti_download = GNUNET_SCHEDULER_NO_TASK;
794 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
796 #if DEBUG_HOSTLIST_CLIENT
797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
798 "Shutdown requested while trying to download hostlist from `%s'\n",
805 if (GNUNET_TIME_absolute_get_remaining (end_time).value == 0)
807 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
808 _("Timeout trying to download hostlist from `%s'\n"),
814 #if DEBUG_HOSTLIST_CLIENT
815 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
816 "Ready for processing hostlist client request\n");
822 mret = curl_multi_perform (multi, &running);
828 msg = curl_multi_info_read (multi, &running);
830 GNUNET_break (msg != NULL);
836 if ( (msg->data.result != CURLE_OK) &&
837 (msg->data.result != CURLE_GOT_NOTHING) )
838 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
839 _("%s failed for `%s' at %s:%d: `%s'\n"),
840 "curl_multi_perform",
844 curl_easy_strerror (msg->data.result));
847 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
848 _("Download of hostlist `%s' completed.\n"),
850 download_successful = GNUNET_YES;
852 if (GNUNET_YES == testing_hostlist)
854 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
855 _("Adding successfully tested hostlist `%s' datastore.\n"),current_url);
857 hostlist_to_test = NULL;
858 testing_hostlist = GNUNET_NO;
871 while (mret == CURLM_CALL_MULTI_PERFORM);
872 if (mret != CURLM_OK)
874 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
875 _("%s failed at %s:%d: `%s'\n"),
876 "curl_multi_perform", __FILE__, __LINE__,
877 curl_multi_strerror (mret));
885 * Main function that will download a hostlist and process its
895 current_url = get_list_url ();
896 if (current_url == NULL)
898 curl = curl_easy_init ();
906 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
907 _("Bootstrapping using hostlist at `%s'.\n"),
910 download_in_progress = GNUNET_YES;
911 download_successful = GNUNET_NO;
914 GNUNET_STATISTICS_update (stats,
915 gettext_noop ("# hostlist downloads initiated"),
919 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
922 CURL_EASY_SETOPT (curl,
923 CURLOPT_WRITEFUNCTION,
924 &download_hostlist_processor);
930 CURL_EASY_SETOPT (curl,
938 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
939 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
940 /* no need to abort if the above failed */
941 CURL_EASY_SETOPT (curl,
949 CURL_EASY_SETOPT (curl,
953 CURL_EASY_SETOPT (curl,
957 CURL_EASY_SETOPT (curl,
959 GNUNET_SERVER_MAX_MESSAGE_SIZE);
960 if (0 == strncmp (current_url, "http", 4))
961 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
962 CURL_EASY_SETOPT (curl,
963 CURLOPT_CONNECTTIMEOUT,
965 CURL_EASY_SETOPT (curl,
969 /* this should no longer be needed; we're now single-threaded! */
970 CURL_EASY_SETOPT (curl,
974 multi = curl_multi_init ();
981 mret = curl_multi_add_handle (multi, curl);
982 if (mret != CURLM_OK)
984 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
985 _("%s failed at %s:%d: `%s'\n"),
986 "curl_multi_add_handle", __FILE__, __LINE__,
987 curl_multi_strerror (mret));
988 mret = curl_multi_cleanup (multi);
989 if (mret != CURLM_OK)
990 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
991 _("%s failed at %s:%d: `%s'\n"),
992 "curl_multi_cleanup", __FILE__, __LINE__,
993 curl_multi_strerror (mret));
998 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
1004 download_dispatcher (void *cls,
1005 const struct GNUNET_SCHEDULER_TaskContext *tc)
1007 ti_download_dispatcher_task = GNUNET_SCHEDULER_NO_TASK;
1008 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1010 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1011 "Download is initiated...\n");
1012 if ( GNUNET_NO == download_in_progress )
1014 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1015 "Download can start immediately...\n");
1016 download_hostlist();
1020 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1021 "Download in progess, have to wait...\n");
1022 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_delayed (sched,
1024 &download_dispatcher,
1030 * Task that checks if we should try to download a hostlist.
1031 * If so, we initiate the download, otherwise we schedule
1032 * this task again for a later time.
1035 check_task (void *cls,
1036 const struct GNUNET_SCHEDULER_TaskContext *tc)
1038 ti_check_download = GNUNET_SCHEDULER_NO_TASK;
1039 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1042 if (connection_count < MIN_CONNECTIONS)
1044 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_now ( sched,
1045 &download_dispatcher,
1050 struct GNUNET_TIME_Relative delay;
1054 curl_global_cleanup ();
1055 return; /* in shutdown */
1057 delay = hostlist_delay;
1058 if (hostlist_delay.value == 0)
1059 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1061 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1062 if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * (1 + connection_count))
1063 hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1064 (1 + connection_count));
1065 GNUNET_STATISTICS_set (stats,
1066 gettext_noop("# seconds between hostlist downloads"),
1067 hostlist_delay.value,
1071 delay = GNUNET_TIME_UNIT_ZERO;
1074 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1075 _("Have %u/%u connections. Will consider downloading hostlist in %llums\n"),
1078 (unsigned long long) delay.value);
1079 ti_check_download = GNUNET_SCHEDULER_add_delayed (sched,
1086 * This tasks sets hostlist testing to allowed after intervall between to testings is reached
1091 task_testing_intervall_reset (void *cls,
1092 const struct GNUNET_SCHEDULER_TaskContext *tc)
1094 ti_testing_intervall_task = GNUNET_SCHEDULER_NO_TASK;
1095 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1097 testing_allowed = GNUNET_OK;
1098 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1099 "Testing new hostlist advertisements is allowed again\n");
1104 * Task that writes hostlist entries to a file on a regular base
1109 task_hostlist_saving (void *cls,
1110 const struct GNUNET_SCHEDULER_TaskContext *tc)
1112 ti_saving_task = GNUNET_SCHEDULER_NO_TASK;
1113 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1115 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1116 _("Scheduled saving of hostlists\n"));
1117 save_hostlist_file ( GNUNET_NO );
1119 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1120 _("Hostlists will be saved to file again in %llums\n"),
1121 (unsigned long long) SAVING_INTERVALL.value);
1122 ti_saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1124 &task_hostlist_saving,
1129 * Method called whenever a given peer connects.
1131 * @param cls closure
1132 * @param peer peer identity this notification is about
1133 * @param latency reported latency of the connection with 'other'
1134 * @param distance reported distance (DV) to 'other'
1137 handler_connect (void *cls,
1139 GNUNET_PeerIdentity * peer,
1140 struct GNUNET_TIME_Relative latency,
1144 GNUNET_STATISTICS_update (stats,
1145 gettext_noop ("# active connections"),
1152 * Method called whenever a given peer disconnects.
1154 * @param cls closure
1155 * @param peer peer identity this notification is about
1158 handler_disconnect (void *cls,
1160 GNUNET_PeerIdentity * peer)
1163 GNUNET_STATISTICS_update (stats,
1164 gettext_noop ("# active connections"),
1170 * Method called whenever an advertisement message arrives.
1172 * @param cls closure (always NULL)
1173 * @param peer the peer sending the message
1174 * @param message the actual message
1175 * @param latency latency
1176 * @param distance distance
1177 * @return GNUNET_OK to keep the connection open,
1178 * GNUNET_SYSERR to close it (signal serious error)
1181 handler_advertisement (void *cls,
1182 const struct GNUNET_PeerIdentity * peer,
1183 const struct GNUNET_MessageHeader * message,
1184 struct GNUNET_TIME_Relative latency,
1189 const struct GNUNET_MessageHeader * incoming;
1191 struct Hostlist * hostlist;
1193 GNUNET_assert (ntohs (message->type) == GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1194 size = ntohs (message->size);
1195 if (size <= sizeof(struct GNUNET_MessageHeader))
1197 GNUNET_break_op (0);
1198 return GNUNET_SYSERR;
1200 incoming = (const struct GNUNET_MessageHeader *) message;
1201 uri = (const char*) &incoming[1];
1202 uri_size = size - sizeof (struct GNUNET_MessageHeader);
1203 if (uri [uri_size - 1] != '\0')
1205 GNUNET_break_op (0);
1206 return GNUNET_SYSERR;
1208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1209 "Hostlist client recieved advertisement from `%s' containing URI `%s'\n",
1212 if (GNUNET_NO != linked_list_contains (uri))
1214 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1215 "URI `%s' is already known\n",
1220 if ( GNUNET_NO == testing_allowed )
1222 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1223 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1224 return GNUNET_SYSERR;
1226 if ( GNUNET_YES == testing_hostlist )
1228 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1229 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1230 return GNUNET_SYSERR;
1233 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1234 hostlist->hostlist_uri = (const char*) &hostlist[1];
1235 memcpy (&hostlist[1], uri, uri_size);
1236 hostlist->time_creation = GNUNET_TIME_absolute_get();
1237 hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero();
1238 hostlist->quality = HOSTLIST_INITIAL;
1239 hostlist_to_test = hostlist;
1241 testing_hostlist = GNUNET_YES;
1242 testing_allowed = GNUNET_NO;
1243 ti_testing_intervall_task = GNUNET_SCHEDULER_add_delayed (sched,
1245 &task_testing_intervall_reset,
1248 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1249 "Testing new hostlist advertisements is locked for the next %u ms\n",
1252 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_now (sched,
1253 &download_dispatcher,
1262 * Continuation called by the statistics code once
1263 * we go the stat. Initiates hostlist download scheduling.
1265 * @param cls closure
1266 * @param success GNUNET_OK if statistics were
1267 * successfully obtained, GNUNET_SYSERR if not.
1270 primary_task (void *cls, int success)
1273 return; /* in shutdown */
1274 #if DEBUG_HOSTLIST_CLIENT
1275 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1276 "Statistics request done, scheduling hostlist download\n");
1278 ti_check_download = GNUNET_SCHEDULER_add_now (sched,
1285 process_stat (void *cls,
1286 const char *subsystem,
1291 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1292 _("Initial time between hostlist downloads is %llums\n"),
1293 (unsigned long long) value);
1294 hostlist_delay.value = value;
1299 * Method to load persistent hostlist file during hostlist client startup
1302 load_hostlist_file ()
1307 struct Hostlist * hostlist;
1309 uint32_t times_used;
1310 uint32_t hellos_returned;
1317 GNUNET_CONFIGURATION_get_value_string (cfg,
1322 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1323 _("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
1324 "HOSTLISTFILE", "HOSTLIST");
1328 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1329 _("Loading saved hostlist entries from file `%s' \n"), filename);
1330 if ( GNUNET_NO == GNUNET_DISK_file_test (filename) )
1332 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1333 _("Hostlist file `%s' is not existing\n"), filename);
1334 GNUNET_free ( filename );
1338 struct GNUNET_BIO_ReadHandle * rh = GNUNET_BIO_read_open (filename);
1341 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1342 _("Could not open file `%s' for reading to load hostlists: %s\n"),
1345 GNUNET_free (filename);
1350 while ( (GNUNET_OK == GNUNET_BIO_read_string (rh, "url" , &uri, MAX_URL_LEN)) &&
1351 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, ×_used)) &&
1352 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1353 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1354 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1355 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)) )
1357 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1358 hostlist->hello_count = hellos_returned;
1359 hostlist->hostlist_uri = (const char *) &hostlist[1];
1360 memcpy (&hostlist[1], uri, strlen(uri)+1);
1361 hostlist->quality = quality;
1362 hostlist->time_creation.value = created;
1363 hostlist->time_last_usage.value = last_used;
1364 GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist);
1366 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1367 "Added hostlist entry eith URI `%s' \n", hostlist->hostlist_uri);
1371 if ( counter >= MAX_NUMBER_HOSTLISTS ) break;
1374 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1375 _("%u hostlist URIs loaded from file\n"), counter);
1376 GNUNET_STATISTICS_set (stats,
1377 gettext_noop("# hostlist URIs read from file"),
1380 GNUNET_STATISTICS_set (stats,
1381 gettext_noop("# advertised hostlist URIs"),
1385 GNUNET_free_non_null (uri);
1387 GNUNET_BIO_read_close (rh, &emsg);
1390 GNUNET_free (filename);
1395 * Method to save persistent hostlist file during hostlist client shutdown
1396 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1398 static void save_hostlist_file ( int shutdown )
1401 struct Hostlist *pos;
1402 struct GNUNET_BIO_WriteHandle * wh;
1407 GNUNET_CONFIGURATION_get_value_string (cfg,
1412 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1413 _("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
1414 "HOSTLISTFILE", "HOSTLIST");
1417 wh = GNUNET_BIO_write_open (filename);
1420 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1421 _("Could not open file `%s' for writing to save hostlists: %s\n"),
1424 GNUNET_free (filename);
1427 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1428 _("Writing %u hostlist URIs to `%s'\n" ),
1429 linked_list_size, filename);
1431 /* add code to write hostlists to file using bio */
1434 while (NULL != (pos = linked_list_head))
1436 if ( GNUNET_YES == shutdown)
1438 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1441 if (GNUNET_YES == ok)
1444 GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1446 GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1448 GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1450 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.value)) ||
1452 GNUNET_BIO_write_int64 (wh, pos->time_creation.value)) ||
1454 GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1456 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1457 _("Error writing hostlist URIs to file `%s'\n"),
1463 if ( GNUNET_YES == shutdown)
1466 if ( counter >= MAX_NUMBER_HOSTLISTS) break;
1468 GNUNET_STATISTICS_set (stats,
1469 gettext_noop("# hostlist URIs written to file"),
1473 if ( GNUNET_OK != GNUNET_BIO_write_close ( wh ) )
1474 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1475 _("Error writing hostlist URIs to file `%s'\n"),
1477 GNUNET_free (filename);
1481 * Start downloading hostlists from hostlist servers as necessary.
1484 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1485 struct GNUNET_SCHEDULER_Handle *s,
1486 struct GNUNET_STATISTICS_Handle *st,
1487 GNUNET_CORE_ConnectEventHandler *ch,
1488 GNUNET_CORE_DisconnectEventHandler *dh,
1489 GNUNET_CORE_MessageCallback *msgh,
1495 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1498 return GNUNET_SYSERR;
1500 transport = GNUNET_TRANSPORT_connect (s, c, NULL, NULL, NULL, NULL);
1501 if (NULL == transport)
1503 curl_global_cleanup ();
1504 return GNUNET_SYSERR;
1510 GNUNET_CONFIGURATION_get_value_string (cfg,
1516 *ch = &handler_connect;
1517 *dh = &handler_disconnect;
1518 linked_list_head = NULL;
1519 linked_list_tail = NULL;
1520 use_preconfigured_list = GNUNET_YES;
1521 testing_hostlist = GNUNET_NO;
1522 testing_allowed = GNUNET_YES;
1524 if ( GNUNET_YES == learning )
1526 *msgh = &handler_advertisement;
1527 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1528 _("Learning is enabled on this peer\n"));
1529 load_hostlist_file ();
1530 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1531 _("Hostlists will be saved to file again in %llums\n"),
1532 (unsigned long long) SAVING_INTERVALL.value);
1533 ti_saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1535 &task_hostlist_saving,
1540 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1541 _("Learning is not enabled on this peer\n"));
1543 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1548 if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
1550 result = remove (filename);
1552 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1553 _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),filename);
1555 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1556 _("Hostlist file `%s' could not be removed\n"),filename);
1559 GNUNET_free ( filename );
1561 GNUNET_STATISTICS_get (stats,
1563 gettext_noop("# seconds between hostlist downloads"),
1564 GNUNET_TIME_UNIT_MINUTES,
1573 * Stop downloading hostlists from hostlist servers as necessary.
1576 GNUNET_HOSTLIST_client_stop ()
1578 #if DEBUG_HOSTLIST_CLIENT
1579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1580 "Hostlist client shutdown\n");
1582 if ( GNUNET_YES == learning )
1583 save_hostlist_file ( GNUNET_YES );
1585 if (ti_saving_task != GNUNET_SCHEDULER_NO_TASK)
1587 GNUNET_SCHEDULER_cancel (sched,
1591 if (ti_download_dispatcher_task != GNUNET_SCHEDULER_NO_TASK)
1593 GNUNET_SCHEDULER_cancel (sched,
1594 ti_download_dispatcher_task);
1596 if (ti_testing_intervall_task != GNUNET_SCHEDULER_NO_TASK)
1598 GNUNET_SCHEDULER_cancel (sched,
1599 ti_testing_intervall_task);
1601 if (ti_download != GNUNET_SCHEDULER_NO_TASK)
1603 GNUNET_SCHEDULER_cancel (sched,
1606 if (ti_check_download != GNUNET_SCHEDULER_NO_TASK)
1608 GNUNET_SCHEDULER_cancel (sched,
1610 curl_global_cleanup ();
1612 if (transport != NULL)
1614 GNUNET_TRANSPORT_disconnect (transport);
1617 GNUNET_assert (NULL == transport);
1618 GNUNET_free_non_null (proxy);
1624 /* end of hostlist-client.c */