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