2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2010 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file hostlist/hostlist-client.c
23 * @brief hostlist support. Downloads HELLOs via HTTP.
24 * @author Christian Grothoff
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 "gnunet-daemon-hostlist.h"
34 #include <curl/curl.h>
35 #include "gnunet_common.h"
36 #include "gnunet_bio_lib.h"
38 #define DEBUG_HOSTLIST_CLIENT GNUNET_YES
41 * Number of connections that we must have to NOT download
44 #define MIN_CONNECTIONS 4
49 static const struct GNUNET_CONFIGURATION_Handle *cfg;
54 static struct GNUNET_SCHEDULER_Handle *sched;
59 struct GNUNET_STATISTICS_Handle *stats;
64 struct GNUNET_TRANSPORT_Handle *transport;
67 * Proxy that we are using (can be NULL).
72 * Buffer for data downloaded via HTTP.
74 static char download_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE];
77 * Number of bytes valid in 'download_buffer'.
79 static size_t download_pos;
82 * Current URL that we are using.
84 static char *current_url;
87 * Current CURL handle.
92 * Current multi-CURL handle.
97 * ID of the current task scheduled.
99 static GNUNET_SCHEDULER_TaskIdentifier current_task;
102 * Amount of time we wait between hostlist downloads.
104 static struct GNUNET_TIME_Relative hostlist_delay;
107 * Set to GNUNET_YES if the current URL had some problems.
109 static int bogus_url;
112 * Number of active connections (according to core service).
114 static unsigned int connection_count;
117 * Set if the user allows us to learn about new hostlists
123 * At what time MUST the current hostlist request be done?
125 static struct GNUNET_TIME_Absolute end_time;
128 * Hashmap of PeerIdentities to "struct GNUNET_Hostlist"
129 * (for fast lookup). NULL until the library
130 * is actually being used.
132 static struct GNUNET_CONTAINER_MultiHashMap *hostlist_hashmap;
135 * Process downloaded bits by calling callback on each HELLO.
137 * @param ptr buffer with downloaded data
138 * @param size size of a record
139 * @param nmemb number of records downloaded
141 * @return number of bytes that were processed (always size*nmemb)
144 download_hostlist_processor (void *ptr,
149 const char * cbuf = ptr;
150 const struct GNUNET_MessageHeader *msg;
156 total = size * nmemb;
157 if ( (total == 0) || (bogus_url) )
159 return total; /* ok, no data or bogus data */
161 GNUNET_STATISTICS_update (stats,
162 gettext_noop ("# bytes downloaded from hostlist servers"),
166 while ( (left > 0) ||
169 cpy = GNUNET_MIN (left, GNUNET_SERVER_MAX_MESSAGE_SIZE - download_pos);
170 memcpy (&download_buffer[download_pos],
176 if (download_pos < sizeof(struct GNUNET_MessageHeader))
178 GNUNET_assert (left == 0);
181 msg = (const struct GNUNET_MessageHeader *) download_buffer;
182 msize = ntohs(msg->size);
183 if (msize < sizeof(struct GNUNET_MessageHeader))
185 GNUNET_STATISTICS_update (stats,
186 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
189 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
190 _("Invalid `%s' message received from hostlist at `%s'\n"),
196 if (download_pos < msize)
198 GNUNET_assert (left == 0);
201 if (GNUNET_HELLO_size ((const struct GNUNET_HELLO_Message*)msg) == msize)
203 #if DEBUG_HOSTLIST_CLIENT
204 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
205 "Received valid `%s' message from hostlist server.\n",
208 GNUNET_STATISTICS_update (stats,
209 gettext_noop ("# valid HELLOs downloaded from hostlist servers"),
212 GNUNET_TRANSPORT_offer_hello (transport, msg);
216 GNUNET_STATISTICS_update (stats,
217 gettext_noop ("# invalid HELLOs downloaded from hostlist servers"),
220 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
221 _("Invalid `%s' message received from hostlist at `%s'\n"),
224 bogus_url = GNUNET_YES;
227 memmove (download_buffer,
228 &download_buffer[msize],
229 download_pos - msize);
230 download_pos -= msize;
237 * Obtain a hostlist URL that we should use.
239 * @return NULL if there is no URL available
250 GNUNET_CONFIGURATION_get_value_string (cfg,
255 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
256 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
257 "SERVERS", "HOSTLIST");
262 if (strlen (servers) > 0)
265 pos = strlen (servers) - 1;
268 if (servers[pos] == ' ')
275 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
276 _("No `%s' specified in `%s' configuration, will not bootstrap.\n"),
277 "SERVERS", "HOSTLIST");
278 GNUNET_free (servers);
282 urls = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, urls) + 1;
283 pos = strlen (servers) - 1;
286 if (servers[pos] == ' ')
298 ret = GNUNET_strdup (&servers[pos]);
299 GNUNET_free (servers);
304 #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);
308 * Schedule the background task that will (possibly)
309 * download a hostlist.
312 schedule_hostlist_task (void);
316 * Clean up the state from the task that downloaded the
317 * hostlist and schedule the next task.
326 mret = curl_multi_remove_handle (multi, curl);
327 if (mret != CURLM_OK)
329 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
330 _("%s failed at %s:%d: `%s'\n"),
331 "curl_multi_remove_handle", __FILE__, __LINE__,
332 curl_multi_strerror (mret));
334 mret = curl_multi_cleanup (multi);
335 if (mret != CURLM_OK)
336 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
337 _("%s failed at %s:%d: `%s'\n"),
338 "curl_multi_cleanup", __FILE__, __LINE__,
339 curl_multi_strerror (mret));
344 curl_easy_cleanup (curl);
347 GNUNET_free_non_null (current_url);
349 schedule_hostlist_task ();
354 * Ask CURL for the select set and then schedule the
355 * receiving task with the scheduler.
362 * Task that is run when we are ready to receive more data from the hostlist
365 * @param cls closure, unused
366 * @param tc task context, unused
369 multi_ready (void *cls,
370 const struct GNUNET_SCHEDULER_TaskContext *tc)
376 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
378 #if DEBUG_HOSTLIST_CLIENT
379 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
380 "Shutdown requested while trying to download hostlist from `%s'\n",
386 if (GNUNET_TIME_absolute_get_remaining (end_time).value == 0)
388 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
389 _("Timeout trying to download hostlist from `%s'\n"),
394 #if DEBUG_HOSTLIST_CLIENT
395 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
396 "Ready for processing hostlist client request\n");
401 mret = curl_multi_perform (multi, &running);
406 msg = curl_multi_info_read (multi, &running);
407 GNUNET_break (msg != NULL);
413 if ( (msg->data.result != CURLE_OK) &&
414 (msg->data.result != CURLE_GOT_NOTHING) )
415 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
416 _("%s failed for `%s' at %s:%d: `%s'\n"),
417 "curl_multi_perform",
421 curl_easy_strerror (msg->data.result));
423 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
424 _("Download of hostlist `%s' completed.\n"),
435 while (mret == CURLM_CALL_MULTI_PERFORM);
436 if (mret != CURLM_OK)
438 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
439 _("%s failed at %s:%d: `%s'\n"),
440 "curl_multi_perform", __FILE__, __LINE__,
441 curl_multi_strerror (mret));
449 * Ask CURL for the select set and then schedule the
450 * receiving task with the scheduler.
460 struct GNUNET_NETWORK_FDSet *grs;
461 struct GNUNET_NETWORK_FDSet *gws;
463 struct GNUNET_TIME_Relative rtime;
469 mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
470 if (mret != CURLM_OK)
472 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
473 _("%s failed at %s:%d: `%s'\n"),
474 "curl_multi_fdset", __FILE__, __LINE__,
475 curl_multi_strerror (mret));
479 mret = curl_multi_timeout (multi, &timeout);
480 if (mret != CURLM_OK)
482 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
483 _("%s failed at %s:%d: `%s'\n"),
484 "curl_multi_timeout", __FILE__, __LINE__,
485 curl_multi_strerror (mret));
489 rtime = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (end_time),
490 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
492 grs = GNUNET_NETWORK_fdset_create ();
493 gws = GNUNET_NETWORK_fdset_create ();
494 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
495 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
496 #if DEBUG_HOSTLIST_CLIENT
497 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
498 "Scheduling task for hostlist download using cURL\n");
501 = GNUNET_SCHEDULER_add_select (sched,
502 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
503 GNUNET_SCHEDULER_NO_TASK,
509 GNUNET_NETWORK_fdset_destroy (gws);
510 GNUNET_NETWORK_fdset_destroy (grs);
515 * Main function that will download a hostlist and process its
524 curl = curl_easy_init ();
532 current_url = get_url ();
533 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
534 _("Bootstrapping using hostlist at `%s'.\n"),
536 GNUNET_STATISTICS_update (stats,
537 gettext_noop ("# hostlist downloads initiated"),
541 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
544 CURL_EASY_SETOPT (curl,
545 CURLOPT_WRITEFUNCTION,
546 &download_hostlist_processor);
552 CURL_EASY_SETOPT (curl,
560 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
561 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
562 /* no need to abort if the above failed */
563 CURL_EASY_SETOPT (curl,
571 CURL_EASY_SETOPT (curl,
575 CURL_EASY_SETOPT (curl,
579 CURL_EASY_SETOPT (curl,
581 GNUNET_SERVER_MAX_MESSAGE_SIZE);
582 if (0 == strncmp (current_url, "http", 4))
583 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
584 CURL_EASY_SETOPT (curl,
585 CURLOPT_CONNECTTIMEOUT,
587 CURL_EASY_SETOPT (curl,
591 /* this should no longer be needed; we're now single-threaded! */
592 CURL_EASY_SETOPT (curl,
596 multi = curl_multi_init ();
603 mret = curl_multi_add_handle (multi, curl);
604 if (mret != CURLM_OK)
606 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
607 _("%s failed at %s:%d: `%s'\n"),
608 "curl_multi_add_handle", __FILE__, __LINE__,
609 curl_multi_strerror (mret));
610 mret = curl_multi_cleanup (multi);
611 if (mret != CURLM_OK)
612 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
613 _("%s failed at %s:%d: `%s'\n"),
614 "curl_multi_cleanup", __FILE__, __LINE__,
615 curl_multi_strerror (mret));
620 end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
626 * Task that checks if we should try to download a hostlist.
627 * If so, we initiate the download, otherwise we schedule
628 * this task again for a later time.
631 check_task (void *cls,
632 const struct GNUNET_SCHEDULER_TaskContext *tc)
634 current_task = GNUNET_SCHEDULER_NO_TASK;
635 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
637 if (connection_count < MIN_CONNECTIONS)
638 download_hostlist ();
640 schedule_hostlist_task ();
645 * Compute when we should check the next time about downloading
646 * a hostlist; then schedule the task accordingly.
649 schedule_hostlist_task ()
652 struct GNUNET_TIME_Relative delay;
656 curl_global_cleanup ();
657 return; /* in shutdown */
659 delay = hostlist_delay;
660 if (hostlist_delay.value == 0)
661 hostlist_delay = GNUNET_TIME_UNIT_SECONDS;
663 hostlist_delay = GNUNET_TIME_relative_multiply (hostlist_delay, 2);
664 if (hostlist_delay.value > GNUNET_TIME_UNIT_HOURS.value * (1 + connection_count))
665 hostlist_delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS,
666 (1 + connection_count));
667 GNUNET_STATISTICS_set (stats,
668 gettext_noop("# seconds between hostlist downloads"),
669 hostlist_delay.value,
673 delay = GNUNET_TIME_UNIT_ZERO;
676 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
677 _("Have %u/%u connections. Will consider downloading hostlist in %llums\n"),
680 (unsigned long long) delay.value);
681 current_task = GNUNET_SCHEDULER_add_delayed (sched,
689 * Method called whenever a given peer connects.
692 * @param peer peer identity this notification is about
693 * @param latency reported latency of the connection with 'other'
694 * @param distance reported distance (DV) to 'other'
697 connect_handler (void *cls,
699 GNUNET_PeerIdentity * peer,
700 struct GNUNET_TIME_Relative latency,
704 GNUNET_STATISTICS_update (stats,
705 gettext_noop ("# active connections"),
712 * Method called whenever a given peer disconnects.
715 * @param peer peer identity this notification is about
718 disconnect_handler (void *cls,
720 GNUNET_PeerIdentity * peer)
723 GNUNET_STATISTICS_update (stats,
724 gettext_noop ("# active connections"),
730 * Method called whenever an advertisement message arrives.
732 * @param cls closure (always NULL)
733 * @param client identification of the client
734 * @param message the actual message
735 * @return GNUNET_OK to keep the connection open,
736 * GNUNET_SYSERR to close it (signal serious error)
739 advertisement_handler (void *cls,
740 const struct GNUNET_PeerIdentity * peer,
741 const struct GNUNET_MessageHeader * message,
742 struct GNUNET_TIME_Relative latency,
748 int size = ntohs (message->size);
749 int uri_size = size - sizeof ( struct GNUNET_HOSTLIST_ADV_Message );
750 char * uri = GNUNET_malloc ( uri_size );
751 struct GNUNET_Hostlist * hostlist;
753 if ( ntohs (message->type) != GNUNET_MESSAGE_TYPE_HOSTLIST_ADVERTISEMENT)
756 const struct GNUNET_HOSTLIST_ADV_Message * incoming = (const struct GNUNET_HOSTLIST_ADV_Message *) message;
757 memcpy ( uri, &incoming[1], uri_size );
758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
759 "Hostlist client recieved advertisement from '%s' containing URI %s\n", GNUNET_i2s (peer), uri );
761 /* search in map for peer identity */
762 hostlist = GNUNET_malloc ( sizeof (struct GNUNET_Hostlist) );
763 hostlist = GNUNET_malloc ( sizeof (struct GNUNET_Hostlist) );
764 hostlist->peer = (*peer);
765 hostlist->hello_count = 0;
766 hostlist->hostlist_uri = GNUNET_malloc ( uri_size);
767 memcpy ( hostlist->hostlist_uri, &incoming[1], uri_size );
768 hostlist->time_creation = GNUNET_TIME_absolute_get();
769 hostlist->time_last_usage = GNUNET_TIME_absolute_get_zero();
771 GNUNET_HashCode * peer_ident_hash = (GNUNET_HashCode * ) &(peer->hashPubKey);
772 if ( GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (hostlist_hashmap, peer_ident_hash) )
774 if ( MAX_NUMBER_HOSTLISTS > GNUNET_CONTAINER_multihashmap_size (hostlist_hashmap) )
776 /* Entries available, add hostlist to hashmap */
777 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
778 "Adding peer '%s' to hashmap %s\n", GNUNET_i2s (peer), uri );
779 GNUNET_CONTAINER_multihashmap_put ( hostlist_hashmap, peer_ident_hash, hostlist, GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE );
783 /* No free entries available, replace existing entry */
788 /* hostlist entry already existing in hashmap */
789 /* compare uri to new uri ? */
790 /* update recieved date (vs using last download time to check reachability)? */
797 * Continuation called by the statistics code once
798 * we go the stat. Initiates hostlist download scheduling.
801 * @param success GNUNET_OK if statistics were
802 * successfully obtained, GNUNET_SYSERR if not.
805 primary_task (void *cls, int success)
808 return; /* in shutdown */
809 #if DEBUG_HOSTLIST_CLIENT
810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
811 "Statistics request done, scheduling hostlist download\n");
813 schedule_hostlist_task ();
818 process_stat (void *cls,
819 const char *subsystem,
824 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
825 _("Initial time between hostlist downloads is %llums\n"),
826 (unsigned long long) value);
827 hostlist_delay.value = value;
832 * Method to load persistent hostlist file during hostlist client startup
833 * param c configuration to use
835 static int load_hostlist_file ()
840 GNUNET_CONFIGURATION_get_value_string (cfg,
845 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
846 _("No `%s' specified in `%s' configuration, cannot load hostlists from file.\n"),
847 "HOSTLISTFILE", "HOSTLIST");
848 return GNUNET_SYSERR;
851 struct GNUNET_BIO_ReadHandle * rh = GNUNET_BIO_read_open (filename);
854 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
855 ("Could not open file %s for reading to load hostlists\n"), filename);
856 return GNUNET_SYSERR;
859 /* add code to read hostlists to file using bio */
860 char * buffer = GNUNET_malloc (100 * sizeof (char));
861 GNUNET_BIO_read_string (rh, NULL , &buffer, 100);
862 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
863 ("Read from file %s : %s \n"), filename, buffer);
865 if ( GNUNET_OK != GNUNET_BIO_read_close ( rh , &buffer) )
866 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
867 ("Error while closing file %s\n"), filename);
872 static int iterate_hashmap(void *cls, const GNUNET_HashCode *key, void *value)
874 /* add code to process hostlist entries */
875 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
876 ("Now iterating over peer entry: %s\n"), GNUNET_i2s ( (const struct GNUNET_PeerIdentity *) key));
879 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
880 ("Entry url: %s \n"), ((struct GNUNET_Hostlist *) value)->hostlist_uri );
883 GNUNET_free ( value );
889 * Method to load persistent hostlist file during hostlist client shutdown
890 * param c configuration to use
892 static int save_hostlist_file ()
897 GNUNET_CONFIGURATION_get_value_string (cfg,
902 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
903 _("No `%s' specified in `%s' configuration, cannot save hostlists to file.\n"),
904 "HOSTLISTFILE", "HOSTLIST");
905 return GNUNET_SYSERR;
908 struct GNUNET_BIO_WriteHandle * wh = GNUNET_BIO_write_open (filename);
911 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
912 ("Could not open file %s for writing to save hostlists\n"),
914 return GNUNET_SYSERR;
917 /* add code to write hostlists to file using bio */
919 /* iterate over all entries in hashmap */
920 GNUNET_CONTAINER_multihashmap_iterate ( hostlist_hashmap,
924 GNUNET_BIO_write_string ( wh, "DUMMY TEXT" );
926 if ( GNUNET_OK != GNUNET_BIO_write_close ( wh ) )
927 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
928 ("Error while closing file %s\n"),
934 * Start downloading hostlists from hostlist servers as necessary.
937 GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c,
938 struct GNUNET_SCHEDULER_Handle *s,
939 struct GNUNET_STATISTICS_Handle *st,
940 GNUNET_CORE_ConnectEventHandler *ch,
941 GNUNET_CORE_DisconnectEventHandler *dh,
942 GNUNET_CORE_MessageCallback *msgh,
945 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
948 return GNUNET_SYSERR;
950 transport = GNUNET_TRANSPORT_connect (s, c, NULL, NULL, NULL, NULL);
951 if (NULL == transport)
953 curl_global_cleanup ();
954 return GNUNET_SYSERR;
960 GNUNET_CONFIGURATION_get_value_string (cfg,
965 *ch = &connect_handler;
966 *dh = &disconnect_handler;
967 *msgh = &advertisement_handler;
972 hostlist_hashmap = GNUNET_CONTAINER_multihashmap_create ( MAX_NUMBER_HOSTLISTS );
974 load_hostlist_file ();
976 GNUNET_STATISTICS_get (stats,
978 gettext_noop("# seconds between hostlist downloads"),
979 GNUNET_TIME_UNIT_MINUTES,
988 * Stop downloading hostlists from hostlist servers as necessary.
991 GNUNET_HOSTLIST_client_stop ()
993 #if DEBUG_HOSTLIST_CLIENT
994 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
995 "Hostlist client shutdown\n");
997 save_hostlist_file ();
1001 GNUNET_CONTAINER_multihashmap_destroy ( hostlist_hashmap );
1004 if (current_task != GNUNET_SCHEDULER_NO_TASK)
1006 GNUNET_SCHEDULER_cancel (sched,
1008 curl_global_cleanup ();
1010 if (transport != NULL)
1012 GNUNET_TRANSPORT_disconnect (transport);
1015 GNUNET_assert (NULL == transport);
1016 GNUNET_free_non_null (proxy);
1022 /* end of hostlist-client.c */