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