2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file hostlist/hostlist-client.c
23 * @brief hostlist support. Downloads HELLOs via HTTP.
24 * @author Christian Grothoff
25 * @author Matthias Wachs
29 #include "hostlist-client.h"
30 #include "gnunet_core_service.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet-daemon-hostlist.h"
35 #include <curl/curl.h>
36 #include "gnunet_common.h"
37 #include "gnunet_bio_lib.h"
39 #define DEBUG_HOSTLIST_CLIENT GNUNET_NO
43 * Number of connections that we must have to NOT download
46 #define MIN_CONNECTIONS 4
49 * Interval between two advertised hostlist tests
51 #define TESTING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
54 * A single hostlist obtained by hostlist advertisements
59 * previous entry, used to manage entries in a double linked list
61 struct Hostlist * prev;
64 * next entry, used to manage entries in a double linked list
66 struct Hostlist * next;
69 * URI where hostlist can be obtained
71 const char *hostlist_uri;
74 * Value describing the quality of the hostlist, the bigger the better but (should) never < 0
75 * used for deciding which hostlist is replaced if MAX_NUMBER_HOSTLISTS in data structure is reached
76 * intial value = HOSTLIST_INITIAL
77 * increased every successful download by HOSTLIST_SUCCESSFULL_DOWNLOAD
78 * increased every successful download by number of obtained HELLO messages
79 * decreased every failed download by HOSTLIST_SUCCESSFULL_DOWNLOAD
84 * Time the hostlist advertisement was recieved and the entry was created
86 struct GNUNET_TIME_Absolute time_creation;
89 * Last time the hostlist was obtained
91 struct GNUNET_TIME_Absolute time_last_usage;
94 * Number of HELLO messages obtained during last download
99 * Number of times the hostlist was successfully obtained
109 static const struct GNUNET_CONFIGURATION_Handle *cfg;
114 static struct GNUNET_STATISTICS_Handle *stats;
119 static struct GNUNET_TRANSPORT_Handle *transport;
122 * Proxy that we are using (can be NULL).
127 * Number of bytes valid in 'download_buffer'.
129 static size_t download_pos;
132 * Current URL that we are using.
134 static char *current_url;
137 * Current CURL handle.
142 * Current multi-CURL handle.
147 * How many bytes did we download from the current hostlist URL?
149 static uint32_t stat_bytes_downloaded;
151 * Amount of time we wait between hostlist downloads.
153 static struct GNUNET_TIME_Relative hostlist_delay;
156 * ID of the task, checking if hostlist download should take plate
158 static GNUNET_SCHEDULER_TaskIdentifier ti_check_download;
161 * ID of the task downloading the hostlist
163 static GNUNET_SCHEDULER_TaskIdentifier ti_download;
166 * ID of the task saving the hostlsit in a regular intervall
168 static GNUNET_SCHEDULER_TaskIdentifier ti_saving_task;
171 * ID of the task called to initiate a download
173 static GNUNET_SCHEDULER_TaskIdentifier ti_download_dispatcher_task;
176 * ID of the task controlling the locking between two hostlist tests
178 static GNUNET_SCHEDULER_TaskIdentifier ti_testing_intervall_task;
181 * At what time MUST the current hostlist request be done?
183 static struct GNUNET_TIME_Absolute end_time;
186 * Head of the linked list used to store hostlists
188 static struct Hostlist * linked_list_head;
191 * Tail of the linked list used to store hostlists
193 static struct Hostlist * linked_list_tail;
196 * Current hostlist used for downloading
198 static struct Hostlist * current_hostlist;
201 * Size of the linke list used to store hostlists
203 static unsigned int linked_list_size;
206 * Head of the linked list used to store hostlists
208 static struct Hostlist * hostlist_to_test;
211 * Set to GNUNET_YES if the current URL had some problems.
213 static int stat_bogus_url;
216 * Value controlling if a hostlist is tested at the moment
218 static int stat_testing_hostlist;
221 * Value controlling if a hostlist testing is allowed at the moment
223 static int stat_testing_allowed;
226 * Value controlling if a hostlist download is running at the moment
228 static int stat_download_in_progress;
231 * Value saying if a preconfigured bootstrap server is used
233 static unsigned int stat_use_bootstrap;
235 * Set if we are allowed to learn new hostlists and use them
237 static int stat_learning;
240 * Value saying if hostlist download was successful
242 static unsigned int stat_download_successful;
245 * Value saying how many valid HELLO messages were obtained during download
247 static unsigned int stat_hellos_obtained;
250 * Number of active connections (according to core service).
252 static unsigned int stat_connection_count;
256 * Process downloaded bits by calling callback on each HELLO.
258 * @param ptr buffer with downloaded data
259 * @param size size of a record
260 * @param nmemb number of records downloaded
262 * @return number of bytes that were processed (always size*nmemb)
265 callback_download (void *ptr,
270 static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
271 const char * cbuf = ptr;
272 const struct GNUNET_MessageHeader *msg;
278 total = size * nmemb;
279 stat_bytes_downloaded += total;
280 if ( (total == 0) || (stat_bogus_url) )
282 return total; /* ok, no data or bogus data */
285 GNUNET_STATISTICS_update (stats,
286 gettext_noop ("# bytes downloaded from hostlist servers"),
290 while ( (left > 0) ||
293 cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - 1 - download_pos);
294 memcpy (&download_buffer[download_pos],
300 if (download_pos < sizeof(struct GNUNET_MessageHeader))
302 GNUNET_assert (left == 0);
305 msg = (const struct GNUNET_MessageHeader *) download_buffer;
306 msize = ntohs(msg->size);
307 if (msize < sizeof(struct GNUNET_MessageHeader))
309 GNUNET_STATISTICS_update (stats,
310 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
313 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
314 _("Invalid `%s' message received from hostlist at `%s'\n"),
317 stat_hellos_obtained++;
321 if (download_pos < msize)
323 GNUNET_assert (left == 0);
326 if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
328 #if DEBUG_HOSTLIST_CLIENT
329 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330 "Received valid `%s' message from hostlist server.\n",
333 GNUNET_STATISTICS_update (stats,
334 gettext_noop ("# valid HELLOs downloaded from hostlist servers"),
337 stat_hellos_obtained++;
338 GNUNET_TRANSPORT_offer_hello (transport, msg);
342 GNUNET_STATISTICS_update (stats,
343 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
346 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
347 _("Invalid `%s' message received from hostlist at `%s'\n"),
350 stat_bogus_url = GNUNET_YES;
351 stat_hellos_obtained++;
354 memmove (download_buffer,
355 &download_buffer[msize],
356 download_pos - msize);
357 download_pos -= msize;
364 * Obtain a hostlist URL that we should use.
366 * @return NULL if there is no URL available
369 get_bootstrap_server ()
377 GNUNET_CONFIGURATION_get_value_string (cfg,
382 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
383 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
384 "SERVERS", "HOSTLIST");
389 if (strlen (servers) > 0)
392 pos = strlen (servers) - 1;
395 if (servers[pos] == ' ')
402 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
403 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
404 "SERVERS", "HOSTLIST");
405 GNUNET_free (servers);
409 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
410 pos = strlen (servers) - 1;
413 if (servers[pos] == ' ')
425 ret = GNUNET_strdup (&servers[pos]);
426 GNUNET_free (servers);
431 * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
432 * @return uri to use, NULL if there is no URL available
438 unsigned int counter;
439 struct Hostlist * pos;
441 if ( GNUNET_NO == stat_learning)
443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
444 "Using preconfigured bootstrap server\n");
445 current_hostlist = NULL;
446 return get_bootstrap_server();
449 if ( ( GNUNET_YES == stat_testing_hostlist) && (NULL != hostlist_to_test) )
451 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
452 "Testing new advertised hostlist if it is obtainable\n");
453 current_hostlist = hostlist_to_test;
454 return strdup(hostlist_to_test->hostlist_uri);
457 if ( (GNUNET_YES == stat_use_bootstrap) ||
458 (linked_list_size == 0) )
460 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
461 "Using preconfigured bootstrap server\n");
462 current_hostlist = NULL;
463 return get_bootstrap_server();
465 index = GNUNET_CRYPTO_random_u32 ( GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
467 pos = linked_list_head;
468 while ( counter < index )
473 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
474 "Using learned hostlist `%s'\n", pos->hostlist_uri);
475 current_hostlist = pos;
476 return strdup(pos->hostlist_uri);
480 #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)
483 * Method to save hostlist to a file during hostlist client shutdown
484 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
486 static void save_hostlist_file ( int shutdown );
489 * add val2 to val1 with overflow check
490 * @param val1 value 1
491 * @param val2 value 2
494 static uint64_t checked_add (uint64_t val1, uint64_t val2)
496 static uint64_t temp;
497 static uint64_t maxv;
510 * Subtract val2 from val1 with underflow check
511 * @param val1 value 1
512 * @param val2 value 2
515 static uint64_t checked_sub (uint64_t val1, uint64_t val2)
524 * Method to check if a URI is in hostlist linked list
525 * @param uri uri to check
526 * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
529 linked_list_contains (const char * uri)
531 struct Hostlist * pos;
533 pos = linked_list_head;
536 if (0 == strcmp(pos->hostlist_uri, uri) )
545 * Method returning the hostlist element with the lowest quality in the datastore
546 * @return hostlist with lowest quality
548 static struct Hostlist *
549 linked_list_get_lowest_quality ( )
551 struct Hostlist * pos;
552 struct Hostlist * lowest;
554 if (linked_list_size == 0)
556 lowest = linked_list_head;
557 pos = linked_list_head->next;
560 if (pos->quality < lowest->quality)
569 * Method to insert a hostlist into the datastore. If datastore contains maximum number of elements, the elements with lowest quality is dismissed
574 struct Hostlist * lowest_quality;
576 if (MAX_NUMBER_HOSTLISTS <= linked_list_size)
578 /* No free entries available, replace existing entry */
579 lowest_quality = linked_list_get_lowest_quality();
580 GNUNET_assert (lowest_quality != NULL);
581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
582 "Removing hostlist with URI `%s' which has the worst quality of all (%llu)\n",
583 lowest_quality->hostlist_uri,
584 (unsigned long long) lowest_quality->quality);
585 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, lowest_quality);
587 GNUNET_free (lowest_quality);
589 GNUNET_CONTAINER_DLL_insert(linked_list_head,
593 GNUNET_STATISTICS_set (stats,
594 gettext_noop("# advertised hostlist URIs"),
597 stat_testing_hostlist = GNUNET_NO;
602 * Method updating hostlist statistics
604 static void update_hostlist ( )
607 if ( ((stat_use_bootstrap == GNUNET_NO) && ( NULL != current_hostlist )) ||
608 ((stat_testing_hostlist == GNUNET_YES) && ( NULL != current_hostlist )) )
610 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
611 "Updating hostlist statics for URI `%s'\n",current_hostlist->hostlist_uri );
612 current_hostlist->hello_count = stat_hellos_obtained;
613 current_hostlist->time_last_usage = GNUNET_TIME_absolute_get();
614 current_hostlist->quality = checked_add ( current_hostlist->quality, (stat_hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
615 if ( GNUNET_YES == stat_download_successful )
617 current_hostlist->times_used++;
618 current_hostlist->quality = checked_add ( current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
619 GNUNET_asprintf (&stat,
620 gettext_noop("# advertised URI `%s' downloaded"),
621 current_hostlist->hostlist_uri);
623 GNUNET_STATISTICS_update ( stats,
630 current_hostlist->quality = checked_sub ( current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD );
632 current_hostlist = NULL;
633 /* Alternating the usage of preconfigured and learned hostlists */
635 if (stat_testing_hostlist == GNUNET_YES)
638 if ( GNUNET_YES == stat_learning)
640 if (stat_use_bootstrap == GNUNET_YES)
641 stat_use_bootstrap = GNUNET_NO;
643 stat_use_bootstrap = GNUNET_YES;
646 stat_use_bootstrap = GNUNET_YES;
650 * Clean up the state from the task that downloaded the
651 * hostlist and schedule the next task.
658 if ( ( stat_testing_hostlist == GNUNET_YES ) && ( GNUNET_NO == stat_download_successful) && (NULL != hostlist_to_test))
660 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
661 _("Advertised hostlist with URI `%s' could not be downloaded. Advertised URI gets dismissed.\n"),hostlist_to_test->hostlist_uri);
664 if ( stat_testing_hostlist == GNUNET_YES )
666 stat_testing_hostlist = GNUNET_NO;
668 if ( NULL != hostlist_to_test)
670 GNUNET_free (hostlist_to_test);
671 hostlist_to_test = NULL;
676 mret = curl_multi_remove_handle (multi, curl);
677 if (mret != CURLM_OK)
679 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
680 _("%s failed at %s:%d: `%s'\n"),
681 "curl_multi_remove_handle", __FILE__, __LINE__,
682 curl_multi_strerror (mret));
684 mret = curl_multi_cleanup (multi);
685 if (mret != CURLM_OK)
686 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
687 _("%s failed at %s:%d: `%s'\n"),
688 "curl_multi_cleanup", __FILE__, __LINE__,
689 curl_multi_strerror (mret));
694 curl_easy_cleanup (curl);
697 GNUNET_free_non_null (current_url);
699 stat_bytes_downloaded = 0;
700 stat_download_in_progress = GNUNET_NO;
704 * Task that is run when we are ready to receive more data from the hostlist
707 * @param cls closure, unused
708 * @param tc task context, unused
711 task_download (void *cls,
712 const struct GNUNET_SCHEDULER_TaskContext *tc);
715 * Ask CURL for the select set and then schedule the
716 * receiving task with the scheduler.
726 struct GNUNET_NETWORK_FDSet *grs;
727 struct GNUNET_NETWORK_FDSet *gws;
729 struct GNUNET_TIME_Relative rtime;
735 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
736 if (mret != CURLM_OK)
738 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
739 _("%s failed at %s:%d: `%s'\n"),
740 "curl_multi_fdset", __FILE__, __LINE__,
741 curl_multi_strerror (mret));
745 mret = curl_multi_timeout (multi, &timeout);
746 if (mret != CURLM_OK)
748 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
749 _("%s failed at %s:%d: `%s'\n"),
750 "curl_multi_timeout", __FILE__, __LINE__,
751 curl_multi_strerror (mret));
755 rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
756 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
758 grs = GNUNET_NETWORK_fdset_create ();
759 gws = GNUNET_NETWORK_fdset_create ();
760 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
761 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
762 #if DEBUG_HOSTLIST_CLIENT
763 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
764 "Scheduling task for hostlist download using cURL\n");
767 = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
768 GNUNET_SCHEDULER_NO_TASK,
774 GNUNET_NETWORK_fdset_destroy (gws);
775 GNUNET_NETWORK_fdset_destroy (grs);
780 * Task that is run when we are ready to receive more data from the hostlist
783 * @param cls closure, unused
784 * @param tc task context, unused
787 task_download (void *cls,
788 const struct GNUNET_SCHEDULER_TaskContext *tc)
795 ti_download = GNUNET_SCHEDULER_NO_TASK;
796 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
798 #if DEBUG_HOSTLIST_CLIENT
799 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
800 "Shutdown requested while trying to download hostlist from `%s'\n",
807 if (GNUNET_TIME_absolute_get_remaining (end_time).rel_value == 0)
809 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
810 _("Timeout trying to download hostlist from `%s'\n"),
816 #if DEBUG_HOSTLIST_CLIENT
817 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
818 "Ready for processing hostlist client request\n");
824 if (stat_bytes_downloaded > MAX_BYTES_PER_HOSTLISTS)
826 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
827 _("Download limit of %u bytes exceeded, stopping download\n"),MAX_BYTES_PER_HOSTLISTS);
831 mret = curl_multi_perform (multi, &running);
838 msg = curl_multi_info_read (multi, &running);
839 GNUNET_break (msg != NULL);
845 if ( (msg->data.result != CURLE_OK) &&
846 (msg->data.result != CURLE_GOT_NOTHING) )
847 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
848 _("%s failed for `%s' at %s:%d: `%s'\n"),
849 "curl_multi_perform",
853 curl_easy_strerror (msg->data.result));
856 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
857 _("Download of hostlist `%s' completed.\n"),
859 stat_download_successful = GNUNET_YES;
861 if (GNUNET_YES == stat_testing_hostlist)
863 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
864 _("Adding successfully tested hostlist `%s' datastore.\n"),current_url);
866 hostlist_to_test = NULL;
867 stat_testing_hostlist = GNUNET_NO;
877 while ( (running > 0) );
880 while (mret == CURLM_CALL_MULTI_PERFORM);
882 if (mret != CURLM_OK)
884 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
885 _("%s failed at %s:%d: `%s'\n"),
886 "curl_multi_perform", __FILE__, __LINE__,
887 curl_multi_strerror (mret));
895 * Main function that will download a hostlist and process its
905 current_url = download_get_url ();
906 if (current_url == NULL)
908 curl = curl_easy_init ();
916 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
917 _("Bootstrapping using hostlist at `%s'.\n"),
920 stat_download_in_progress = GNUNET_YES;
921 stat_download_successful = GNUNET_NO;
922 stat_hellos_obtained = 0;
923 stat_bytes_downloaded = 0;
925 GNUNET_STATISTICS_update (stats,
926 gettext_noop ("# hostlist downloads initiated"),
930 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
933 CURL_EASY_SETOPT (curl,
934 CURLOPT_WRITEFUNCTION,
941 CURL_EASY_SETOPT (curl,
949 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
950 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
951 /* no need to abort if the above failed */
952 CURL_EASY_SETOPT (curl,
960 CURL_EASY_SETOPT (curl,
964 CURL_EASY_SETOPT (curl,
968 CURL_EASY_SETOPT (curl,
970 GNUNET_SERVER_MAX_MESSAGE_SIZE);
971 if (0 == strncmp (current_url, "http", 4))
972 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
973 CURL_EASY_SETOPT (curl,
974 CURLOPT_CONNECTTIMEOUT,
976 CURL_EASY_SETOPT (curl,
980 /* this should no longer be needed; we're now single-threaded! */
981 CURL_EASY_SETOPT (curl,
985 multi = curl_multi_init ();
992 mret = curl_multi_add_handle (multi, curl);
993 if (mret != CURLM_OK)
995 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
996 _("%s failed at %s:%d: `%s'\n"),
997 "curl_multi_add_handle", __FILE__, __LINE__,
998 curl_multi_strerror (mret));
999 mret = curl_multi_cleanup (multi);
1000 if (mret != CURLM_OK)
1001 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1002 _("%s failed at %s:%d: `%s'\n"),
1003 "curl_multi_cleanup", __FILE__, __LINE__,
1004 curl_multi_strerror (mret));
1009 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
1010 download_prepare ();
1015 task_download_dispatcher (void *cls,
1016 const struct GNUNET_SCHEDULER_TaskContext *tc)
1018 ti_download_dispatcher_task = GNUNET_SCHEDULER_NO_TASK;
1019 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1021 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1022 "Download is initiated...\n");
1023 if ( GNUNET_NO == stat_download_in_progress )
1025 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1026 "Download can start immediately...\n");
1027 download_hostlist();
1031 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1032 "Download in progess, have to wait...\n");
1033 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_delayed (WAITING_INTERVALL,
1034 &task_download_dispatcher,
1040 * Task that checks if we should try to download a hostlist.
1041 * If so, we initiate the download, otherwise we schedule
1042 * this task again for a later time.
1045 task_check (void *cls,
1046 const struct GNUNET_SCHEDULER_TaskContext *tc)
1049 struct GNUNET_TIME_Relative delay;
1051 ti_check_download = GNUNET_SCHEDULER_NO_TASK;
1052 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1055 if (stat_connection_count < MIN_CONNECTIONS)
1057 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_now (&task_download_dispatcher,
1063 curl_global_cleanup ();
1064 return; /* in shutdown */
1066 delay = hostlist_delay;
1067 if (hostlist_delay.rel_value == 0)
1068 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1070 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1071 if (hostlist_delay.rel_value > GNUNET_TIME_UNIT_HOURS.rel_value * (1 + stat_connection_count))
1072 hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1073 (1 + stat_connection_count));
1074 GNUNET_STATISTICS_set (stats,
1075 gettext_noop("# milliseconds between hostlist downloads"),
1076 hostlist_delay.rel_value,
1080 delay = GNUNET_TIME_UNIT_ZERO;
1083 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1084 _("Have %u/%u connections. Will consider downloading hostlist in %llums\n"),
1085 stat_connection_count,
1087 (unsigned long long) delay.rel_value);
1088 ti_check_download = GNUNET_SCHEDULER_add_delayed (delay,
1094 * This tasks sets hostlist testing to allowed after intervall between to testings is reached
1099 task_testing_intervall_reset (void *cls,
1100 const struct GNUNET_SCHEDULER_TaskContext *tc)
1102 ti_testing_intervall_task = GNUNET_SCHEDULER_NO_TASK;
1103 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1105 stat_testing_allowed = GNUNET_OK;
1106 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1107 "Testing new hostlist advertisements is allowed again\n");
1112 * Task that writes hostlist entries to a file on a regular base
1117 task_hostlist_saving (void *cls,
1118 const struct GNUNET_SCHEDULER_TaskContext *tc)
1120 ti_saving_task = GNUNET_SCHEDULER_NO_TASK;
1121 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1123 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1124 _("Scheduled saving of hostlists\n"));
1125 save_hostlist_file ( GNUNET_NO );
1127 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1128 _("Hostlists will be saved to file again in %llums\n"),
1129 (unsigned long long) SAVING_INTERVALL.rel_value);
1130 ti_saving_task = GNUNET_SCHEDULER_add_delayed (SAVING_INTERVALL,
1131 &task_hostlist_saving,
1136 * Method called whenever a given peer connects.
1138 * @param cls closure
1139 * @param peer peer identity this notification is about
1140 * @param latency reported latency of the connection with 'other'
1141 * @param distance reported distance (DV) to 'other'
1144 handler_connect (void *cls,
1146 GNUNET_PeerIdentity * peer,
1147 struct GNUNET_TIME_Relative latency,
1150 GNUNET_assert (stat_connection_count < UINT_MAX);
1151 stat_connection_count++;
1152 GNUNET_STATISTICS_update (stats,
1153 gettext_noop ("# active connections"),
1160 * Method called whenever a given peer disconnects.
1162 * @param cls closure
1163 * @param peer peer identity this notification is about
1166 handler_disconnect (void *cls,
1168 GNUNET_PeerIdentity * peer)
1170 GNUNET_assert (stat_connection_count > 0);
1171 stat_connection_count--;
1172 GNUNET_STATISTICS_update (stats,
1173 gettext_noop ("# active connections"),
1179 * Method called whenever an advertisement message arrives.
1181 * @param cls closure (always NULL)
1182 * @param peer the peer sending the message
1183 * @param message the actual message
1184 * @param latency latency
1185 * @param distance distance
1186 * @return GNUNET_OK to keep the connection open,
1187 * GNUNET_SYSERR to close it (signal serious error)
1190 handler_advertisement (void *cls,
1191 const struct GNUNET_PeerIdentity * peer,
1192 const struct GNUNET_MessageHeader * message,
1193 struct GNUNET_TIME_Relative latency,
1198 const struct GNUNET_MessageHeader * incoming;
1200 struct Hostlist * hostlist;
1202 GNUNET_assert (ntohs (message->type) == GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1203 size = ntohs (message->size);
1204 if (size <= sizeof(struct GNUNET_MessageHeader))
1206 GNUNET_break_op (0);
1207 return GNUNET_SYSERR;
1209 incoming = (const struct GNUNET_MessageHeader *) message;
1210 uri = (const char*) &incoming[1];
1211 uri_size = size - sizeof (struct GNUNET_MessageHeader);
1212 if (uri [uri_size - 1] != '\0')
1214 GNUNET_break_op (0);
1215 return GNUNET_SYSERR;
1217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1218 "Hostlist client recieved advertisement from `%s' containing URI `%s'\n",
1221 if (GNUNET_NO != linked_list_contains (uri))
1223 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1224 "URI `%s' is already known\n",
1229 if ( GNUNET_NO == stat_testing_allowed )
1231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1232 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1233 return GNUNET_SYSERR;
1235 if ( GNUNET_YES == stat_testing_hostlist )
1237 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1238 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1239 return GNUNET_SYSERR;
1242 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1243 hostlist->hostlist_uri = (const char*) &hostlist[1];
1244 memcpy (&hostlist[1], uri, uri_size);
1245 hostlist->time_creation = GNUNET_TIME_absolute_get();
1246 hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero();
1247 hostlist->quality = HOSTLIST_INITIAL;
1248 hostlist_to_test = hostlist;
1250 stat_testing_hostlist = GNUNET_YES;
1251 stat_testing_allowed = GNUNET_NO;
1252 ti_testing_intervall_task = GNUNET_SCHEDULER_add_delayed (TESTING_INTERVAL,
1253 &task_testing_intervall_reset,
1256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1257 "Testing new hostlist advertisements is locked for the next %u ms\n",
1258 TESTING_INTERVAL.rel_value);
1260 ti_download_dispatcher_task = GNUNET_SCHEDULER_add_now (&task_download_dispatcher,
1269 * Continuation called by the statistics code once
1270 * we go the stat. Initiates hostlist download scheduling.
1272 * @param cls closure
1273 * @param success GNUNET_OK if statistics were
1274 * successfully obtained, GNUNET_SYSERR if not.
1277 primary_task (void *cls, int success)
1280 return; /* in shutdown */
1281 #if DEBUG_HOSTLIST_CLIENT
1282 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1283 "Statistics request done, scheduling hostlist download\n");
1285 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check,
1291 process_stat (void *cls,
1292 const char *subsystem,
1297 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1298 _("Initial time between hostlist downloads is %llums\n"),
1299 (unsigned long long) value);
1300 hostlist_delay.rel_value = value;
1305 * Method to load persistent hostlist file during hostlist client startup
1308 load_hostlist_file ()
1313 struct Hostlist * hostlist;
1315 uint32_t times_used;
1316 uint32_t hellos_returned;
1323 GNUNET_CONFIGURATION_get_value_filename (cfg,
1328 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1329 _("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
1330 "HOSTLISTFILE", "HOSTLIST");
1334 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1335 _("Loading saved hostlist entries from file `%s' \n"), filename);
1336 if ( GNUNET_NO == GNUNET_DISK_file_test (filename) )
1338 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1339 _("Hostlist file `%s' is not existing\n"), filename);
1340 GNUNET_free ( filename );
1344 struct GNUNET_BIO_ReadHandle * rh = GNUNET_BIO_read_open (filename);
1347 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1348 _("Could not open file `%s' for reading to load hostlists: %s\n"),
1351 GNUNET_free (filename);
1356 while ( (GNUNET_OK == GNUNET_BIO_read_string (rh, "url" , &uri, MAX_URL_LEN)) &&
1358 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, ×_used)) &&
1359 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1360 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1361 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1362 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)) )
1364 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1365 hostlist->hello_count = hellos_returned;
1366 hostlist->hostlist_uri = (const char *) &hostlist[1];
1367 memcpy (&hostlist[1], uri, strlen(uri)+1);
1368 hostlist->quality = quality;
1369 hostlist->time_creation.abs_value = created;
1370 hostlist->time_last_usage.abs_value = last_used;
1371 GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist);
1373 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1374 "Added hostlist entry eith URI `%s' \n", hostlist->hostlist_uri);
1378 if ( counter >= MAX_NUMBER_HOSTLISTS ) break;
1381 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1382 _("%u hostlist URIs loaded from file\n"), counter);
1383 GNUNET_STATISTICS_set (stats,
1384 gettext_noop("# hostlist URIs read from file"),
1387 GNUNET_STATISTICS_set (stats,
1388 gettext_noop("# advertised hostlist URIs"),
1392 GNUNET_free_non_null (uri);
1394 GNUNET_BIO_read_close (rh, &emsg);
1397 GNUNET_free (filename);
1402 * Method to save persistent hostlist file during hostlist client shutdown
1403 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1405 static void save_hostlist_file ( int shutdown )
1408 struct Hostlist *pos;
1409 struct GNUNET_BIO_WriteHandle * wh;
1414 GNUNET_CONFIGURATION_get_value_filename (cfg,
1419 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1420 _("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
1421 "HOSTLISTFILE", "HOSTLIST");
1424 GNUNET_DISK_directory_create_for_file (filename);
1425 wh = GNUNET_BIO_write_open (filename);
1428 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1429 _("Could not open file `%s' for writing to save hostlists: %s\n"),
1432 GNUNET_free (filename);
1435 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1436 _("Writing %u hostlist URIs to `%s'\n" ),
1437 linked_list_size, filename);
1439 /* add code to write hostlists to file using bio */
1442 while (NULL != (pos = linked_list_head))
1444 if ( GNUNET_YES == shutdown)
1446 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1449 if (GNUNET_YES == ok)
1452 GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1454 GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1456 GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1458 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.abs_value)) ||
1460 GNUNET_BIO_write_int64 (wh, pos->time_creation.abs_value)) ||
1462 GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1464 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1465 _("Error writing hostlist URIs to file `%s'\n"),
1471 if ( GNUNET_YES == shutdown)
1474 if ( counter >= MAX_NUMBER_HOSTLISTS) break;
1476 GNUNET_STATISTICS_set (stats,
1477 gettext_noop("# hostlist URIs written to file"),
1481 if ( GNUNET_OK != GNUNET_BIO_write_close ( wh ) )
1482 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1483 _("Error writing hostlist URIs to file `%s'\n"),
1485 GNUNET_free (filename);
1489 * Start downloading hostlists from hostlist servers as necessary.
1492 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1493 struct GNUNET_STATISTICS_Handle *st,
1494 GNUNET_CORE_ConnectEventHandler *ch,
1495 GNUNET_CORE_DisconnectEventHandler *dh,
1496 GNUNET_CORE_MessageCallback *msgh,
1502 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1505 return GNUNET_SYSERR;
1507 transport = GNUNET_TRANSPORT_connect (c, NULL, NULL, NULL, NULL, NULL);
1508 if (NULL == transport)
1510 curl_global_cleanup ();
1511 return GNUNET_SYSERR;
1516 GNUNET_CONFIGURATION_get_value_string (cfg,
1521 stat_learning = learn;
1522 *ch = &handler_connect;
1523 *dh = &handler_disconnect;
1524 linked_list_head = NULL;
1525 linked_list_tail = NULL;
1526 stat_use_bootstrap = GNUNET_YES;
1527 stat_testing_hostlist = GNUNET_NO;
1528 stat_testing_allowed = GNUNET_YES;
1530 if ( GNUNET_YES == stat_learning )
1532 *msgh = &handler_advertisement;
1533 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1534 _("Learning is enabled on this peer\n"));
1535 load_hostlist_file ();
1536 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1537 _("Hostlists will be saved to file again in %llums\n"),
1538 (unsigned long long) SAVING_INTERVALL.rel_value);
1539 ti_saving_task = GNUNET_SCHEDULER_add_delayed (SAVING_INTERVALL,
1540 &task_hostlist_saving,
1545 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1546 _("Learning is not enabled on this peer\n"));
1548 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg,
1553 if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
1555 result = remove (filename);
1557 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1558 _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),filename);
1560 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1561 _("Hostlist file `%s' could not be removed\n"),filename);
1564 GNUNET_free ( filename );
1566 GNUNET_STATISTICS_get (stats,
1568 gettext_noop("# milliseconds between hostlist downloads"),
1569 GNUNET_TIME_UNIT_MINUTES,
1578 * Stop downloading hostlists from hostlist servers as necessary.
1581 GNUNET_HOSTLIST_client_stop ()
1583 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1584 "Hostlist client shutdown\n");
1585 #if DEBUG_HOSTLIST_CLIENT
1586 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1587 "Hostlist client shutdown\n");
1589 if ( GNUNET_YES == stat_learning )
1590 save_hostlist_file ( GNUNET_YES );
1592 if (ti_saving_task != GNUNET_SCHEDULER_NO_TASK)
1594 GNUNET_SCHEDULER_cancel (ti_saving_task);
1597 if (ti_download_dispatcher_task != GNUNET_SCHEDULER_NO_TASK)
1599 GNUNET_SCHEDULER_cancel (ti_download_dispatcher_task);
1601 if (ti_testing_intervall_task != GNUNET_SCHEDULER_NO_TASK)
1603 GNUNET_SCHEDULER_cancel (ti_testing_intervall_task);
1605 if (ti_download != GNUNET_SCHEDULER_NO_TASK)
1607 GNUNET_SCHEDULER_cancel (ti_download);
1609 if (ti_check_download != GNUNET_SCHEDULER_NO_TASK)
1611 GNUNET_SCHEDULER_cancel (ti_check_download);
1612 curl_global_cleanup ();
1614 if (transport != NULL)
1616 GNUNET_TRANSPORT_disconnect (transport);
1619 GNUNET_assert (NULL == transport);
1620 GNUNET_free_non_null (proxy);
1625 /* end of hostlist-client.c */