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