2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file hostlist/hostlist-client.c
23 * @brief hostlist support. Downloads HELLOs via HTTP.
24 * @author Christian Grothoff
25 * @author Matthias Wachs
29 #include "hostlist-client.h"
30 #include "gnunet_core_service.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet-daemon-hostlist.h"
35 #include <curl/curl.h>
36 #include "gnunet_common.h"
37 #include "gnunet_bio_lib.h"
39 #define DEBUG_HOSTLIST_CLIENT GNUNET_YES
43 * Number of connections that we must have to NOT download
46 #define MIN_CONNECTIONS 4
49 * A single hostlist obtained by hostlist advertisements
54 * previous entry, used to manage entries in a double linked list
56 struct Hostlist * prev;
59 * next entry, used to manage entries in a double linked list
61 struct Hostlist * next;
64 * URI where hostlist can be obtained
66 const char *hostlist_uri;
69 * Value describing the quality of the hostlist, the bigger the better but (should) never < 0
70 * used for deciding which hostlist is replaced if MAX_NUMBER_HOSTLISTS in data structure is reached
71 * intial value = HOSTLIST_INITIAL
72 * increased every successful download by HOSTLIST_SUCCESSFULL_DOWNLOAD
73 * increased every successful download by number of obtained HELLO messages
74 * decreased every failed download by HOSTLIST_SUCCESSFULL_DOWNLOAD
79 * Time the hostlist advertisement was recieved and the entry was created
81 struct GNUNET_TIME_Absolute time_creation;
84 * Last time the hostlist was obtained
86 struct GNUNET_TIME_Absolute time_last_usage;
89 * Number of HELLO messages obtained during last download
94 * Number of times the hostlist was successfully obtained
104 static const struct GNUNET_CONFIGURATION_Handle *cfg;
109 static struct GNUNET_SCHEDULER_Handle *sched;
114 struct GNUNET_STATISTICS_Handle *stats;
119 struct GNUNET_TRANSPORT_Handle *transport;
122 * Proxy that we are using (can be NULL).
127 * Buffer for data downloaded via HTTP.
129 static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
132 * Number of bytes valid in 'download_buffer'.
134 static size_t download_pos;
137 * Current URL that we are using.
139 static char *current_url;
142 * Current CURL handle.
147 * Current multi-CURL handle.
152 * ID of the current task scheduled.
154 static GNUNET_SCHEDULER_TaskIdentifier current_task;
157 * ID of the current hostlist saving task scheduled.
159 static GNUNET_SCHEDULER_TaskIdentifier saving_task;
162 * Amount of time we wait between hostlist downloads.
164 static struct GNUNET_TIME_Relative hostlist_delay;
167 * Set to GNUNET_YES if the current URL had some problems.
169 static int bogus_url;
172 * Number of active connections (according to core service).
174 static unsigned int connection_count;
177 * At what time MUST the current hostlist request be done?
179 static struct GNUNET_TIME_Absolute end_time;
182 * Head of the linked list used to store hostlists
184 static struct Hostlist * linked_list_head;
187 * Tail of the linked list used to store hostlists
189 static struct Hostlist * linked_list_tail;
192 * Current hostlist used for downloading
194 static struct Hostlist * current_hostlist;
197 * Size of the linke list used to store hostlists
199 static unsigned int linked_list_size;
202 * Head of the linked list used to store hostlists
204 static struct Hostlist * hostlist_to_test;
206 static int testing_hostlist;
209 * Value saying if preconfigured is used
211 static unsigned int use_preconfigured_list;
214 * Set if we are allowed to learn new hostlists and use them
219 * Value saying how many valid HELLO messages were obtained during download
221 static unsigned int hellos_obtained;
224 * Value saying if hostlist download was successful
226 static unsigned int download_successful;
229 * Process downloaded bits by calling callback on each HELLO.
231 * @param ptr buffer with downloaded data
232 * @param size size of a record
233 * @param nmemb number of records downloaded
235 * @return number of bytes that were processed (always size*nmemb)
238 download_hostlist_processor (void *ptr,
243 const char * cbuf = ptr;
244 const struct GNUNET_MessageHeader *msg;
250 total = size * nmemb;
251 if ( (total == 0) || (bogus_url) )
253 return total; /* ok, no data or bogus data */
255 GNUNET_STATISTICS_update (stats,
256 gettext_noop ("# bytes downloaded from hostlist servers"),
260 while ( (left > 0) ||
263 cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - download_pos);
264 memcpy (&download_buffer[download_pos],
270 if (download_pos < sizeof(struct GNUNET_MessageHeader))
272 GNUNET_assert (left == 0);
275 msg = (const struct GNUNET_MessageHeader *) download_buffer;
276 msize = ntohs(msg->size);
277 if (msize < sizeof(struct GNUNET_MessageHeader))
279 GNUNET_STATISTICS_update (stats,
280 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
283 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
284 _("Invalid `%s' message received from hostlist at `%s'\n"),
290 if (download_pos < msize)
292 GNUNET_assert (left == 0);
295 if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
297 #if DEBUG_HOSTLIST_CLIENT
298 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
299 "Received valid `%s' message from hostlist server.\n",
302 GNUNET_STATISTICS_update (stats,
303 gettext_noop ("# valid HELLOs downloaded from hostlist servers"),
307 GNUNET_TRANSPORT_offer_hello (transport, msg);
311 GNUNET_STATISTICS_update (stats,
312 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
315 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
316 _("Invalid `%s' message received from hostlist at `%s'\n"),
319 bogus_url = GNUNET_YES;
322 memmove (download_buffer,
323 &download_buffer[msize],
324 download_pos - msize);
325 download_pos -= msize;
332 * Obtain a hostlist URL that we should use.
334 * @return NULL if there is no URL available
345 GNUNET_CONFIGURATION_get_value_string (cfg,
350 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
351 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
352 "SERVERS", "HOSTLIST");
357 if (strlen (servers) > 0)
360 pos = strlen (servers) - 1;
363 if (servers[pos] == ' ')
370 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
371 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
372 "SERVERS", "HOSTLIST");
373 GNUNET_free (servers);
377 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
378 pos = strlen (servers) - 1;
381 if (servers[pos] == ' ')
393 ret = GNUNET_strdup (&servers[pos]);
394 GNUNET_free (servers);
399 * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
400 * @return uri to use, NULL if there is no URL available
406 unsigned int counter;
407 struct Hostlist * pos;
409 if ( GNUNET_NO == learning)
411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412 "Using preconfigured bootstrap server\n");
413 current_hostlist = NULL;
414 return get_bootstrap_url();
417 if ( ( GNUNET_YES == testing_hostlist) && (NULL != hostlist_to_test) )
419 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
420 "Testing new advertised hostlist if it is obtainable\n");
421 return strdup(hostlist_to_test->hostlist_uri);
424 if ( (GNUNET_YES == use_preconfigured_list) ||
425 (linked_list_size == 0) )
427 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428 "Using preconfigured bootstrap server\n");
429 current_hostlist = NULL;
430 return get_bootstrap_url();
432 index = GNUNET_CRYPTO_random_u32 ( GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
434 pos = linked_list_head;
435 while ( counter < index )
440 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
441 "Using learned hostlist `%s'\n", pos->hostlist_uri);
442 current_hostlist = pos;
443 return strdup(pos->hostlist_uri);
447 #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);
451 * Schedule the background task that will (possibly)
452 * download a hostlist.
455 schedule_hostlist_task (void);
458 * Method to load persistent hostlist file during hostlist client shutdown
459 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
461 static void save_hostlist_file ( int shutdown );
464 * add val2 to val1 with overflow check
465 * @param val1 value 1
466 * @param val2 value 2
469 static uint64_t checked_add (uint64_t val1, uint64_t val2)
471 static uint64_t temp;
472 static uint64_t maxv;
485 * Subtract val2 from val1 with underflow check
486 * @param val1 value 1
487 * @param val2 value 2
490 static uint64_t checked_sub (uint64_t val1, uint64_t val2)
499 * Method to check if URI is in hostlist linked list
500 * @param uri uri to check
501 * @return GNUNET_YES if existing in linked list, GNUNET_NO if not
504 linked_list_contains (const char * uri)
506 struct Hostlist * pos;
508 pos = linked_list_head;
511 if (0 == strcmp(pos->hostlist_uri, uri) )
520 static struct Hostlist *
521 linked_list_get_lowest_quality ( )
523 struct Hostlist * pos;
524 struct Hostlist * lowest;
526 if (linked_list_size == 0)
528 lowest = linked_list_head;
529 pos = linked_list_head->next;
532 if (pos->quality < lowest->quality)
541 * Task that checks if we should try to download a hostlist.
542 * If so, we initiate the download, otherwise we schedule
543 * this task again for a later time.
546 insert_hostlist ( void )
548 GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist_to_test);
551 GNUNET_STATISTICS_set (stats,
552 gettext_noop("# advertised hostlist URIs"),
556 if (MAX_NUMBER_HOSTLISTS >= linked_list_size)
559 /* No free entries available, replace existing entry */
560 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
561 "Removing lowest quality entry\n" );
562 struct Hostlist * lowest_quality = linked_list_get_lowest_quality();
563 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
564 "Hostlist with URI `%s' has the worst quality of all with value %llu\n",
565 lowest_quality->hostlist_uri,
566 (unsigned long long) lowest_quality->quality);
567 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, lowest_quality);
570 GNUNET_STATISTICS_set (stats,
571 gettext_noop("# advertised hostlist URIs"),
575 GNUNET_free (lowest_quality);
577 testing_hostlist = GNUNET_NO;
582 * Method updating hostlist statistics
584 static void update_hostlist ( )
587 if ( (use_preconfigured_list == GNUNET_NO) && ( NULL != current_hostlist ) )
589 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
590 "Updating hostlist statics for URI `%s'\n",current_hostlist->hostlist_uri );
591 current_hostlist->hello_count = hellos_obtained;
592 current_hostlist->time_last_usage = GNUNET_TIME_absolute_get();
593 current_hostlist->quality = checked_add ( current_hostlist->quality, (hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
594 if ( GNUNET_YES == download_successful )
596 current_hostlist->times_used++;
597 current_hostlist->quality = checked_add ( current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
598 GNUNET_asprintf (&stat,
599 gettext_noop("# advertised URI `%s' downloaded"),
600 current_hostlist->hostlist_uri);
602 GNUNET_STATISTICS_update ( stats,
609 current_hostlist->quality = checked_sub ( current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD );
611 current_hostlist = NULL;
612 /* Alternating the usage of preconfigured and learned hostlists */
614 if ( GNUNET_YES == learning)
616 if (use_preconfigured_list == GNUNET_YES)
617 use_preconfigured_list = GNUNET_NO;
619 use_preconfigured_list = GNUNET_YES;
622 use_preconfigured_list = GNUNET_YES;
626 * Clean up the state from the task that downloaded the
627 * hostlist and schedule the next task.
636 mret = curl_multi_remove_handle (multi, curl);
637 if (mret != CURLM_OK)
639 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
640 _("%s failed at %s:%d: `%s'\n"),
641 "curl_multi_remove_handle", __FILE__, __LINE__,
642 curl_multi_strerror (mret));
644 mret = curl_multi_cleanup (multi);
645 if (mret != CURLM_OK)
646 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
647 _("%s failed at %s:%d: `%s'\n"),
648 "curl_multi_cleanup", __FILE__, __LINE__,
649 curl_multi_strerror (mret));
654 curl_easy_cleanup (curl);
657 GNUNET_free_non_null (current_url);
660 schedule_hostlist_task ();
664 * Task that is run when we are ready to receive more data from the hostlist
667 * @param cls closure, unused
668 * @param tc task context, unused
671 multi_ready (void *cls,
672 const struct GNUNET_SCHEDULER_TaskContext *tc);
675 * Ask CURL for the select set and then schedule the
676 * receiving task with the scheduler.
686 struct GNUNET_NETWORK_FDSet *grs;
687 struct GNUNET_NETWORK_FDSet *gws;
689 struct GNUNET_TIME_Relative rtime;
695 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
696 if (mret != CURLM_OK)
698 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
699 _("%s failed at %s:%d: `%s'\n"),
700 "curl_multi_fdset", __FILE__, __LINE__,
701 curl_multi_strerror (mret));
705 mret = curl_multi_timeout (multi, &timeout);
706 if (mret != CURLM_OK)
708 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
709 _("%s failed at %s:%d: `%s'\n"),
710 "curl_multi_timeout", __FILE__, __LINE__,
711 curl_multi_strerror (mret));
715 rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
716 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
718 grs = GNUNET_NETWORK_fdset_create ();
719 gws = GNUNET_NETWORK_fdset_create ();
720 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
721 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
722 #if DEBUG_HOSTLIST_CLIENT
723 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
724 "Scheduling task for hostlist download using cURL\n");
727 = GNUNET_SCHEDULER_add_select (sched,
728 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
729 GNUNET_SCHEDULER_NO_TASK,
735 GNUNET_NETWORK_fdset_destroy (gws);
736 GNUNET_NETWORK_fdset_destroy (grs);
741 * Task that is run when we are ready to receive more data from the hostlist
744 * @param cls closure, unused
745 * @param tc task context, unused
748 multi_ready (void *cls,
749 const struct GNUNET_SCHEDULER_TaskContext *tc)
751 unsigned int counter;
756 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
758 #if DEBUG_HOSTLIST_CLIENT
759 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
760 "Shutdown requested while trying to download hostlist from `%s'\n",
767 if (GNUNET_TIME_absolute_get_remaining (end_time).value == 0)
769 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
770 _("Timeout trying to download hostlist from `%s'\n"),
776 #if DEBUG_HOSTLIST_CLIENT
777 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
778 "Ready for processing hostlist client request\n");
784 mret = curl_multi_perform (multi, &running);
790 msg = curl_multi_info_read (multi, &running);
792 if (counter >= MAX_HELLO_PER_HOSTLISTS)
794 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
795 _("Maximum number of %u HELLOs per hostlist exceeded, stopping download...\n"),
796 MAX_HELLO_PER_HOSTLISTS);
801 GNUNET_break (msg != NULL);
807 if ( (msg->data.result != CURLE_OK) &&
808 (msg->data.result != CURLE_GOT_NOTHING) )
809 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
810 _("%s failed for `%s' at %s:%d: `%s'\n"),
811 "curl_multi_perform",
815 curl_easy_strerror (msg->data.result));
818 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
819 _("Download of hostlist `%s' completed.\n"),
821 download_successful = GNUNET_YES;
822 if (GNUNET_YES == testing_hostlist)
824 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
825 _("Adding successfully tested hostlist `%s' datastore.\n"),current_url);
827 testing_hostlist = GNUNET_NO;
841 while (mret == CURLM_CALL_MULTI_PERFORM);
842 if (mret != CURLM_OK)
844 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
845 _("%s failed at %s:%d: `%s'\n"),
846 "curl_multi_perform", __FILE__, __LINE__,
847 curl_multi_strerror (mret));
855 * Main function that will download a hostlist and process its
864 current_url = get_list_url ();
865 if (current_url == NULL)
867 curl = curl_easy_init ();
875 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
876 _("Bootstrapping using hostlist at `%s'.\n"),
879 download_successful = GNUNET_NO;
880 GNUNET_STATISTICS_update (stats,
881 gettext_noop ("# hostlist downloads initiated"),
885 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
888 CURL_EASY_SETOPT (curl,
889 CURLOPT_WRITEFUNCTION,
890 &download_hostlist_processor);
896 CURL_EASY_SETOPT (curl,
904 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
905 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
906 /* no need to abort if the above failed */
907 CURL_EASY_SETOPT (curl,
915 CURL_EASY_SETOPT (curl,
919 CURL_EASY_SETOPT (curl,
923 CURL_EASY_SETOPT (curl,
925 GNUNET_SERVER_MAX_MESSAGE_SIZE);
926 if (0 == strncmp (current_url, "http", 4))
927 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
928 CURL_EASY_SETOPT (curl,
929 CURLOPT_CONNECTTIMEOUT,
931 CURL_EASY_SETOPT (curl,
935 /* this should no longer be needed; we're now single-threaded! */
936 CURL_EASY_SETOPT (curl,
940 multi = curl_multi_init ();
947 mret = curl_multi_add_handle (multi, curl);
948 if (mret != CURLM_OK)
950 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
951 _("%s failed at %s:%d: `%s'\n"),
952 "curl_multi_add_handle", __FILE__, __LINE__,
953 curl_multi_strerror (mret));
954 mret = curl_multi_cleanup (multi);
955 if (mret != CURLM_OK)
956 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
957 _("%s failed at %s:%d: `%s'\n"),
958 "curl_multi_cleanup", __FILE__, __LINE__,
959 curl_multi_strerror (mret));
964 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
970 * Task that checks if we should try to download a hostlist.
971 * If so, we initiate the download, otherwise we schedule
972 * this task again for a later time.
975 check_task (void *cls,
976 const struct GNUNET_SCHEDULER_TaskContext *tc)
978 current_task = GNUNET_SCHEDULER_NO_TASK;
979 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
981 if (connection_count < MIN_CONNECTIONS)
982 download_hostlist ();
984 schedule_hostlist_task ();
989 * Compute when we should check the next time about downloading
990 * a hostlist; then schedule the task accordingly.
993 schedule_hostlist_task ()
996 struct GNUNET_TIME_Relative delay;
1000 curl_global_cleanup ();
1001 return; /* in shutdown */
1003 delay = hostlist_delay;
1004 if (hostlist_delay.value == 0)
1005 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1007 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1008 if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * (1 + connection_count))
1009 hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1010 (1 + connection_count));
1011 GNUNET_STATISTICS_set (stats,
1012 gettext_noop("# seconds between hostlist downloads"),
1013 hostlist_delay.value,
1017 delay = GNUNET_TIME_UNIT_ZERO;
1020 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1021 _("Have %u/%u connections. Will consider downloading hostlist in %llums\n"),
1024 (unsigned long long) delay.value);
1025 current_task = GNUNET_SCHEDULER_add_delayed (sched,
1032 * Task that writes hostlist entries to a file on a regular base
1037 hostlist_saving_task (void *cls,
1038 const struct GNUNET_SCHEDULER_TaskContext *tc)
1040 saving_task = GNUNET_SCHEDULER_NO_TASK;
1041 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1043 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1044 _("Scheduled saving of hostlists\n"));
1045 save_hostlist_file ( GNUNET_NO );
1047 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1048 _("Hostlists will be saved to file again in %llums\n"),
1049 (unsigned long long) SAVING_INTERVALL.value);
1050 saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1052 &hostlist_saving_task,
1057 * Method called whenever a given peer connects.
1059 * @param cls closure
1060 * @param peer peer identity this notification is about
1061 * @param latency reported latency of the connection with 'other'
1062 * @param distance reported distance (DV) to 'other'
1065 connect_handler (void *cls,
1067 GNUNET_PeerIdentity * peer,
1068 struct GNUNET_TIME_Relative latency,
1072 GNUNET_STATISTICS_update (stats,
1073 gettext_noop ("# active connections"),
1080 * Method called whenever a given peer disconnects.
1082 * @param cls closure
1083 * @param peer peer identity this notification is about
1086 disconnect_handler (void *cls,
1088 GNUNET_PeerIdentity * peer)
1091 GNUNET_STATISTICS_update (stats,
1092 gettext_noop ("# active connections"),
1099 * Method called whenever an advertisement message arrives.
1101 * @param cls closure (always NULL)
1102 * @param peer the peer sending the message
1103 * @param message the actual message
1104 * @param latency latency
1105 * @param distance distance
1106 * @return GNUNET_OK to keep the connection open,
1107 * GNUNET_SYSERR to close it (signal serious error)
1110 advertisement_handler (void *cls,
1111 const struct GNUNET_PeerIdentity * peer,
1112 const struct GNUNET_MessageHeader * message,
1113 struct GNUNET_TIME_Relative latency,
1118 const struct GNUNET_MessageHeader * incoming;
1120 struct Hostlist * hostlist;
1122 GNUNET_assert (ntohs (message->type) == GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT);
1123 size = ntohs (message->size);
1124 if (size <= sizeof(struct GNUNET_MessageHeader))
1126 GNUNET_break_op (0);
1127 return GNUNET_SYSERR;
1129 incoming = (const struct GNUNET_MessageHeader *) message;
1130 uri = (const char*) &incoming[1];
1131 uri_size = size - sizeof (struct GNUNET_MessageHeader);
1132 if (uri [uri_size - 1] != '\0')
1134 GNUNET_break_op (0);
1135 return GNUNET_SYSERR;
1137 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1138 "Hostlist client recieved advertisement from `%s' containing URI `%s'\n",
1141 if (GNUNET_NO != linked_list_contains (uri))
1143 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1144 "URI `%s' is already known\n",
1149 if ( GNUNET_YES == testing_hostlist )
1151 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1152 "Currently not accepting adverts\n");
1156 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + uri_size);
1157 hostlist->hostlist_uri = (const char*) &hostlist[1];
1158 memcpy (&hostlist[1], uri, uri_size);
1159 hostlist->time_creation = GNUNET_TIME_absolute_get();
1160 hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero();
1161 hostlist->quality = HOSTLIST_INITIAL;
1162 hostlist_to_test = hostlist;
1164 testing_hostlist = GNUNET_YES;
1171 * Continuation called by the statistics code once
1172 * we go the stat. Initiates hostlist download scheduling.
1174 * @param cls closure
1175 * @param success GNUNET_OK if statistics were
1176 * successfully obtained, GNUNET_SYSERR if not.
1179 primary_task (void *cls, int success)
1182 return; /* in shutdown */
1183 #if DEBUG_HOSTLIST_CLIENT
1184 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1185 "Statistics request done, scheduling hostlist download\n");
1187 schedule_hostlist_task ();
1192 process_stat (void *cls,
1193 const char *subsystem,
1198 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1199 _("Initial time between hostlist downloads is %llums\n"),
1200 (unsigned long long) value);
1201 hostlist_delay.value = value;
1206 * Method to load persistent hostlist file during hostlist client startup
1209 load_hostlist_file ()
1214 struct Hostlist * hostlist;
1216 uint32_t times_used;
1217 uint32_t hellos_returned;
1224 GNUNET_CONFIGURATION_get_value_string (cfg,
1229 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1230 _("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
1231 "HOSTLISTFILE", "HOSTLIST");
1235 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1236 _("Loading saved hostlist entries from file `%s' \n"), filename);
1237 if ( GNUNET_NO == GNUNET_DISK_file_test (filename) )
1239 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1240 _("Hostlist file `%s' is not existing\n"), filename);
1241 GNUNET_free ( filename );
1245 struct GNUNET_BIO_ReadHandle * rh = GNUNET_BIO_read_open (filename);
1248 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1249 _("Could not open file `%s' for reading to load hostlists: %s\n"),
1252 GNUNET_free (filename);
1257 while ( (GNUNET_OK == GNUNET_BIO_read_string (rh, "url" , &uri, MAX_URL_LEN)) &&
1258 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, ×_used)) &&
1259 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &quality)) &&
1260 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &last_used)) &&
1261 (GNUNET_OK == GNUNET_BIO_read_int64 (rh, &created)) &&
1262 (GNUNET_OK == GNUNET_BIO_read_int32 (rh, &hellos_returned)) )
1264 hostlist = GNUNET_malloc (sizeof (struct Hostlist) + strlen (uri) + 1);
1265 hostlist->hello_count = hellos_returned;
1266 hostlist->hostlist_uri = (const char *) &hostlist[1];
1267 memcpy (&hostlist[1], uri, strlen(uri)+1);
1268 hostlist->quality = quality;
1269 hostlist->time_creation.value = created;
1270 hostlist->time_last_usage.value = last_used;
1271 GNUNET_CONTAINER_DLL_insert(linked_list_head, linked_list_tail, hostlist);
1273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1274 "Added hostlist entry eith URI `%s' \n", hostlist->hostlist_uri);
1278 if ( counter >= MAX_NUMBER_HOSTLISTS ) break;
1281 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1282 _("%u hostlist URIs loaded from file\n"), counter);
1283 GNUNET_STATISTICS_set (stats,
1284 gettext_noop("# hostlist URIs read from file"),
1287 GNUNET_STATISTICS_set (stats,
1288 gettext_noop("# advertised hostlist URIs"),
1292 GNUNET_free_non_null (uri);
1294 GNUNET_BIO_read_close (rh, &emsg);
1297 GNUNET_free (filename);
1302 * Method to save persistent hostlist file during hostlist client shutdown
1303 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1305 static void save_hostlist_file ( int shutdown )
1308 struct Hostlist *pos;
1309 struct GNUNET_BIO_WriteHandle * wh;
1314 GNUNET_CONFIGURATION_get_value_string (cfg,
1319 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1320 _("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
1321 "HOSTLISTFILE", "HOSTLIST");
1324 wh = GNUNET_BIO_write_open (filename);
1327 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1328 _("Could not open file `%s' for writing to save hostlists: %s\n"),
1331 GNUNET_free (filename);
1334 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1335 _("Writing %u hostlist URIs to `%s'\n" ),
1336 linked_list_size, filename);
1338 /* add code to write hostlists to file using bio */
1341 while (NULL != (pos = linked_list_head))
1343 if ( GNUNET_YES == shutdown)
1345 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1348 if (GNUNET_YES == ok)
1351 GNUNET_BIO_write_string (wh, pos->hostlist_uri)) ||
1353 GNUNET_BIO_write_int32 (wh, pos->times_used)) ||
1355 GNUNET_BIO_write_int64 (wh, pos->quality)) ||
1357 GNUNET_BIO_write_int64 (wh, pos->time_last_usage.value)) ||
1359 GNUNET_BIO_write_int64 (wh, pos->time_creation.value)) ||
1361 GNUNET_BIO_write_int32 (wh, pos->hello_count)))
1363 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1364 _("Error writing hostlist URIs to file `%s'\n"),
1370 if ( GNUNET_YES == shutdown)
1373 if ( counter >= MAX_NUMBER_HOSTLISTS) break;
1375 GNUNET_STATISTICS_set (stats,
1376 gettext_noop("# hostlist URIs written to file"),
1380 if ( GNUNET_OK != GNUNET_BIO_write_close ( wh ) )
1381 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1382 _("Error writing hostlist URIs to file `%s'\n"),
1384 GNUNET_free (filename);
1388 * Start downloading hostlists from hostlist servers as necessary.
1391 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1392 struct GNUNET_SCHEDULER_Handle *s,
1393 struct GNUNET_STATISTICS_Handle *st,
1394 GNUNET_CORE_ConnectEventHandler *ch,
1395 GNUNET_CORE_DisconnectEventHandler *dh,
1396 GNUNET_CORE_MessageCallback *msgh,
1402 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1405 return GNUNET_SYSERR;
1407 transport = GNUNET_TRANSPORT_connect (s, c, NULL, NULL, NULL, NULL);
1408 if (NULL == transport)
1410 curl_global_cleanup ();
1411 return GNUNET_SYSERR;
1417 GNUNET_CONFIGURATION_get_value_string (cfg,
1423 *ch = &connect_handler;
1424 *dh = &disconnect_handler;
1425 linked_list_head = NULL;
1426 linked_list_tail = NULL;
1427 use_preconfigured_list = GNUNET_YES;
1428 testing_hostlist = GNUNET_NO;
1430 if ( GNUNET_YES == learning )
1432 *msgh = &advertisement_handler;
1433 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1434 _("Learning is enabled on this peer\n"));
1435 load_hostlist_file ();
1436 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1437 _("Hostlists will be saved to file again in %llums\n"),
1438 (unsigned long long) SAVING_INTERVALL.value);
1439 saving_task = GNUNET_SCHEDULER_add_delayed (sched,
1441 &hostlist_saving_task,
1446 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1447 _("Learning is not enabled on this peer\n"));
1449 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1454 if ( GNUNET_YES == GNUNET_DISK_file_test (filename) )
1456 result = remove (filename);
1458 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1459 _("Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),filename);
1461 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1462 _("Hostlist file `%s' could not be removed\n"),filename);
1465 GNUNET_free ( filename );
1467 GNUNET_STATISTICS_get (stats,
1469 gettext_noop("# seconds between hostlist downloads"),
1470 GNUNET_TIME_UNIT_MINUTES,
1479 * Stop downloading hostlists from hostlist servers as necessary.
1482 GNUNET_HOSTLIST_client_stop ()
1484 #if DEBUG_HOSTLIST_CLIENT
1485 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1486 "Hostlist client shutdown\n");
1488 if ( GNUNET_YES == learning )
1489 save_hostlist_file ( GNUNET_YES );
1491 if (current_task != GNUNET_SCHEDULER_NO_TASK)
1493 GNUNET_SCHEDULER_cancel (sched,
1495 curl_global_cleanup ();
1497 if (transport != NULL)
1499 GNUNET_TRANSPORT_disconnect (transport);
1502 GNUNET_assert (NULL == transport);
1503 GNUNET_free_non_null (proxy);
1509 /* end of hostlist-client.c */