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