aboutsummaryrefslogtreecommitdiff
path: root/src/service/hostlist/gnunet-daemon-hostlist_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/hostlist/gnunet-daemon-hostlist_client.c')
-rw-r--r--src/service/hostlist/gnunet-daemon-hostlist_client.c1824
1 files changed, 1824 insertions, 0 deletions
diff --git a/src/service/hostlist/gnunet-daemon-hostlist_client.c b/src/service/hostlist/gnunet-daemon-hostlist_client.c
new file mode 100644
index 000000000..bedc52612
--- /dev/null
+++ b/src/service/hostlist/gnunet-daemon-hostlist_client.c
@@ -0,0 +1,1824 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2010, 2014, 2016 GNUnet e.V.
4
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.
9
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.
14
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/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file hostlist/gnunet-daemon-hostlist_client.c
22 * @brief hostlist support. Downloads HELLOs via HTTP.
23 * @author Christian Grothoff
24 * @author Matthias Wachs
25 */
26#include "platform.h"
27#include "gnunet-daemon-hostlist_client.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_statistics_service.h"
30#include "gnunet_peerstore_service.h"
31#include "gnunet-daemon-hostlist.h"
32/* Just included for the right curl.h */
33#include "gnunet_curl_lib.h"
34
35
36/**
37 * Number of connections that we must have to NOT download
38 * hostlists anymore.
39 */
40#define MIN_CONNECTIONS 4
41
42/**
43 * Maximum number of hostlist that are saved
44 */
45#define MAX_NUMBER_HOSTLISTS 30
46
47/**
48 * Time interval hostlists are saved to disk
49 */
50#define SAVING_INTERVAL \
51 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
52
53/**
54 * Time interval between two hostlist tests
55 */
56#define TESTING_INTERVAL \
57 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
58
59/**
60 * Time interval for download dispatcher before a download is re-scheduled
61 */
62#define WAITING_INTERVAL \
63 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
64
65/**
66 * Defines concerning the hostlist quality metric
67 */
68
69/**
70 * Initial quality of a new created hostlist
71 */
72#define HOSTLIST_INITIAL 10000
73
74/**
75 * Value subtracted each time a hostlist download fails
76 */
77#define HOSTLIST_FAILED_DOWNLOAD 100
78
79/**
80 * Value added each time a hostlist download is successful
81 */
82#define HOSTLIST_SUCCESSFUL_DOWNLOAD 100
83
84/**
85 * Value added for each valid HELLO received during a hostlist download
86 */
87#define HOSTLIST_SUCCESSFUL_HELLO 1
88
89
90/**
91 * A single hostlist obtained by hostlist advertisements
92 */
93struct Hostlist
94{
95 /**
96 * previous entry, used to manage entries in a double linked list
97 */
98 struct Hostlist *prev;
99
100 /**
101 * next entry, used to manage entries in a double linked list
102 */
103 struct Hostlist *next;
104
105 /**
106 * URI where hostlist can be obtained
107 */
108 const char *hostlist_uri;
109
110 /**
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 * initial 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
117 */
118 uint64_t quality;
119
120 /**
121 * Time the hostlist advertisement was received and the entry was created
122 */
123 struct GNUNET_TIME_Absolute time_creation;
124
125 /**
126 * Last time the hostlist was obtained
127 */
128 struct GNUNET_TIME_Absolute time_last_usage;
129
130 /**
131 * Number of HELLO messages obtained during last download
132 */
133 uint32_t hello_count;
134
135 /**
136 * Number of times the hostlist was successfully obtained
137 */
138 uint32_t times_used;
139};
140
141
142/**
143 * Our configuration.
144 */
145static const struct GNUNET_CONFIGURATION_Handle *cfg;
146
147/**
148 * Statistics handle.
149 */
150static struct GNUNET_STATISTICS_Handle *stats;
151
152/**
153 * Proxy hostname or ip we are using (can be NULL).
154 */
155static char *proxy;
156
157/**
158 * Proxy username we are using (can be NULL).
159 */
160static char *proxy_username;
161
162/**
163 * Proxy password we are using (can be NULL).
164 */
165static char *proxy_password;
166
167/**
168 * Proxy type we are using (can be NULL).
169 */
170static curl_proxytype proxy_type;
171
172/**
173 * Number of bytes valid in 'download_buffer'.
174 */
175static size_t download_pos;
176
177/**
178 * Current URL that we are using.
179 */
180static char *current_url;
181
182/**
183 * Current CURL handle.
184 */
185static CURL *curl;
186
187/**
188 * Current multi-CURL handle.
189 */
190static CURLM *multi;
191
192/**
193 * How many bytes did we download from the current hostlist URL?
194 */
195static uint32_t stat_bytes_downloaded;
196
197/**
198 * Amount of time we wait between hostlist downloads.
199 */
200static struct GNUNET_TIME_Relative hostlist_delay;
201
202/**
203 * ID of the task, checking if hostlist download should take plate
204 */
205static struct GNUNET_SCHEDULER_Task *ti_check_download;
206
207/**
208 * ID of the task downloading the hostlist
209 */
210static struct GNUNET_SCHEDULER_Task *ti_download;
211
212/**
213 * ID of the task saving the hostlsit in a regular interval
214 */
215static struct GNUNET_SCHEDULER_Task *ti_saving_task;
216
217/**
218 * ID of the task called to initiate a download
219 */
220static struct GNUNET_SCHEDULER_Task *ti_download_dispatcher_task;
221
222/**
223 * ID of the task controlling the locking between two hostlist tests
224 */
225static struct GNUNET_SCHEDULER_Task *ti_testing_intervall_task;
226
227/**
228 * At what time MUST the current hostlist request be done?
229 */
230static struct GNUNET_TIME_Absolute end_time;
231
232/**
233 * Head of the linkd list to store the store context for hellos.
234 */
235static struct GNUNET_PEERSTORE_StoreHelloContext *shc_head;
236
237/**
238 * Tail of the linkd list to store the store context for hellos.
239 */
240static struct GNUNET_PEERSTORE_StoreHelloContext *shc_tail;
241
242/**
243 * Head of the linked list used to store hostlists
244 */
245static struct Hostlist *linked_list_head;
246
247/**
248 * Tail of the linked list used to store hostlists
249 */
250static struct Hostlist *linked_list_tail;
251
252/**
253 * Current hostlist used for downloading
254 */
255static struct Hostlist *current_hostlist;
256
257/**
258 * Size of the linked list used to store hostlists
259 */
260static unsigned int linked_list_size;
261
262/**
263 * Head of the linked list used to store hostlists
264 */
265static struct Hostlist *hostlist_to_test;
266
267/**
268 * Handle for our statistics GET operation.
269 */
270static struct GNUNET_STATISTICS_GetHandle *sget;
271
272/**
273 * Set to GNUNET_YES if the current URL had some problems.
274 */
275static int stat_bogus_url;
276
277/**
278 * Value controlling if a hostlist is tested at the moment
279 */
280static int stat_testing_hostlist;
281
282/**
283 * Value controlling if a hostlist testing is allowed at the moment
284 */
285static int stat_testing_allowed;
286
287/**
288 * Value controlling if a hostlist download is running at the moment
289 */
290static int stat_download_in_progress;
291
292/**
293 * Value saying if a preconfigured bootstrap server is used
294 */
295static unsigned int stat_use_bootstrap;
296
297/**
298 * Set if we are allowed to learn new hostlists and use them
299 */
300static int stat_learning;
301
302/**
303 * Value saying if hostlist download was successful
304 */
305static unsigned int stat_download_successful;
306
307/**
308 * Value saying how many valid HELLO messages were obtained during download
309 */
310static unsigned int stat_hellos_obtained;
311
312/**
313 * Number of active connections (according to core service).
314 */
315static unsigned int stat_connection_count;
316
317/**
318 * Handle to the PEERSTORE service.
319 */
320static struct GNUNET_PEERSTORE_Handle *peerstore;
321
322
323static void
324shc_cont (void *cls, int success)
325{
326 struct GNUNET_PEERSTORE_StoreHelloContextClosure *shc_cls = cls;
327
328 if (GNUNET_YES == success)
329 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330 "Hostlist entry stored successfully!\n");
331 else
332 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
333 "Error storing hostlist entry!\n");
334 GNUNET_CONTAINER_DLL_remove (shc_head, shc_tail, shc_cls->shc);
335 GNUNET_free (shc_cls);
336}
337
338
339/**
340 * Process downloaded bits by calling callback on each HELLO.
341 *
342 * @param ptr buffer with downloaded data
343 * @param size size of a record
344 * @param nmemb number of records downloaded
345 * @param ctx unused
346 * @return number of bytes that were processed (always size*nmemb)
347 */
348static size_t
349callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
350{
351 static char download_buffer[GNUNET_MAX_MESSAGE_SIZE - 1];
352 struct GNUNET_PEERSTORE_StoreHelloContext *shc;
353 struct GNUNET_PEERSTORE_StoreHelloContextClosure *shc_cls;
354 const char *cbuf = ptr;
355 const struct GNUNET_MessageHeader *msg;
356 size_t total;
357 size_t cpy;
358 size_t left;
359 uint16_t msize;
360
361 total = size * nmemb;
362 stat_bytes_downloaded += total;
363 if ((total == 0) || (stat_bogus_url))
364 {
365 return total; /* ok, no data or bogus data */
366 }
367
368 GNUNET_STATISTICS_update (stats,
369 gettext_noop (
370 "# bytes downloaded from hostlist servers"),
371 (int64_t) total,
372 GNUNET_NO);
373 left = total;
374 while ((left > 0) || (download_pos > 0))
375 {
376 cpy = GNUNET_MIN (left, GNUNET_MAX_MESSAGE_SIZE - 1 - download_pos);
377 GNUNET_memcpy (&download_buffer[download_pos], cbuf, cpy);
378 cbuf += cpy;
379 download_pos += cpy;
380 left -= cpy;
381 if (download_pos < sizeof(struct GNUNET_MessageHeader))
382 {
383 GNUNET_assert (0 == left);
384 break;
385 }
386 msg = (const struct GNUNET_MessageHeader *) download_buffer;
387 msize = ntohs (msg->size);
388 if (msize < sizeof(struct GNUNET_MessageHeader))
389 {
390 GNUNET_STATISTICS_update (
391 stats,
392 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
393 1,
394 GNUNET_NO);
395 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
396 _ ("Invalid `%s' message received from hostlist at `%s'\n"),
397 "HELLO",
398 current_url);
399 stat_hellos_obtained++;
400 stat_bogus_url = 1;
401 return total;
402 }
403 if (download_pos < msize)
404 {
405 GNUNET_assert (left == 0);
406 break;
407 }
408 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
409 "Received valid `%s' message from hostlist server.\n",
410 "HELLO");
411 GNUNET_STATISTICS_update (
412 stats,
413 gettext_noop ("# valid HELLOs downloaded from hostlist servers"),
414 1,
415 GNUNET_NO);
416 stat_hellos_obtained++;
417 shc_cls = GNUNET_new (struct GNUNET_PEERSTORE_StoreHelloContextClosure);
418 shc = GNUNET_PEERSTORE_hello_add (peerstore,
419 msg,
420 shc_cont,
421 shc_cls);
422 if (NULL != shc)
423 {
424 shc_cls->shc = shc;
425 GNUNET_CONTAINER_DLL_insert (shc_head, shc_tail, shc);
426 }
427 else
428 GNUNET_free (shc_cls);
429 memmove (download_buffer, &download_buffer[msize], download_pos - msize);
430 download_pos -= msize;
431 }
432 return total;
433}
434
435
436/**
437 * Obtain a hostlist URL that we should use.
438 *
439 * @return NULL if there is no URL available
440 */
441static char *
442get_bootstrap_server ()
443{
444 char *servers;
445 char *ret;
446 size_t urls;
447 size_t pos;
448
449 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
450 "HOSTLIST",
451 "SERVERS",
452 &servers))
453 {
454 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
455 "hostlist",
456 "SERVERS");
457 return NULL;
458 }
459
460 urls = 0;
461 if (strlen (servers) > 0)
462 {
463 urls++;
464 pos = strlen (servers) - 1;
465 while (pos > 0)
466 {
467 if (servers[pos] == ' ')
468 urls++;
469 pos--;
470 }
471 }
472 if (urls == 0)
473 {
474 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
475 "hostlist",
476 "SERVERS");
477 GNUNET_free (servers);
478 return NULL;
479 }
480
481 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
482 pos = strlen (servers) - 1;
483 while (pos > 0)
484 {
485 if (servers[pos] == ' ')
486 {
487 urls--;
488 servers[pos] = '\0';
489 }
490 if (urls == 0)
491 {
492 pos++;
493 break;
494 }
495 pos--;
496 }
497 ret = GNUNET_strdup (&servers[pos]);
498 GNUNET_free (servers);
499 return ret;
500}
501
502
503/**
504 * Method deciding if a preconfigured or advertisied hostlist is used on a 50:50 ratio
505 * @return uri to use, NULL if there is no URL available
506 */
507static char *
508download_get_url ()
509{
510 uint32_t index;
511 unsigned int counter;
512 struct Hostlist *pos;
513
514 if (GNUNET_NO == stat_learning)
515 {
516 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
517 "Using preconfigured bootstrap server\n");
518 current_hostlist = NULL;
519 return get_bootstrap_server ();
520 }
521
522 if ((GNUNET_YES == stat_testing_hostlist) && (NULL != hostlist_to_test))
523 {
524 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
525 "Testing new advertised hostlist if it is obtainable\n");
526 current_hostlist = hostlist_to_test;
527 return GNUNET_strdup (hostlist_to_test->hostlist_uri);
528 }
529
530 if ((GNUNET_YES == stat_use_bootstrap) || (linked_list_size == 0))
531 {
532 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
533 "Using preconfigured bootstrap server\n");
534 current_hostlist = NULL;
535 return get_bootstrap_server ();
536 }
537 index =
538 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, linked_list_size);
539 counter = 0;
540 pos = linked_list_head;
541 while (counter < index)
542 {
543 pos = pos->next;
544 counter++;
545 }
546 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
547 "Using learned hostlist `%s'\n",
548 pos->hostlist_uri);
549 current_hostlist = pos;
550 return GNUNET_strdup (pos->hostlist_uri);
551}
552
553
554#define CURL_EASY_SETOPT(c, a, b) \
555 do \
556 { \
557 ret = curl_easy_setopt (c, a, b); \
558 if (CURLE_OK != ret) \
559 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
560 _ ("%s failed at %s:%d: `%s'\n"), \
561 "curl_easy_setopt", \
562 __FILE__, \
563 __LINE__, \
564 curl_easy_strerror (ret)); \
565 } while (0)
566
567
568/**
569 * Method to save hostlist to a file during hostlist client shutdown
570 *
571 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
572 */
573static void
574save_hostlist_file (int shutdown);
575
576
577/**
578 * Add val2 to val1 with overflow check
579 *
580 * @param val1 value 1
581 * @param val2 value 2
582 * @return result
583 */
584static uint64_t
585checked_add (uint64_t val1, uint64_t val2)
586{
587 static uint64_t temp;
588 static uint64_t maxv;
589
590 maxv = 0;
591 maxv--;
592
593 temp = val1 + val2;
594 if (temp < val1)
595 return maxv;
596 return temp;
597}
598
599
600/**
601 * Subtract val2 from val1 with underflow check
602 *
603 * @param val1 value 1
604 * @param val2 value 2
605 * @return result
606 */
607static uint64_t
608checked_sub (uint64_t val1, uint64_t val2)
609{
610 if (val1 <= val2)
611 return 0;
612 return(val1 - val2);
613}
614
615
616/**
617 * Method to check if a URI is in hostlist linked list
618 *
619 * @param uri uri to check
620 * @return #GNUNET_YES if existing in linked list, #GNUNET_NO if not
621 */
622static int
623linked_list_contains (const char *uri)
624{
625 struct Hostlist *pos;
626
627 pos = linked_list_head;
628 while (pos != NULL)
629 {
630 if (0 == strcmp (pos->hostlist_uri, uri))
631 return GNUNET_YES;
632 pos = pos->next;
633 }
634 return GNUNET_NO;
635}
636
637
638/**
639 * Method returning the hostlist element with the lowest quality in the datastore
640 * @return hostlist with lowest quality
641 */
642static struct Hostlist *
643linked_list_get_lowest_quality ()
644{
645 struct Hostlist *pos;
646 struct Hostlist *lowest;
647
648 if (linked_list_size == 0)
649 return NULL;
650 lowest = linked_list_head;
651 pos = linked_list_head->next;
652 while (pos != NULL)
653 {
654 if (pos->quality < lowest->quality)
655 lowest = pos;
656 pos = pos->next;
657 }
658 return lowest;
659}
660
661
662/**
663 * Method to insert a hostlist into the datastore. If datastore
664 * contains maximum number of elements, the elements with lowest
665 * quality is dismissed
666 */
667static void
668insert_hostlist ()
669{
670 struct Hostlist *lowest_quality;
671
672 if (MAX_NUMBER_HOSTLISTS <= linked_list_size)
673 {
674 /* No free entries available, replace existing entry */
675 lowest_quality = linked_list_get_lowest_quality ();
676 GNUNET_assert (lowest_quality != NULL);
677 GNUNET_log (
678 GNUNET_ERROR_TYPE_DEBUG,
679 "Removing hostlist with URI `%s' which has the worst quality of all (%llu)\n",
680 lowest_quality->hostlist_uri,
681 (unsigned long long) lowest_quality->quality);
682 GNUNET_CONTAINER_DLL_remove (linked_list_head,
683 linked_list_tail,
684 lowest_quality);
685 linked_list_size--;
686 GNUNET_free (lowest_quality);
687 }
688 GNUNET_CONTAINER_DLL_insert (linked_list_head,
689 linked_list_tail,
690 hostlist_to_test);
691 linked_list_size++;
692 GNUNET_STATISTICS_set (stats,
693 gettext_noop ("# advertised hostlist URIs"),
694 linked_list_size,
695 GNUNET_NO);
696 stat_testing_hostlist = GNUNET_NO;
697}
698
699
700/**
701 * Method updating hostlist statistics
702 */
703static void
704update_hostlist ()
705{
706 char *stat;
707
708 if (((stat_use_bootstrap == GNUNET_NO) && (NULL != current_hostlist)) ||
709 ((stat_testing_hostlist == GNUNET_YES) && (NULL != current_hostlist)))
710 {
711 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
712 "Updating hostlist statics for URI `%s'\n",
713 current_hostlist->hostlist_uri);
714 current_hostlist->hello_count = stat_hellos_obtained;
715 current_hostlist->time_last_usage = GNUNET_TIME_absolute_get ();
716 current_hostlist->quality =
717 checked_add (current_hostlist->quality,
718 (stat_hellos_obtained * HOSTLIST_SUCCESSFUL_HELLO));
719 if (GNUNET_YES == stat_download_successful)
720 {
721 current_hostlist->times_used++;
722 current_hostlist->quality =
723 checked_add (current_hostlist->quality, HOSTLIST_SUCCESSFUL_DOWNLOAD);
724 GNUNET_asprintf (&stat,
725 gettext_noop ("# advertised URI `%s' downloaded"),
726 current_hostlist->hostlist_uri);
727
728 GNUNET_STATISTICS_update (stats, stat, 1, GNUNET_YES);
729 GNUNET_free (stat);
730 }
731 else
732 current_hostlist->quality =
733 checked_sub (current_hostlist->quality, HOSTLIST_FAILED_DOWNLOAD);
734 }
735 current_hostlist = NULL;
736 /* Alternating the usage of preconfigured and learned hostlists */
737
738 if (stat_testing_hostlist == GNUNET_YES)
739 return;
740
741 if (GNUNET_YES == stat_learning)
742 {
743 if (stat_use_bootstrap == GNUNET_YES)
744 stat_use_bootstrap = GNUNET_NO;
745 else
746 stat_use_bootstrap = GNUNET_YES;
747 }
748 else
749 stat_use_bootstrap = GNUNET_YES;
750}
751
752
753/**
754 * Clean up the state from the task that downloaded the
755 * hostlist and schedule the next task.
756 */
757static void
758clean_up ()
759{
760 CURLMcode mret;
761
762 if ((stat_testing_hostlist == GNUNET_YES) &&
763 (GNUNET_NO == stat_download_successful) && (NULL != hostlist_to_test))
764 {
765 GNUNET_log (
766 GNUNET_ERROR_TYPE_INFO,
767 _ (
768 "Advertised hostlist with URI `%s' could not be downloaded. Advertised URI gets dismissed.\n"),
769 hostlist_to_test->hostlist_uri);
770 }
771
772 if (stat_testing_hostlist == GNUNET_YES)
773 {
774 stat_testing_hostlist = GNUNET_NO;
775 }
776 if (NULL != hostlist_to_test)
777 {
778 GNUNET_free (hostlist_to_test);
779 hostlist_to_test = NULL;
780 }
781
782 if (NULL != multi)
783 {
784 mret = curl_multi_remove_handle (multi, curl);
785 if (mret != CURLM_OK)
786 {
787 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
788 _ ("%s failed at %s:%d: `%s'\n"),
789 "curl_multi_remove_handle",
790 __FILE__,
791 __LINE__,
792 curl_multi_strerror (mret));
793 }
794 mret = curl_multi_cleanup (multi);
795 if (mret != CURLM_OK)
796 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
797 _ ("%s failed at %s:%d: `%s'\n"),
798 "curl_multi_cleanup",
799 __FILE__,
800 __LINE__,
801 curl_multi_strerror (mret));
802 multi = NULL;
803 }
804 if (NULL != curl)
805 {
806 curl_easy_cleanup (curl);
807 curl = NULL;
808 }
809 GNUNET_free (current_url);
810 current_url = NULL;
811 stat_bytes_downloaded = 0;
812 stat_download_in_progress = GNUNET_NO;
813}
814
815
816/**
817 * Task that is run when we are ready to receive more data from the hostlist
818 * server.
819 *
820 * @param cls closure, unused
821 */
822static void
823task_download (void *cls);
824
825
826/**
827 * Ask CURL for the select set and then schedule the
828 * receiving task with the scheduler.
829 */
830static void
831download_prepare ()
832{
833 CURLMcode mret;
834 fd_set rs;
835 fd_set ws;
836 fd_set es;
837 int max;
838 struct GNUNET_NETWORK_FDSet *grs;
839 struct GNUNET_NETWORK_FDSet *gws;
840 long timeout;
841 struct GNUNET_TIME_Relative rtime;
842
843 max = -1;
844 FD_ZERO (&rs);
845 FD_ZERO (&ws);
846 FD_ZERO (&es);
847 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
848 if (mret != CURLM_OK)
849 {
850 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
851 _ ("%s failed at %s:%d: `%s'\n"),
852 "curl_multi_fdset",
853 __FILE__,
854 __LINE__,
855 curl_multi_strerror (mret));
856 clean_up ();
857 return;
858 }
859 mret = curl_multi_timeout (multi, &timeout);
860 if (mret != CURLM_OK)
861 {
862 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
863 _ ("%s failed at %s:%d: `%s'\n"),
864 "curl_multi_timeout",
865 __FILE__,
866 __LINE__,
867 curl_multi_strerror (mret));
868 clean_up ();
869 return;
870 }
871 rtime = GNUNET_TIME_relative_min (
872 GNUNET_TIME_absolute_get_remaining (end_time),
873 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, timeout));
874 grs = GNUNET_NETWORK_fdset_create ();
875 gws = GNUNET_NETWORK_fdset_create ();
876 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
877 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
878 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
879 "Scheduling task for hostlist download using cURL\n");
880 ti_download = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
881 rtime,
882 grs,
883 gws,
884 &task_download,
885 multi);
886 GNUNET_NETWORK_fdset_destroy (gws);
887 GNUNET_NETWORK_fdset_destroy (grs);
888}
889
890
891static void
892task_download (void *cls)
893{
894 int running;
895 struct CURLMsg *msg;
896 CURLMcode mret;
897
898 ti_download = NULL;
899 if (0 == GNUNET_TIME_absolute_get_remaining (end_time).rel_value_us)
900 {
901 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
902 _ ("Timeout trying to download hostlist from `%s'\n"),
903 current_url);
904 update_hostlist ();
905 clean_up ();
906 return;
907 }
908 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
909 "Ready for processing hostlist client request\n");
910 do
911 {
912 running = 0;
913 if (stat_bytes_downloaded > MAX_BYTES_PER_HOSTLISTS)
914 {
915 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
916 _ (
917 "Download limit of %u bytes exceeded, stopping download\n"),
918 MAX_BYTES_PER_HOSTLISTS);
919 clean_up ();
920 return;
921 }
922 mret = curl_multi_perform (multi, &running);
923 if (running == 0)
924 {
925 do
926 {
927 msg = curl_multi_info_read (multi, &running);
928 GNUNET_break (msg != NULL);
929 if (msg == NULL)
930 break;
931 switch (msg->msg)
932 {
933 case CURLMSG_DONE:
934 if ((msg->data.result != CURLE_OK) &&
935 (msg->data.result != CURLE_GOT_NOTHING))
936 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
937 _ ("Download of hostlist from `%s' failed: `%s'\n"),
938 current_url,
939 curl_easy_strerror (msg->data.result));
940 else
941 {
942 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
943 _ ("Download of hostlist `%s' completed.\n"),
944 current_url);
945 stat_download_successful = GNUNET_YES;
946 update_hostlist ();
947 if (GNUNET_YES == stat_testing_hostlist)
948 {
949 GNUNET_log (
950 GNUNET_ERROR_TYPE_INFO,
951 _ ("Adding successfully tested hostlist `%s' datastore.\n"),
952 current_url);
953 insert_hostlist ();
954 hostlist_to_test = NULL;
955 stat_testing_hostlist = GNUNET_NO;
956 }
957 }
958 clean_up ();
959 return;
960
961 default:
962 break;
963 }
964 }
965 while ((running > 0));
966 }
967 }
968 while (mret == CURLM_CALL_MULTI_PERFORM);
969
970 if (mret != CURLM_OK)
971 {
972 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
973 _ ("%s failed at %s:%d: `%s'\n"),
974 "curl_multi_perform",
975 __FILE__,
976 __LINE__,
977 curl_multi_strerror (mret));
978 clean_up ();
979 }
980 download_prepare ();
981}
982
983
984/**
985 * Main function that will download a hostlist and process its
986 * data.
987 */
988static void
989download_hostlist ()
990{
991 CURLcode ret;
992 CURLMcode mret;
993
994
995 current_url = download_get_url ();
996 if (current_url == NULL)
997 return;
998 curl = curl_easy_init ();
999 multi = NULL;
1000 if (curl == NULL)
1001 {
1002 GNUNET_break (0);
1003 clean_up ();
1004 return;
1005 }
1006 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1007 _ ("Bootstrapping using hostlist at `%s'.\n"),
1008 current_url);
1009
1010 stat_download_in_progress = GNUNET_YES;
1011 stat_download_successful = GNUNET_NO;
1012 stat_hellos_obtained = 0;
1013 stat_bytes_downloaded = 0;
1014
1015 GNUNET_STATISTICS_update (stats,
1016 gettext_noop ("# hostlist downloads initiated"),
1017 1,
1018 GNUNET_NO);
1019 if (NULL != proxy)
1020 {
1021 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
1022 CURL_EASY_SETOPT (curl, CURLOPT_PROXYTYPE, proxy_type);
1023 if (NULL != proxy_username)
1024 CURL_EASY_SETOPT (curl, CURLOPT_PROXYUSERNAME, proxy_username);
1025 if (NULL != proxy_password)
1026 CURL_EASY_SETOPT (curl, CURLOPT_PROXYPASSWORD, proxy_password);
1027 }
1028 download_pos = 0;
1029 stat_bogus_url = 0;
1030 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &callback_download);
1031 if (ret != CURLE_OK)
1032 {
1033 clean_up ();
1034 return;
1035 }
1036 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, NULL);
1037 if (ret != CURLE_OK)
1038 {
1039 clean_up ();
1040 return;
1041 }
1042 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
1043 CURL_EASY_SETOPT (curl,
1044 CURLOPT_REDIR_PROTOCOLS,
1045 CURLPROTO_HTTP | CURLPROTO_HTTPS);
1046 CURL_EASY_SETOPT (curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
1047 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
1048 /* no need to abort if the above failed */
1049 CURL_EASY_SETOPT (curl, CURLOPT_URL, current_url);
1050 if (ret != CURLE_OK)
1051 {
1052 clean_up ();
1053 return;
1054 }
1055 CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
1056#if 0
1057 CURL_EASY_SETOPT (curl, CURLOPT_VERBOSE, 1);
1058#endif
1059 CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, GNUNET_MAX_MESSAGE_SIZE);
1060 if (0 == strncmp (current_url, "http", 4))
1061 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
1062 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 60L);
1063 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 60L);
1064 multi = curl_multi_init ();
1065 if (multi == NULL)
1066 {
1067 GNUNET_break (0);
1068 /* clean_up (); */
1069 return;
1070 }
1071 mret = curl_multi_add_handle (multi, curl);
1072 if (mret != CURLM_OK)
1073 {
1074 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1075 _ ("%s failed at %s:%d: `%s'\n"),
1076 "curl_multi_add_handle",
1077 __FILE__,
1078 __LINE__,
1079 curl_multi_strerror (mret));
1080 mret = curl_multi_cleanup (multi);
1081 if (mret != CURLM_OK)
1082 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1083 _ ("%s failed at %s:%d: `%s'\n"),
1084 "curl_multi_cleanup",
1085 __FILE__,
1086 __LINE__,
1087 curl_multi_strerror (mret));
1088 multi = NULL;
1089 clean_up ();
1090 return;
1091 }
1092 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
1093 download_prepare ();
1094}
1095
1096
1097static void
1098task_download_dispatcher (void *cls)
1099{
1100 ti_download_dispatcher_task = NULL;
1101 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download is initiated...\n");
1102 if (GNUNET_NO == stat_download_in_progress)
1103 {
1104 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download can start immediately...\n");
1105 download_hostlist ();
1106 }
1107 else
1108 {
1109 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1110 "Download in progress, have to wait...\n");
1111 ti_download_dispatcher_task =
1112 GNUNET_SCHEDULER_add_delayed (WAITING_INTERVAL,
1113 &task_download_dispatcher,
1114 NULL);
1115 }
1116}
1117
1118
1119/**
1120 * Task that checks if we should try to download a hostlist.
1121 * If so, we initiate the download, otherwise we schedule
1122 * this task again for a later time.
1123 */
1124static void
1125task_check (void *cls)
1126{
1127 static int once;
1128 struct GNUNET_TIME_Relative delay;
1129
1130 ti_check_download = NULL;
1131 if (stats == NULL)
1132 {
1133 curl_global_cleanup ();
1134 return; /* in shutdown */
1135 }
1136 if ((stat_connection_count < MIN_CONNECTIONS) &&
1137 (NULL == ti_download_dispatcher_task))
1138 ti_download_dispatcher_task =
1139 GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1140
1141 delay = hostlist_delay;
1142 if (0 == hostlist_delay.rel_value_us)
1143 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1144 else
1145 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1146 if (hostlist_delay.rel_value_us >
1147 GNUNET_TIME_UNIT_HOURS.rel_value_us * (1 + stat_connection_count))
1148 hostlist_delay =
1149 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1150 (1 + stat_connection_count));
1151 GNUNET_STATISTICS_set (stats,
1152 gettext_noop (
1153 "# milliseconds between hostlist downloads"),
1154 hostlist_delay.rel_value_us / 1000LL,
1155 GNUNET_YES);
1156 if (0 == once)
1157 {
1158 delay = GNUNET_TIME_UNIT_ZERO;
1159 once = 1;
1160 }
1161 GNUNET_log (
1162 GNUNET_ERROR_TYPE_INFO,
1163 _ ("Have %u/%u connections. Will consider downloading hostlist in %s\n"),
1164 stat_connection_count,
1165 MIN_CONNECTIONS,
1166 GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES));
1167 ti_check_download = GNUNET_SCHEDULER_add_delayed (delay, &task_check, NULL);
1168}
1169
1170
1171/**
1172 * This tasks sets hostlist testing to allowed after interval between to testings is reached
1173 *
1174 * @param cls closure
1175 */
1176static void
1177task_testing_intervall_reset (void *cls)
1178{
1179 ti_testing_intervall_task = NULL;
1180 stat_testing_allowed = GNUNET_OK;
1181 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1182 "Testing new hostlist advertisements is allowed again\n");
1183}
1184
1185
1186/**
1187 * Task that writes hostlist entries to a file on a regular base
1188 *
1189 * @param cls closure
1190 */
1191static void
1192task_hostlist_saving (void *cls)
1193{
1194 ti_saving_task = NULL;
1195 save_hostlist_file (GNUNET_NO);
1196
1197 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1198 "Hostlists will be saved to file again in %s\n",
1199 GNUNET_STRINGS_relative_time_to_string (SAVING_INTERVAL,
1200 GNUNET_YES));
1201 ti_saving_task =
1202 GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL, &task_hostlist_saving, NULL);
1203}
1204
1205
1206/**
1207 * Method called whenever a given peer connects.
1208 *
1209 * @param cls closure
1210 * @param peer peer identity this notification is about
1211 * @param mq message queue for transmissions to @a peer
1212 */
1213static void *
1214handler_connect (void *cls,
1215 const struct GNUNET_PeerIdentity *peer,
1216 struct GNUNET_MQ_Handle *mq)
1217{
1218 GNUNET_assert (stat_connection_count < UINT_MAX);
1219 stat_connection_count++;
1220 GNUNET_STATISTICS_update (stats,
1221 gettext_noop ("# active connections"),
1222 1,
1223 GNUNET_NO);
1224 return NULL;
1225}
1226
1227
1228/**
1229 * Method called whenever a given peer disconnects.
1230 *
1231 * @param cls closure
1232 * @param peer peer identity this notification is about
1233 */
1234static void
1235handler_disconnect (void *cls,
1236 const struct GNUNET_PeerIdentity *peer,
1237 void *internal_cls)
1238{
1239 GNUNET_assert (stat_connection_count > 0);
1240 stat_connection_count--;
1241 GNUNET_STATISTICS_update (stats,
1242 gettext_noop ("# active connections"),
1243 -1,
1244 GNUNET_NO);
1245}
1246
1247
1248/**
1249 * Method called whenever an advertisement message arrives.
1250 *
1251 * @param uri the advertised URI
1252 */
1253static void
1254handler_advertisement (const char *uri)
1255{
1256 size_t uri_size;
1257 struct Hostlist *hostlist;
1258
1259 uri_size = strlen (uri) + 1;
1260 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1261 "Hostlist client received advertisement containing URI `%s'\n",
1262 uri);
1263 if (GNUNET_NO != linked_list_contains (uri))
1264 {
1265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "URI `%s' is already known\n", uri);
1266 return;
1267 }
1268
1269 if (GNUNET_NO == stat_testing_allowed)
1270 {
1271 GNUNET_log (
1272 GNUNET_ERROR_TYPE_DEBUG,
1273 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1274 return;
1275 }
1276 if (GNUNET_YES == stat_testing_hostlist)
1277 {
1278 GNUNET_log (
1279 GNUNET_ERROR_TYPE_DEBUG,
1280 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1281 return;
1282 }
1283
1284 hostlist = GNUNET_malloc (sizeof(struct Hostlist) + uri_size);
1285 hostlist->hostlist_uri = (const char *) &hostlist[1];
1286 GNUNET_memcpy (&hostlist[1], uri, uri_size);
1287 hostlist->time_creation = GNUNET_TIME_absolute_get ();
1288 hostlist->quality = HOSTLIST_INITIAL;
1289 hostlist_to_test = hostlist;
1290
1291 stat_testing_hostlist = GNUNET_YES;
1292 stat_testing_allowed = GNUNET_NO;
1293 ti_testing_intervall_task =
1294 GNUNET_SCHEDULER_add_delayed (TESTING_INTERVAL,
1295 &task_testing_intervall_reset,
1296 NULL);
1297
1298 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1299 "Testing new hostlist advertisements is locked for the next %s\n",
1300 GNUNET_STRINGS_relative_time_to_string (TESTING_INTERVAL,
1301 GNUNET_YES));
1302
1303 ti_download_dispatcher_task =
1304 GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1305}
1306
1307
1308/**
1309 * Continuation called by the statistics code once
1310 * we go the stat. Initiates hostlist download scheduling.
1311 *
1312 * @param cls closure
1313 * @param success #GNUNET_OK if statistics were
1314 * successfully obtained, #GNUNET_SYSERR if not.
1315 */
1316static void
1317primary_task (void *cls, int success)
1318{
1319 if (NULL != ti_check_download)
1320 {
1321 GNUNET_SCHEDULER_cancel (ti_check_download);
1322 ti_check_download = NULL;
1323 }
1324 sget = NULL;
1325 GNUNET_assert (NULL != stats);
1326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1327 "Statistics request done, scheduling hostlist download\n");
1328 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1329}
1330
1331
1332/**
1333 * Continuation called by the statistics code once
1334 * we go the stat. Initiates hostlist download scheduling.
1335 *
1336 * @param cls closure
1337 */
1338static void
1339stat_timeout_task (void *cls)
1340{
1341 GNUNET_STATISTICS_get_cancel (sget);
1342 sget = NULL;
1343 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1344}
1345
1346
1347/**
1348 * We've received the previous delay value from statistics. Remember it.
1349 *
1350 * @param cls NULL, unused
1351 * @param subsystem should be "hostlist", unused
1352 * @param name will be "milliseconds between hostlist downloads", unused
1353 * @param value previous delay value, in milliseconds (!)
1354 * @param is_persistent unused, will be #GNUNET_YES
1355 */
1356static int
1357process_stat (void *cls,
1358 const char *subsystem,
1359 const char *name,
1360 uint64_t value,
1361 int is_persistent)
1362{
1363 hostlist_delay.rel_value_us = value * 1000LL;
1364 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1365 "Initial time between hostlist downloads is %s\n",
1366 GNUNET_STRINGS_relative_time_to_string (hostlist_delay,
1367 GNUNET_YES));
1368 return GNUNET_OK;
1369}
1370
1371
1372/**
1373 * Method to load persistent hostlist file during hostlist client startup
1374 */
1375static void
1376load_hostlist_file ()
1377{
1378 char *filename;
1379 char *uri;
1380 char *emsg;
1381 struct Hostlist *hostlist;
1382 uint32_t times_used;
1383 uint32_t hellos_returned;
1384 uint64_t quality;
1385 uint64_t last_used;
1386 uint64_t created;
1387 uint32_t counter;
1388 struct GNUNET_BIO_ReadHandle *rh;
1389
1390 uri = NULL;
1391 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1392 "HOSTLIST",
1393 "HOSTLISTFILE",
1394 &filename))
1395 {
1396 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1397 "hostlist",
1398 "HOSTLISTFILE");
1399 return;
1400 }
1401
1402 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1403 _ ("Loading saved hostlist entries from file `%s' \n"),
1404 filename);
1405 if (GNUNET_NO == GNUNET_DISK_file_test (filename))
1406 {
1407 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1408 _ ("Hostlist file `%s' does not exist\n"),
1409 filename);
1410 GNUNET_free (filename);
1411 return;
1412 }
1413
1414 rh = GNUNET_BIO_read_open_file (filename);
1415 if (NULL == rh)
1416 {
1417 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1418 _ (
1419 "Could not open file `%s' for reading to load hostlists: %s\n"),
1420 filename,
1421 strerror (errno));
1422 GNUNET_free (filename);
1423 return;
1424 }
1425
1426 counter = 0;
1427 struct GNUNET_BIO_ReadSpec rs[] = {
1428 GNUNET_BIO_read_spec_int32 ("times used", (int32_t *) &times_used),
1429 GNUNET_BIO_read_spec_int64 ("quality", (int64_t *) &quality),
1430 GNUNET_BIO_read_spec_int64 ("last used", (int64_t *) &last_used),
1431 GNUNET_BIO_read_spec_int64 ("created", (int64_t *) &created),
1432 GNUNET_BIO_read_spec_int32 ("hellos returned",
1433 (int32_t *) &hellos_returned),
1434 GNUNET_BIO_read_spec_end (),
1435 };
1436 while ((GNUNET_OK == GNUNET_BIO_read_string (rh, "url", &uri, MAX_URL_LEN)) &&
1437 (NULL != uri) &&
1438 (GNUNET_OK == GNUNET_BIO_read_spec_commit (rh, rs)))
1439 {
1440 hostlist = GNUNET_malloc (sizeof(struct Hostlist) + strlen (uri) + 1);
1441 hostlist->hello_count = hellos_returned;
1442 hostlist->hostlist_uri = (const char *) &hostlist[1];
1443 GNUNET_memcpy (&hostlist[1], uri, strlen (uri) + 1);
1444 hostlist->quality = quality;
1445 hostlist->time_creation.abs_value_us = created;
1446 hostlist->time_last_usage.abs_value_us = last_used;
1447 GNUNET_CONTAINER_DLL_insert (linked_list_head, linked_list_tail, hostlist);
1448 linked_list_size++;
1449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1450 "Added hostlist entry with URI `%s' \n",
1451 hostlist->hostlist_uri);
1452 GNUNET_free (uri);
1453 uri = NULL;
1454 counter++;
1455 if (counter >= MAX_NUMBER_HOSTLISTS)
1456 break;
1457 }
1458
1459 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1460 _ ("%u hostlist URIs loaded from file\n"),
1461 counter);
1462 GNUNET_STATISTICS_set (stats,
1463 gettext_noop ("# hostlist URIs read from file"),
1464 counter,
1465 GNUNET_YES);
1466 GNUNET_STATISTICS_set (stats,
1467 gettext_noop ("# advertised hostlist URIs"),
1468 linked_list_size,
1469 GNUNET_NO);
1470
1471 GNUNET_free (uri);
1472 emsg = NULL;
1473 (void) GNUNET_BIO_read_close (rh, &emsg);
1474 if (emsg != NULL)
1475 GNUNET_free (emsg);
1476 GNUNET_free (filename);
1477}
1478
1479
1480/**
1481 * Method to save persistent hostlist file during hostlist client shutdown
1482 *
1483 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1484 */
1485static void
1486save_hostlist_file (int shutdown)
1487{
1488 char *filename;
1489 struct Hostlist *pos;
1490 struct GNUNET_BIO_WriteHandle *wh;
1491 int ok;
1492 uint32_t counter;
1493
1494 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1495 "HOSTLIST",
1496 "HOSTLISTFILE",
1497 &filename))
1498 {
1499 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1500 "hostlist",
1501 "HOSTLISTFILE");
1502 return;
1503 }
1504 if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
1505 {
1506 GNUNET_free (filename);
1507 return;
1508 }
1509 wh = GNUNET_BIO_write_open_file (filename);
1510 if (NULL == wh)
1511 {
1512 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1513 _ (
1514 "Could not open file `%s' for writing to save hostlists: %s\n"),
1515 filename,
1516 strerror (errno));
1517 GNUNET_free (filename);
1518 return;
1519 }
1520 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1521 _ ("Writing %u hostlist URIs to `%s'\n"),
1522 linked_list_size,
1523 filename);
1524 /* add code to write hostlists to file using bio */
1525 ok = GNUNET_YES;
1526 counter = 0;
1527 while (NULL != (pos = linked_list_head))
1528 {
1529 if (GNUNET_YES == shutdown)
1530 {
1531 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1532 linked_list_size--;
1533 }
1534 if (GNUNET_YES == ok)
1535 {
1536 struct GNUNET_BIO_WriteSpec ws[] = {
1537 GNUNET_BIO_write_spec_string ("hostlist uri", pos->hostlist_uri),
1538 GNUNET_BIO_write_spec_int32 ("times used",
1539 (int32_t *) &pos->times_used),
1540 GNUNET_BIO_write_spec_int64 ("quality", (int64_t *) &pos->quality),
1541 GNUNET_BIO_write_spec_int64 (
1542 "last usage",
1543 (int64_t *) &pos->time_last_usage.abs_value_us),
1544 GNUNET_BIO_write_spec_int64 (
1545 "creation time",
1546 (int64_t *) &pos->time_creation.abs_value_us),
1547 GNUNET_BIO_write_spec_int32 ("hellos count",
1548 (int32_t *) &pos->hello_count),
1549 GNUNET_BIO_write_spec_end (),
1550 };
1551 if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)))
1552 {
1553 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1554 _ ("Error writing hostlist URIs to file `%s'\n"),
1555 filename);
1556 ok = GNUNET_NO;
1557 }
1558 }
1559
1560 if (GNUNET_YES == shutdown)
1561 GNUNET_free (pos);
1562 counter++;
1563 if (counter >= MAX_NUMBER_HOSTLISTS)
1564 break;
1565 }
1566 GNUNET_STATISTICS_set (stats,
1567 gettext_noop ("# hostlist URIs written to file"),
1568 counter,
1569 GNUNET_YES);
1570
1571 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
1572 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1573 _ ("Error writing hostlist URIs to file `%s'\n"),
1574 filename);
1575 GNUNET_free (filename);
1576}
1577
1578
1579/**
1580 * Start downloading hostlists from hostlist servers as necessary.
1581 *
1582 * @param c configuration to use
1583 * @param st statistics handle to use
1584 * @param[out] ch set to handler for CORE connect events
1585 * @param[out] dh set to handler for CORE disconnect events
1586 * @param[out] msgh set to handler for CORE advertisement messages
1587 * @param learn should we learn hostlist URLs from CORE
1588 * @return #GNUNET_OK on success
1589 */
1590int
1591GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1592 struct GNUNET_STATISTICS_Handle *st,
1593 GNUNET_CORE_ConnectEventHandler *ch,
1594 GNUNET_CORE_DisconnectEventHandler *dh,
1595 GNUNET_HOSTLIST_UriHandler *msgh,
1596 int learn)
1597{
1598 char *filename;
1599 char *proxytype_str;
1600 int result;
1601
1602 GNUNET_assert (NULL != st);
1603 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1604 {
1605 GNUNET_break (0);
1606 return GNUNET_SYSERR;
1607 }
1608 cfg = c;
1609 stats = st;
1610
1611 /* Read proxy configuration */
1612 peerstore = GNUNET_PEERSTORE_connect (c);
1613 if (GNUNET_OK ==
1614 GNUNET_CONFIGURATION_get_value_string (cfg, "HOSTLIST", "PROXY", &proxy))
1615 {
1616 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found proxy host: `%s'\n", proxy);
1617 /* proxy username */
1618 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1619 "HOSTLIST",
1620 "PROXY_USERNAME",
1621 &proxy_username))
1622 {
1623 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1624 "Found proxy username name: `%s'\n",
1625 proxy_username);
1626 }
1627
1628 /* proxy password */
1629 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1630 "HOSTLIST",
1631 "PROXY_PASSWORD",
1632 &proxy_password))
1633 {
1634 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1635 "Found proxy password name: `%s'\n",
1636 proxy_password);
1637 }
1638
1639 /* proxy type */
1640 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1641 "HOSTLIST",
1642 "PROXY_TYPE",
1643 &proxytype_str))
1644 {
1645 if (GNUNET_OK != GNUNET_STRINGS_utf8_toupper (proxytype_str,
1646 proxytype_str))
1647 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1648 "Unable to convert `%s' to UTF-8 uppercase\n",
1649 proxytype_str);
1650 proxy_type = CURLPROXY_HTTP;
1651 if (0 == strcmp (proxytype_str, "HTTP"))
1652 proxy_type = CURLPROXY_HTTP;
1653 else if (0 == strcmp (proxytype_str, "HTTP_1_0"))
1654 proxy_type = CURLPROXY_HTTP_1_0;
1655 else if (0 == strcmp (proxytype_str, "SOCKS4"))
1656 proxy_type = CURLPROXY_SOCKS4;
1657 else if (0 == strcmp (proxytype_str, "SOCKS5"))
1658 proxy_type = CURLPROXY_SOCKS5;
1659 else if (0 == strcmp (proxytype_str, "SOCKS4A"))
1660 proxy_type = CURLPROXY_SOCKS4A;
1661 else if (0 == strcmp (proxytype_str, "SOCKS5_HOSTNAME"))
1662 proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
1663 else
1664 {
1665 GNUNET_log (
1666 GNUNET_ERROR_TYPE_ERROR,
1667 _ (
1668 "Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
1669 proxytype_str);
1670 GNUNET_free (proxytype_str);
1671 GNUNET_free (proxy);
1672 proxy = NULL;
1673 GNUNET_free (proxy_username);
1674 proxy_username = NULL;
1675 GNUNET_free (proxy_password);
1676 proxy_password = NULL;
1677
1678 return GNUNET_SYSERR;
1679 }
1680 }
1681 GNUNET_free (proxytype_str);
1682 }
1683
1684 stat_learning = learn;
1685 *ch = &handler_connect;
1686 *dh = &handler_disconnect;
1687 linked_list_head = NULL;
1688 linked_list_tail = NULL;
1689 stat_use_bootstrap = GNUNET_YES;
1690 stat_testing_hostlist = GNUNET_NO;
1691 stat_testing_allowed = GNUNET_YES;
1692
1693 if (GNUNET_YES == stat_learning)
1694 {
1695 *msgh = &handler_advertisement;
1696 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1697 _ ("Learning is enabled on this peer\n"));
1698 load_hostlist_file ();
1699 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1700 "Hostlists will be saved to file again in %s\n",
1701 GNUNET_STRINGS_relative_time_to_string (SAVING_INTERVAL,
1702 GNUNET_YES));
1703 ti_saving_task = GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL,
1704 &task_hostlist_saving,
1705 NULL);
1706 }
1707 else
1708 {
1709 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1710 _ ("Learning is not enabled on this peer\n"));
1711 *msgh = NULL;
1712 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg,
1713 "HOSTLIST",
1714 "HOSTLISTFILE",
1715 &filename))
1716 {
1717 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
1718 {
1719 result = remove (filename);
1720 if (0 == result)
1721 GNUNET_log (
1722 GNUNET_ERROR_TYPE_INFO,
1723 _ (
1724 "Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),
1725 filename);
1726 else
1727 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1728 "remove",
1729 filename);
1730 }
1731 }
1732 GNUNET_free (filename);
1733 }
1734 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1735 "Loading stats value on hostlist download frequency\n");
1736 sget = GNUNET_STATISTICS_get (stats,
1737 "hostlist",
1738 gettext_noop (
1739 "# milliseconds between hostlist downloads"),
1740 &primary_task,
1741 &process_stat,
1742 NULL);
1743 if (NULL == sget)
1744 {
1745 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1746 "Statistics request failed, scheduling hostlist download\n");
1747 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1748 }
1749 else
1750 {
1751 ti_check_download = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
1752 &stat_timeout_task,
1753 NULL);
1754 }
1755 return GNUNET_OK;
1756}
1757
1758
1759/**
1760 * Stop downloading hostlists from hostlist servers as necessary.
1761 */
1762void
1763GNUNET_HOSTLIST_client_stop ()
1764{
1765 struct GNUNET_PEERSTORE_StoreHelloContext *pos;
1766
1767 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostlist client shutdown\n");
1768 while (NULL != (pos = shc_head))
1769 {
1770 GNUNET_CONTAINER_DLL_remove (shc_head, shc_tail, pos);
1771 GNUNET_PEERSTORE_hello_add_cancel (pos);
1772 }
1773 if (NULL != sget)
1774 {
1775 GNUNET_STATISTICS_get_cancel (sget);
1776 sget = NULL;
1777 }
1778 stats = NULL;
1779 if (GNUNET_YES == stat_learning)
1780 save_hostlist_file (GNUNET_YES);
1781 if (NULL != ti_saving_task)
1782 {
1783 GNUNET_SCHEDULER_cancel (ti_saving_task);
1784 ti_saving_task = NULL;
1785 }
1786 if (NULL != ti_download_dispatcher_task)
1787 {
1788 GNUNET_SCHEDULER_cancel (ti_download_dispatcher_task);
1789 ti_download_dispatcher_task = NULL;
1790 }
1791 if (NULL != ti_testing_intervall_task)
1792 {
1793 GNUNET_SCHEDULER_cancel (ti_testing_intervall_task);
1794 ti_testing_intervall_task = NULL;
1795 }
1796 if (NULL != ti_download)
1797 {
1798 GNUNET_SCHEDULER_cancel (ti_download);
1799 ti_download = NULL;
1800 update_hostlist ();
1801 clean_up ();
1802 }
1803 if (NULL != ti_check_download)
1804 {
1805 GNUNET_SCHEDULER_cancel (ti_check_download);
1806 ti_check_download = NULL;
1807 curl_global_cleanup ();
1808 }
1809 GNUNET_free (proxy);
1810 proxy = NULL;
1811 GNUNET_free (proxy_username);
1812 proxy_username = NULL;
1813 GNUNET_free (proxy_password);
1814 proxy_password = NULL;
1815 if (NULL != peerstore)
1816 {
1817 GNUNET_PEERSTORE_disconnect (peerstore, GNUNET_YES);
1818 peerstore = NULL;
1819 }
1820 cfg = NULL;
1821}
1822
1823
1824/* end of gnunet-daemon-hostlist_client.c */