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 * 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;
236 * Set if we are allowed to learn new hostlists and use them
238 static int stat_learning;
241 * Value saying if hostlist download was successful
243 static unsigned int stat_download_successful;
246 * Value saying how many valid HELLO messages were obtained during download
248 static unsigned int stat_hellos_obtained;
251 * Number of active connections (according to core service).
253 static unsigned int stat_connection_count;
255 unsigned int downloaded_hellos;
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,
272 const char * cbuf = ptr;
273 const struct GNUNET_MessageHeader *msg;
279 total = size * nmemb;
280 if ( (total == 0) || (stat_bogus_url) )
282 return total; /* ok, no data or bogus data */
285 if ( downloaded_hellos >= MAX_HELLO_PER_HOSTLISTS )
287 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
288 _("Maximum number of HELLO Messages per download reached: %u'\n"),
289 MAX_HELLO_PER_HOSTLISTS );
293 GNUNET_STATISTICS_update (stats,
294 gettext_noop ("# bytes downloaded from hostlist servers"),
298 while ( (left > 0) ||
301 cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - download_pos);
302 memcpy (&download_buffer[download_pos],
308 if (download_pos < sizeof(struct GNUNET_MessageHeader))
310 GNUNET_assert (left == 0);
313 msg = (const struct GNUNET_MessageHeader *) download_buffer;
314 msize = ntohs(msg->size);
315 if (msize < sizeof(struct GNUNET_MessageHeader))
317 GNUNET_STATISTICS_update (stats,
318 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
321 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
322 _("Invalid `%s' message received from hostlist at `%s'\n"),
325 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++;
348 GNUNET_TRANSPORT_offer_hello (transport, msg);
352 GNUNET_STATISTICS_update (stats,
353 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
356 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
357 _("Invalid `%s' message received from hostlist at `%s'\n"),
360 stat_bogus_url = GNUNET_YES;
361 stat_hellos_obtained++;
365 memmove (download_buffer,
366 &download_buffer[msize],
367 download_pos - msize);
368 download_pos -= msize;
375 * Obtain a hostlist URL that we should use.
377 * @return NULL if there is no URL available
380 get_bootstrap_server ()
388 GNUNET_CONFIGURATION_get_value_string (cfg,
393 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
394 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
395 "SERVERS", "HOSTLIST");
400 if (strlen (servers) > 0)
403 pos = strlen (servers) - 1;
406 if (servers[pos] == ' ')
413 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
414 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
415 "SERVERS", "HOSTLIST");
416 GNUNET_free (servers);
420 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
421 pos = strlen (servers) - 1;
424 if (servers[pos] == ' ')
436 ret = GNUNET_strdup (&servers[pos]);
437 GNUNET_free (servers);
442 * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
443 * @return uri to use, NULL if there is no URL available
449 unsigned int counter;
450 struct Hostlist * pos;
452 if ( GNUNET_NO == stat_learning)
454 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
455 "Using preconfigured bootstrap server\n");
456 current_hostlist = NULL;
457 return get_bootstrap_server();
460 if ( ( GNUNET_YES == stat_testing_hostlist) && (NULL != hostlist_to_test) )
462 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
463 "Testing new advertised hostlist if it is obtainable\n");
464 current_hostlist = hostlist_to_test;
465 return strdup(hostlist_to_test->hostlist_uri);
468 if ( (GNUNET_YES == stat_use_bootstrap) ||
469 (linked_list_size == 0) )
471 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
472 "Using preconfigured bootstrap server\n");
473 current_hostlist = NULL;
474 return get_bootstrap_server();
476 index = GNUNET_CRYPTO_random_u32 ( GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
478 pos = linked_list_head;
479 while ( counter < index )
484 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485 "Using learned hostlist `%s'\n", pos->hostlist_uri);
486 current_hostlist = pos;
487 return strdup(pos->hostlist_uri);
491 #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);
494 * Method to save hostlist to a file during hostlist client shutdown
495 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
497 static void save_hostlist_file ( int shutdown );
500 * add val2 to val1 with overflow check
501 * @param val1 value 1
502 * @param val2 value 2
505 static uint64_t checked_add (uint64_t val1, uint64_t val2)
507 static uint64_t temp;
508 static uint64_t maxv;
521 * Subtract val2 from val1 with underflow check
522 * @param val1 value 1
523 * @param val2 value 2
526 static uint64_t checked_sub (uint64_t val1, uint64_t val2)
535 * Method to check if a URI is in hostlist linked list
536 * @param uri uri to check
537 * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
540 linked_list_contains (const char * uri)
542 struct Hostlist * pos;
544 pos = linked_list_head;
547 if (0 == strcmp(pos->hostlist_uri, uri) )
556 * Method returning the hostlist element with the lowest quality in the datastore
557 * @return hostlist with lowest quality
559 static struct Hostlist *
560 linked_list_get_lowest_quality ( )
562 struct Hostlist * pos;
563 struct Hostlist * lowest;
565 if (linked_list_size == 0)
567 lowest = linked_list_head;
568 pos = linked_list_head->next;
571 if (pos->quality < lowest->quality)
580 * Method to insert a hostlist into the datastore. If datastore contains maximum number of elements, the elements with lowest quality is dismissed
583 insert_hostlist ( void )
585 GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist_to_test);
588 GNUNET_STATISTICS_set (stats,
589 gettext_noop("# advertised hostlist URIs"),
593 if (MAX_NUMBER_HOSTLISTS >= linked_list_size)
596 /* No free entries available, replace existing entry */
597 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
598 "Removing lowest quality entry\n" );
599 struct Hostlist * lowest_quality = linked_list_get_lowest_quality();
600 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
601 "Hostlist with URI `%s' has the worst quality of all with value %llu\n",
602 lowest_quality->hostlist_uri,
603 (unsigned long long) lowest_quality->quality);
604 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, lowest_quality);
607 GNUNET_STATISTICS_set (stats,
608 gettext_noop("# advertised hostlist URIs"),
612 GNUNET_free (lowest_quality);
614 stat_testing_hostlist = GNUNET_NO;
620 * Method updating hostlist statistics
622 static void update_hostlist ( )
625 if ( ((stat_use_bootstrap == GNUNET_NO) && ( NULL != current_hostlist )) ||
626 ((stat_testing_hostlist == GNUNET_YES) && ( NULL != current_hostlist )) )
628 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
629 "Updating hostlist statics for URI `%s'\n",current_hostlist->hostlist_uri );
630 current_hostlist->hello_count = stat_hellos_obtained;
631 current_hostlist->time_last_usage = GNUNET_TIME_absolute_get();
632 current_hostlist->quality = checked_add ( current_hostlist->quality, (stat_hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
633 if ( GNUNET_YES == stat_download_successful )
635 current_hostlist->times_used++;
636 current_hostlist->quality = checked_add ( current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
637 GNUNET_asprintf (&stat,
638 gettext_noop("# advertised URI `%s' downloaded"),
639 current_hostlist->hostlist_uri);
641 GNUNET_STATISTICS_update ( stats,
648 current_hostlist->quality = checked_sub ( current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD );
650 current_hostlist = NULL;
651 /* Alternating the usage of preconfigured and learned hostlists */
653 if (stat_testing_hostlist == GNUNET_YES)
656 if ( GNUNET_YES == stat_learning)
658 if (stat_use_bootstrap == GNUNET_YES)
659 stat_use_bootstrap = GNUNET_NO;
661 stat_use_bootstrap = GNUNET_YES;
664 stat_use_bootstrap = GNUNET_YES;
668 * Clean up the state from the task that downloaded the
669 * hostlist and schedule the next task.
676 if ( ( stat_testing_hostlist == GNUNET_YES ) && ( GNUNET_NO == stat_download_successful) && (NULL != hostlist_to_test))
678 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
679 _("Advertised hostlist with URI `%s' could not be downloaded. Advertised URI gets dismissed.\n"),hostlist_to_test->hostlist_uri);
682 if ( stat_testing_hostlist == GNUNET_YES )
684 stat_testing_hostlist = GNUNET_NO;
686 if ( NULL != hostlist_to_test)
688 GNUNET_free (hostlist_to_test);
689 hostlist_to_test = NULL;
694 mret = curl_multi_remove_handle (multi, curl);
695 if (mret != CURLM_OK)
697 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
698 _("%s failed at %s:%d: `%s'\n"),
699 "curl_multi_remove_handle", __FILE__, __LINE__,
700 curl_multi_strerror (mret));
702 mret = curl_multi_cleanup (multi);
703 if (mret != CURLM_OK)
704 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
705 _("%s failed at %s:%d: `%s'\n"),
706 "curl_multi_cleanup", __FILE__, __LINE__,
707 curl_multi_strerror (mret));
712 curl_easy_cleanup (curl);
715 GNUNET_free_non_null (current_url);
717 downloaded_hellos = 0;
718 stat_download_in_progress = GNUNET_NO;
722 * Task that is run when we are ready to receive more data from the hostlist
725 * @param cls closure, unused
726 * @param tc task context, unused
729 task_download (void *cls,
730 const struct GNUNET_SCHEDULER_TaskContext *tc);
733 * Ask CURL for the select set and then schedule the
734 * receiving task with the scheduler.
744 struct GNUNET_NETWORK_FDSet *grs;
745 struct GNUNET_NETWORK_FDSet *gws;
747 struct GNUNET_TIME_Relative rtime;
753 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
754 if (mret != CURLM_OK)
756 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
757 _("%s failed at %s:%d: `%s'\n"),
758 "curl_multi_fdset", __FILE__, __LINE__,
759 curl_multi_strerror (mret));
763 mret = curl_multi_timeout (multi, &timeout);
764 if (mret != CURLM_OK)
766 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
767 _("%s failed at %s:%d: `%s'\n"),
768 "curl_multi_timeout", __FILE__, __LINE__,
769 curl_multi_strerror (mret));
773 rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
774 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
776 grs = GNUNET_NETWORK_fdset_create ();
777 gws = GNUNET_NETWORK_fdset_create ();
778 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
779 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
780 #if DEBUG_HOSTLIST_CLIENT
781 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
782 "Scheduling task for hostlist download using cURL\n");
785 = GNUNET_SCHEDULER_add_select (sched,
786 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
787 GNUNET_SCHEDULER_NO_TASK,
793 GNUNET_NETWORK_fdset_destroy (gws);
794 GNUNET_NETWORK_fdset_destroy (grs);
799 * Task that is run when we are ready to receive more data from the hostlist
802 * @param cls closure, unused
803 * @param tc task context, unused
806 task_download (void *cls,
807 const struct GNUNET_SCHEDULER_TaskContext *tc)
815 ti_download = GNUNET_SCHEDULER_NO_TASK;
816 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
818 #if DEBUG_HOSTLIST_CLIENT
819 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
820 "Shutdown requested while trying to download hostlist from `%s'\n",
827 if (GNUNET_TIME_absolute_get_remaining (end_time).value == 0)
829 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
830 _("Timeout trying to download hostlist from `%s'\n"),
836 #if DEBUG_HOSTLIST_CLIENT
837 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
838 "Ready for processing hostlist client request\n");
843 mret = curl_multi_perform (multi, &running);
849 msg = curl_multi_info_read (multi, &running);
850 GNUNET_break (msg != NULL);
856 if ( (msg->data.result != CURLE_OK) &&
857 (msg->data.result != CURLE_GOT_NOTHING) )
858 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
859 _("%s failed for `%s' at %s:%d: `%s'\n"),
860 "curl_multi_perform",
864 curl_easy_strerror (msg->data.result));
867 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
868 _("Download of hostlist `%s' completed.\n"),
870 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
871 _("HELLOs recieved: %u \n"),downloaded_hellos);
872 stat_download_successful = GNUNET_YES;
874 if (GNUNET_YES == stat_testing_hostlist)
876 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
877 _("Adding successfully tested hostlist `%s' datastore.\n"),current_url);
879 hostlist_to_test = NULL;
880 stat_testing_hostlist = GNUNET_NO;
893 while (mret == CURLM_CALL_MULTI_PERFORM);
894 if (mret != CURLM_OK)
896 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
897 _("%s failed at %s:%d: `%s'\n"),
898 "curl_multi_perform", __FILE__, __LINE__,
899 curl_multi_strerror (mret));
907 * Main function that will download a hostlist and process its
917 current_url = download_get_url ();
918 if (current_url == NULL)
920 curl = curl_easy_init ();
928 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
929 _("Bootstrapping using hostlist at `%s'.\n"),
932 stat_download_in_progress = GNUNET_YES;
933 stat_download_successful = GNUNET_NO;
934 stat_hellos_obtained = 0;
935 downloaded_hellos = 0;
937 GNUNET_STATISTICS_update (stats,
938 gettext_noop ("# hostlist downloads initiated"),
942 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
945 CURL_EASY_SETOPT (curl,
946 CURLOPT_WRITEFUNCTION,
953 CURL_EASY_SETOPT (curl,
961 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
962 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
963 /* no need to abort if the above failed */
964 CURL_EASY_SETOPT (curl,
972 CURL_EASY_SETOPT (curl,
976 CURL_EASY_SETOPT (curl,
980 CURL_EASY_SETOPT (curl,
982 GNUNET_SERVER_MAX_MESSAGE_SIZE);
983 if (0 == strncmp (current_url, "http", 4))
984 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
985 CURL_EASY_SETOPT (curl,
986 CURLOPT_CONNECTTIMEOUT,
988 CURL_EASY_SETOPT (curl,
992 /* this should no longer be needed; we're now single-threaded! */
993 CURL_EASY_SETOPT (curl,
997 multi = curl_multi_init ();
1004 mret = curl_multi_add_handle (multi, curl);
1005 if (mret != CURLM_OK)
1007 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1008 _("%s failed at %s:%d: `%s'\n"),
1009 "curl_multi_add_handle", __FILE__, __LINE__,
1010 curl_multi_strerror (mret));
1011 mret = curl_multi_cleanup (multi);
1012 if (mret != CURLM_OK)
1013 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1014 _("%s failed at %s:%d: `%s'\n"),
1015 "curl_multi_cleanup", __FILE__, __LINE__,
1016 curl_multi_strerror (mret));
1021 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
1022 download_prepare ();
1027 task_download_dispatcher (void *cls,
1028 const struct GNUNET_SCHEDULER_TaskContext *tc)
1030 ti_download_dispatcher_task = GNUNET_SCHEDULER_NO_TASK;
1031 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1033 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1034 "Download is initiated...\n");
1035 if ( GNUNET_NO == stat_download_in_progress )
1037 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1038 "Download can start immediately...\n");
1039 download_hostlist();
1043 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1044 "Download in progess, have to wait...\n");
1045 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_delayed (sched,
1047 &task_download_dispatcher,
1053 * Task that checks if we should try to download a hostlist.
1054 * If so, we initiate the download, otherwise we schedule
1055 * this task again for a later time.
1058 task_check (void *cls,
1059 const struct GNUNET_SCHEDULER_TaskContext *tc)
1061 ti_check_download = GNUNET_SCHEDULER_NO_TASK;
1062 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1065 if (stat_connection_count < MIN_CONNECTIONS)
1067 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_now ( sched,
1068 &task_download_dispatcher,
1073 struct GNUNET_TIME_Relative delay;
1077 curl_global_cleanup ();
1078 return; /* in shutdown */
1080 delay = hostlist_delay;
1081 if (hostlist_delay.value == 0)
1082 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1084 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1085 if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * (1 + stat_connection_count))
1086 hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1087 (1 + stat_connection_count));
1088 GNUNET_STATISTICS_set (stats,
1089 gettext_noop("# seconds between hostlist downloads"),
1090 hostlist_delay.value,
1094 delay = GNUNET_TIME_UNIT_ZERO;
1097 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1098 _("Have %u/%u connections. Will consider downloading hostlist in %llums\n"),
1099 stat_connection_count,
1101 (unsigned long long) delay.value);
1102 ti_check_download = GNUNET_SCHEDULER_add_delayed (sched,
1109 * This tasks sets hostlist testing to allowed after intervall between to testings is reached
1114 task_testing_intervall_reset (void *cls,
1115 const struct GNUNET_SCHEDULER_TaskContext *tc)
1117 ti_testing_intervall_task = GNUNET_SCHEDULER_NO_TASK;
1118 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1120 stat_testing_allowed = GNUNET_OK;
1121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1122 "Testing new hostlist advertisements is allowed again\n");
1127 * Task that writes hostlist entries to a file on a regular base
1132 task_hostlist_saving (void *cls,
1133 const struct GNUNET_SCHEDULER_TaskContext *tc)
1135 ti_saving_task = GNUNET_SCHEDULER_NO_TASK;
1136 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1138 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1139 _("Scheduled saving of hostlists\n"));
1140 save_hostlist_file ( GNUNET_NO );
1142 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1143 _("Hostlists will be saved to file again in %llums\n"),
1144 (unsigned long long) SAVING_INTERVALL.value);
1145 ti_saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1147 &task_hostlist_saving,
1152 * Method called whenever a given peer connects.
1154 * @param cls closure
1155 * @param peer peer identity this notification is about
1156 * @param latency reported latency of the connection with 'other'
1157 * @param distance reported distance (DV) to 'other'
1160 handler_connect (void *cls,
1162 GNUNET_PeerIdentity * peer,
1163 struct GNUNET_TIME_Relative latency,
1166 stat_connection_count++;
1167 GNUNET_STATISTICS_update (stats,
1168 gettext_noop ("# active connections"),
1175 * Method called whenever a given peer disconnects.
1177 * @param cls closure
1178 * @param peer peer identity this notification is about
1181 handler_disconnect (void *cls,
1183 GNUNET_PeerIdentity * peer)
1185 stat_connection_count--;
1186 GNUNET_STATISTICS_update (stats,
1187 gettext_noop ("# active connections"),
1193 * Method called whenever an advertisement message arrives.
1195 * @param cls closure (always NULL)
1196 * @param peer the peer sending the message
1197 * @param message the actual message
1198 * @param latency latency
1199 * @param distance distance
1200 * @return GNUNET_OK to keep the connection open,
1201 * GNUNET_SYSERR to close it (signal serious error)
1204 handler_advertisement (void *cls,
1205 const struct GNUNET_PeerIdentity * peer,
1206 const struct GNUNET_MessageHeader * message,
1207 struct GNUNET_TIME_Relative latency,
1212 const struct GNUNET_MessageHeader * incoming;
1214 struct Hostlist * hostlist;
1216 GNUNET_assert (ntohs (message->type) == GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1217 size = ntohs (message->size);
1218 if (size <= sizeof(struct GNUNET_MessageHeader))
1220 GNUNET_break_op (0);
1221 return GNUNET_SYSERR;
1223 incoming = (const struct GNUNET_MessageHeader *) message;
1224 uri = (const char*) &incoming[1];
1225 uri_size = size - sizeof (struct GNUNET_MessageHeader);
1226 if (uri [uri_size - 1] != '\0')
1228 GNUNET_break_op (0);
1229 return GNUNET_SYSERR;
1231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1232 "Hostlist client recieved advertisement from `%s' containing URI `%s'\n",
1235 if (GNUNET_NO != linked_list_contains (uri))
1237 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1238 "URI `%s' is already known\n",
1243 if ( GNUNET_NO == stat_testing_allowed )
1245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1246 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1247 return GNUNET_SYSERR;
1249 if ( GNUNET_YES == stat_testing_hostlist )
1251 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1252 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1253 return GNUNET_SYSERR;
1256 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1257 hostlist->hostlist_uri = (const char*) &hostlist[1];
1258 memcpy (&hostlist[1], uri, uri_size);
1259 hostlist->time_creation = GNUNET_TIME_absolute_get();
1260 hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero();
1261 hostlist->quality = HOSTLIST_INITIAL;
1262 hostlist_to_test = hostlist;
1264 stat_testing_hostlist = GNUNET_YES;
1265 stat_testing_allowed = GNUNET_NO;
1266 ti_testing_intervall_task = GNUNET_SCHEDULER_add_delayed (sched,
1268 &task_testing_intervall_reset,
1271 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1272 "Testing new hostlist advertisements is locked for the next %u ms\n",
1275 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_now (sched,
1276 &task_download_dispatcher,
1285 * Continuation called by the statistics code once
1286 * we go the stat. Initiates hostlist download scheduling.
1288 * @param cls closure
1289 * @param success GNUNET_OK if statistics were
1290 * successfully obtained, GNUNET_SYSERR if not.
1293 primary_task (void *cls, int success)
1296 return; /* in shutdown */
1297 #if DEBUG_HOSTLIST_CLIENT
1298 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1299 "Statistics request done, scheduling hostlist download\n");
1301 ti_check_download = GNUNET_SCHEDULER_add_now (sched,
1308 process_stat (void *cls,
1309 const char *subsystem,
1314 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1315 _("Initial time between hostlist downloads is %llums\n"),
1316 (unsigned long long) value);
1317 hostlist_delay.value = value;
1322 * Method to load persistent hostlist file during hostlist client startup
1325 load_hostlist_file ()
1330 struct Hostlist * hostlist;
1332 uint32_t times_used;
1333 uint32_t hellos_returned;
1340 GNUNET_CONFIGURATION_get_value_string (cfg,
1345 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1346 _("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
1347 "HOSTLISTFILE", "HOSTLIST");
1351 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1352 _("Loading saved hostlist entries from file `%s' \n"), filename);
1353 if ( GNUNET_NO == GNUNET_DISK_file_test (filename) )
1355 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1356 _("Hostlist file `%s' is not existing\n"), filename);
1357 GNUNET_free ( filename );
1361 struct GNUNET_BIO_ReadHandle * rh = GNUNET_BIO_read_open (filename);
1364 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1365 _("Could not open file `%s' for reading to load hostlists: %s\n"),
1368 GNUNET_free (filename);
1373 while ( (GNUNET_OK == GNUNET_BIO_read_string (rh, "url" , &uri, MAX_URL_LEN)) &&
1374 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, ×_used)) &&
1375 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1376 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1377 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1378 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)) )
1380 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1381 hostlist->hello_count = hellos_returned;
1382 hostlist->hostlist_uri = (const char *) &hostlist[1];
1383 memcpy (&hostlist[1], uri, strlen(uri)+1);
1384 hostlist->quality = quality;
1385 hostlist->time_creation.value = created;
1386 hostlist->time_last_usage.value = last_used;
1387 GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist);
1389 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1390 "Added hostlist entry eith URI `%s' \n", hostlist->hostlist_uri);
1394 if ( counter >= MAX_NUMBER_HOSTLISTS ) break;
1397 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1398 _("%u hostlist URIs loaded from file\n"), counter);
1399 GNUNET_STATISTICS_set (stats,
1400 gettext_noop("# hostlist URIs read from file"),
1403 GNUNET_STATISTICS_set (stats,
1404 gettext_noop("# advertised hostlist URIs"),
1408 GNUNET_free_non_null (uri);
1410 GNUNET_BIO_read_close (rh, &emsg);
1413 GNUNET_free (filename);
1418 * Method to save persistent hostlist file during hostlist client shutdown
1419 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1421 static void save_hostlist_file ( int shutdown )
1424 struct Hostlist *pos;
1425 struct GNUNET_BIO_WriteHandle * wh;
1430 GNUNET_CONFIGURATION_get_value_string (cfg,
1435 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1436 _("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
1437 "HOSTLISTFILE", "HOSTLIST");
1440 wh = GNUNET_BIO_write_open (filename);
1443 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1444 _("Could not open file `%s' for writing to save hostlists: %s\n"),
1447 GNUNET_free (filename);
1450 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1451 _("Writing %u hostlist URIs to `%s'\n" ),
1452 linked_list_size, filename);
1454 /* add code to write hostlists to file using bio */
1457 while (NULL != (pos = linked_list_head))
1459 if ( GNUNET_YES == shutdown)
1461 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1464 if (GNUNET_YES == ok)
1467 GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1469 GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1471 GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1473 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.value)) ||
1475 GNUNET_BIO_write_int64 (wh, pos->time_creation.value)) ||
1477 GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1479 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1480 _("Error writing hostlist URIs to file `%s'\n"),
1486 if ( GNUNET_YES == shutdown)
1489 if ( counter >= MAX_NUMBER_HOSTLISTS) break;
1491 GNUNET_STATISTICS_set (stats,
1492 gettext_noop("# hostlist URIs written to file"),
1496 if ( GNUNET_OK != GNUNET_BIO_write_close ( wh ) )
1497 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1498 _("Error writing hostlist URIs to file `%s'\n"),
1500 GNUNET_free (filename);
1504 * Start downloading hostlists from hostlist servers as necessary.
1507 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1508 struct GNUNET_SCHEDULER_Handle *s,
1509 struct GNUNET_STATISTICS_Handle *st,
1510 GNUNET_CORE_ConnectEventHandler *ch,
1511 GNUNET_CORE_DisconnectEventHandler *dh,
1512 GNUNET_CORE_MessageCallback *msgh,
1518 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1521 return GNUNET_SYSERR;
1523 transport = GNUNET_TRANSPORT_connect (s, c, NULL, NULL, NULL, NULL);
1524 if (NULL == transport)
1526 curl_global_cleanup ();
1527 return GNUNET_SYSERR;
1533 GNUNET_CONFIGURATION_get_value_string (cfg,
1538 stat_learning = learn;
1539 *ch = &handler_connect;
1540 *dh = &handler_disconnect;
1541 linked_list_head = NULL;
1542 linked_list_tail = NULL;
1543 stat_use_bootstrap = GNUNET_YES;
1544 stat_testing_hostlist = GNUNET_NO;
1545 stat_testing_allowed = GNUNET_YES;
1547 if ( GNUNET_YES == stat_learning )
1549 *msgh = &handler_advertisement;
1550 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1551 _("Learning is enabled on this peer\n"));
1552 load_hostlist_file ();
1553 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1554 _("Hostlists will be saved to file again in %llums\n"),
1555 (unsigned long long) SAVING_INTERVALL.value);
1556 ti_saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1558 &task_hostlist_saving,
1563 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1564 _("Learning is not enabled on this peer\n"));
1566 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1571 if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
1573 result = remove (filename);
1575 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1576 _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),filename);
1578 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1579 _("Hostlist file `%s' could not be removed\n"),filename);
1582 GNUNET_free ( filename );
1584 GNUNET_STATISTICS_get (stats,
1586 gettext_noop("# seconds between hostlist downloads"),
1587 GNUNET_TIME_UNIT_MINUTES,
1596 * Stop downloading hostlists from hostlist servers as necessary.
1599 GNUNET_HOSTLIST_client_stop ()
1601 #if DEBUG_HOSTLIST_CLIENT
1602 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1603 "Hostlist client shutdown\n");
1605 if ( GNUNET_YES == stat_learning )
1606 save_hostlist_file ( GNUNET_YES );
1608 if (ti_saving_task != GNUNET_SCHEDULER_NO_TASK)
1610 GNUNET_SCHEDULER_cancel (sched,
1614 if (ti_download_dispatcher_task != GNUNET_SCHEDULER_NO_TASK)
1616 GNUNET_SCHEDULER_cancel (sched,
1617 ti_download_dispatcher_task);
1619 if (ti_testing_intervall_task != GNUNET_SCHEDULER_NO_TASK)
1621 GNUNET_SCHEDULER_cancel (sched,
1622 ti_testing_intervall_task);
1624 if (ti_download != GNUNET_SCHEDULER_NO_TASK)
1626 GNUNET_SCHEDULER_cancel (sched,
1629 if (ti_check_download != GNUNET_SCHEDULER_NO_TASK)
1631 GNUNET_SCHEDULER_cancel (sched,
1633 curl_global_cleanup ();
1635 if (transport != NULL)
1637 GNUNET_TRANSPORT_disconnect (transport);
1640 GNUNET_assert (NULL == transport);
1641 GNUNET_free_non_null (proxy);
1647 /* end of hostlist-client.c */