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