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