2 This file is part of GNUnet.
3 Copyright (C) 2001-2010, 2014, 2016 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
21 * @file hostlist/gnunet-daemon-hostlist_client.c
22 * @brief hostlist support. Downloads HELLOs via HTTP.
23 * @author Christian Grothoff
24 * @author Matthias Wachs
27 #include "gnunet-daemon-hostlist_client.h"
28 #include "gnunet_hello_lib.h"
29 #include "gnunet_statistics_service.h"
30 #include "gnunet_transport_service.h"
31 #include "gnunet_peerinfo_service.h"
32 #include "gnunet-daemon-hostlist.h"
33 /* Just included for the right curl.h */
34 #include "gnunet_curl_lib.h"
39 * Number of connections that we must have to NOT download
42 #define MIN_CONNECTIONS 4
45 * Maximum number of hostlist that are saved
47 #define MAX_NUMBER_HOSTLISTS 30
50 * Time interval hostlists are saved to disk
52 #define SAVING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
55 * Time interval between two hostlist tests
57 #define TESTING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
60 * Time interval for download dispatcher before a download is re-scheduled
62 #define WAITING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
65 * Defines concerning the hostlist quality metric
69 * Initial quality of a new created hostlist
71 #define HOSTLIST_INITIAL 10000
74 * Value subtracted each time a hostlist download fails
76 #define HOSTLIST_FAILED_DOWNLOAD 100
79 * Value added each time a hostlist download is successful
81 #define HOSTLIST_SUCCESSFUL_DOWNLOAD 100
84 * Value added for each valid HELLO recived during a hostlist download
86 #define HOSTLIST_SUCCESSFUL_HELLO 1
91 * A single hostlist obtained by hostlist advertisements
96 * previous entry, used to manage entries in a double linked list
98 struct Hostlist *prev;
101 * next entry, used to manage entries in a double linked list
103 struct Hostlist *next;
106 * URI where hostlist can be obtained
108 const char *hostlist_uri;
111 * Value describing the quality of the hostlist, the bigger the better but (should) never < 0
112 * used for deciding which hostlist is replaced if MAX_NUMBER_HOSTLISTS in data structure is reached
113 * intial value = HOSTLIST_INITIAL
114 * increased every successful download by HOSTLIST_SUCCESSFULL_DOWNLOAD
115 * increased every successful download by number of obtained HELLO messages
116 * decreased every failed download by HOSTLIST_SUCCESSFULL_DOWNLOAD
121 * Time the hostlist advertisement was recieved and the entry was created
123 struct GNUNET_TIME_Absolute time_creation;
126 * Last time the hostlist was obtained
128 struct GNUNET_TIME_Absolute time_last_usage;
131 * Number of HELLO messages obtained during last download
133 uint32_t hello_count;
136 * Number of times the hostlist was successfully obtained
146 static const struct GNUNET_CONFIGURATION_Handle *cfg;
151 static struct GNUNET_STATISTICS_Handle *stats;
154 * Proxy hostname or ip we are using (can be NULL).
159 * Proxy username we are using (can be NULL).
161 static char *proxy_username;
164 * Proxy password we are using (can be NULL).
166 static char *proxy_password;
169 * Proxy type we are using (can be NULL).
171 static curl_proxytype proxy_type;
174 * Number of bytes valid in 'download_buffer'.
176 static size_t download_pos;
179 * Current URL that we are using.
181 static char *current_url;
184 * Current CURL handle.
189 * Current multi-CURL handle.
194 * How many bytes did we download from the current hostlist URL?
196 static uint32_t stat_bytes_downloaded;
199 * Amount of time we wait between hostlist downloads.
201 static struct GNUNET_TIME_Relative hostlist_delay;
204 * ID of the task, checking if hostlist download should take plate
206 static struct GNUNET_SCHEDULER_Task *ti_check_download;
209 * ID of the task downloading the hostlist
211 static struct GNUNET_SCHEDULER_Task *ti_download;
214 * ID of the task saving the hostlsit in a regular intervall
216 static struct GNUNET_SCHEDULER_Task *ti_saving_task;
219 * ID of the task called to initiate a download
221 static struct GNUNET_SCHEDULER_Task *ti_download_dispatcher_task;
224 * ID of the task controlling the locking between two hostlist tests
226 static struct GNUNET_SCHEDULER_Task *ti_testing_intervall_task;
229 * At what time MUST the current hostlist request be done?
231 static struct GNUNET_TIME_Absolute end_time;
234 * Head of the linked list used to store hostlists
236 static struct Hostlist *linked_list_head;
239 * Tail of the linked list used to store hostlists
241 static struct Hostlist *linked_list_tail;
244 * Current hostlist used for downloading
246 static struct Hostlist *current_hostlist;
249 * Size of the linke list used to store hostlists
251 static unsigned int linked_list_size;
254 * Head of the linked list used to store hostlists
256 static struct Hostlist *hostlist_to_test;
259 * Handle for our statistics GET operation.
261 static struct GNUNET_STATISTICS_GetHandle *sget;
264 * Set to GNUNET_YES if the current URL had some problems.
266 static int stat_bogus_url;
269 * Value controlling if a hostlist is tested at the moment
271 static int stat_testing_hostlist;
274 * Value controlling if a hostlist testing is allowed at the moment
276 static int stat_testing_allowed;
279 * Value controlling if a hostlist download is running at the moment
281 static int stat_download_in_progress;
284 * Value saying if a preconfigured bootstrap server is used
286 static unsigned int stat_use_bootstrap;
289 * Set if we are allowed to learn new hostlists and use them
291 static int stat_learning;
294 * Value saying if hostlist download was successful
296 static unsigned int stat_download_successful;
299 * Value saying how many valid HELLO messages were obtained during download
301 static unsigned int stat_hellos_obtained;
304 * Number of active connections (according to core service).
306 static unsigned int stat_connection_count;
309 * Handle to peerinfo service.
311 static struct GNUNET_PEERINFO_Handle *pi;
315 * Process downloaded bits by calling callback on each HELLO.
317 * @param ptr buffer with downloaded data
318 * @param size size of a record
319 * @param nmemb number of records downloaded
321 * @return number of bytes that were processed (always size*nmemb)
324 callback_download (void *ptr,
329 static char download_buffer[GNUNET_MAX_MESSAGE_SIZE - 1];
330 const char *cbuf = ptr;
331 const struct GNUNET_MessageHeader *msg;
337 total = size * nmemb;
338 stat_bytes_downloaded += total;
339 if ((total == 0) || (stat_bogus_url))
341 return total; /* ok, no data or bogus data */
344 GNUNET_STATISTICS_update (stats,
346 ("# bytes downloaded from hostlist servers"),
347 (int64_t) total, GNUNET_NO);
349 while ((left > 0) || (download_pos > 0))
351 cpy = GNUNET_MIN (left, GNUNET_MAX_MESSAGE_SIZE - 1 - download_pos);
352 GNUNET_memcpy (&download_buffer[download_pos], cbuf, cpy);
356 if (download_pos < sizeof (struct GNUNET_MessageHeader))
358 GNUNET_assert (0 == left);
361 msg = (const struct GNUNET_MessageHeader *) download_buffer;
362 msize = ntohs (msg->size);
363 if (msize < sizeof (struct GNUNET_MessageHeader))
365 GNUNET_STATISTICS_update (stats,
367 ("# invalid HELLOs downloaded from hostlist servers"),
369 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
370 _("Invalid `%s' message received from hostlist at `%s'\n"),
371 "HELLO", current_url);
372 stat_hellos_obtained++;
376 if (download_pos < msize)
378 GNUNET_assert (left == 0);
381 if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message *) msg) == msize)
383 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
384 "Received valid `%s' message from hostlist server.\n",
386 GNUNET_STATISTICS_update (stats,
388 ("# valid HELLOs downloaded from hostlist servers"),
390 stat_hellos_obtained++;
391 (void) GNUNET_PEERINFO_add_peer (pi,
392 (const struct GNUNET_HELLO_Message *) msg,
398 GNUNET_STATISTICS_update (stats,
400 ("# invalid HELLOs downloaded from hostlist servers"),
402 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
403 _("Invalid `%s' message received from hostlist at `%s'\n"),
404 "HELLO", current_url);
405 stat_bogus_url = GNUNET_YES;
406 stat_hellos_obtained++;
409 memmove (download_buffer,
410 &download_buffer[msize],
411 download_pos - msize);
412 download_pos -= msize;
419 * Obtain a hostlist URL that we should use.
421 * @return NULL if there is no URL available
424 get_bootstrap_server ()
432 GNUNET_CONFIGURATION_get_value_string (cfg, "HOSTLIST", "SERVERS",
435 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
436 "hostlist", "SERVERS");
441 if (strlen (servers) > 0)
444 pos = strlen (servers) - 1;
447 if (servers[pos] == ' ')
454 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
455 "hostlist", "SERVERS");
456 GNUNET_free (servers);
460 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
461 pos = strlen (servers) - 1;
464 if (servers[pos] == ' ')
476 ret = GNUNET_strdup (&servers[pos]);
477 GNUNET_free (servers);
483 * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
484 * @return uri to use, NULL if there is no URL available
490 unsigned int counter;
491 struct Hostlist *pos;
493 if (GNUNET_NO == stat_learning)
495 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
496 "Using preconfigured bootstrap server\n");
497 current_hostlist = NULL;
498 return get_bootstrap_server ();
501 if ((GNUNET_YES == stat_testing_hostlist) && (NULL != hostlist_to_test))
503 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
504 "Testing new advertised hostlist if it is obtainable\n");
505 current_hostlist = hostlist_to_test;
506 return GNUNET_strdup (hostlist_to_test->hostlist_uri);
509 if ((GNUNET_YES == stat_use_bootstrap) || (linked_list_size == 0))
511 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
512 "Using preconfigured bootstrap server\n");
513 current_hostlist = NULL;
514 return get_bootstrap_server ();
517 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
519 pos = linked_list_head;
520 while (counter < index)
525 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Using learned hostlist `%s'\n",
527 current_hostlist = pos;
528 return GNUNET_strdup (pos->hostlist_uri);
532 #define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt (c, a, b); if (CURLE_OK != ret) GNUNET_log(GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror (ret)); } while (0)
536 * Method to save hostlist to a file during hostlist client shutdown
538 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
541 save_hostlist_file (int shutdown);
545 * Add val2 to val1 with overflow check
547 * @param val1 value 1
548 * @param val2 value 2
552 checked_add (uint64_t val1,
555 static uint64_t temp;
556 static uint64_t maxv;
569 * Subtract val2 from val1 with underflow check
571 * @param val1 value 1
572 * @param val2 value 2
576 checked_sub (uint64_t val1,
581 return (val1 - val2);
586 * Method to check if a URI is in hostlist linked list
588 * @param uri uri to check
589 * @return #GNUNET_YES if existing in linked list, #GNUNET_NO if not
592 linked_list_contains (const char *uri)
594 struct Hostlist *pos;
596 pos = linked_list_head;
599 if (0 == strcmp (pos->hostlist_uri, uri))
608 * Method returning the hostlist element with the lowest quality in the datastore
609 * @return hostlist with lowest quality
611 static struct Hostlist *
612 linked_list_get_lowest_quality ()
614 struct Hostlist *pos;
615 struct Hostlist *lowest;
617 if (linked_list_size == 0)
619 lowest = linked_list_head;
620 pos = linked_list_head->next;
623 if (pos->quality < lowest->quality)
632 * Method to insert a hostlist into the datastore. If datastore
633 * contains maximum number of elements, the elements with lowest
634 * quality is dismissed
639 struct Hostlist *lowest_quality;
641 if (MAX_NUMBER_HOSTLISTS <= linked_list_size)
643 /* No free entries available, replace existing entry */
644 lowest_quality = linked_list_get_lowest_quality ();
645 GNUNET_assert (lowest_quality != NULL);
646 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
647 "Removing hostlist with URI `%s' which has the worst quality of all (%llu)\n",
648 lowest_quality->hostlist_uri,
649 (unsigned long long) lowest_quality->quality);
650 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail,
653 GNUNET_free (lowest_quality);
655 GNUNET_CONTAINER_DLL_insert (linked_list_head, linked_list_tail,
658 GNUNET_STATISTICS_set (stats, gettext_noop ("# advertised hostlist URIs"),
659 linked_list_size, GNUNET_NO);
660 stat_testing_hostlist = GNUNET_NO;
665 * Method updating hostlist statistics
672 if (((stat_use_bootstrap == GNUNET_NO) && (NULL != current_hostlist)) ||
673 ((stat_testing_hostlist == GNUNET_YES) && (NULL != current_hostlist)))
675 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
676 "Updating hostlist statics for URI `%s'\n",
677 current_hostlist->hostlist_uri);
678 current_hostlist->hello_count = stat_hellos_obtained;
679 current_hostlist->time_last_usage = GNUNET_TIME_absolute_get ();
680 current_hostlist->quality =
681 checked_add (current_hostlist->quality,
682 (stat_hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
683 if (GNUNET_YES == stat_download_successful)
685 current_hostlist->times_used++;
686 current_hostlist->quality =
687 checked_add (current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
688 GNUNET_asprintf (&stat, gettext_noop ("# advertised URI `%s' downloaded"),
689 current_hostlist->hostlist_uri);
691 GNUNET_STATISTICS_update (stats, stat, 1, GNUNET_YES);
695 current_hostlist->quality =
696 checked_sub (current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD);
698 current_hostlist = NULL;
699 /* Alternating the usage of preconfigured and learned hostlists */
701 if (stat_testing_hostlist == GNUNET_YES)
704 if (GNUNET_YES == stat_learning)
706 if (stat_use_bootstrap == GNUNET_YES)
707 stat_use_bootstrap = GNUNET_NO;
709 stat_use_bootstrap = GNUNET_YES;
712 stat_use_bootstrap = GNUNET_YES;
717 * Clean up the state from the task that downloaded the
718 * hostlist and schedule the next task.
725 if ( (stat_testing_hostlist == GNUNET_YES) &&
726 (GNUNET_NO == stat_download_successful) &&
727 (NULL != hostlist_to_test) )
729 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
731 ("Advertised hostlist with URI `%s' could not be downloaded. Advertised URI gets dismissed.\n"),
732 hostlist_to_test->hostlist_uri);
735 if (stat_testing_hostlist == GNUNET_YES)
737 stat_testing_hostlist = GNUNET_NO;
739 if (NULL != hostlist_to_test)
741 GNUNET_free (hostlist_to_test);
742 hostlist_to_test = NULL;
747 mret = curl_multi_remove_handle (multi, curl);
748 if (mret != CURLM_OK)
750 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
751 "curl_multi_remove_handle", __FILE__, __LINE__,
752 curl_multi_strerror (mret));
754 mret = curl_multi_cleanup (multi);
755 if (mret != CURLM_OK)
756 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
757 "curl_multi_cleanup", __FILE__, __LINE__,
758 curl_multi_strerror (mret));
763 curl_easy_cleanup (curl);
766 GNUNET_free_non_null (current_url);
768 stat_bytes_downloaded = 0;
769 stat_download_in_progress = GNUNET_NO;
774 * Task that is run when we are ready to receive more data from the hostlist
777 * @param cls closure, unused
778 * @param tc task context, unused
781 task_download (void *cls);
785 * Ask CURL for the select set and then schedule the
786 * receiving task with the scheduler.
796 struct GNUNET_NETWORK_FDSet *grs;
797 struct GNUNET_NETWORK_FDSet *gws;
799 struct GNUNET_TIME_Relative rtime;
805 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
806 if (mret != CURLM_OK)
808 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
809 "curl_multi_fdset", __FILE__, __LINE__,
810 curl_multi_strerror (mret));
814 mret = curl_multi_timeout (multi, &timeout);
815 if (mret != CURLM_OK)
817 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
818 "curl_multi_timeout", __FILE__, __LINE__,
819 curl_multi_strerror (mret));
824 GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
825 GNUNET_TIME_relative_multiply
826 (GNUNET_TIME_UNIT_MILLISECONDS, timeout));
827 grs = GNUNET_NETWORK_fdset_create ();
828 gws = GNUNET_NETWORK_fdset_create ();
829 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
830 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
831 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
832 "Scheduling task for hostlist download using cURL\n");
834 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
836 &task_download, multi);
837 GNUNET_NETWORK_fdset_destroy (gws);
838 GNUNET_NETWORK_fdset_destroy (grs);
843 * Task that is run when we are ready to receive more data from the hostlist
846 * @param cls closure, unused
849 task_download (void *cls)
856 if (0 == GNUNET_TIME_absolute_get_remaining (end_time).rel_value_us)
858 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
859 _("Timeout trying to download hostlist from `%s'\n"),
865 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
866 "Ready for processing hostlist client request\n");
870 if (stat_bytes_downloaded > MAX_BYTES_PER_HOSTLISTS)
872 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
873 _("Download limit of %u bytes exceeded, stopping download\n"),
874 MAX_BYTES_PER_HOSTLISTS);
878 mret = curl_multi_perform (multi, &running);
883 msg = curl_multi_info_read (multi, &running);
884 GNUNET_break (msg != NULL);
890 if ((msg->data.result != CURLE_OK) &&
891 (msg->data.result != CURLE_GOT_NOTHING))
892 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
893 _("Download of hostlist from `%s' failed: `%s'\n"),
895 curl_easy_strerror (msg->data.result));
898 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
899 _("Download of hostlist `%s' completed.\n"),
901 stat_download_successful = GNUNET_YES;
903 if (GNUNET_YES == stat_testing_hostlist)
905 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
907 ("Adding successfully tested hostlist `%s' datastore.\n"),
910 hostlist_to_test = NULL;
911 stat_testing_hostlist = GNUNET_NO;
921 while ((running > 0));
924 while (mret == CURLM_CALL_MULTI_PERFORM);
926 if (mret != CURLM_OK)
928 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("%s failed at %s:%d: `%s'\n"),
929 "curl_multi_perform", __FILE__, __LINE__,
930 curl_multi_strerror (mret));
938 * Main function that will download a hostlist and process its
948 current_url = download_get_url ();
949 if (current_url == NULL)
951 curl = curl_easy_init ();
959 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
960 _("Bootstrapping using hostlist at `%s'.\n"), current_url);
962 stat_download_in_progress = GNUNET_YES;
963 stat_download_successful = GNUNET_NO;
964 stat_hellos_obtained = 0;
965 stat_bytes_downloaded = 0;
967 GNUNET_STATISTICS_update (stats,
968 gettext_noop ("# hostlist downloads initiated"), 1,
972 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
973 CURL_EASY_SETOPT (curl, CURLOPT_PROXYTYPE, proxy_type);
974 if (NULL != proxy_username)
975 CURL_EASY_SETOPT (curl, CURLOPT_PROXYUSERNAME, proxy_username);
976 if (NULL != proxy_password)
977 CURL_EASY_SETOPT (curl, CURLOPT_PROXYPASSWORD, proxy_password);
981 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &callback_download);
987 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, NULL);
993 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
994 CURL_EASY_SETOPT (curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
995 CURL_EASY_SETOPT (curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
996 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
997 /* no need to abort if the above failed */
998 CURL_EASY_SETOPT (curl, CURLOPT_URL, current_url);
1004 CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
1006 CURL_EASY_SETOPT (curl, CURLOPT_VERBOSE, 1);
1008 CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, GNUNET_MAX_MESSAGE_SIZE);
1009 if (0 == strncmp (current_url, "http", 4))
1010 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
1011 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 60L);
1012 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 60L);
1013 multi = curl_multi_init ();
1020 mret = curl_multi_add_handle (multi, curl);
1021 if (mret != CURLM_OK)
1023 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
1024 "curl_multi_add_handle", __FILE__, __LINE__,
1025 curl_multi_strerror (mret));
1026 mret = curl_multi_cleanup (multi);
1027 if (mret != CURLM_OK)
1028 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
1029 "curl_multi_cleanup", __FILE__, __LINE__,
1030 curl_multi_strerror (mret));
1035 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
1036 download_prepare ();
1041 task_download_dispatcher (void *cls)
1043 ti_download_dispatcher_task = NULL;
1044 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download is initiated...\n");
1045 if (GNUNET_NO == stat_download_in_progress)
1047 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download can start immediately...\n");
1048 download_hostlist ();
1052 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1053 "Download in progess, have to wait...\n");
1054 ti_download_dispatcher_task =
1055 GNUNET_SCHEDULER_add_delayed (WAITING_INTERVAL,
1056 &task_download_dispatcher, NULL);
1062 * Task that checks if we should try to download a hostlist.
1063 * If so, we initiate the download, otherwise we schedule
1064 * this task again for a later time.
1067 task_check (void *cls)
1070 struct GNUNET_TIME_Relative delay;
1072 ti_check_download = NULL;
1075 curl_global_cleanup ();
1076 return; /* in shutdown */
1078 if ( (stat_connection_count < MIN_CONNECTIONS) &&
1079 (NULL == ti_download_dispatcher_task) )
1080 ti_download_dispatcher_task =
1081 GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1083 delay = hostlist_delay;
1084 if (0 == hostlist_delay.rel_value_us)
1085 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1087 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1088 if (hostlist_delay.rel_value_us >
1089 GNUNET_TIME_UNIT_HOURS.rel_value_us * (1 + stat_connection_count))
1091 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1092 (1 + stat_connection_count));
1093 GNUNET_STATISTICS_set (stats,
1095 ("# milliseconds between hostlist downloads"),
1096 hostlist_delay.rel_value_us / 1000LL,
1100 delay = GNUNET_TIME_UNIT_ZERO;
1103 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1104 _("Have %u/%u connections. Will consider downloading hostlist in %s\n"),
1105 stat_connection_count, MIN_CONNECTIONS,
1106 GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES));
1107 ti_check_download = GNUNET_SCHEDULER_add_delayed (delay,
1113 * This tasks sets hostlist testing to allowed after intervall between to testings is reached
1115 * @param cls closure
1118 task_testing_intervall_reset (void *cls)
1120 ti_testing_intervall_task = NULL;
1121 stat_testing_allowed = GNUNET_OK;
1122 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1123 "Testing new hostlist advertisements is allowed again\n");
1128 * Task that writes hostlist entries to a file on a regular base
1130 * @param cls closure
1133 task_hostlist_saving (void *cls)
1135 ti_saving_task = NULL;
1136 save_hostlist_file (GNUNET_NO);
1138 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1139 "Hostlists will be saved to file again in %s\n",
1140 GNUNET_STRINGS_relative_time_to_string(SAVING_INTERVAL, GNUNET_YES));
1142 GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL,
1143 &task_hostlist_saving,
1149 * Method called whenever a given peer connects.
1151 * @param cls closure
1152 * @param peer peer identity this notification is about
1153 * @param mq message queue for transmissions to @a peer
1156 handler_connect (void *cls,
1157 const struct GNUNET_PeerIdentity *peer,
1158 struct GNUNET_MQ_Handle *mq)
1160 GNUNET_assert (stat_connection_count < UINT_MAX);
1161 stat_connection_count++;
1162 GNUNET_STATISTICS_update (stats,
1163 gettext_noop ("# active connections"),
1171 * Method called whenever a given peer disconnects.
1173 * @param cls closure
1174 * @param peer peer identity this notification is about
1177 handler_disconnect (void *cls,
1178 const struct GNUNET_PeerIdentity *peer,
1181 GNUNET_assert (stat_connection_count > 0);
1182 stat_connection_count--;
1183 GNUNET_STATISTICS_update (stats,
1184 gettext_noop ("# active connections"),
1191 * Method called whenever an advertisement message arrives.
1193 * @param uri the advertised URI
1196 handler_advertisement (const char *uri)
1199 struct Hostlist *hostlist;
1201 uri_size = strlen (uri) + 1;
1202 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1203 "Hostlist client recieved advertisement containing URI `%s'\n",
1205 if (GNUNET_NO != linked_list_contains (uri))
1207 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1208 "URI `%s' is already known\n",
1213 if (GNUNET_NO == stat_testing_allowed)
1215 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1216 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1219 if (GNUNET_YES == stat_testing_hostlist)
1221 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1222 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1226 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1227 hostlist->hostlist_uri = (const char *) &hostlist[1];
1228 GNUNET_memcpy (&hostlist[1],
1231 hostlist->time_creation = GNUNET_TIME_absolute_get ();
1232 hostlist->quality = HOSTLIST_INITIAL;
1233 hostlist_to_test = hostlist;
1235 stat_testing_hostlist = GNUNET_YES;
1236 stat_testing_allowed = GNUNET_NO;
1237 ti_testing_intervall_task =
1238 GNUNET_SCHEDULER_add_delayed (TESTING_INTERVAL,
1239 &task_testing_intervall_reset,
1242 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1243 "Testing new hostlist advertisements is locked for the next %s\n",
1244 GNUNET_STRINGS_relative_time_to_string (TESTING_INTERVAL,
1247 ti_download_dispatcher_task =
1248 GNUNET_SCHEDULER_add_now (&task_download_dispatcher,
1254 * Continuation called by the statistics code once
1255 * we go the stat. Initiates hostlist download scheduling.
1257 * @param cls closure
1258 * @param success #GNUNET_OK if statistics were
1259 * successfully obtained, #GNUNET_SYSERR if not.
1262 primary_task (void *cls,
1265 if (NULL != ti_check_download)
1267 GNUNET_SCHEDULER_cancel (ti_check_download);
1268 ti_check_download = NULL;
1271 GNUNET_assert (NULL != stats);
1272 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1273 "Statistics request done, scheduling hostlist download\n");
1274 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1279 * Continuation called by the statistics code once
1280 * we go the stat. Initiates hostlist download scheduling.
1282 * @param cls closure
1283 * @param success #GNUNET_OK if statistics were
1284 * successfully obtained, #GNUNET_SYSERR if not.
1287 stat_timeout_task (void *cls)
1289 GNUNET_STATISTICS_get_cancel (sget);
1291 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check,
1297 * We've received the previous delay value from statistics. Remember it.
1299 * @param cls NULL, unused
1300 * @param subsystem should be "hostlist", unused
1301 * @param name will be "milliseconds between hostlist downloads", unused
1302 * @param value previous delay value, in milliseconds (!)
1303 * @param is_persistent unused, will be #GNUNET_YES
1306 process_stat (void *cls,
1307 const char *subsystem,
1312 hostlist_delay.rel_value_us = value * 1000LL;
1313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1314 "Initial time between hostlist downloads is %s\n",
1315 GNUNET_STRINGS_relative_time_to_string (hostlist_delay,
1322 * Method to load persistent hostlist file during hostlist client startup
1325 load_hostlist_file ()
1330 struct Hostlist *hostlist;
1331 uint32_t times_used;
1332 uint32_t hellos_returned;
1337 struct GNUNET_BIO_ReadHandle *rh;
1341 GNUNET_CONFIGURATION_get_value_filename (cfg, "HOSTLIST", "HOSTLISTFILE",
1344 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1345 "hostlist", "HOSTLISTFILE");
1349 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1350 _("Loading saved hostlist entries from file `%s' \n"),
1352 if (GNUNET_NO == GNUNET_DISK_file_test (filename))
1354 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1355 _("Hostlist file `%s' does not exist\n"), filename);
1356 GNUNET_free (filename);
1360 rh = GNUNET_BIO_read_open (filename);
1363 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1364 _("Could not open file `%s' for reading to load hostlists: %s\n"),
1365 filename, STRERROR (errno));
1366 GNUNET_free (filename);
1371 while ((GNUNET_OK == GNUNET_BIO_read_string (rh, "url", &uri, MAX_URL_LEN)) &&
1372 (NULL != uri) && (GNUNET_OK == GNUNET_BIO_read_int32 (rh, ×_used))
1373 && (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1374 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1375 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1376 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)))
1378 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1379 hostlist->hello_count = hellos_returned;
1380 hostlist->hostlist_uri = (const char *) &hostlist[1];
1381 GNUNET_memcpy (&hostlist[1], uri, strlen (uri) + 1);
1382 hostlist->quality = quality;
1383 hostlist->time_creation.abs_value_us = created;
1384 hostlist->time_last_usage.abs_value_us = last_used;
1385 GNUNET_CONTAINER_DLL_insert (linked_list_head, linked_list_tail, hostlist);
1387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1388 "Added hostlist entry eith URI `%s' \n",
1389 hostlist->hostlist_uri);
1393 if (counter >= MAX_NUMBER_HOSTLISTS)
1397 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1398 _("%u hostlist URIs loaded from file\n"),
1400 GNUNET_STATISTICS_set (stats,
1401 gettext_noop ("# hostlist URIs read from file"),
1402 counter, GNUNET_YES);
1403 GNUNET_STATISTICS_set (stats,
1404 gettext_noop ("# advertised hostlist URIs"),
1405 linked_list_size, GNUNET_NO);
1407 GNUNET_free_non_null (uri);
1409 (void) GNUNET_BIO_read_close (rh, &emsg);
1412 GNUNET_free (filename);
1417 * 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
1422 save_hostlist_file (int shutdown)
1425 struct Hostlist *pos;
1426 struct GNUNET_BIO_WriteHandle *wh;
1431 GNUNET_CONFIGURATION_get_value_filename (cfg, "HOSTLIST", "HOSTLISTFILE",
1434 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1435 "hostlist", "HOSTLISTFILE");
1438 if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
1440 GNUNET_free (filename);
1443 wh = GNUNET_BIO_write_open (filename);
1446 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1447 _("Could not open file `%s' for writing to save hostlists: %s\n"),
1450 GNUNET_free (filename);
1453 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1454 _("Writing %u hostlist URIs to `%s'\n"),
1455 linked_list_size, filename);
1456 /* add code to write hostlists to file using bio */
1459 while (NULL != (pos = linked_list_head))
1461 if (GNUNET_YES == shutdown)
1463 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1466 if (GNUNET_YES == ok)
1468 if ((GNUNET_OK != GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1469 (GNUNET_OK != GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1470 (GNUNET_OK != GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1472 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.abs_value_us)) ||
1474 GNUNET_BIO_write_int64 (wh, pos->time_creation.abs_value_us)) ||
1475 (GNUNET_OK != GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1477 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1478 _("Error writing hostlist URIs to file `%s'\n"), filename);
1483 if (GNUNET_YES == shutdown)
1486 if (counter >= MAX_NUMBER_HOSTLISTS)
1489 GNUNET_STATISTICS_set (stats,
1490 gettext_noop ("# hostlist URIs written to file"),
1491 counter, GNUNET_YES);
1493 if (GNUNET_OK != GNUNET_BIO_write_close (wh))
1494 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1495 _("Error writing hostlist URIs to file `%s'\n"), filename);
1496 GNUNET_free (filename);
1501 * Start downloading hostlists from hostlist servers as necessary.
1503 * @param c configuration to use
1504 * @param st statistics handle to use
1505 * @param[out] ch set to handler for CORE connect events
1506 * @param[out] dh set to handler for CORE disconnect events
1507 * @param[out] msgh set to handler for CORE advertisement messages
1508 * @param learn should we learn hostlist URLs from CORE
1509 * @return #GNUNET_OK on success
1512 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1513 struct GNUNET_STATISTICS_Handle *st,
1514 GNUNET_CORE_ConnectEventHandler *ch,
1515 GNUNET_CORE_DisconnectEventHandler *dh,
1516 GNUNET_HOSTLIST_UriHandler *msgh,
1520 char *proxytype_str;
1523 GNUNET_assert (NULL != st);
1524 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1527 return GNUNET_SYSERR;
1532 /* Read proxy configuration */
1533 pi = GNUNET_PEERINFO_connect (c);
1535 GNUNET_CONFIGURATION_get_value_string (cfg,
1540 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1541 "Found proxy host: `%s'\n",
1543 /* proxy username */
1545 GNUNET_CONFIGURATION_get_value_string (cfg,
1550 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1551 "Found proxy username name: `%s'\n",
1555 /* proxy password */
1557 GNUNET_CONFIGURATION_get_value_string (cfg,
1562 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1563 "Found proxy password name: `%s'\n",
1569 GNUNET_CONFIGURATION_get_value_string (cfg,
1574 GNUNET_STRINGS_utf8_toupper (proxytype_str,
1576 proxy_type = CURLPROXY_HTTP;
1577 if (0 == strcmp (proxytype_str, "HTTP"))
1578 proxy_type = CURLPROXY_HTTP;
1579 else if (0 == strcmp (proxytype_str, "HTTP_1_0"))
1580 proxy_type = CURLPROXY_HTTP_1_0;
1581 else if (0 == strcmp (proxytype_str, "SOCKS4"))
1582 proxy_type = CURLPROXY_SOCKS4;
1583 else if (0 == strcmp (proxytype_str, "SOCKS5"))
1584 proxy_type = CURLPROXY_SOCKS5;
1585 else if (0 == strcmp (proxytype_str, "SOCKS4A"))
1586 proxy_type = CURLPROXY_SOCKS4A;
1587 else if (0 == strcmp (proxytype_str, "SOCKS5_HOSTNAME"))
1588 proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
1591 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1592 _("Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
1594 GNUNET_free (proxytype_str);
1595 GNUNET_free (proxy);
1597 GNUNET_free_non_null (proxy_username);
1598 proxy_username = NULL;
1599 GNUNET_free_non_null (proxy_password);
1600 proxy_password = NULL;
1602 return GNUNET_SYSERR;
1605 GNUNET_free_non_null (proxytype_str);
1608 stat_learning = learn;
1609 *ch = &handler_connect;
1610 *dh = &handler_disconnect;
1611 linked_list_head = NULL;
1612 linked_list_tail = NULL;
1613 stat_use_bootstrap = GNUNET_YES;
1614 stat_testing_hostlist = GNUNET_NO;
1615 stat_testing_allowed = GNUNET_YES;
1617 if (GNUNET_YES == stat_learning)
1619 *msgh = &handler_advertisement;
1620 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1621 _("Learning is enabled on this peer\n"));
1622 load_hostlist_file ();
1623 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1624 "Hostlists will be saved to file again in %s\n",
1625 GNUNET_STRINGS_relative_time_to_string (SAVING_INTERVAL,
1628 GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL,
1629 &task_hostlist_saving,
1634 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1635 _("Learning is not enabled on this peer\n"));
1638 GNUNET_CONFIGURATION_get_value_filename (cfg,
1643 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
1645 result = remove (filename);
1647 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1648 _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),
1651 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1656 GNUNET_free (filename);
1658 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1659 "Loading stats value on hostlist download frequency\n");
1660 sget = GNUNET_STATISTICS_get (stats, "hostlist",
1662 ("# milliseconds between hostlist downloads"),
1668 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1669 "Statistics request failed, scheduling hostlist download\n");
1670 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check,
1676 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
1685 * Stop downloading hostlists from hostlist servers as necessary.
1688 GNUNET_HOSTLIST_client_stop ()
1690 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1691 "Hostlist client shutdown\n");
1694 GNUNET_STATISTICS_get_cancel (sget);
1698 if (GNUNET_YES == stat_learning)
1699 save_hostlist_file (GNUNET_YES);
1700 if (NULL != ti_saving_task)
1702 GNUNET_SCHEDULER_cancel (ti_saving_task);
1703 ti_saving_task = NULL;
1705 if (NULL != ti_download_dispatcher_task)
1707 GNUNET_SCHEDULER_cancel (ti_download_dispatcher_task);
1708 ti_download_dispatcher_task = NULL;
1710 if (NULL != ti_testing_intervall_task)
1712 GNUNET_SCHEDULER_cancel (ti_testing_intervall_task);
1713 ti_testing_intervall_task = NULL;
1715 if (NULL != ti_download)
1717 GNUNET_SCHEDULER_cancel (ti_download);
1722 if (NULL != ti_check_download)
1724 GNUNET_SCHEDULER_cancel (ti_check_download);
1725 ti_check_download = NULL;
1726 curl_global_cleanup ();
1728 GNUNET_free_non_null (proxy);
1730 GNUNET_free_non_null (proxy_username);
1731 proxy_username = NULL;
1732 GNUNET_free_non_null (proxy_password);
1733 proxy_password = NULL;
1736 GNUNET_PEERINFO_disconnect (pi);
1742 /* end of gnunet-daemon-hostlist_client.c */