gnunet

Main GNUnet Logic
Log | Files | Refs | Submodules | README | LICENSE

gnunet-daemon-hostlist_client.c (53238B)


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