hostlist-client.c 44 KB

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