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