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.c1810
1 files changed, 0 insertions, 1810 deletions
diff --git a/src/hostlist/gnunet-daemon-hostlist_client.c b/src/hostlist/gnunet-daemon-hostlist_client.c
deleted file mode 100644
index 1f7d4cc35..000000000
--- a/src/hostlist/gnunet-daemon-hostlist_client.c
+++ /dev/null
@@ -1,1810 +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 * @param tc task context, unused
806 */
807static void
808task_download (void *cls);
809
810
811/**
812 * Ask CURL for the select set and then schedule the
813 * receiving task with the scheduler.
814 */
815static void
816download_prepare ()
817{
818 CURLMcode mret;
819 fd_set rs;
820 fd_set ws;
821 fd_set es;
822 int max;
823 struct GNUNET_NETWORK_FDSet *grs;
824 struct GNUNET_NETWORK_FDSet *gws;
825 long timeout;
826 struct GNUNET_TIME_Relative rtime;
827
828 max = -1;
829 FD_ZERO (&rs);
830 FD_ZERO (&ws);
831 FD_ZERO (&es);
832 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
833 if (mret != CURLM_OK)
834 {
835 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
836 _ ("%s failed at %s:%d: `%s'\n"),
837 "curl_multi_fdset",
838 __FILE__,
839 __LINE__,
840 curl_multi_strerror (mret));
841 clean_up ();
842 return;
843 }
844 mret = curl_multi_timeout (multi, &timeout);
845 if (mret != CURLM_OK)
846 {
847 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
848 _ ("%s failed at %s:%d: `%s'\n"),
849 "curl_multi_timeout",
850 __FILE__,
851 __LINE__,
852 curl_multi_strerror (mret));
853 clean_up ();
854 return;
855 }
856 rtime = GNUNET_TIME_relative_min (
857 GNUNET_TIME_absolute_get_remaining (end_time),
858 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, timeout));
859 grs = GNUNET_NETWORK_fdset_create ();
860 gws = GNUNET_NETWORK_fdset_create ();
861 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
862 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
863 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864 "Scheduling task for hostlist download using cURL\n");
865 ti_download = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
866 rtime,
867 grs,
868 gws,
869 &task_download,
870 multi);
871 GNUNET_NETWORK_fdset_destroy (gws);
872 GNUNET_NETWORK_fdset_destroy (grs);
873}
874
875
876/**
877 * Task that is run when we are ready to receive more data from the hostlist
878 * server.
879 *
880 * @param cls closure, unused
881 */
882static void
883task_download (void *cls)
884{
885 int running;
886 struct CURLMsg *msg;
887 CURLMcode mret;
888
889 ti_download = NULL;
890 if (0 == GNUNET_TIME_absolute_get_remaining (end_time).rel_value_us)
891 {
892 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
893 _ ("Timeout trying to download hostlist from `%s'\n"),
894 current_url);
895 update_hostlist ();
896 clean_up ();
897 return;
898 }
899 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
900 "Ready for processing hostlist client request\n");
901 do
902 {
903 running = 0;
904 if (stat_bytes_downloaded > MAX_BYTES_PER_HOSTLISTS)
905 {
906 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
907 _ (
908 "Download limit of %u bytes exceeded, stopping download\n"),
909 MAX_BYTES_PER_HOSTLISTS);
910 clean_up ();
911 return;
912 }
913 mret = curl_multi_perform (multi, &running);
914 if (running == 0)
915 {
916 do
917 {
918 msg = curl_multi_info_read (multi, &running);
919 GNUNET_break (msg != NULL);
920 if (msg == NULL)
921 break;
922 switch (msg->msg)
923 {
924 case CURLMSG_DONE:
925 if ((msg->data.result != CURLE_OK) &&
926 (msg->data.result != CURLE_GOT_NOTHING))
927 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
928 _ ("Download of hostlist from `%s' failed: `%s'\n"),
929 current_url,
930 curl_easy_strerror (msg->data.result));
931 else
932 {
933 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
934 _ ("Download of hostlist `%s' completed.\n"),
935 current_url);
936 stat_download_successful = GNUNET_YES;
937 update_hostlist ();
938 if (GNUNET_YES == stat_testing_hostlist)
939 {
940 GNUNET_log (
941 GNUNET_ERROR_TYPE_INFO,
942 _ ("Adding successfully tested hostlist `%s' datastore.\n"),
943 current_url);
944 insert_hostlist ();
945 hostlist_to_test = NULL;
946 stat_testing_hostlist = GNUNET_NO;
947 }
948 }
949 clean_up ();
950 return;
951
952 default:
953 break;
954 }
955 }
956 while ((running > 0));
957 }
958 }
959 while (mret == CURLM_CALL_MULTI_PERFORM);
960
961 if (mret != CURLM_OK)
962 {
963 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
964 _ ("%s failed at %s:%d: `%s'\n"),
965 "curl_multi_perform",
966 __FILE__,
967 __LINE__,
968 curl_multi_strerror (mret));
969 clean_up ();
970 }
971 download_prepare ();
972}
973
974
975/**
976 * Main function that will download a hostlist and process its
977 * data.
978 */
979static void
980download_hostlist ()
981{
982 CURLcode ret;
983 CURLMcode mret;
984
985
986 current_url = download_get_url ();
987 if (current_url == NULL)
988 return;
989 curl = curl_easy_init ();
990 multi = NULL;
991 if (curl == NULL)
992 {
993 GNUNET_break (0);
994 clean_up ();
995 return;
996 }
997 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
998 _ ("Bootstrapping using hostlist at `%s'.\n"),
999 current_url);
1000
1001 stat_download_in_progress = GNUNET_YES;
1002 stat_download_successful = GNUNET_NO;
1003 stat_hellos_obtained = 0;
1004 stat_bytes_downloaded = 0;
1005
1006 GNUNET_STATISTICS_update (stats,
1007 gettext_noop ("# hostlist downloads initiated"),
1008 1,
1009 GNUNET_NO);
1010 if (NULL != proxy)
1011 {
1012 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
1013 CURL_EASY_SETOPT (curl, CURLOPT_PROXYTYPE, proxy_type);
1014 if (NULL != proxy_username)
1015 CURL_EASY_SETOPT (curl, CURLOPT_PROXYUSERNAME, proxy_username);
1016 if (NULL != proxy_password)
1017 CURL_EASY_SETOPT (curl, CURLOPT_PROXYPASSWORD, proxy_password);
1018 }
1019 download_pos = 0;
1020 stat_bogus_url = 0;
1021 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &callback_download);
1022 if (ret != CURLE_OK)
1023 {
1024 clean_up ();
1025 return;
1026 }
1027 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, NULL);
1028 if (ret != CURLE_OK)
1029 {
1030 clean_up ();
1031 return;
1032 }
1033 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
1034 CURL_EASY_SETOPT (curl,
1035 CURLOPT_REDIR_PROTOCOLS,
1036 CURLPROTO_HTTP | CURLPROTO_HTTPS);
1037 CURL_EASY_SETOPT (curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
1038 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
1039 /* no need to abort if the above failed */
1040 CURL_EASY_SETOPT (curl, CURLOPT_URL, current_url);
1041 if (ret != CURLE_OK)
1042 {
1043 clean_up ();
1044 return;
1045 }
1046 CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
1047#if 0
1048 CURL_EASY_SETOPT (curl, CURLOPT_VERBOSE, 1);
1049#endif
1050 CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, GNUNET_MAX_MESSAGE_SIZE);
1051 if (0 == strncmp (current_url, "http", 4))
1052 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
1053 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 60L);
1054 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 60L);
1055 multi = curl_multi_init ();
1056 if (multi == NULL)
1057 {
1058 GNUNET_break (0);
1059 /* clean_up (); */
1060 return;
1061 }
1062 mret = curl_multi_add_handle (multi, curl);
1063 if (mret != CURLM_OK)
1064 {
1065 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1066 _ ("%s failed at %s:%d: `%s'\n"),
1067 "curl_multi_add_handle",
1068 __FILE__,
1069 __LINE__,
1070 curl_multi_strerror (mret));
1071 mret = curl_multi_cleanup (multi);
1072 if (mret != CURLM_OK)
1073 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1074 _ ("%s failed at %s:%d: `%s'\n"),
1075 "curl_multi_cleanup",
1076 __FILE__,
1077 __LINE__,
1078 curl_multi_strerror (mret));
1079 multi = NULL;
1080 clean_up ();
1081 return;
1082 }
1083 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
1084 download_prepare ();
1085}
1086
1087
1088static void
1089task_download_dispatcher (void *cls)
1090{
1091 ti_download_dispatcher_task = NULL;
1092 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download is initiated...\n");
1093 if (GNUNET_NO == stat_download_in_progress)
1094 {
1095 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download can start immediately...\n");
1096 download_hostlist ();
1097 }
1098 else
1099 {
1100 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1101 "Download in progress, have to wait...\n");
1102 ti_download_dispatcher_task =
1103 GNUNET_SCHEDULER_add_delayed (WAITING_INTERVAL,
1104 &task_download_dispatcher,
1105 NULL);
1106 }
1107}
1108
1109
1110/**
1111 * Task that checks if we should try to download a hostlist.
1112 * If so, we initiate the download, otherwise we schedule
1113 * this task again for a later time.
1114 */
1115static void
1116task_check (void *cls)
1117{
1118 static int once;
1119 struct GNUNET_TIME_Relative delay;
1120
1121 ti_check_download = NULL;
1122 if (stats == NULL)
1123 {
1124 curl_global_cleanup ();
1125 return; /* in shutdown */
1126 }
1127 if ((stat_connection_count < MIN_CONNECTIONS) &&
1128 (NULL == ti_download_dispatcher_task))
1129 ti_download_dispatcher_task =
1130 GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1131
1132 delay = hostlist_delay;
1133 if (0 == hostlist_delay.rel_value_us)
1134 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
1135 else
1136 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
1137 if (hostlist_delay.rel_value_us >
1138 GNUNET_TIME_UNIT_HOURS.rel_value_us * (1 + stat_connection_count))
1139 hostlist_delay =
1140 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
1141 (1 + stat_connection_count));
1142 GNUNET_STATISTICS_set (stats,
1143 gettext_noop (
1144 "# milliseconds between hostlist downloads"),
1145 hostlist_delay.rel_value_us / 1000LL,
1146 GNUNET_YES);
1147 if (0 == once)
1148 {
1149 delay = GNUNET_TIME_UNIT_ZERO;
1150 once = 1;
1151 }
1152 GNUNET_log (
1153 GNUNET_ERROR_TYPE_INFO,
1154 _ ("Have %u/%u connections. Will consider downloading hostlist in %s\n"),
1155 stat_connection_count,
1156 MIN_CONNECTIONS,
1157 GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES));
1158 ti_check_download = GNUNET_SCHEDULER_add_delayed (delay, &task_check, NULL);
1159}
1160
1161
1162/**
1163 * This tasks sets hostlist testing to allowed after interval between to testings is reached
1164 *
1165 * @param cls closure
1166 */
1167static void
1168task_testing_intervall_reset (void *cls)
1169{
1170 ti_testing_intervall_task = NULL;
1171 stat_testing_allowed = GNUNET_OK;
1172 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1173 "Testing new hostlist advertisements is allowed again\n");
1174}
1175
1176
1177/**
1178 * Task that writes hostlist entries to a file on a regular base
1179 *
1180 * @param cls closure
1181 */
1182static void
1183task_hostlist_saving (void *cls)
1184{
1185 ti_saving_task = NULL;
1186 save_hostlist_file (GNUNET_NO);
1187
1188 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1189 "Hostlists will be saved to file again in %s\n",
1190 GNUNET_STRINGS_relative_time_to_string (SAVING_INTERVAL,
1191 GNUNET_YES));
1192 ti_saving_task =
1193 GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL, &task_hostlist_saving, NULL);
1194}
1195
1196
1197/**
1198 * Method called whenever a given peer connects.
1199 *
1200 * @param cls closure
1201 * @param peer peer identity this notification is about
1202 * @param mq message queue for transmissions to @a peer
1203 */
1204static void *
1205handler_connect (void *cls,
1206 const struct GNUNET_PeerIdentity *peer,
1207 struct GNUNET_MQ_Handle *mq)
1208{
1209 GNUNET_assert (stat_connection_count < UINT_MAX);
1210 stat_connection_count++;
1211 GNUNET_STATISTICS_update (stats,
1212 gettext_noop ("# active connections"),
1213 1,
1214 GNUNET_NO);
1215 return NULL;
1216}
1217
1218
1219/**
1220 * Method called whenever a given peer disconnects.
1221 *
1222 * @param cls closure
1223 * @param peer peer identity this notification is about
1224 */
1225static void
1226handler_disconnect (void *cls,
1227 const struct GNUNET_PeerIdentity *peer,
1228 void *internal_cls)
1229{
1230 GNUNET_assert (stat_connection_count > 0);
1231 stat_connection_count--;
1232 GNUNET_STATISTICS_update (stats,
1233 gettext_noop ("# active connections"),
1234 -1,
1235 GNUNET_NO);
1236}
1237
1238
1239/**
1240 * Method called whenever an advertisement message arrives.
1241 *
1242 * @param uri the advertised URI
1243 */
1244static void
1245handler_advertisement (const char *uri)
1246{
1247 size_t uri_size;
1248 struct Hostlist *hostlist;
1249
1250 uri_size = strlen (uri) + 1;
1251 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1252 "Hostlist client received advertisement containing URI `%s'\n",
1253 uri);
1254 if (GNUNET_NO != linked_list_contains (uri))
1255 {
1256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "URI `%s' is already known\n", uri);
1257 return;
1258 }
1259
1260 if (GNUNET_NO == stat_testing_allowed)
1261 {
1262 GNUNET_log (
1263 GNUNET_ERROR_TYPE_DEBUG,
1264 "Currently not accepting new advertisements: interval between to advertisements is not reached\n");
1265 return;
1266 }
1267 if (GNUNET_YES == stat_testing_hostlist)
1268 {
1269 GNUNET_log (
1270 GNUNET_ERROR_TYPE_DEBUG,
1271 "Currently not accepting new advertisements: we are already testing a hostlist\n");
1272 return;
1273 }
1274
1275 hostlist = GNUNET_malloc (sizeof(struct Hostlist) + uri_size);
1276 hostlist->hostlist_uri = (const char *) &hostlist[1];
1277 GNUNET_memcpy (&hostlist[1], uri, uri_size);
1278 hostlist->time_creation = GNUNET_TIME_absolute_get ();
1279 hostlist->quality = HOSTLIST_INITIAL;
1280 hostlist_to_test = hostlist;
1281
1282 stat_testing_hostlist = GNUNET_YES;
1283 stat_testing_allowed = GNUNET_NO;
1284 ti_testing_intervall_task =
1285 GNUNET_SCHEDULER_add_delayed (TESTING_INTERVAL,
1286 &task_testing_intervall_reset,
1287 NULL);
1288
1289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1290 "Testing new hostlist advertisements is locked for the next %s\n",
1291 GNUNET_STRINGS_relative_time_to_string (TESTING_INTERVAL,
1292 GNUNET_YES));
1293
1294 ti_download_dispatcher_task =
1295 GNUNET_SCHEDULER_add_now (&task_download_dispatcher, NULL);
1296}
1297
1298
1299/**
1300 * Continuation called by the statistics code once
1301 * we go the stat. Initiates hostlist download scheduling.
1302 *
1303 * @param cls closure
1304 * @param success #GNUNET_OK if statistics were
1305 * successfully obtained, #GNUNET_SYSERR if not.
1306 */
1307static void
1308primary_task (void *cls, int success)
1309{
1310 if (NULL != ti_check_download)
1311 {
1312 GNUNET_SCHEDULER_cancel (ti_check_download);
1313 ti_check_download = NULL;
1314 }
1315 sget = NULL;
1316 GNUNET_assert (NULL != stats);
1317 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1318 "Statistics request done, scheduling hostlist download\n");
1319 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1320}
1321
1322
1323/**
1324 * Continuation called by the statistics code once
1325 * we go the stat. Initiates hostlist download scheduling.
1326 *
1327 * @param cls closure
1328 * @param success #GNUNET_OK if statistics were
1329 * successfully obtained, #GNUNET_SYSERR if not.
1330 */
1331static void
1332stat_timeout_task (void *cls)
1333{
1334 GNUNET_STATISTICS_get_cancel (sget);
1335 sget = NULL;
1336 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1337}
1338
1339
1340/**
1341 * We've received the previous delay value from statistics. Remember it.
1342 *
1343 * @param cls NULL, unused
1344 * @param subsystem should be "hostlist", unused
1345 * @param name will be "milliseconds between hostlist downloads", unused
1346 * @param value previous delay value, in milliseconds (!)
1347 * @param is_persistent unused, will be #GNUNET_YES
1348 */
1349static int
1350process_stat (void *cls,
1351 const char *subsystem,
1352 const char *name,
1353 uint64_t value,
1354 int is_persistent)
1355{
1356 hostlist_delay.rel_value_us = value * 1000LL;
1357 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1358 "Initial time between hostlist downloads is %s\n",
1359 GNUNET_STRINGS_relative_time_to_string (hostlist_delay,
1360 GNUNET_YES));
1361 return GNUNET_OK;
1362}
1363
1364
1365/**
1366 * Method to load persistent hostlist file during hostlist client startup
1367 */
1368static void
1369load_hostlist_file ()
1370{
1371 char *filename;
1372 char *uri;
1373 char *emsg;
1374 struct Hostlist *hostlist;
1375 uint32_t times_used;
1376 uint32_t hellos_returned;
1377 uint64_t quality;
1378 uint64_t last_used;
1379 uint64_t created;
1380 uint32_t counter;
1381 struct GNUNET_BIO_ReadHandle *rh;
1382
1383 uri = NULL;
1384 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1385 "HOSTLIST",
1386 "HOSTLISTFILE",
1387 &filename))
1388 {
1389 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1390 "hostlist",
1391 "HOSTLISTFILE");
1392 return;
1393 }
1394
1395 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1396 _ ("Loading saved hostlist entries from file `%s' \n"),
1397 filename);
1398 if (GNUNET_NO == GNUNET_DISK_file_test (filename))
1399 {
1400 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1401 _ ("Hostlist file `%s' does not exist\n"),
1402 filename);
1403 GNUNET_free (filename);
1404 return;
1405 }
1406
1407 rh = GNUNET_BIO_read_open_file (filename);
1408 if (NULL == rh)
1409 {
1410 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1411 _ (
1412 "Could not open file `%s' for reading to load hostlists: %s\n"),
1413 filename,
1414 strerror (errno));
1415 GNUNET_free (filename);
1416 return;
1417 }
1418
1419 counter = 0;
1420 struct GNUNET_BIO_ReadSpec rs[] = {
1421 GNUNET_BIO_read_spec_int32 ("times used", (int32_t *) &times_used),
1422 GNUNET_BIO_read_spec_int64 ("quality", (int64_t *) &quality),
1423 GNUNET_BIO_read_spec_int64 ("last used", (int64_t *) &last_used),
1424 GNUNET_BIO_read_spec_int64 ("created", (int64_t *) &created),
1425 GNUNET_BIO_read_spec_int32 ("hellos returned",
1426 (int32_t *) &hellos_returned),
1427 GNUNET_BIO_read_spec_end (),
1428 };
1429 while ((GNUNET_OK == GNUNET_BIO_read_string (rh, "url", &uri, MAX_URL_LEN)) &&
1430 (NULL != uri) &&
1431 (GNUNET_OK == GNUNET_BIO_read_spec_commit (rh, rs)))
1432 {
1433 hostlist = GNUNET_malloc (sizeof(struct Hostlist) + strlen (uri) + 1);
1434 hostlist->hello_count = hellos_returned;
1435 hostlist->hostlist_uri = (const char *) &hostlist[1];
1436 GNUNET_memcpy (&hostlist[1], uri, strlen (uri) + 1);
1437 hostlist->quality = quality;
1438 hostlist->time_creation.abs_value_us = created;
1439 hostlist->time_last_usage.abs_value_us = last_used;
1440 GNUNET_CONTAINER_DLL_insert (linked_list_head, linked_list_tail, hostlist);
1441 linked_list_size++;
1442 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1443 "Added hostlist entry with URI `%s' \n",
1444 hostlist->hostlist_uri);
1445 GNUNET_free (uri);
1446 uri = NULL;
1447 counter++;
1448 if (counter >= MAX_NUMBER_HOSTLISTS)
1449 break;
1450 }
1451
1452 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1453 _ ("%u hostlist URIs loaded from file\n"),
1454 counter);
1455 GNUNET_STATISTICS_set (stats,
1456 gettext_noop ("# hostlist URIs read from file"),
1457 counter,
1458 GNUNET_YES);
1459 GNUNET_STATISTICS_set (stats,
1460 gettext_noop ("# advertised hostlist URIs"),
1461 linked_list_size,
1462 GNUNET_NO);
1463
1464 GNUNET_free (uri);
1465 emsg = NULL;
1466 (void) GNUNET_BIO_read_close (rh, &emsg);
1467 if (emsg != NULL)
1468 GNUNET_free (emsg);
1469 GNUNET_free (filename);
1470}
1471
1472
1473/**
1474 * Method to save persistent hostlist file during hostlist client shutdown
1475 *
1476 * @param shutdown set if called because of shutdown, entries in linked list will be destroyed
1477 */
1478static void
1479save_hostlist_file (int shutdown)
1480{
1481 char *filename;
1482 struct Hostlist *pos;
1483 struct GNUNET_BIO_WriteHandle *wh;
1484 int ok;
1485 uint32_t counter;
1486
1487 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1488 "HOSTLIST",
1489 "HOSTLISTFILE",
1490 &filename))
1491 {
1492 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1493 "hostlist",
1494 "HOSTLISTFILE");
1495 return;
1496 }
1497 if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
1498 {
1499 GNUNET_free (filename);
1500 return;
1501 }
1502 wh = GNUNET_BIO_write_open_file (filename);
1503 if (NULL == wh)
1504 {
1505 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1506 _ (
1507 "Could not open file `%s' for writing to save hostlists: %s\n"),
1508 filename,
1509 strerror (errno));
1510 GNUNET_free (filename);
1511 return;
1512 }
1513 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1514 _ ("Writing %u hostlist URIs to `%s'\n"),
1515 linked_list_size,
1516 filename);
1517 /* add code to write hostlists to file using bio */
1518 ok = GNUNET_YES;
1519 counter = 0;
1520 while (NULL != (pos = linked_list_head))
1521 {
1522 if (GNUNET_YES == shutdown)
1523 {
1524 GNUNET_CONTAINER_DLL_remove (linked_list_head, linked_list_tail, pos);
1525 linked_list_size--;
1526 }
1527 if (GNUNET_YES == ok)
1528 {
1529 struct GNUNET_BIO_WriteSpec ws[] = {
1530 GNUNET_BIO_write_spec_string ("hostlist uri", pos->hostlist_uri),
1531 GNUNET_BIO_write_spec_int32 ("times used",
1532 (int32_t *) &pos->times_used),
1533 GNUNET_BIO_write_spec_int64 ("quality", (int64_t *) &pos->quality),
1534 GNUNET_BIO_write_spec_int64 (
1535 "last usage",
1536 (int64_t *) &pos->time_last_usage.abs_value_us),
1537 GNUNET_BIO_write_spec_int64 (
1538 "creation time",
1539 (int64_t *) &pos->time_creation.abs_value_us),
1540 GNUNET_BIO_write_spec_int32 ("hellos count",
1541 (int32_t *) &pos->hello_count),
1542 GNUNET_BIO_write_spec_end (),
1543 };
1544 if ((GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws)))
1545 {
1546 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1547 _ ("Error writing hostlist URIs to file `%s'\n"),
1548 filename);
1549 ok = GNUNET_NO;
1550 }
1551 }
1552
1553 if (GNUNET_YES == shutdown)
1554 GNUNET_free (pos);
1555 counter++;
1556 if (counter >= MAX_NUMBER_HOSTLISTS)
1557 break;
1558 }
1559 GNUNET_STATISTICS_set (stats,
1560 gettext_noop ("# hostlist URIs written to file"),
1561 counter,
1562 GNUNET_YES);
1563
1564 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
1565 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1566 _ ("Error writing hostlist URIs to file `%s'\n"),
1567 filename);
1568 GNUNET_free (filename);
1569}
1570
1571
1572/**
1573 * Start downloading hostlists from hostlist servers as necessary.
1574 *
1575 * @param c configuration to use
1576 * @param st statistics handle to use
1577 * @param[out] ch set to handler for CORE connect events
1578 * @param[out] dh set to handler for CORE disconnect events
1579 * @param[out] msgh set to handler for CORE advertisement messages
1580 * @param learn should we learn hostlist URLs from CORE
1581 * @return #GNUNET_OK on success
1582 */
1583int
1584GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
1585 struct GNUNET_STATISTICS_Handle *st,
1586 GNUNET_CORE_ConnectEventHandler *ch,
1587 GNUNET_CORE_DisconnectEventHandler *dh,
1588 GNUNET_HOSTLIST_UriHandler *msgh,
1589 int learn)
1590{
1591 char *filename;
1592 char *proxytype_str;
1593 int result;
1594
1595 GNUNET_assert (NULL != st);
1596 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1597 {
1598 GNUNET_break (0);
1599 return GNUNET_SYSERR;
1600 }
1601 cfg = c;
1602 stats = st;
1603
1604 /* Read proxy configuration */
1605 pi = GNUNET_PEERINFO_connect (c);
1606 if (GNUNET_OK ==
1607 GNUNET_CONFIGURATION_get_value_string (cfg, "HOSTLIST", "PROXY", &proxy))
1608 {
1609 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found proxy host: `%s'\n", proxy);
1610 /* proxy username */
1611 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1612 "HOSTLIST",
1613 "PROXY_USERNAME",
1614 &proxy_username))
1615 {
1616 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1617 "Found proxy username name: `%s'\n",
1618 proxy_username);
1619 }
1620
1621 /* proxy password */
1622 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1623 "HOSTLIST",
1624 "PROXY_PASSWORD",
1625 &proxy_password))
1626 {
1627 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1628 "Found proxy password name: `%s'\n",
1629 proxy_password);
1630 }
1631
1632 /* proxy type */
1633 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1634 "HOSTLIST",
1635 "PROXY_TYPE",
1636 &proxytype_str))
1637 {
1638 if (GNUNET_OK != GNUNET_STRINGS_utf8_toupper (proxytype_str,
1639 proxytype_str))
1640 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1641 "Unable to convert `%s' to UTF-8 uppercase\n",
1642 proxytype_str);
1643 proxy_type = CURLPROXY_HTTP;
1644 if (0 == strcmp (proxytype_str, "HTTP"))
1645 proxy_type = CURLPROXY_HTTP;
1646 else if (0 == strcmp (proxytype_str, "HTTP_1_0"))
1647 proxy_type = CURLPROXY_HTTP_1_0;
1648 else if (0 == strcmp (proxytype_str, "SOCKS4"))
1649 proxy_type = CURLPROXY_SOCKS4;
1650 else if (0 == strcmp (proxytype_str, "SOCKS5"))
1651 proxy_type = CURLPROXY_SOCKS5;
1652 else if (0 == strcmp (proxytype_str, "SOCKS4A"))
1653 proxy_type = CURLPROXY_SOCKS4A;
1654 else if (0 == strcmp (proxytype_str, "SOCKS5_HOSTNAME"))
1655 proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
1656 else
1657 {
1658 GNUNET_log (
1659 GNUNET_ERROR_TYPE_ERROR,
1660 _ (
1661 "Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
1662 proxytype_str);
1663 GNUNET_free (proxytype_str);
1664 GNUNET_free (proxy);
1665 proxy = NULL;
1666 GNUNET_free (proxy_username);
1667 proxy_username = NULL;
1668 GNUNET_free (proxy_password);
1669 proxy_password = NULL;
1670
1671 return GNUNET_SYSERR;
1672 }
1673 }
1674 GNUNET_free (proxytype_str);
1675 }
1676
1677 stat_learning = learn;
1678 *ch = &handler_connect;
1679 *dh = &handler_disconnect;
1680 linked_list_head = NULL;
1681 linked_list_tail = NULL;
1682 stat_use_bootstrap = GNUNET_YES;
1683 stat_testing_hostlist = GNUNET_NO;
1684 stat_testing_allowed = GNUNET_YES;
1685
1686 if (GNUNET_YES == stat_learning)
1687 {
1688 *msgh = &handler_advertisement;
1689 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1690 _ ("Learning is enabled on this peer\n"));
1691 load_hostlist_file ();
1692 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1693 "Hostlists will be saved to file again in %s\n",
1694 GNUNET_STRINGS_relative_time_to_string (SAVING_INTERVAL,
1695 GNUNET_YES));
1696 ti_saving_task = GNUNET_SCHEDULER_add_delayed (SAVING_INTERVAL,
1697 &task_hostlist_saving,
1698 NULL);
1699 }
1700 else
1701 {
1702 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1703 _ ("Learning is not enabled on this peer\n"));
1704 *msgh = NULL;
1705 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_filename (cfg,
1706 "HOSTLIST",
1707 "HOSTLISTFILE",
1708 &filename))
1709 {
1710 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
1711 {
1712 result = remove (filename);
1713 if (0 == result)
1714 GNUNET_log (
1715 GNUNET_ERROR_TYPE_INFO,
1716 _ (
1717 "Since learning is not enabled on this peer, hostlist file `%s' was removed\n"),
1718 filename);
1719 else
1720 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1721 "remove",
1722 filename);
1723 }
1724 }
1725 GNUNET_free (filename);
1726 }
1727 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1728 "Loading stats value on hostlist download frequency\n");
1729 sget = GNUNET_STATISTICS_get (stats,
1730 "hostlist",
1731 gettext_noop (
1732 "# milliseconds between hostlist downloads"),
1733 &primary_task,
1734 &process_stat,
1735 NULL);
1736 if (NULL == sget)
1737 {
1738 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1739 "Statistics request failed, scheduling hostlist download\n");
1740 ti_check_download = GNUNET_SCHEDULER_add_now (&task_check, NULL);
1741 }
1742 else
1743 {
1744 ti_check_download = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
1745 &stat_timeout_task,
1746 NULL);
1747 }
1748 return GNUNET_OK;
1749}
1750
1751
1752/**
1753 * Stop downloading hostlists from hostlist servers as necessary.
1754 */
1755void
1756GNUNET_HOSTLIST_client_stop ()
1757{
1758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostlist client shutdown\n");
1759 if (NULL != sget)
1760 {
1761 GNUNET_STATISTICS_get_cancel (sget);
1762 sget = NULL;
1763 }
1764 stats = NULL;
1765 if (GNUNET_YES == stat_learning)
1766 save_hostlist_file (GNUNET_YES);
1767 if (NULL != ti_saving_task)
1768 {
1769 GNUNET_SCHEDULER_cancel (ti_saving_task);
1770 ti_saving_task = NULL;
1771 }
1772 if (NULL != ti_download_dispatcher_task)
1773 {
1774 GNUNET_SCHEDULER_cancel (ti_download_dispatcher_task);
1775 ti_download_dispatcher_task = NULL;
1776 }
1777 if (NULL != ti_testing_intervall_task)
1778 {
1779 GNUNET_SCHEDULER_cancel (ti_testing_intervall_task);
1780 ti_testing_intervall_task = NULL;
1781 }
1782 if (NULL != ti_download)
1783 {
1784 GNUNET_SCHEDULER_cancel (ti_download);
1785 ti_download = NULL;
1786 update_hostlist ();
1787 clean_up ();
1788 }
1789 if (NULL != ti_check_download)
1790 {
1791 GNUNET_SCHEDULER_cancel (ti_check_download);
1792 ti_check_download = NULL;
1793 curl_global_cleanup ();
1794 }
1795 GNUNET_free (proxy);
1796 proxy = NULL;
1797 GNUNET_free (proxy_username);
1798 proxy_username = NULL;
1799 GNUNET_free (proxy_password);
1800 proxy_password = NULL;
1801 if (NULL != pi)
1802 {
1803 GNUNET_PEERINFO_disconnect (pi);
1804 pi = NULL;
1805 }
1806 cfg = NULL;
1807}
1808
1809
1810/* end of gnunet-daemon-hostlist_client.c */