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 *) ×_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 */