99b43f0082c8dd8e2701d521dc29f29fee8045dd
[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  */
26
27 #include "platform.h"
28 #include "hostlist-client.h"
29 #include "gnunet_core_service.h"
30 #include "gnunet_hello_lib.h"
31 #include "gnunet_statistics_service.h"
32 #include "gnunet_transport_service.h"
33 #include <curl/curl.h>
34
35 #define DEBUG_HOSTLIST_CLIENT GNUNET_YES
36
37 /**
38  * Number of connections that we must have to NOT download
39  * hostlists anymore.
40  */
41 #define MIN_CONNECTIONS 4
42
43 /**
44  * Our configuration.
45  */
46 static const struct GNUNET_CONFIGURATION_Handle *cfg;
47
48 /**
49  * Our scheduler.
50  */
51 static struct GNUNET_SCHEDULER_Handle *sched;
52
53 /**
54  * Statistics handle.
55  */
56 struct GNUNET_STATISTICS_Handle *stats; 
57
58 /**
59  * Transport handle.
60  */
61 struct GNUNET_TRANSPORT_Handle *transport;
62                        
63 /**
64  * Proxy that we are using (can be NULL).
65  */
66 static char *proxy;
67
68 /**
69  * Buffer for data downloaded via HTTP.
70  */
71 static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
72
73 /**
74  * Number of bytes valid in 'download_buffer'.
75  */
76 static size_t download_pos;
77
78 /**
79  * Current URL that we are using.
80  */
81 static char *current_url;
82
83 /**
84  * Current CURL handle.
85  */
86 static CURL *curl;
87
88 /**
89  * Current multi-CURL handle.
90  */
91 static CURLM *multi;
92
93 /**
94  * ID of the current task scheduled.
95  */
96 static GNUNET_SCHEDULER_TaskIdentifier current_task;
97
98 /**
99  * Amount of time we wait between hostlist downloads.
100  */
101 static struct GNUNET_TIME_Relative hostlist_delay;
102
103 /**
104  * Set to GNUNET_YES if the current URL had some problems.
105  */ 
106 static int bogus_url;
107
108 /**
109  * Number of active connections (according to core service).
110  */
111 static unsigned int connection_count;
112
113 /**
114  * At what time MUST the current hostlist request be done?
115  */
116 static struct GNUNET_TIME_Absolute end_time;
117
118
119 /**
120  * Process downloaded bits by calling callback on each HELLO.
121  *
122  * @param ptr buffer with downloaded data
123  * @param size size of a record
124  * @param nmemb number of records downloaded
125  * @param ctx unused
126  * @return number of bytes that were processed (always size*nmemb)
127  */
128 static size_t
129 download_hostlist_processor (void *ptr, 
130                              size_t size, 
131                              size_t nmemb, 
132                              void *ctx)
133 {
134   const char * cbuf = ptr;
135   const struct GNUNET_MessageHeader *msg;
136   size_t total;
137   size_t cpy;
138   size_t left;
139   uint16_t msize;
140
141   total = size * nmemb;
142   if ( (total == 0) || (bogus_url) )
143     {
144       return total;  /* ok, no data or bogus data */
145     }
146   GNUNET_STATISTICS_update (stats, 
147                             gettext_noop ("# bytes downloaded from hostlist servers"), 
148                             (int64_t) total, 
149                             GNUNET_NO);  
150   left = total;
151   while (left > 0)
152     {
153       cpy = GNUNET_MIN (total, GNUNET_SERVER_MAX_MESSAGE_SIZE - download_pos);
154       GNUNET_assert (cpy > 0);
155       memcpy (&download_buffer[download_pos],
156               cbuf,
157               cpy);
158       cbuf += cpy;
159       download_pos += cpy;
160       left -= cpy;
161       if (download_pos < sizeof(struct GNUNET_MessageHeader))
162         {
163           GNUNET_assert (left == 0);
164           break;
165         }
166       msg = (const struct GNUNET_MessageHeader *) download_buffer;
167       msize = ntohs(msg->size);
168       if (msize < sizeof(struct GNUNET_MessageHeader))
169         {        
170           GNUNET_STATISTICS_update (stats, 
171                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
172                                     1, 
173                                     GNUNET_NO);  
174           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
175                       _("Invalid `%s' message received from hostlist at `%s'\n"),
176                       "HELLO",
177                       current_url); 
178           bogus_url = 1;
179           return total;
180         }
181       if (download_pos < msize)
182         {
183           GNUNET_assert (left == 0);
184           break;
185         }
186       if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
187         {
188 #if DEBUG_HOSTLIST_CLIENT
189           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
190                       "Received valid `%s' message from hostlist server.\n",
191                       "HELLO");
192 #endif
193           GNUNET_STATISTICS_update (stats, 
194                                     gettext_noop ("# valid HELLOs downloaded from hostlist servers"), 
195                                     1, 
196                                     GNUNET_NO);  
197           GNUNET_TRANSPORT_offer_hello (transport, msg);
198         }
199       else
200         {
201           GNUNET_STATISTICS_update (stats, 
202                                     gettext_noop ("# invalid HELLOs downloaded from hostlist servers"), 
203                                     1, 
204                                     GNUNET_NO);  
205           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
206                       _("Invalid `%s' message received from hostlist at `%s'\n"),
207                       "HELLO",
208                       current_url);
209           bogus_url = GNUNET_YES;
210           return total;
211         }
212       memmove (download_buffer,
213                &download_buffer[msize],
214                download_pos - msize);
215       download_pos -= msize;
216     }
217   return total;
218 }
219
220
221 /**
222  * Obtain a hostlist URL that we should use.
223  *
224  * @return NULL if there is no URL available
225  */
226 static char *
227 get_url ()
228 {
229   char *servers;
230   char *ret;
231   size_t urls;
232   size_t pos;
233
234   if (GNUNET_OK != 
235       GNUNET_CONFIGURATION_get_value_string (cfg,
236                                              "HOSTLIST",
237                                              "SERVERS",
238                                              &servers))
239     {
240       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
241                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
242                   "SERVERS", "HOSTLIST");
243       return NULL;
244     }
245
246   urls = 0;
247   if (strlen (servers) > 0)
248     {
249       urls++;
250       pos = strlen (servers) - 1;
251       while (pos > 0)
252         {
253           if (servers[pos] == ' ')
254             urls++;
255           pos--;
256         }
257     }
258   if (urls == 0)
259     {
260       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
261                   _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
262                   "SERVERS", "HOSTLIST");
263       GNUNET_free (servers);
264       return NULL;
265     }
266
267   urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
268   pos = strlen (servers) - 1;
269   while (pos > 0)
270     {
271       if (servers[pos] == ' ')
272         {
273           urls--;
274           servers[pos] = '\0';
275         }
276       if (urls == 0)
277         {
278           pos++;
279           break;
280         }
281       pos--;    
282     }
283   ret = GNUNET_strdup (&servers[pos]);
284   GNUNET_free (servers);
285   return ret;
286 }
287
288
289 #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);
290
291
292 /**
293  * Schedule the background task that will (possibly)
294  * download a hostlist.
295  */
296 static void
297 schedule_hostlist_task (void);
298
299
300 /**
301  * Clean up the state from the task that downloaded the
302  * hostlist and schedule the next task.
303  */
304 static void 
305 clean_up ()
306 {
307   CURLMcode mret;
308
309   if (multi != NULL)
310     {
311       mret = curl_multi_remove_handle (multi, curl);
312       if (mret != CURLM_OK)
313         {
314           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
315                       _("%s failed at %s:%d: `%s'\n"),
316                       "curl_multi_remove_handle", __FILE__, __LINE__,
317                       curl_multi_strerror (mret));
318         }
319       mret = curl_multi_cleanup (multi);
320       if (mret != CURLM_OK)
321         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
322                     _("%s failed at %s:%d: `%s'\n"),
323                     "curl_multi_cleanup", __FILE__, __LINE__,
324                     curl_multi_strerror (mret));
325       multi = NULL;
326     }
327   if (curl != NULL)
328     {
329       curl_easy_cleanup (curl);
330       curl = NULL;
331     }  
332   GNUNET_free_non_null (current_url);
333   current_url = NULL;
334   schedule_hostlist_task ();
335 }
336
337
338 /**
339  * Ask CURL for the select set and then schedule the
340  * receiving task with the scheduler.
341  */
342 static void
343 run_multi ();
344
345
346 /**
347  * Task that is run when we are ready to receive more data from the hostlist
348  * server. 
349  *
350  * @param cls closure, unused
351  * @param tc task context, unused
352  */
353 static void
354 multi_ready (void *cls,
355              const struct GNUNET_SCHEDULER_TaskContext *tc)
356 {
357   int running;
358   struct CURLMsg *msg;
359   CURLMcode mret;
360   
361   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
362     {
363 #if DEBUG_HOSTLIST_CLIENT
364       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
365                   "Shutdown requested while trying to download hostlist from `%s'\n",
366                   current_url);
367 #endif
368       clean_up ();
369       return;
370     }
371   if (GNUNET_TIME_absolute_get_remaining (end_time).value == 0)
372     {
373       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
374                   _("Timeout trying to download hostlist from `%s'\n"),
375                   current_url);
376       clean_up ();
377       return;
378     }
379 #if DEBUG_HOSTLIST_CLIENT
380   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
381               "Ready for processing hostlist client request\n");
382 #endif
383   do 
384     {
385       running = 0;
386       mret = curl_multi_perform (multi, &running);
387       if (running == 0)
388         {
389           do
390             {
391               msg = curl_multi_info_read (multi, &running);
392               GNUNET_break (msg != NULL);
393               if (msg == NULL)
394                 break;
395               switch (msg->msg)
396                 {
397                 case CURLMSG_DONE:
398                   if ( (msg->data.result != CURLE_OK) &&
399                        (msg->data.result != CURLE_GOT_NOTHING) )                       
400                     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
401                                _("%s failed for `%s' at %s:%d: `%s'\n"),
402                                "curl_multi_perform", 
403                                current_url,
404                                __FILE__,
405                                __LINE__,
406                                curl_easy_strerror (msg->data.result));            
407                   else
408                     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
409                                 _("Download of hostlist `%s' completed.\n"),
410                                 current_url);
411                   clean_up ();
412                   return;
413                 default:
414                   break;
415                 }
416             }
417           while (running > 0);
418         }
419     }
420   while (mret == CURLM_CALL_MULTI_PERFORM);
421   if (mret != CURLM_OK)
422     {
423       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
424                   _("%s failed at %s:%d: `%s'\n"),
425                   "curl_multi_perform", __FILE__, __LINE__,
426                   curl_multi_strerror (mret));
427       clean_up ();
428     }
429   run_multi ();
430 }
431
432
433 /**
434  * Ask CURL for the select set and then schedule the
435  * receiving task with the scheduler.
436  */
437 static void
438 run_multi () 
439 {
440   CURLMcode mret;
441   fd_set rs;
442   fd_set ws;
443   fd_set es;
444   int max;
445   struct GNUNET_NETWORK_FDSet *grs;
446   struct GNUNET_NETWORK_FDSet *gws;
447   long timeout;
448   struct GNUNET_TIME_Relative rtime;
449   
450   max = -1;
451   FD_ZERO (&rs);
452   FD_ZERO (&ws);
453   FD_ZERO (&es);
454   mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
455   if (mret != CURLM_OK)
456     {
457       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
458                   _("%s failed at %s:%d: `%s'\n"),
459                   "curl_multi_fdset", __FILE__, __LINE__,
460                   curl_multi_strerror (mret));
461       clean_up ();
462       return;
463     }
464   mret = curl_multi_timeout (multi, &timeout);
465   if (mret != CURLM_OK)
466     {
467       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
468                   _("%s failed at %s:%d: `%s'\n"),
469                   "curl_multi_timeout", __FILE__, __LINE__,
470                   curl_multi_strerror (mret));
471       clean_up ();
472       return;
473     }
474   rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
475                                     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
476                                                                    timeout));
477   grs = GNUNET_NETWORK_fdset_create ();
478   gws = GNUNET_NETWORK_fdset_create ();
479   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
480   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);  
481 #if DEBUG_HOSTLIST_CLIENT
482   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
483               "Scheduling task for hostlist download using cURL\n");
484 #endif
485   current_task 
486     = GNUNET_SCHEDULER_add_select (sched,
487                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
488                                    GNUNET_SCHEDULER_NO_TASK,
489                                    rtime,
490                                    grs,
491                                    gws,
492                                    &multi_ready,
493                                    multi);
494   GNUNET_NETWORK_fdset_destroy (gws);
495   GNUNET_NETWORK_fdset_destroy (grs);
496 }
497
498
499 /**
500  * Main function that will download a hostlist and process its
501  * data.
502  */
503 static void
504 download_hostlist () 
505 {
506   CURLcode ret;
507   CURLMcode mret;
508
509   curl = curl_easy_init ();
510   multi = NULL;
511   if (curl == NULL)
512     {
513       GNUNET_break (0);
514       clean_up ();
515       return;
516     }
517   current_url = get_url ();
518   GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
519               _("Bootstrapping using hostlist at `%s'.\n"), 
520               current_url);
521   GNUNET_STATISTICS_update (stats, 
522                             gettext_noop ("# hostlist downloads initiated"), 
523                             1, 
524                             GNUNET_NO);  
525   if (proxy != NULL)
526     CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);    
527   download_pos = 0;
528   bogus_url = 0;
529   CURL_EASY_SETOPT (curl,
530                     CURLOPT_WRITEFUNCTION, 
531                     &download_hostlist_processor);
532   if (ret != CURLE_OK)
533     {
534       clean_up ();
535       return;
536     }
537   CURL_EASY_SETOPT (curl,
538                     CURLOPT_WRITEDATA, 
539                     NULL);
540   if (ret != CURLE_OK)
541     {
542       clean_up ();
543       return;
544     }
545   CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
546   CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
547   /* no need to abort if the above failed */
548   CURL_EASY_SETOPT (curl, 
549                     CURLOPT_URL, 
550                     current_url);
551   if (ret != CURLE_OK)
552     {
553       clean_up ();
554       return;
555     }
556   CURL_EASY_SETOPT (curl, 
557                     CURLOPT_FAILONERROR, 
558                     1);
559 #if 0
560   CURL_EASY_SETOPT (curl, 
561                     CURLOPT_VERBOSE, 
562                     1);
563 #endif
564   CURL_EASY_SETOPT (curl, 
565                     CURLOPT_BUFFERSIZE, 
566                     GNUNET_SERVER_MAX_MESSAGE_SIZE);
567   if (0 == strncmp (current_url, "http", 4))
568     CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
569   CURL_EASY_SETOPT (curl, 
570                     CURLOPT_CONNECTTIMEOUT, 
571                     60L);
572   CURL_EASY_SETOPT (curl, 
573                     CURLOPT_TIMEOUT, 
574                     60L);
575 #if 0
576   /* this should no longer be needed; we're now single-threaded! */
577   CURL_EASY_SETOPT (curl,
578                     CURLOPT_NOSIGNAL, 
579                     1);
580 #endif
581   multi = curl_multi_init ();
582   if (multi == NULL)
583     {
584       GNUNET_break (0);
585       clean_up ();
586       return;
587     }
588   mret = curl_multi_add_handle (multi, curl);
589   if (mret != CURLM_OK)
590     {
591       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
592                   _("%s failed at %s:%d: `%s'\n"),
593                   "curl_multi_add_handle", __FILE__, __LINE__,
594                   curl_multi_strerror (mret));
595       mret = curl_multi_cleanup (multi);
596       if (mret != CURLM_OK)
597         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
598                     _("%s failed at %s:%d: `%s'\n"),
599                     "curl_multi_cleanup", __FILE__, __LINE__,
600                     curl_multi_strerror (mret));
601       multi = NULL;
602       clean_up ();
603       return;
604     }
605   end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
606   run_multi ();
607 }  
608
609
610 /**
611  * Task that checks if we should try to download a hostlist.
612  * If so, we initiate the download, otherwise we schedule
613  * this task again for a later time.
614  */
615 static void
616 check_task (void *cls,
617             const struct GNUNET_SCHEDULER_TaskContext *tc)
618 {
619   current_task = GNUNET_SCHEDULER_NO_TASK;
620   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
621     return;
622   if (connection_count < MIN_CONNECTIONS)
623     download_hostlist ();
624   else
625     schedule_hostlist_task ();
626 }
627
628
629 /**
630  * Compute when we should check the next time about downloading
631  * a hostlist; then schedule the task accordingly.
632  */
633 static void
634 schedule_hostlist_task ()
635 {
636   static int once;
637   struct GNUNET_TIME_Relative delay;
638
639   if (stats == NULL)
640     {
641       curl_global_cleanup ();
642       return; /* in shutdown */
643     }
644   delay = hostlist_delay;
645   if (hostlist_delay.value == 0)
646     hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
647   else
648     hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
649   if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * (1 + connection_count))
650     hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
651                                                     (1 + connection_count));
652   GNUNET_STATISTICS_set (stats,
653                          gettext_noop("Minimum time between hostlist downloads"),
654                          hostlist_delay.value,
655                          GNUNET_YES);
656   if (0 == once)
657     {
658       delay = GNUNET_TIME_UNIT_ZERO;
659       once = 1;
660     }  
661   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
662               _("Have %u/%u connections.  Will consider downloading hostlist in %llums\n"),
663               connection_count,
664               MIN_CONNECTIONS,
665               (unsigned long long) delay.value);
666   current_task = GNUNET_SCHEDULER_add_delayed (sched,
667                                                delay,
668                                                &check_task,
669                                                NULL);
670 }
671
672
673 /**
674  * Method called whenever a given peer connects.
675  *
676  * @param cls closure
677  * @param peer peer identity this notification is about
678  * @param latency reported latency of the connection with 'other'
679  * @param distance reported distance (DV) to 'other' 
680  */
681 static void
682 connect_handler (void *cls,
683                  const struct
684                  GNUNET_PeerIdentity * peer,
685                  struct GNUNET_TIME_Relative latency,
686                  uint32_t distance)
687 {
688   connection_count++;
689   GNUNET_STATISTICS_update (stats, 
690                             gettext_noop ("# active connections"), 
691                             1, 
692                             GNUNET_NO);  
693 }
694
695
696 /**
697  * Method called whenever a given peer connects.
698  *
699  * @param cls closure
700  * @param peer peer identity this notification is about
701  */
702 static void
703 disconnect_handler (void *cls,
704                     const struct
705                     GNUNET_PeerIdentity * peer)
706 {
707   connection_count--;
708   GNUNET_STATISTICS_update (stats, 
709                             gettext_noop ("# active connections"), 
710                             -1, 
711                             GNUNET_NO);  
712 }
713
714
715 /**
716  * Continuation called by the statistics code once 
717  * we go the stat.  Initiates hostlist download scheduling.
718  *
719  * @param cls closure
720  * @param success GNUNET_OK if statistics were
721  *        successfully obtained, GNUNET_SYSERR if not.
722  */
723 static void
724 primary_task (void *cls, int success)
725 {
726   if (stats == NULL)
727     return; /* in shutdown */
728 #if DEBUG_HOSTLIST_CLIENT
729   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
730               "Statistics request done, scheduling hostlist download\n");
731 #endif
732   schedule_hostlist_task ();
733 }
734
735
736 static int
737 process_stat (void *cls,
738               const char *subsystem,
739               const char *name,
740               uint64_t value,
741               int is_persistent)
742 {
743   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
744               _("Initial time between hostlist downloads is %llums\n"),
745               (unsigned long long) value);
746   hostlist_delay.value = value;
747   return GNUNET_OK;
748 }
749
750
751 /**
752  * Start downloading hostlists from hostlist servers as necessary.
753  */
754 int
755 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
756                               struct GNUNET_SCHEDULER_Handle *s,
757                               struct GNUNET_STATISTICS_Handle *st,
758                               GNUNET_CORE_ConnectEventHandler *ch,
759                               GNUNET_CORE_DisconnectEventHandler *dh)
760 {
761   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
762     {
763       GNUNET_break (0);
764       return GNUNET_SYSERR;
765     }
766   transport = GNUNET_TRANSPORT_connect (s, c, NULL, NULL, NULL, NULL);
767   if (NULL == transport)
768     {
769       curl_global_cleanup ();
770       return GNUNET_SYSERR;
771     }
772   cfg = c;
773   sched = s;
774   stats = st;
775   if (GNUNET_OK !=
776       GNUNET_CONFIGURATION_get_value_string (cfg,
777                                              "HOSTLIST",
778                                              "HTTP-PROXY", 
779                                              &proxy))
780     proxy = NULL;
781   *ch = &connect_handler;
782   *dh = &disconnect_handler;
783   GNUNET_STATISTICS_get (stats,
784                          "hostlist",
785                          gettext_noop("Minimum time between hostlist downloads"),
786                          GNUNET_TIME_UNIT_MINUTES,
787                          &primary_task,
788                          &process_stat,
789                          NULL);
790   return GNUNET_OK;
791 }
792
793
794 /**
795  * Stop downloading hostlists from hostlist servers as necessary.
796  */
797 void
798 GNUNET_HOSTLIST_client_stop ()
799 {
800 #if DEBUG_HOSTLIST_CLIENT
801   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
802               "Hostlist client shutdown\n");
803 #endif
804   if (current_task != GNUNET_SCHEDULER_NO_TASK)
805     {
806       GNUNET_SCHEDULER_cancel (sched,
807                                current_task);
808       curl_global_cleanup ();
809     }
810   if (transport != NULL)
811     {
812       GNUNET_TRANSPORT_disconnect (transport);
813       transport = NULL;
814     }
815   GNUNET_assert (NULL == transport);
816   GNUNET_free_non_null (proxy);
817   proxy = NULL;
818   cfg = NULL;
819   sched = NULL;
820 }
821
822 /* end of hostlist-client.c */