2 This file is part of GNUnet.
3 Copyright (C) 2001-2010, 2014 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
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-daemon-hostlist.h"
33 #include <curl/curl.h>
34 #elif HAVE_GNURL_CURL_H
35 #include <gnurl/curl.h>
41 * Number of connections that we must have to NOT download
44 #define MIN_CONNECTIONS 4
47 * Maximum number of hostlist that are saved
49 #define MAX_NUMBER_HOSTLISTS 30
52 * Time interval hostlists are saved to disk
54 #define SAVING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
57 * Time interval between two hostlist tests
59 #define TESTING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
62 * Time interval for download dispatcher before a download is re-scheduled
64 #define WAITING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
67 * Defines concerning the hostlist quality metric
71 * Initial quality of a new created hostlist
73 #define HOSTLIST_INITIAL 10000
76 * Value subtracted each time a hostlist download fails
78 #define HOSTLIST_FAILED_DOWNLOAD 100
81 * Value added each time a hostlist download is successful
83 #define HOSTLIST_SUCCESSFUL_DOWNLOAD 100
86 * Value added for each valid HELLO recived during a hostlist download
88 #define HOSTLIST_SUCCESSFUL_HELLO 1
93 * A single hostlist obtained by hostlist advertisements
98 * previous entry, used to manage entries in a double linked list
100 struct Hostlist *prev;
103 * next entry, used to manage entries in a double linked list
105 struct Hostlist *next;
108 * URI where hostlist can be obtained
110 const char *hostlist_uri;
113 * Value describing the quality of the hostlist, the bigger the better but (should) never < 0
114 * used for deciding which hostlist is replaced if MAX_NUMBER_HOSTLISTS in data structure is reached
115 * intial value = HOSTLIST_INITIAL
116 * increased every successful download by HOSTLIST_SUCCESSFULL_DOWNLOAD
117 * increased every successful download by number of obtained HELLO messages
118 * decreased every failed download by HOSTLIST_SUCCESSFULL_DOWNLOAD
123 * Time the hostlist advertisement was recieved and the entry was created
125 struct GNUNET_TIME_Absolute time_creation;
128 * Last time the hostlist was obtained
130 struct GNUNET_TIME_Absolute time_last_usage;
133 * Number of HELLO messages obtained during last download
135 uint32_t hello_count;
138 * Number of times the hostlist was successfully obtained
148 static const struct GNUNET_CONFIGURATION_Handle *cfg;
153 static struct GNUNET_STATISTICS_Handle *stats;
158 static struct GNUNET_TRANSPORT_Handle *transport;
161 * Proxy hostname or ip we are using (can be NULL).
166 * Proxy username we are using (can be NULL).
168 static char *proxy_username;
171 * Proxy password we are using (can be NULL).
173 static char *proxy_password;
176 * Proxy type we are using (can be NULL).
178 static curl_proxytype proxy_type;
181 * Number of bytes valid in 'download_buffer'.
183 static size_t download_pos;
186 * Current URL that we are using.
188 static char *current_url;
191 * Current CURL handle.
196 * Current multi-CURL handle.
201 * How many bytes did we download from the current hostlist URL?
203 static uint32_t stat_bytes_downloaded;
206 * Amount of time we wait between hostlist downloads.
208 static struct GNUNET_TIME_Relative hostlist_delay;
211 * ID of the task, checking if hostlist download should take plate
213 static struct GNUNET_SCHEDULER_Task * ti_check_download;
216 * ID of the task downloading the hostlist
218 static struct GNUNET_SCHEDULER_Task * ti_download;
221 * ID of the task saving the hostlsit in a regular intervall
223 static struct GNUNET_SCHEDULER_Task * ti_saving_task;
226 * ID of the task called to initiate a download
228 static struct GNUNET_SCHEDULER_Task * ti_download_dispatcher_task;
231 * ID of the task controlling the locking between two hostlist tests
233 static struct GNUNET_SCHEDULER_Task * ti_testing_intervall_task;
236 * At what time MUST the current hostlist request be done?
238 static struct GNUNET_TIME_Absolute end_time;
241 * Head of the linked list used to store hostlists
243 static struct Hostlist *linked_list_head;
246 * Tail of the linked list used to store hostlists
248 static struct Hostlist *linked_list_tail;
251 * Current hostlist used for downloading
253 static struct Hostlist *current_hostlist;
256 * Size of the linke list used to store hostlists
258 static unsigned int linked_list_size;
261 * Head of the linked list used to store hostlists
263 static struct Hostlist *hostlist_to_test;
266 * Handle for our statistics GET operation.
268 static struct GNUNET_STATISTICS_GetHandle *sget;
271 * Set to GNUNET_YES if the current URL had some problems.
273 static int stat_bogus_url;
276 * Value controlling if a hostlist is tested at the moment
278 static int stat_testing_hostlist;
281 * Value controlling if a hostlist testing is allowed at the moment
283 static int stat_testing_allowed;
286 * Value controlling if a hostlist download is running at the moment
288 static int stat_download_in_progress;
291 * Value saying if a preconfigured bootstrap server is used
293 static unsigned int stat_use_bootstrap;
296 * Set if we are allowed to learn new hostlists and use them
298 static int stat_learning;
301 * Value saying if hostlist download was successful
303 static unsigned int stat_download_successful;
306 * Value saying how many valid HELLO messages were obtained during download
308 static unsigned int stat_hellos_obtained;
311 * Number of active connections (according to core service).
313 static unsigned int stat_connection_count;
317 * Process downloaded bits by calling callback on each HELLO.
319 * @param ptr buffer with downloaded data
320 * @param size size of a record
321 * @param nmemb number of records downloaded
323 * @return number of bytes that were processed (always size*nmemb)
326 callback_download (void *ptr,
331 static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
332 const char *cbuf = ptr;
333 const struct GNUNET_MessageHeader *msg;
339 total = size * nmemb;
340 stat_bytes_downloaded += total;
341 if ((total == 0) || (stat_bogus_url))
343 return total; /* ok, no data or bogus data */
346 GNUNET_STATISTICS_update (stats,
348 ("# bytes downloaded from hostlist servers"),
349 (int64_t) total, GNUNET_NO);
351 while ((left > 0) || (download_pos > 0))
353 cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - 1 - download_pos);
354 memcpy (&download_buffer[download_pos], cbuf, cpy);
358 if (download_pos < sizeof (struct GNUNET_MessageHeader))
360 GNUNET_assert (0 == left);
363 msg = (const struct GNUNET_MessageHeader *) download_buffer;
364 msize = ntohs (msg->size);
365 if (msize < sizeof (struct GNUNET_MessageHeader))
367 GNUNET_STATISTICS_update (stats,
369 ("# invalid HELLOs downloaded from hostlist servers"),
371 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
372 _("Invalid `%s' message received from hostlist at `%s'\n"),
373 "HELLO", current_url);
374 stat_hellos_obtained++;
378 if (download_pos < msize)
380 GNUNET_assert (left == 0);
383 if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message *) msg) == msize)
385 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
386 "Received valid `%s' message from hostlist server.\n",
388 GNUNET_STATISTICS_update (stats,
390 ("# valid HELLOs downloaded from hostlist servers"),
392 stat_hellos_obtained++;
393 GNUNET_TRANSPORT_offer_hello (transport, msg, NULL, NULL);
397 GNUNET_STATISTICS_update (stats,
399 ("# invalid HELLOs downloaded from hostlist servers"),
401 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
402 _("Invalid `%s' message received from hostlist at `%s'\n"),
403 "HELLO", current_url);
404 stat_bogus_url = GNUNET_YES;
405 stat_hellos_obtained++;
408 memmove (download_buffer, &download_buffer[msize], download_pos - msize);
409 download_pos -= msize;
416 * Obtain a hostlist URL that we should use.
418 * @return NULL if there is no URL available
421 get_bootstrap_server ()
429 GNUNET_CONFIGURATION_get_value_string (cfg, "HOSTLIST", "SERVERS",
432 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
433 "hostlist", "SERVERS");
438 if (strlen (servers) > 0)
441 pos = strlen (servers) - 1;
444 if (servers[pos] == ' ')
451 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
452 "hostlist", "SERVERS");
453 GNUNET_free (servers);
457 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
458 pos = strlen (servers) - 1;
461 if (servers[pos] == ' ')
473 ret = GNUNET_strdup (&servers[pos]);
474 GNUNET_free (servers);
480 * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
481 * @return uri to use, NULL if there is no URL available
487 unsigned int counter;
488 struct Hostlist *pos;
490 if (GNUNET_NO == stat_learning)
492 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
493 "Using preconfigured bootstrap server\n");
494 current_hostlist = NULL;
495 return get_bootstrap_server ();
498 if ((GNUNET_YES == stat_testing_hostlist) && (NULL != hostlist_to_test))
500 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
501 "Testing new advertised hostlist if it is obtainable\n");
502 current_hostlist = hostlist_to_test;
503 return GNUNET_strdup (hostlist_to_test->hostlist_uri);
506 if ((GNUNET_YES == stat_use_bootstrap) || (linked_list_size == 0))
508 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
509 "Using preconfigured bootstrap server\n");
510 current_hostlist = NULL;
511 return get_bootstrap_server ();
514 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
516 pos = linked_list_head;
517 while (counter < index)
522 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Using learned hostlist `%s'\n",
524 current_hostlist = pos;
525 return GNUNET_strdup (pos->hostlist_uri);
529 #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)
533 * Method to save hostlist to a file during hostlist client shutdown
535 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
538 save_hostlist_file (int shutdown);
542 * Add val2 to val1 with overflow check
544 * @param val1 value 1
545 * @param val2 value 2
549 checked_add (uint64_t val1,
552 static uint64_t temp;
553 static uint64_t maxv;
566 * Subtract val2 from val1 with underflow check
568 * @param val1 value 1
569 * @param val2 value 2
573 checked_sub (uint64_t val1,
578 return (val1 - val2);
583 * Method to check if a URI is in hostlist linked list
585 * @param uri uri to check
586 * @return #GNUNET_YES if existing in linked list, #GNUNET_NO if not
589 linked_list_contains (const char *uri)
591 struct Hostlist *pos;
593 pos = linked_list_head;
596 if (0 == strcmp (pos->hostlist_uri, uri))
605 * Method returning the hostlist element with the lowest quality in the datastore
606 * @return hostlist with lowest quality
608 static struct Hostlist *
609 linked_list_get_lowest_quality ()
611 struct Hostlist *pos;
612 struct Hostlist *lowest;
614 if (linked_list_size == 0)
616 lowest = linked_list_head;
617 pos = linked_list_head->next;
620 if (pos->quality < lowest->quality)
629 * Method to insert a hostlist into the datastore. If datastore
630 * contains maximum number of elements, the elements with lowest
631 * quality is dismissed
636 struct Hostlist *lowest_quality;
638 if (MAX_NUMBER_HOSTLISTS <= linked_list_size)
640 /* No free entries available, replace existing entry */
641 lowest_quality = linked_list_get_lowest_quality ();
642 GNUNET_assert (lowest_quality != NULL);
643 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
644 "Removing hostlist with URI `%s' which has the worst quality of all (%llu)\n",
645 lowest_quality->hostlist_uri,
646 (unsigned long long) lowest_quality->quality);
647 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail,
650 GNUNET_free (lowest_quality);
652 GNUNET_CONTAINER_DLL_insert (linked_list_head, linked_list_tail,
655 GNUNET_STATISTICS_set (stats, gettext_noop ("# advertised hostlist URIs"),
656 linked_list_size, GNUNET_NO);
657 stat_testing_hostlist = GNUNET_NO;
662 * Method updating hostlist statistics
669 if (((stat_use_bootstrap == GNUNET_NO) && (NULL != current_hostlist)) ||
670 ((stat_testing_hostlist == GNUNET_YES) && (NULL != current_hostlist)))
672 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
673 "Updating hostlist statics for URI `%s'\n",
674 current_hostlist->hostlist_uri);
675 current_hostlist->hello_count = stat_hellos_obtained;
676 current_hostlist->time_last_usage = GNUNET_TIME_absolute_get ();
677 current_hostlist->quality =
678 checked_add (current_hostlist->quality,
679 (stat_hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
680 if (GNUNET_YES == stat_download_successful)
682 current_hostlist->times_used++;
683 current_hostlist->quality =
684 checked_add (current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
685 GNUNET_asprintf (&stat, gettext_noop ("# advertised URI `%s' downloaded"),
686 current_hostlist->hostlist_uri);
688 GNUNET_STATISTICS_update (stats, stat, 1, GNUNET_YES);
692 current_hostlist->quality =
693 checked_sub (current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD);
695 current_hostlist = NULL;
696 /* Alternating the usage of preconfigured and learned hostlists */
698 if (stat_testing_hostlist == GNUNET_YES)
701 if (GNUNET_YES == stat_learning)
703 if (stat_use_bootstrap == GNUNET_YES)
704 stat_use_bootstrap = GNUNET_NO;
706 stat_use_bootstrap = GNUNET_YES;
709 stat_use_bootstrap = GNUNET_YES;
714 * Clean up the state from the task that downloaded the
715 * hostlist and schedule the next task.
722 if ((stat_testing_hostlist == GNUNET_YES) &&
723 (GNUNET_NO == stat_download_successful) && (NULL != hostlist_to_test))
725 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
727 ("Advertised hostlist with URI `%s' could not be downloaded. Advertised URI gets dismissed.\n"),
728 hostlist_to_test->hostlist_uri);
731 if (stat_testing_hostlist == GNUNET_YES)
733 stat_testing_hostlist = GNUNET_NO;
735 if (NULL != hostlist_to_test)
737 GNUNET_free (hostlist_to_test);
738 hostlist_to_test = NULL;
743 mret = curl_multi_remove_handle (multi, curl);
744 if (mret != CURLM_OK)
746 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
747 "curl_multi_remove_handle", __FILE__, __LINE__,
748 curl_multi_strerror (mret));
750 mret = curl_multi_cleanup (multi);
751 if (mret != CURLM_OK)
752 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
753 "curl_multi_cleanup", __FILE__, __LINE__,
754 curl_multi_strerror (mret));
759 curl_easy_cleanup (curl);
762 GNUNET_free_non_null (current_url);
764 stat_bytes_downloaded = 0;
765 stat_download_in_progress = GNUNET_NO;
770 * Task that is run when we are ready to receive more data from the hostlist
773 * @param cls closure, unused
774 * @param tc task context, unused
777 task_download (void *cls);
781 * Ask CURL for the select set and then schedule the
782 * receiving task with the scheduler.
792 struct GNUNET_NETWORK_FDSet *grs;
793 struct GNUNET_NETWORK_FDSet *gws;
795 struct GNUNET_TIME_Relative rtime;
801 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
802 if (mret != CURLM_OK)
804 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
805 "curl_multi_fdset", __FILE__, __LINE__,
806 curl_multi_strerror (mret));
810 mret = curl_multi_timeout (multi, &timeout);
811 if (mret != CURLM_OK)
813 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
814 "curl_multi_timeout", __FILE__, __LINE__,
815 curl_multi_strerror (mret));
820 GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
821 GNUNET_TIME_relative_multiply
822 (GNUNET_TIME_UNIT_MILLISECONDS, timeout));
823 grs = GNUNET_NETWORK_fdset_create ();
824 gws = GNUNET_NETWORK_fdset_create ();
825 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
826 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
827 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
828 "Scheduling task for hostlist download using cURL\n");
830 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
832 &task_download, multi);
833 GNUNET_NETWORK_fdset_destroy (gws);
834 GNUNET_NETWORK_fdset_destroy (grs);
839 * Task that is run when we are ready to receive more data from the hostlist
842 * @param cls closure, unused
845 task_download (void *cls)
850 const struct GNUNET_SCHEDULER_TaskContext *tc;
853 tc = GNUNET_SCHEDULER_get_task_context ();
854 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
856 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
857 "Shutdown requested while trying to download hostlist from `%s'\n",
863 if (0 == GNUNET_TIME_absolute_get_remaining (end_time).rel_value_us)
865 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
866 _("Timeout trying to download hostlist from `%s'\n"),
872 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
873 "Ready for processing hostlist client request\n");
877 if (stat_bytes_downloaded > MAX_BYTES_PER_HOSTLISTS)
879 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
880 _("Download limit of %u bytes exceeded, stopping download\n"),
881 MAX_BYTES_PER_HOSTLISTS);
885 mret = curl_multi_perform (multi, &running);
890 msg = curl_multi_info_read (multi, &running);
891 GNUNET_break (msg != NULL);
897 if ((msg->data.result != CURLE_OK) &&
898 (msg->data.result != CURLE_GOT_NOTHING))
899 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
900 _("Download of hostlist from `%s' failed: `%s'\n"),
902 curl_easy_strerror (msg->data.result));
905 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
906 _("Download of hostlist `%s' completed.\n"),
908 stat_download_successful = GNUNET_YES;
910 if (GNUNET_YES == stat_testing_hostlist)
912 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
914 ("Adding successfully tested hostlist `%s' datastore.\n"),
917 hostlist_to_test = NULL;
918 stat_testing_hostlist = GNUNET_NO;
928 while ((running > 0));
931 while (mret == CURLM_CALL_MULTI_PERFORM);
933 if (mret != CURLM_OK)
935 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("%s failed at %s:%d: `%s'\n"),
936 "curl_multi_perform", __FILE__, __LINE__,
937 curl_multi_strerror (mret));
945 * Main function that will download a hostlist and process its
955 current_url = download_get_url ();
956 if (current_url == NULL)
958 curl = curl_easy_init ();
966 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
967 _("Bootstrapping using hostlist at `%s'.\n"), current_url);
969 stat_download_in_progress = GNUNET_YES;
970 stat_download_successful = GNUNET_NO;
971 stat_hellos_obtained = 0;
972 stat_bytes_downloaded = 0;
974 GNUNET_STATISTICS_update (stats,
975 gettext_noop ("# hostlist downloads initiated"), 1,
979 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
980 CURL_EASY_SETOPT (curl, CURLOPT_PROXYTYPE, proxy_type);
981 if (NULL != proxy_username)
982 CURL_EASY_SETOPT (curl, CURLOPT_PROXYUSERNAME, proxy_username);
983 if (NULL != proxy_password)
984 CURL_EASY_SETOPT (curl, CURLOPT_PROXYPASSWORD, proxy_password);
988 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &callback_download);
994 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, NULL);
1000 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
1001 CURL_EASY_SETOPT (curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
1002 CURL_EASY_SETOPT (curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
1003 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
1004 /* no need to abort if the above failed */
1005 CURL_EASY_SETOPT (curl, CURLOPT_URL, current_url);
1006 if (ret != CURLE_OK)
1011 CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
1013 CURL_EASY_SETOPT (curl, CURLOPT_VERBOSE, 1);
1015 CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE);
1016 if (0 == strncmp (current_url, "http", 4))
1017 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
1018 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 60L);
1019 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 60L);
1020 multi = curl_multi_init ();
1027 mret = curl_multi_add_handle (multi, curl);
1028 if (mret != CURLM_OK)
1030 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
1031 "curl_multi_add_handle", __FILE__, __LINE__,
1032 curl_multi_strerror (mret));
1033 mret = curl_multi_cleanup (multi);
1034 if (mret != CURLM_OK)
1035 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
1036 "curl_multi_cleanup", __FILE__, __LINE__,
1037 curl_multi_strerror (mret));
1042 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
1043 download_prepare ();
1048 task_download_dispatcher (void *cls)
1050 const struct GNUNET_SCHEDULER_TaskContext *tc;
1052 ti_download_dispatcher_task = NULL;
1053 tc = GNUNET_SCHEDULER_get_task_context ();
1054 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1056 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download is initiated...\n");
1057 if (GNUNET_NO == stat_download_in_progress)
1059 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download can start immediately...\n");
1060 download_hostlist ();
1064 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1065 "Download in progess, have to wait...\n");
1066 ti_download_dispatcher_task =
1067 GNUNET_SCHEDULER_add_delayed (WAITING_INTERVAL,
1068 &task_download_dispatcher, NULL);
1074 * Task that checks if we should try to download a hostlist.
1075 * If so, we initiate the download, otherwise we schedule
1076 * this task again for a later time.
1079 task_check (void *cls)
1082 struct GNUNET_TIME_Relative delay;
1083 const struct GNUNET_SCHEDULER_TaskContext *tc;
1085 ti_check_download = NULL;
1086 tc = GNUNET_SCHEDULER_get_task_context ();
1087 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1091 curl_global_cleanup ();
1092 return; /* in shutdown */
1094 if ( (stat_connection_count < MIN_CONNECTIONS) &&
1095 (NULL == ti_download_dispatcher_task) )
1096 ti_download_dispatcher_task =
1097 GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1099 delay = hostlist_delay;
1100 if (0 == hostlist_delay.rel_value_us)
1101 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1103 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1104 if (hostlist_delay.rel_value_us >
1105 GNUNET_TIME_UNIT_HOURS.rel_value_us * (1 + stat_connection_count))
1107 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1108 (1 + stat_connection_count));
1109 GNUNET_STATISTICS_set (stats,
1111 ("# milliseconds between hostlist downloads"),
1112 hostlist_delay.rel_value_us / 1000LL,
1116 delay = GNUNET_TIME_UNIT_ZERO;
1119 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1120 _("Have %u/%u connections. Will consider downloading hostlist in %s\n"),
1121 stat_connection_count, MIN_CONNECTIONS,
1122 GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES));
1123 ti_check_download = GNUNET_SCHEDULER_add_delayed (delay, &task_check, NULL);
1128 * This tasks sets hostlist testing to allowed after intervall between to testings is reached
1130 * @param cls closure
1133 task_testing_intervall_reset (void *cls)
1135 const struct GNUNET_SCHEDULER_TaskContext *tc;
1137 ti_testing_intervall_task = NULL;
1138 tc = GNUNET_SCHEDULER_get_task_context ();
1139 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1141 stat_testing_allowed = GNUNET_OK;
1142 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1143 "Testing new hostlist advertisements is allowed again\n");
1148 * Task that writes hostlist entries to a file on a regular base
1150 * @param cls closure
1153 task_hostlist_saving (void *cls)
1155 const struct GNUNET_SCHEDULER_TaskContext *tc;
1157 ti_saving_task = NULL;
1158 tc = GNUNET_SCHEDULER_get_task_context ();
1159 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1161 save_hostlist_file (GNUNET_NO);
1163 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1164 "Hostlists will be saved to file again in %s\n",
1165 GNUNET_STRINGS_relative_time_to_string(SAVING_INTERVAL, GNUNET_YES));
1167 GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL, &task_hostlist_saving,
1173 * Method called whenever a given peer connects.
1175 * @param cls closure
1176 * @param peer peer identity this notification is about
1179 handler_connect (void *cls, const struct GNUNET_PeerIdentity *peer)
1181 GNUNET_assert (stat_connection_count < UINT_MAX);
1182 stat_connection_count++;
1183 GNUNET_STATISTICS_update (stats, gettext_noop ("# active connections"), 1,
1189 * Method called whenever a given peer disconnects.
1191 * @param cls closure
1192 * @param peer peer identity this notification is about
1195 handler_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
1197 GNUNET_assert (stat_connection_count > 0);
1198 stat_connection_count--;
1199 GNUNET_STATISTICS_update (stats, gettext_noop ("# active connections"), -1,
1205 * Method called whenever an advertisement message arrives.
1207 * @param cls closure (always NULL)
1208 * @param peer the peer sending the message
1209 * @param message the actual message
1210 * @return #GNUNET_OK to keep the connection open,
1211 * #GNUNET_SYSERR to close it (signal serious error)
1214 handler_advertisement (void *cls, const struct GNUNET_PeerIdentity *peer,
1215 const struct GNUNET_MessageHeader *message)
1219 const struct GNUNET_MessageHeader *incoming;
1221 struct Hostlist *hostlist;
1223 GNUNET_assert (ntohs (message->type) ==
1224 GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1225 size = ntohs (message->size);
1226 if (size <= sizeof (struct GNUNET_MessageHeader))
1228 GNUNET_break_op (0);
1229 return GNUNET_SYSERR;
1231 incoming = (const struct GNUNET_MessageHeader *) message;
1232 uri = (const char *) &incoming[1];
1233 uri_size = size - sizeof (struct GNUNET_MessageHeader);
1234 if (uri[uri_size - 1] != '\0')
1236 GNUNET_break_op (0);
1237 return GNUNET_SYSERR;
1239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1240 "Hostlist client recieved advertisement from `%s' containing URI `%s'\n",
1241 GNUNET_i2s (peer), uri);
1242 if (GNUNET_NO != linked_list_contains (uri))
1244 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "URI `%s' is already known\n", uri);
1248 if (GNUNET_NO == stat_testing_allowed)
1250 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1251 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1252 return GNUNET_SYSERR;
1254 if (GNUNET_YES == stat_testing_hostlist)
1256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1257 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1258 return GNUNET_SYSERR;
1261 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1262 hostlist->hostlist_uri = (const char *) &hostlist[1];
1263 memcpy (&hostlist[1], uri, uri_size);
1264 hostlist->time_creation = GNUNET_TIME_absolute_get ();
1265 hostlist->quality = HOSTLIST_INITIAL;
1266 hostlist_to_test = hostlist;
1268 stat_testing_hostlist = GNUNET_YES;
1269 stat_testing_allowed = GNUNET_NO;
1270 ti_testing_intervall_task =
1271 GNUNET_SCHEDULER_add_delayed (TESTING_INTERVAL,
1272 &task_testing_intervall_reset, NULL);
1274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1275 "Testing new hostlist advertisements is locked for the next %s\n",
1276 GNUNET_STRINGS_relative_time_to_string (TESTING_INTERVAL,
1279 ti_download_dispatcher_task =
1280 GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1287 * Continuation called by the statistics code once
1288 * we go the stat. Initiates hostlist download scheduling.
1290 * @param cls closure
1291 * @param success #GNUNET_OK if statistics were
1292 * successfully obtained, #GNUNET_SYSERR if not.
1295 primary_task (void *cls, int success)
1298 GNUNET_assert (NULL != stats);
1299 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1300 "Statistics request done, scheduling hostlist download\n");
1301 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1306 * We've received the previous delay value from statistics. Remember it.
1308 * @param cls NULL, unused
1309 * @param subsystem should be "hostlist", unused
1310 * @param name will be "milliseconds between hostlist downloads", unused
1311 * @param value previous delay value, in milliseconds (!)
1312 * @param is_persistent unused, will be #GNUNET_YES
1315 process_stat (void *cls,
1316 const char *subsystem,
1321 hostlist_delay.rel_value_us = value * 1000LL;
1322 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1323 "Initial time between hostlist downloads is %s\n",
1324 GNUNET_STRINGS_relative_time_to_string (hostlist_delay,
1331 * Method to load persistent hostlist file during hostlist client startup
1334 load_hostlist_file ()
1339 struct Hostlist *hostlist;
1340 uint32_t times_used;
1341 uint32_t hellos_returned;
1346 struct GNUNET_BIO_ReadHandle *rh;
1350 GNUNET_CONFIGURATION_get_value_filename (cfg, "HOSTLIST", "HOSTLISTFILE",
1353 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1354 "hostlist", "HOSTLISTFILE");
1358 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1359 _("Loading saved hostlist entries from file `%s' \n"),
1361 if (GNUNET_NO == GNUNET_DISK_file_test (filename))
1363 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1364 _("Hostlist file `%s' does not exist\n"), filename);
1365 GNUNET_free (filename);
1369 rh = GNUNET_BIO_read_open (filename);
1372 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1373 _("Could not open file `%s' for reading to load hostlists: %s\n"),
1374 filename, STRERROR (errno));
1375 GNUNET_free (filename);
1380 while ((GNUNET_OK == GNUNET_BIO_read_string (rh, "url", &uri, MAX_URL_LEN)) &&
1381 (NULL != uri) && (GNUNET_OK == GNUNET_BIO_read_int32 (rh, ×_used))
1382 && (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1383 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1384 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1385 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)))
1387 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1388 hostlist->hello_count = hellos_returned;
1389 hostlist->hostlist_uri = (const char *) &hostlist[1];
1390 memcpy (&hostlist[1], uri, strlen (uri) + 1);
1391 hostlist->quality = quality;
1392 hostlist->time_creation.abs_value_us = created;
1393 hostlist->time_last_usage.abs_value_us = last_used;
1394 GNUNET_CONTAINER_DLL_insert (linked_list_head, linked_list_tail, hostlist);
1396 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1397 "Added hostlist entry eith URI `%s' \n",
1398 hostlist->hostlist_uri);
1402 if (counter >= MAX_NUMBER_HOSTLISTS)
1406 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1407 _("%u hostlist URIs loaded from file\n"),
1409 GNUNET_STATISTICS_set (stats,
1410 gettext_noop ("# hostlist URIs read from file"),
1411 counter, GNUNET_YES);
1412 GNUNET_STATISTICS_set (stats,
1413 gettext_noop ("# advertised hostlist URIs"),
1414 linked_list_size, GNUNET_NO);
1416 GNUNET_free_non_null (uri);
1418 (void) GNUNET_BIO_read_close (rh, &emsg);
1421 GNUNET_free (filename);
1426 * Method to save persistent hostlist file during hostlist client shutdown
1428 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1431 save_hostlist_file (int shutdown)
1434 struct Hostlist *pos;
1435 struct GNUNET_BIO_WriteHandle *wh;
1440 GNUNET_CONFIGURATION_get_value_filename (cfg, "HOSTLIST", "HOSTLISTFILE",
1443 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1444 "hostlist", "HOSTLISTFILE");
1447 if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
1449 GNUNET_free (filename);
1452 wh = GNUNET_BIO_write_open (filename);
1455 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1456 _("Could not open file `%s' for writing to save hostlists: %s\n"),
1459 GNUNET_free (filename);
1462 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1463 _("Writing %u hostlist URIs to `%s'\n"),
1464 linked_list_size, filename);
1465 /* add code to write hostlists to file using bio */
1468 while (NULL != (pos = linked_list_head))
1470 if (GNUNET_YES == shutdown)
1472 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1475 if (GNUNET_YES == ok)
1477 if ((GNUNET_OK != GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1478 (GNUNET_OK != GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1479 (GNUNET_OK != GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1481 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.abs_value_us)) ||
1483 GNUNET_BIO_write_int64 (wh, pos->time_creation.abs_value_us)) ||
1484 (GNUNET_OK != GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1486 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1487 _("Error writing hostlist URIs to file `%s'\n"), filename);
1492 if (GNUNET_YES == shutdown)
1495 if (counter >= MAX_NUMBER_HOSTLISTS)
1498 GNUNET_STATISTICS_set (stats,
1499 gettext_noop ("# hostlist URIs written to file"),
1500 counter, GNUNET_YES);
1502 if (GNUNET_OK != GNUNET_BIO_write_close (wh))
1503 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1504 _("Error writing hostlist URIs to file `%s'\n"), filename);
1505 GNUNET_free (filename);
1510 * Start downloading hostlists from hostlist servers as necessary.
1512 * @param c configuration to use
1513 * @param st statistics handle to use
1514 * @param[out] ch set to handler for CORE connect events
1515 * @param[out] dh set to handler for CORE disconnect events
1516 * @param[out] msgh set to handler for CORE advertisement messages
1517 * @param learn should we learn hostlist URLs from CORE
1518 * @return #GNUNET_OK on success
1521 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1522 struct GNUNET_STATISTICS_Handle *st,
1523 GNUNET_CORE_ConnectEventHandler *ch,
1524 GNUNET_CORE_DisconnectEventHandler *dh,
1525 GNUNET_CORE_MessageCallback *msgh,
1529 char *proxytype_str;
1532 GNUNET_assert (NULL != st);
1533 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1536 return GNUNET_SYSERR;
1538 transport = GNUNET_TRANSPORT_connect (c, NULL, NULL, NULL, NULL, NULL);
1539 if (NULL == transport)
1542 curl_global_cleanup ();
1543 return GNUNET_SYSERR;
1548 /* Read proxy configuration */
1550 GNUNET_CONFIGURATION_get_value_string (cfg,
1551 "HOSTLIST", "PROXY", &proxy))
1553 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1554 "Found proxy host: `%s'\n",
1556 /* proxy username */
1557 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1558 "HOSTLIST", "PROXY_USERNAME", &proxy_username))
1560 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1561 "Found proxy username name: `%s'\n",
1565 /* proxy password */
1566 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1567 "HOSTLIST", "PROXY_PASSWORD", &proxy_password))
1569 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1570 "Found proxy password name: `%s'\n",
1576 GNUNET_CONFIGURATION_get_value_string (cfg,
1581 GNUNET_STRINGS_utf8_toupper (proxytype_str,
1583 proxy_type = CURLPROXY_HTTP;
1584 if (0 == strcmp (proxytype_str, "HTTP"))
1585 proxy_type = CURLPROXY_HTTP;
1586 else if (0 == strcmp (proxytype_str, "HTTP_1_0"))
1587 proxy_type = CURLPROXY_HTTP_1_0;
1588 else if (0 == strcmp (proxytype_str, "SOCKS4"))
1589 proxy_type = CURLPROXY_SOCKS4;
1590 else if (0 == strcmp (proxytype_str, "SOCKS5"))
1591 proxy_type = CURLPROXY_SOCKS5;
1592 else if (0 == strcmp (proxytype_str, "SOCKS4A"))
1593 proxy_type = CURLPROXY_SOCKS4A;
1594 else if (0 == strcmp (proxytype_str, "SOCKS5_HOSTNAME"))
1595 proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
1598 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1599 _("Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
1601 GNUNET_free (proxytype_str);
1602 GNUNET_free (proxy);
1604 GNUNET_free_non_null (proxy_username);
1605 proxy_username = NULL;
1606 GNUNET_free_non_null (proxy_password);
1607 proxy_password = NULL;
1609 return GNUNET_SYSERR;
1612 GNUNET_free_non_null (proxytype_str);
1615 stat_learning = learn;
1616 *ch = &handler_connect;
1617 *dh = &handler_disconnect;
1618 linked_list_head = NULL;
1619 linked_list_tail = NULL;
1620 stat_use_bootstrap = GNUNET_YES;
1621 stat_testing_hostlist = GNUNET_NO;
1622 stat_testing_allowed = GNUNET_YES;
1624 if (GNUNET_YES == stat_learning)
1626 *msgh = &handler_advertisement;
1627 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1628 _("Learning is enabled on this peer\n"));
1629 load_hostlist_file ();
1630 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1631 "Hostlists will be saved to file again in %s\n",
1632 GNUNET_STRINGS_relative_time_to_string (SAVING_INTERVAL, GNUNET_YES));
1634 GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL,
1635 &task_hostlist_saving,
1640 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1641 _("Learning is not enabled on this peer\n"));
1644 GNUNET_CONFIGURATION_get_value_filename (cfg, "HOSTLIST",
1645 "HOSTLISTFILE", &filename))
1647 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
1649 result = remove (filename);
1651 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1652 _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),
1655 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1660 GNUNET_free (filename);
1662 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1663 "Loading stats value on hostlist download frequency\n");
1664 sget = GNUNET_STATISTICS_get (stats, "hostlist",
1666 ("# milliseconds between hostlist downloads"),
1667 GNUNET_TIME_UNIT_MINUTES,
1673 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1674 "Statistics request failed, scheduling hostlist download\n");
1675 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1682 * Stop downloading hostlists from hostlist servers as necessary.
1685 GNUNET_HOSTLIST_client_stop ()
1687 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostlist client shutdown\n");
1690 GNUNET_STATISTICS_get_cancel (sget);
1694 if (GNUNET_YES == stat_learning)
1695 save_hostlist_file (GNUNET_YES);
1696 if (ti_saving_task != NULL)
1698 GNUNET_SCHEDULER_cancel (ti_saving_task);
1699 ti_saving_task = NULL;
1702 if (ti_download_dispatcher_task != NULL)
1704 GNUNET_SCHEDULER_cancel (ti_download_dispatcher_task);
1705 ti_download_dispatcher_task = NULL;
1707 if (ti_testing_intervall_task != NULL)
1709 GNUNET_SCHEDULER_cancel (ti_testing_intervall_task);
1710 ti_testing_intervall_task = NULL;
1712 if (ti_download != NULL)
1714 GNUNET_SCHEDULER_cancel (ti_download);
1717 if (ti_check_download != NULL)
1719 GNUNET_SCHEDULER_cancel (ti_check_download);
1720 ti_check_download = NULL;
1721 curl_global_cleanup ();
1723 if (NULL != transport)
1725 GNUNET_TRANSPORT_disconnect (transport);
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;
1738 /* end of gnunet-daemon-hostlist_client.c */