1/* 2 * This file Copyright (C) Mnemosyne LLC 3 * 4 * This file is licensed by the GPL version 2. Works owned by the 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 7 * This exemption does not extend to derived works not owned by 8 * the Transmission project. 9 * 10 * $Id: announcer.c 13313 2012-05-22 20:21:00Z jordan $ 11 */ 12 13#include <assert.h> 14#include <limits.h> /* INT_MAX */ 15#include <stdio.h> 16#include <stdlib.h> /* qsort() */ 17#include <string.h> /* strcmp(), memcpy() */ 18 19#include <event2/buffer.h> 20#include <event2/event.h> /* evtimer */ 21 22#define __LIBTRANSMISSION_ANNOUNCER_MODULE___ 23 24#include "transmission.h" 25#include "announcer.h" 26#include "announcer-common.h" 27#include "crypto.h" /* tr_cryptoRandInt(), tr_cryptoWeakRandInt() */ 28#include "peer-mgr.h" /* tr_peerMgrCompactToPex() */ 29#include "ptrarray.h" 30#include "session.h" 31#include "torrent.h" 32#include "utils.h" 33 34struct tr_tier; 35 36static void tier_build_log_name( const struct tr_tier * tier, 37 char * buf, size_t buflen ); 38 39#define dbgmsg( tier, ... ) \ 40if( tr_deepLoggingIsActive( ) ) do { \ 41 char name[128]; \ 42 tier_build_log_name( tier, name, sizeof( name ) ); \ 43 tr_deepLog( __FILE__, __LINE__, name, __VA_ARGS__ ); \ 44} while( 0 ) 45 46enum 47{ 48 /* unless the tracker says otherwise, rescrape this frequently */ 49 DEFAULT_SCRAPE_INTERVAL_SEC = ( 60 * 30 ), 50 51 /* unless the tracker says otherwise, this is the announce interval */ 52 DEFAULT_ANNOUNCE_INTERVAL_SEC = ( 60 * 10 ), 53 54 /* unless the tracker says otherwise, this is the announce min_interval */ 55 DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC = ( 60 * 2 ), 56 57 /* how many web tasks we allow at one time */ 58 MAX_CONCURRENT_TASKS = 48, 59 60 /* the value of the 'numwant' argument passed in tracker requests. */ 61 NUMWANT = 80, 62 63 UPKEEP_INTERVAL_SECS = 1, 64 65 /* this is how often to call the UDP tracker upkeep */ 66 TAU_UPKEEP_INTERVAL_SECS = 5 67}; 68 69/*** 70**** 71***/ 72 73const char* 74tr_announce_event_get_string( tr_announce_event e ) 75{ 76 switch( e ) 77 { 78 case TR_ANNOUNCE_EVENT_COMPLETED: return "completed"; 79 case TR_ANNOUNCE_EVENT_STARTED: return "started"; 80 case TR_ANNOUNCE_EVENT_STOPPED: return "stopped"; 81 default: return ""; 82 } 83} 84 85/*** 86**** 87***/ 88 89static int 90compareTransfer( uint64_t a_uploaded, uint64_t a_downloaded, 91 uint64_t b_uploaded, uint64_t b_downloaded ) 92{ 93 /* higher upload count goes first */ 94 if( a_uploaded != b_uploaded ) 95 return a_uploaded > b_uploaded ? -1 : 1; 96 97 /* then higher download count goes first */ 98 if( a_downloaded != b_downloaded ) 99 return a_downloaded > b_downloaded ? -1 : 1; 100 101 return 0; 102} 103 104/** 105 * Comparison function for tr_announce_requests. 106 * 107 * The primary key (amount of data transferred) is used to prioritize 108 * tracker announcements of active torrents. The remaining keys are 109 * used to satisfy the uniqueness requirement of a sorted tr_ptrArray. 110 */ 111static int 112compareStops( const void * va, const void * vb ) 113{ 114 int i; 115 const tr_announce_request * a = va; 116 const tr_announce_request * b = vb; 117 118 /* primary key: volume of data transferred. */ 119 if(( i = compareTransfer( a->up, a->down, b->up, b->down))) 120 return i; 121 122 /* secondary key: the torrent's info_hash */ 123 if(( i = memcmp( a->info_hash, b->info_hash, SHA_DIGEST_LENGTH ))) 124 return i; 125 126 /* tertiary key: the tracker's announec url */ 127 return tr_strcmp0( a->url, b->url ); 128} 129 130/*** 131**** 132***/ 133 134/** 135 * "global" (per-tr_session) fields 136 */ 137typedef struct tr_announcer 138{ 139 tr_ptrArray stops; /* tr_announce_request */ 140 141 tr_session * session; 142 struct event * upkeepTimer; 143 int slotsAvailable; 144 int key; 145 time_t tauUpkeepAt; 146} 147tr_announcer; 148 149bool 150tr_announcerHasBacklog( const struct tr_announcer * announcer ) 151{ 152 return announcer->slotsAvailable < 1; 153} 154 155static void 156onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer ); 157 158void 159tr_announcerInit( tr_session * session ) 160{ 161 tr_announcer * a; 162 163 assert( tr_isSession( session ) ); 164 165 a = tr_new0( tr_announcer, 1 ); 166 a->stops = TR_PTR_ARRAY_INIT; 167 a->key = tr_cryptoRandInt( INT_MAX ); 168 a->session = session; 169 a->slotsAvailable = MAX_CONCURRENT_TASKS; 170 a->upkeepTimer = evtimer_new( session->event_base, onUpkeepTimer, a ); 171 tr_timerAdd( a->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 ); 172 173 session->announcer = a; 174} 175 176static void flushCloseMessages( tr_announcer * announcer ); 177 178void 179tr_announcerClose( tr_session * session ) 180{ 181 tr_announcer * announcer = session->announcer; 182 183 flushCloseMessages( announcer ); 184 185 tr_tracker_udp_start_shutdown( session ); 186 187 event_free( announcer->upkeepTimer ); 188 announcer->upkeepTimer = NULL; 189 190 tr_ptrArrayDestruct( &announcer->stops, NULL ); 191 192 session->announcer = NULL; 193 tr_free( announcer ); 194} 195 196/*** 197**** 198***/ 199 200/* a row in tr_tier's list of trackers */ 201typedef struct 202{ 203 char * key; 204 char * announce; 205 char * scrape; 206 207 char * tracker_id_str; 208 209 int seederCount; 210 int leecherCount; 211 int downloadCount; 212 int downloaderCount; 213 214 int consecutiveFailures; 215 216 uint32_t id; 217} 218tr_tracker; 219 220/* format: host+':'+ port */ 221static char * 222getKey( const char * url ) 223{ 224 char * ret; 225 char * scheme = NULL; 226 char * host = NULL; 227 int port = 0; 228 229 tr_urlParse( url, -1, &scheme, &host, &port, NULL ); 230 ret = tr_strdup_printf( "%s://%s:%d", (scheme?scheme:"invalid"), (host?host:"invalid"), port ); 231 232 tr_free( host ); 233 tr_free( scheme ); 234 return ret; 235} 236 237static void 238trackerConstruct( tr_tracker * tracker, const tr_tracker_info * inf ) 239{ 240 memset( tracker, 0, sizeof( tr_tracker ) ); 241 tracker->key = getKey( inf->announce ); 242 tracker->announce = tr_strdup( inf->announce ); 243 tracker->scrape = tr_strdup( inf->scrape ); 244 tracker->id = inf->id; 245 tracker->seederCount = -1; 246 tracker->leecherCount = -1; 247 tracker->downloadCount = -1; 248} 249 250static void 251trackerDestruct( tr_tracker * tracker ) 252{ 253 tr_free( tracker->tracker_id_str ); 254 tr_free( tracker->scrape ); 255 tr_free( tracker->announce ); 256 tr_free( tracker->key ); 257} 258 259/*** 260**** 261***/ 262 263struct tr_torrent_tiers; 264 265/** @brief A group of trackers in a single tier, as per the multitracker spec */ 266typedef struct tr_tier 267{ 268 /* number of up/down/corrupt bytes since the last time we sent an 269 * "event=stopped" message that was acknowledged by the tracker */ 270 uint64_t byteCounts[3]; 271 272 tr_tracker * trackers; 273 int tracker_count; 274 tr_tracker * currentTracker; 275 int currentTrackerIndex; 276 277 tr_torrent * tor; 278 279 time_t scrapeAt; 280 time_t lastScrapeStartTime; 281 time_t lastScrapeTime; 282 bool lastScrapeSucceeded; 283 bool lastScrapeTimedOut; 284 285 time_t announceAt; 286 time_t manualAnnounceAllowedAt; 287 time_t lastAnnounceStartTime; 288 time_t lastAnnounceTime; 289 bool lastAnnounceSucceeded; 290 bool lastAnnounceTimedOut; 291 292 tr_announce_event * announce_events; 293 int announce_event_count; 294 int announce_event_alloc; 295 296 /* unique lookup key */ 297 int key; 298 299 int scrapeIntervalSec; 300 int announceIntervalSec; 301 int announceMinIntervalSec; 302 303 int lastAnnouncePeerCount; 304 305 bool isRunning; 306 bool isAnnouncing; 307 bool isScraping; 308 bool wasCopied; 309 310 char lastAnnounceStr[128]; 311 char lastScrapeStr[128]; 312} 313tr_tier; 314 315static time_t 316get_next_scrape_time( const tr_session * session, const tr_tier * tier, int interval ) 317{ 318 time_t ret; 319 const time_t now = tr_time( ); 320 321 /* Maybe don't scrape paused torrents */ 322 if( !tier->isRunning && !session->scrapePausedTorrents ) 323 ret = 0; 324 325 /* Add the interval, and then increment to the nearest 10th second. 326 * The latter step is to increase the odds of several torrents coming 327 * due at the same time to improve multiscrape. */ 328 else { 329 ret = now + interval; 330 while( ret % 10 ) 331 ++ret; 332 } 333 334 return ret; 335} 336 337static void 338tierConstruct( tr_tier * tier, tr_torrent * tor ) 339{ 340 static int nextKey = 1; 341 342 memset( tier, 0, sizeof( tr_tier ) ); 343 344 tier->key = nextKey++; 345 tier->currentTrackerIndex = -1; 346 tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC; 347 tier->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC; 348 tier->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC; 349 tier->scrapeAt = get_next_scrape_time( tor->session, tier, tr_cryptoWeakRandInt( 180 ) ); 350 tier->tor = tor; 351} 352 353static void 354tierDestruct( tr_tier * tier ) 355{ 356 tr_free( tier->announce_events ); 357} 358 359static void 360tier_build_log_name( const tr_tier * tier, char * buf, size_t buflen ) 361{ 362 tr_snprintf( buf, buflen, "[%s---%s]", 363 ( tier && tier->tor ) ? tr_torrentName( tier->tor ) : "?", 364 ( tier && tier->currentTracker ) ? tier->currentTracker->key : "?" ); 365} 366 367static void 368tierIncrementTracker( tr_tier * tier ) 369{ 370 /* move our index to the next tracker in the tier */ 371 const int i = ( tier->currentTracker == NULL ) 372 ? 0 373 : ( tier->currentTrackerIndex + 1 ) % tier->tracker_count; 374 tier->currentTrackerIndex = i; 375 tier->currentTracker = &tier->trackers[i]; 376 377 /* reset some of the tier's fields */ 378 tier->scrapeIntervalSec = DEFAULT_SCRAPE_INTERVAL_SEC; 379 tier->announceIntervalSec = DEFAULT_ANNOUNCE_INTERVAL_SEC; 380 tier->announceMinIntervalSec = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC; 381 tier->isAnnouncing = false; 382 tier->isScraping = false; 383 tier->lastAnnounceStartTime = 0; 384 tier->lastScrapeStartTime = 0; 385} 386 387/*** 388**** 389***/ 390 391/** 392 * @brief Opaque, per-torrent data structure for tracker announce information 393 * 394 * this opaque data structure can be found in tr_torrent.tiers 395 */ 396typedef struct tr_torrent_tiers 397{ 398 tr_tier * tiers; 399 int tier_count; 400 401 tr_tracker * trackers; 402 int tracker_count; 403 404 tr_tracker_callback * callback; 405 void * callbackData; 406} 407tr_torrent_tiers; 408 409static tr_torrent_tiers* 410tiersNew( void ) 411{ 412 return tr_new0( tr_torrent_tiers, 1 ); 413} 414 415static void 416tiersDestruct( tr_torrent_tiers * tt ) 417{ 418 int i; 419 420 for( i=0; i<tt->tracker_count; ++i ) 421 trackerDestruct( &tt->trackers[i] ); 422 tr_free( tt->trackers ); 423 424 for( i=0; i<tt->tier_count; ++i ) 425 tierDestruct( &tt->tiers[i] ); 426 tr_free( tt->tiers ); 427} 428 429static void 430tiersFree( tr_torrent_tiers * tt ) 431{ 432 tiersDestruct( tt ); 433 tr_free( tt ); 434} 435 436static tr_tier* 437getTier( tr_announcer * announcer, const uint8_t * info_hash, int tierId ) 438{ 439 tr_tier * tier = NULL; 440 441 if( announcer != NULL ) 442 { 443 tr_session * session = announcer->session; 444 tr_torrent * tor = tr_torrentFindFromHash( session, info_hash ); 445 446 if( tor && tor->tiers ) 447 { 448 int i; 449 tr_torrent_tiers * tt = tor->tiers; 450 451 for( i=0; !tier && i<tt->tier_count; ++i ) 452 if( tt->tiers[i].key == tierId ) 453 tier = &tt->tiers[i]; 454 } 455 } 456 457 return tier; 458} 459 460/*** 461**** PUBLISH 462***/ 463 464static const tr_tracker_event TRACKER_EVENT_INIT = { 0, 0, 0, 0, 0, 0 }; 465 466static void 467publishMessage( tr_tier * tier, const char * msg, int type ) 468{ 469 if( tier && tier->tor && tier->tor->tiers && tier->tor->tiers->callback ) 470 { 471 tr_torrent_tiers * tiers = tier->tor->tiers; 472 tr_tracker_event event = TRACKER_EVENT_INIT; 473 event.messageType = type; 474 event.text = msg; 475 if( tier->currentTracker ) 476 event.tracker = tier->currentTracker->announce; 477 478 tiers->callback( tier->tor, &event, tiers->callbackData ); 479 } 480} 481 482static void 483publishErrorClear( tr_tier * tier ) 484{ 485 publishMessage( tier, NULL, TR_TRACKER_ERROR_CLEAR ); 486} 487 488static void 489publishWarning( tr_tier * tier, const char * msg ) 490{ 491 publishMessage( tier, msg, TR_TRACKER_WARNING ); 492} 493 494static void 495publishError( tr_tier * tier, const char * msg ) 496{ 497 publishMessage( tier, msg, TR_TRACKER_ERROR ); 498} 499 500static int8_t 501getSeedProbability( tr_tier * tier, int seeds, int leechers, int pex_count ) 502{ 503 /* special case optimization: 504 ocelot omits seeds from peer lists sent to seeds on private trackers. 505 so check for that case... */ 506 if( ( leechers == pex_count ) && tr_torrentIsPrivate( tier->tor ) 507 && tr_torrentIsSeed( tier->tor ) 508 && ( seeds + leechers < NUMWANT ) ) 509 return 0; 510 511 if( seeds>=0 && leechers>=0 && (seeds+leechers>0) ) 512 return (int8_t)((100.0*seeds)/(seeds+leechers)); 513 514 return -1; /* unknown */ 515} 516 517static void 518publishPeersPex( tr_tier * tier, int seeds, int leechers, 519 const tr_pex * pex, int n ) 520{ 521 if( tier->tor->tiers->callback ) 522 { 523 tr_tracker_event e = TRACKER_EVENT_INIT; 524 e.messageType = TR_TRACKER_PEERS; 525 e.seedProbability = getSeedProbability( tier, seeds, leechers, n ); 526 e.pex = pex; 527 e.pexCount = n; 528 dbgmsg( tier, "got %d peers; seed prob %d", n, (int)e.seedProbability ); 529 530 tier->tor->tiers->callback( tier->tor, &e, NULL ); 531 } 532} 533 534/*** 535**** 536***/ 537 538struct ann_tracker_info 539{ 540 tr_tracker_info info; 541 542 char * scheme; 543 char * host; 544 char * path; 545 int port; 546}; 547 548 549/* primary key: tier 550 * secondary key: udp comes before http */ 551static int 552filter_trackers_compare_func( const void * va, const void * vb ) 553{ 554 const struct ann_tracker_info * a = va; 555 const struct ann_tracker_info * b = vb; 556 if( a->info.tier != b->info.tier ) 557 return a->info.tier - b->info.tier; 558 return -strcmp( a->scheme, b->scheme ); 559} 560 561/** 562 * Massages the incoming list of trackers into something we can use. 563 */ 564static tr_tracker_info * 565filter_trackers( tr_tracker_info * input, int input_count, int * setme_count ) 566{ 567 int i, in; 568 int j, jn; 569 int n = 0; 570 struct tr_tracker_info * ret; 571 struct ann_tracker_info * tmp = tr_new0( struct ann_tracker_info, input_count ); 572 573 /*for( i=0, in=input_count; i<in; ++i ) fprintf( stderr, "IN: [%d][%s]\n", input[i].tier, input[i].announce );*/ 574 575 /* build a list of valid trackers */ 576 for( i=0, in=input_count; i<in; ++i ) { 577 if( tr_urlIsValidTracker( input[i].announce ) ) { 578 int port; 579 char * scheme; 580 char * host; 581 char * path; 582 bool is_duplicate = false; 583 tr_urlParse( input[i].announce, -1, &scheme, &host, &port, &path ); 584 585 /* weed out one common source of duplicates: 586 * "http://tracker/announce" + 587 * "http://tracker:80/announce" 588 */ 589 for( j=0, jn=n; !is_duplicate && j<jn; ++j ) 590 is_duplicate = (tmp[j].port==port) 591 && !strcmp(tmp[j].scheme,scheme) 592 && !strcmp(tmp[j].host,host) 593 && !strcmp(tmp[j].path,path); 594 595 if( is_duplicate ) { 596 tr_free( path ); 597 tr_free( host ); 598 tr_free( scheme ); 599 continue; 600 } 601 tmp[n].info = input[i]; 602 tmp[n].scheme = scheme; 603 tmp[n].host = host; 604 tmp[n].port = port; 605 tmp[n].path = path; 606 n++; 607 } 608 } 609 610 /* if two announce URLs differ only by scheme, put them in the same tier. 611 * (note: this can leave gaps in the `tier' values, but since the calling 612 * function doesn't care, there's no point in removing the gaps...) */ 613 for( i=0, in=n; i<in; ++i ) 614 for( j=i+1, jn=n; j<jn; ++j ) 615 if( (tmp[i].info.tier!=tmp[j].info.tier) 616 && (tmp[i].port==tmp[j].port) 617 && !tr_strcmp0(tmp[i].host,tmp[j].host) 618 && !tr_strcmp0(tmp[i].path,tmp[j].path) ) 619 tmp[j].info.tier = tmp[i].info.tier; 620 621 /* sort them, for two reasons: 622 * (1) unjumble the tiers from the previous step 623 * (2) move the UDP trackers to the front of each tier */ 624 qsort( tmp, n, sizeof(struct ann_tracker_info), filter_trackers_compare_func ); 625 626 /* build the output */ 627 *setme_count = n; 628 ret = tr_new0( tr_tracker_info, n ); 629 for( i=0, in=n; i<in; ++i ) 630 ret[i] = tmp[i].info; 631 632 /* cleanup */ 633 for( i=0, in=n; i<in; ++i ) { 634 tr_free( tmp[i].path ); 635 tr_free( tmp[i].host ); 636 tr_free( tmp[i].scheme ); 637 } 638 tr_free( tmp ); 639 640 /*for( i=0, in=n; i<in; ++i ) fprintf( stderr, "OUT: [%d][%s]\n", ret[i].tier, ret[i].announce );*/ 641 return ret; 642} 643 644 645static void 646addTorrentToTier( tr_torrent_tiers * tt, tr_torrent * tor ) 647{ 648 int i, n; 649 int tier_count; 650 tr_tier * tier; 651 tr_tracker_info * infos = filter_trackers( tor->info.trackers, 652 tor->info.trackerCount, &n ); 653 654 /* build the array of trackers */ 655 tt->trackers = tr_new0( tr_tracker, n ); 656 tt->tracker_count = n; 657 for( i=0; i<n; ++i ) 658 trackerConstruct( &tt->trackers[i], &infos[i] ); 659 660 /* count how many tiers there are */ 661 tier_count = 0; 662 for( i=0; i<n; ++i ) 663 if( !i || ( infos[i].tier != infos[i-1].tier ) ) 664 ++tier_count; 665 666 /* build the array of tiers */ 667 tier = NULL; 668 tt->tiers = tr_new0( tr_tier, tier_count ); 669 tt->tier_count = 0; 670 for( i=0; i<n; ++i ) { 671 if( i && ( infos[i].tier == infos[i-1].tier ) ) 672 ++tier->tracker_count; 673 else { 674 tier = &tt->tiers[tt->tier_count++]; 675 tierConstruct( tier, tor ); 676 tier->trackers = &tt->trackers[i]; 677 tier->tracker_count = 1; 678 tierIncrementTracker( tier ); 679 } 680 } 681 682 /* cleanup */ 683 tr_free( infos ); 684} 685 686tr_torrent_tiers * 687tr_announcerAddTorrent( tr_torrent * tor, 688 tr_tracker_callback * callback, 689 void * callbackData ) 690{ 691 tr_torrent_tiers * tiers; 692 693 assert( tr_isTorrent( tor ) ); 694 695 tiers = tiersNew( ); 696 tiers->callback = callback; 697 tiers->callbackData = callbackData; 698 699 addTorrentToTier( tiers, tor ); 700 701 return tiers; 702} 703 704/*** 705**** 706***/ 707 708static bool 709tierCanManualAnnounce( const tr_tier * tier ) 710{ 711 return tier->manualAnnounceAllowedAt <= tr_time( ); 712} 713 714bool 715tr_announcerCanManualAnnounce( const tr_torrent * tor ) 716{ 717 int i; 718 struct tr_torrent_tiers * tt = NULL; 719 720 assert( tr_isTorrent( tor ) ); 721 assert( tor->tiers != NULL ); 722 723 if( tor->isRunning ) 724 tt = tor->tiers; 725 726 /* return true if any tier can manual announce */ 727 for( i=0; tt && i<tt->tier_count; ++i ) 728 if( tierCanManualAnnounce( &tt->tiers[i] ) ) 729 return true; 730 731 return false; 732} 733 734time_t 735tr_announcerNextManualAnnounce( const tr_torrent * tor ) 736{ 737 int i; 738 time_t ret = ~(time_t)0; 739 struct tr_torrent_tiers * tt = tor->tiers; 740 741 /* find the earliest manual announce time from all peers */ 742 for( i=0; tt && i<tt->tier_count; ++i ) 743 if( tt->tiers[i].isRunning ) 744 ret = MIN( ret, tt->tiers[i].manualAnnounceAllowedAt ); 745 746 return ret; 747} 748 749static void 750dbgmsg_tier_announce_queue( const tr_tier * tier ) 751{ 752 if( tr_deepLoggingIsActive( ) ) 753 { 754 int i; 755 char name[128]; 756 char * message; 757 struct evbuffer * buf = evbuffer_new( ); 758 759 tier_build_log_name( tier, name, sizeof( name ) ); 760 for( i=0; i<tier->announce_event_count; ++i ) 761 { 762 const tr_announce_event e = tier->announce_events[i]; 763 const char * str = tr_announce_event_get_string( e ); 764 evbuffer_add_printf( buf, "[%d:%s]", i, str ); 765 } 766 767 message = evbuffer_free_to_str( buf ); 768 tr_deepLog( __FILE__, __LINE__, name, "announce queue is %s", message ); 769 tr_free( message ); 770 } 771} 772 773static void 774tier_announce_remove_trailing( tr_tier * tier, tr_announce_event e ) 775{ 776 while( ( tier->announce_event_count > 0 ) 777 && ( tier->announce_events[tier->announce_event_count-1] == e ) ) 778 --tier->announce_event_count; 779} 780 781static void 782tier_announce_event_push( tr_tier * tier, 783 tr_announce_event e, 784 time_t announceAt ) 785{ 786 int i; 787 788 assert( tier != NULL ); 789 790 dbgmsg_tier_announce_queue( tier ); 791 dbgmsg( tier, "queued \"%s\"", tr_announce_event_get_string( e ) ); 792 793 if( tier->announce_event_count > 0 ) 794 { 795 /* special case #1: if we're adding a "stopped" event, 796 * dump everything leading up to it except "completed" */ 797 if( e == TR_ANNOUNCE_EVENT_STOPPED ) { 798 bool has_completed = false; 799 const tr_announce_event c = TR_ANNOUNCE_EVENT_COMPLETED; 800 for( i=0; !has_completed && i<tier->announce_event_count; ++i ) 801 has_completed = c == tier->announce_events[i]; 802 tier->announce_event_count = 0; 803 if( has_completed ) 804 tier->announce_events[tier->announce_event_count++] = c; 805 } 806 807 /* special case #2: dump all empty strings leading up to this event */ 808 tier_announce_remove_trailing( tier, TR_ANNOUNCE_EVENT_NONE ); 809 810 /* special case #3: no consecutive duplicates */ 811 tier_announce_remove_trailing( tier, e ); 812 } 813 814 /* make room in the array for another event */ 815 if( tier->announce_event_alloc <= tier->announce_event_count ) { 816 tier->announce_event_alloc += 4; 817 tier->announce_events = tr_renew( tr_announce_event, 818 tier->announce_events, 819 tier->announce_event_alloc ); 820 } 821 822 /* add it */ 823 tier->announce_events[tier->announce_event_count++] = e; 824 tier->announceAt = announceAt; 825 826 dbgmsg_tier_announce_queue( tier ); 827 dbgmsg( tier, "announcing in %d seconds", (int)difftime(announceAt,tr_time()) ); 828} 829 830static tr_announce_event 831tier_announce_event_pull( tr_tier * tier ) 832{ 833 const tr_announce_event e = tier->announce_events[0]; 834 835 tr_removeElementFromArray( tier->announce_events, 836 0, sizeof( tr_announce_event ), 837 tier->announce_event_count-- ); 838 839 return e; 840} 841 842static void 843torrentAddAnnounce( tr_torrent * tor, tr_announce_event e, time_t announceAt ) 844{ 845 int i; 846 struct tr_torrent_tiers * tt = tor->tiers; 847 848 /* walk through each tier and tell them to announce */ 849 for( i=0; i<tt->tier_count; ++i ) 850 tier_announce_event_push( &tt->tiers[i], e, announceAt ); 851} 852 853void 854tr_announcerTorrentStarted( tr_torrent * tor ) 855{ 856 torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_STARTED, tr_time( ) ); 857} 858void 859tr_announcerManualAnnounce( tr_torrent * tor ) 860{ 861 torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_NONE, tr_time( ) ); 862} 863void 864tr_announcerTorrentStopped( tr_torrent * tor ) 865{ 866 torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_STOPPED, tr_time( ) ); 867} 868void 869tr_announcerTorrentCompleted( tr_torrent * tor ) 870{ 871 torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_COMPLETED, tr_time( ) ); 872} 873void 874tr_announcerChangeMyPort( tr_torrent * tor ) 875{ 876 tr_announcerTorrentStarted( tor ); 877} 878 879/*** 880**** 881***/ 882 883void 884tr_announcerAddBytes( tr_torrent * tor, int type, uint32_t byteCount ) 885{ 886 int i; 887 struct tr_torrent_tiers * tt = tor->tiers; 888 889 assert( tr_isTorrent( tor ) ); 890 assert( type==TR_ANN_UP || type==TR_ANN_DOWN || type==TR_ANN_CORRUPT ); 891 892 for( i=0; i<tt->tier_count; ++i ) 893 tt->tiers[i].byteCounts[ type ] += byteCount; 894} 895 896/*** 897**** 898***/ 899 900static tr_announce_request * 901announce_request_new( const tr_announcer * announcer, 902 const tr_torrent * tor, 903 const tr_tier * tier, 904 tr_announce_event event ) 905{ 906 tr_announce_request * req = tr_new0( tr_announce_request, 1 ); 907 req->port = tr_sessionGetPublicPeerPort( announcer->session ); 908 req->url = tr_strdup( tier->currentTracker->announce ); 909 req->tracker_id_str = tr_strdup( tier->currentTracker->tracker_id_str ); 910 memcpy( req->info_hash, tor->info.hash, SHA_DIGEST_LENGTH ); 911 memcpy( req->peer_id, tor->peer_id, PEER_ID_LEN ); 912 req->up = tier->byteCounts[TR_ANN_UP]; 913 req->down = tier->byteCounts[TR_ANN_DOWN]; 914 req->corrupt = tier->byteCounts[TR_ANN_CORRUPT]; 915 req->leftUntilComplete = tr_torrentHasMetadata( tor ) 916 ? tor->info.totalSize - tr_cpHaveTotal( &tor->completion ) 917 : ~(uint64_t)0; 918 req->event = event; 919 req->numwant = event == TR_ANNOUNCE_EVENT_STOPPED ? 0 : NUMWANT; 920 req->key = announcer->key; 921 req->partial_seed = tr_cpGetStatus( &tor->completion ) == TR_PARTIAL_SEED; 922 tier_build_log_name( tier, req->log_name, sizeof( req->log_name ) ); 923 return req; 924} 925 926static void announce_request_free( tr_announce_request * req ); 927 928void 929tr_announcerRemoveTorrent( tr_announcer * announcer, tr_torrent * tor ) 930{ 931 struct tr_torrent_tiers * tt = tor->tiers; 932 933 if( tt != NULL ) 934 { 935 int i; 936 for( i=0; i<tt->tier_count; ++i ) 937 { 938 tr_tier * tier = &tt->tiers[i]; 939 if( tier->isRunning ) 940 { 941 const tr_announce_event e = TR_ANNOUNCE_EVENT_STOPPED; 942 tr_announce_request * req = announce_request_new( announcer, tor, tier, e ); 943 944 if( tr_ptrArrayFindSorted( &announcer->stops, req, compareStops ) != NULL ) 945 announce_request_free( req ); 946 else 947 tr_ptrArrayInsertSorted( &announcer->stops, req, compareStops ); 948 } 949 } 950 951 tiersFree( tor->tiers ); 952 tor->tiers = NULL; 953 } 954} 955 956static int 957getRetryInterval( const tr_tracker * t ) 958{ 959 int minutes; 960 const unsigned int jitter_seconds = tr_cryptoWeakRandInt( 60 ); 961 switch( t->consecutiveFailures ) { 962 case 0: minutes = 1; break; 963 case 1: minutes = 5; break; 964 case 2: minutes = 15; break; 965 case 3: minutes = 30; break; 966 case 4: minutes = 60; break; 967 default: minutes = 120; break; 968 } 969 return ( minutes * 60 ) + jitter_seconds; 970} 971 972struct announce_data 973{ 974 int tierId; 975 time_t timeSent; 976 tr_announce_event event; 977 tr_session * session; 978 979 /** If the request succeeds, the value for tier's "isRunning" flag */ 980 bool isRunningOnSuccess; 981}; 982 983static void 984on_announce_error( tr_tier * tier, const char * err, tr_announce_event e ) 985{ 986 int interval; 987 988 /* increment the error count */ 989 if( tier->currentTracker != NULL ) 990 ++tier->currentTracker->consecutiveFailures; 991 992 /* set the error message */ 993 dbgmsg( tier, "%s", err ); 994 tr_torinf( tier->tor, "%s", err ); 995 tr_strlcpy( tier->lastAnnounceStr, err, sizeof( tier->lastAnnounceStr ) ); 996 997 /* switch to the next tracker */ 998 tierIncrementTracker( tier ); 999 1000 /* schedule a reannounce */ 1001 interval = getRetryInterval( tier->currentTracker ); 1002 dbgmsg( tier, "Retrying announce in %d seconds.", interval ); 1003 tr_torinf( tier->tor, "Retrying announce in %d seconds.", interval ); 1004 tier_announce_event_push( tier, e, tr_time( ) + interval ); 1005} 1006 1007static void 1008on_announce_done( const tr_announce_response * response, 1009 void * vdata ) 1010{ 1011 struct announce_data * data = vdata; 1012 tr_announcer * announcer = data->session->announcer; 1013 tr_tier * tier = getTier( announcer, response->info_hash, data->tierId ); 1014 const time_t now = tr_time( ); 1015 const tr_announce_event event = data->event; 1016 1017 if( announcer ) 1018 ++announcer->slotsAvailable; 1019 1020 if( tier != NULL ) 1021 { 1022 tr_tracker * tracker; 1023 1024 dbgmsg( tier, "Got announce response: " 1025 "connected:%d " 1026 "timeout:%d " 1027 "seeders:%d " 1028 "leechers:%d " 1029 "downloads:%d " 1030 "interval:%d " 1031 "min_interval:%d " 1032 "tracker_id_str:%s " 1033 "pex:%zu " 1034 "pex6:%zu " 1035 "err:%s " 1036 "warn:%s", 1037 (int)response->did_connect, 1038 (int)response->did_timeout, 1039 response->seeders, 1040 response->leechers, 1041 response->downloads, 1042 response->interval, 1043 response->min_interval, 1044 response->tracker_id_str ? response->tracker_id_str : "none", 1045 response->pex_count, 1046 response->pex6_count, 1047 response->errmsg ? response->errmsg : "none", 1048 response->warning ? response->warning : "none" ); 1049 1050 tier->lastAnnounceTime = now; 1051 tier->lastAnnounceTimedOut = response->did_timeout; 1052 tier->lastAnnounceSucceeded = false; 1053 tier->isAnnouncing = false; 1054 tier->manualAnnounceAllowedAt = now + tier->announceMinIntervalSec; 1055 1056 if( !response->did_connect ) 1057 { 1058 on_announce_error( tier, _( "Could not connect to tracker" ), event ); 1059 } 1060 else if( response->did_timeout ) 1061 { 1062 on_announce_error( tier, _( "Tracker did not respond" ), event ); 1063 } 1064 else if( response->errmsg ) 1065 { 1066 /* If the torrent's only tracker returned an error, publish it. 1067 Don't bother publishing if there are other trackers -- it's 1068 all too common for people to load up dozens of dead trackers 1069 in a torrent's metainfo... */ 1070 if( tier->tor->info.trackerCount < 2 ) 1071 publishError( tier, response->errmsg ); 1072 1073 on_announce_error( tier, response->errmsg, event ); 1074 } 1075 else 1076 { 1077 int i; 1078 const char * str; 1079 int scrape_fields = 0; 1080 int seeders = 0; 1081 int leechers = 0; 1082 int downloads = 0; 1083 const bool isStopped = event == TR_ANNOUNCE_EVENT_STOPPED; 1084 1085 publishErrorClear( tier ); 1086 1087 if(( tracker = tier->currentTracker )) 1088 { 1089 tracker->consecutiveFailures = 0; 1090 1091 if( response->seeders >= 0 ) 1092 { 1093 tracker->seederCount = seeders = response->seeders; 1094 ++scrape_fields; 1095 } 1096 1097 if( response->leechers >= 0 ) 1098 { 1099 tracker->leecherCount = leechers = response->leechers; 1100 ++scrape_fields; 1101 } 1102 if( response->downloads >= 0 ) 1103 { 1104 tracker->downloadCount = downloads = response->downloads; 1105 ++scrape_fields; 1106 } 1107 1108 if(( str = response->tracker_id_str )) 1109 { 1110 tr_free( tracker->tracker_id_str ); 1111 tracker->tracker_id_str = tr_strdup( str ); 1112 } 1113 } 1114 1115 if(( str = response->warning )) 1116 { 1117 tr_strlcpy( tier->lastAnnounceStr, str, 1118 sizeof( tier->lastAnnounceStr ) ); 1119 dbgmsg( tier, "tracker gave \"%s\"", str ); 1120 publishWarning( tier, str ); 1121 } 1122 else 1123 { 1124 tr_strlcpy( tier->lastAnnounceStr, _( "Success" ), 1125 sizeof( tier->lastAnnounceStr ) ); 1126 } 1127 1128 if(( i = response->min_interval )) 1129 tier->announceMinIntervalSec = i; 1130 1131 if(( i = response->interval )) 1132 tier->announceIntervalSec = i; 1133 1134 if( response->pex_count > 0 ) 1135 publishPeersPex( tier, seeders, leechers, 1136 response->pex, response->pex_count ); 1137 1138 if( response->pex6_count > 0 ) 1139 publishPeersPex( tier, seeders, leechers, 1140 response->pex6, response->pex6_count ); 1141 1142 tier->isRunning = data->isRunningOnSuccess; 1143 1144 /* if the tracker included scrape fields in its announce response, 1145 then a separate scrape isn't needed */ 1146 if( ( scrape_fields >= 3 ) || ( !tracker->scrape && ( scrape_fields >= 1 ) ) ) 1147 { 1148 tr_tordbg( tier->tor, "Announce response contained scrape info; " 1149 "rescheduling next scrape to %d seconds from now.", 1150 tier->scrapeIntervalSec ); 1151 tier->scrapeAt = get_next_scrape_time( announcer->session, tier, tier->scrapeIntervalSec ); 1152 tier->lastScrapeTime = now; 1153 tier->lastScrapeSucceeded = true; 1154 } 1155 else if( tier->lastScrapeTime + tier->scrapeIntervalSec <= now ) 1156 { 1157 tier->scrapeAt = get_next_scrape_time( announcer->session, tier, 0 ); 1158 } 1159 1160 tier->lastAnnounceSucceeded = true; 1161 tier->lastAnnouncePeerCount = response->pex_count 1162 + response->pex6_count; 1163 1164 if( isStopped ) 1165 { 1166 /* now that we've successfully stopped the torrent, 1167 * we can reset the up/down/corrupt count we've kept 1168 * for this tracker */ 1169 tier->byteCounts[ TR_ANN_UP ] = 0; 1170 tier->byteCounts[ TR_ANN_DOWN ] = 0; 1171 tier->byteCounts[ TR_ANN_CORRUPT ] = 0; 1172 } 1173 1174 if( !isStopped && !tier->announce_event_count ) 1175 { 1176 /* the queue is empty, so enqueue a perodic update */ 1177 i = tier->announceIntervalSec; 1178 dbgmsg( tier, "Sending periodic reannounce in %d seconds", i ); 1179 tier_announce_event_push( tier, TR_ANNOUNCE_EVENT_NONE, now + i ); 1180 } 1181 } 1182 } 1183 1184 tr_free( data ); 1185} 1186 1187static void 1188announce_request_free( tr_announce_request * req ) 1189{ 1190 tr_free( req->tracker_id_str ); 1191 tr_free( req->url ); 1192 tr_free( req ); 1193} 1194 1195static void 1196announce_request_delegate( tr_announcer * announcer, 1197 tr_announce_request * request, 1198 tr_announce_response_func * callback, 1199 void * callback_data ) 1200{ 1201 tr_session * session = announcer->session; 1202 1203 if( !memcmp( request->url, "http", 4 ) ) 1204 tr_tracker_http_announce( session, request, callback, callback_data ); 1205 else if( !memcmp( request->url, "udp://", 6 ) ) 1206 tr_tracker_udp_announce( session, request, callback, callback_data ); 1207 else 1208 tr_err( "Unsupported url: %s", request->url ); 1209 1210 announce_request_free( request ); 1211} 1212 1213static void 1214tierAnnounce( tr_announcer * announcer, tr_tier * tier ) 1215{ 1216 tr_announce_event announce_event; 1217 tr_announce_request * req; 1218 struct announce_data * data; 1219 const tr_torrent * tor = tier->tor; 1220 const time_t now = tr_time( ); 1221 1222 assert( !tier->isAnnouncing ); 1223 assert( tier->announce_event_count > 0 ); 1224 1225 announce_event = tier_announce_event_pull( tier ); 1226 req = announce_request_new( announcer, tor, tier, announce_event ); 1227 1228 data = tr_new0( struct announce_data, 1 ); 1229 data->session = announcer->session; 1230 data->tierId = tier->key; 1231 data->isRunningOnSuccess = tor->isRunning; 1232 data->timeSent = now; 1233 data->event = announce_event; 1234 1235 tier->isAnnouncing = true; 1236 tier->lastAnnounceStartTime = now; 1237 --announcer->slotsAvailable; 1238 1239 announce_request_delegate( announcer, req, on_announce_done, data ); 1240} 1241 1242/*** 1243**** 1244**** SCRAPE 1245**** 1246***/ 1247 1248static void 1249on_scrape_error( tr_session * session, tr_tier * tier, const char * errmsg ) 1250{ 1251 int interval; 1252 1253 /* increment the error count */ 1254 if( tier->currentTracker != NULL ) 1255 ++tier->currentTracker->consecutiveFailures; 1256 1257 /* set the error message */ 1258 dbgmsg( tier, "Scrape error: %s", errmsg ); 1259 tr_torinf( tier->tor, "Scrape error: %s", errmsg ); 1260 tr_strlcpy( tier->lastScrapeStr, errmsg, sizeof( tier->lastScrapeStr ) ); 1261 1262 /* switch to the next tracker */ 1263 tierIncrementTracker( tier ); 1264 1265 /* schedule a rescrape */ 1266 interval = getRetryInterval( tier->currentTracker ); 1267 dbgmsg( tier, "Retrying scrape in %zu seconds.", (size_t)interval ); 1268 tr_torinf( tier->tor, "Retrying scrape in %zu seconds.", (size_t)interval ); 1269 tier->lastScrapeSucceeded = false; 1270 tier->scrapeAt = get_next_scrape_time( session, tier, interval ); 1271} 1272 1273static tr_tier * 1274find_tier( tr_torrent * tor, const char * scrape ) 1275{ 1276 int i; 1277 struct tr_torrent_tiers * tt = tor->tiers; 1278 1279 for( i=0; tt && i<tt->tier_count; ++i ) { 1280 const tr_tracker * const tracker = tt->tiers[i].currentTracker; 1281 if( tracker && !tr_strcmp0( scrape, tracker->scrape ) ) 1282 return &tt->tiers[i]; 1283 } 1284 1285 return NULL; 1286} 1287 1288static void 1289on_scrape_done( const tr_scrape_response * response, void * vsession ) 1290{ 1291 int i; 1292 const time_t now = tr_time( ); 1293 tr_session * session = vsession; 1294 tr_announcer * announcer = session->announcer; 1295 1296 for( i=0; i<response->row_count; ++i ) 1297 { 1298 const struct tr_scrape_response_row * row = &response->rows[i]; 1299 tr_torrent * tor = tr_torrentFindFromHash( session, row->info_hash ); 1300 1301 if( tor != NULL ) 1302 { 1303 tr_tier * tier = find_tier( tor, response->url ); 1304 1305 if( tier != NULL ) 1306 { 1307 dbgmsg( tier, "scraped url:%s -- " 1308 "did_connect:%d " 1309 "did_timeout:%d " 1310 "seeders:%d " 1311 "leechers:%d " 1312 "downloads:%d " 1313 "downloaders:%d " 1314 "min_request_interval:%d " 1315 "err:%s ", 1316 response->url, 1317 (int)response->did_connect, 1318 (int)response->did_timeout, 1319 row->seeders, 1320 row->leechers, 1321 row->downloads, 1322 row->downloaders, 1323 response->min_request_interval, 1324 response->errmsg ? response->errmsg : "none" ); 1325 1326 tier->isScraping = false; 1327 tier->lastScrapeTime = now; 1328 tier->lastScrapeSucceeded = false; 1329 tier->lastScrapeTimedOut = response->did_timeout; 1330 1331 if( !response->did_connect ) 1332 { 1333 on_scrape_error( session, tier, _( "Could not connect to tracker" ) ); 1334 } 1335 else if( response->did_timeout ) 1336 { 1337 on_scrape_error( session, tier, _( "Tracker did not respond" ) ); 1338 } 1339 else if( response->errmsg ) 1340 { 1341 on_scrape_error( session, tier, response->errmsg ); 1342 } 1343 else 1344 { 1345 tr_tracker * tracker; 1346 1347 tier->lastScrapeSucceeded = true; 1348 tier->scrapeIntervalSec = MAX( DEFAULT_SCRAPE_INTERVAL_SEC, 1349 response->min_request_interval ); 1350 tier->scrapeAt = get_next_scrape_time( session, tier, tier->scrapeIntervalSec ); 1351 tr_tordbg( tier->tor, "Scrape successful. Rescraping in %d seconds.", 1352 tier->scrapeIntervalSec ); 1353 1354 if(( tracker = tier->currentTracker )) 1355 { 1356 if( row->seeders >= 0 ) 1357 tracker->seederCount = row->seeders; 1358 if( row->leechers >= 0 ) 1359 tracker->leecherCount = row->leechers; 1360 if( row->downloads >= 0 ) 1361 tracker->downloadCount = row->downloads; 1362 tracker->downloaderCount = row->downloaders; 1363 tracker->consecutiveFailures = 0; 1364 } 1365 } 1366 } 1367 } 1368 } 1369 1370 if( announcer ) 1371 ++announcer->slotsAvailable; 1372} 1373 1374static void 1375scrape_request_delegate( tr_announcer * announcer, 1376 const tr_scrape_request * request, 1377 tr_scrape_response_func * callback, 1378 void * callback_data ) 1379{ 1380 tr_session * session = announcer->session; 1381 1382 if( !memcmp( request->url, "http", 4 ) ) 1383 tr_tracker_http_scrape( session, request, callback, callback_data ); 1384 else if( !memcmp( request->url, "udp://", 6 ) ) 1385 tr_tracker_udp_scrape( session, request, callback, callback_data ); 1386 else 1387 tr_err( "Unsupported url: %s", request->url ); 1388} 1389 1390static void 1391multiscrape( tr_announcer * announcer, tr_ptrArray * tiers ) 1392{ 1393 int i; 1394 int request_count = 0; 1395 const time_t now = tr_time( ); 1396 const int tier_count = tr_ptrArraySize( tiers ); 1397 const int max_request_count = MIN( announcer->slotsAvailable, tier_count ); 1398 tr_scrape_request * requests = tr_new0( tr_scrape_request, max_request_count ); 1399 1400 /* batch as many info_hashes into a request as we can */ 1401 for( i=0; i<tier_count; ++i ) 1402 { 1403 int j; 1404 tr_tier * tier = tr_ptrArrayNth( tiers, i ); 1405 char * url = tier->currentTracker->scrape; 1406 const uint8_t * hash = tier->tor->info.hash; 1407 1408 /* if there's a request with this scrape URL and a free slot, use it */ 1409 for( j=0; j<request_count; ++j ) 1410 { 1411 tr_scrape_request * req = &requests[j]; 1412 1413 if( req->info_hash_count >= TR_MULTISCRAPE_MAX ) 1414 continue; 1415 if( tr_strcmp0( req->url, url ) ) 1416 continue; 1417 1418 memcpy( req->info_hash[req->info_hash_count++], hash, SHA_DIGEST_LENGTH ); 1419 tier->isScraping = true; 1420 tier->lastScrapeStartTime = now; 1421 break; 1422 } 1423 1424 /* otherwise, if there's room for another request, build a new one */ 1425 if( ( j==request_count ) && ( request_count < max_request_count ) ) 1426 { 1427 tr_scrape_request * req = &requests[request_count++]; 1428 req->url = url; 1429 tier_build_log_name( tier, req->log_name, sizeof( req->log_name ) ); 1430 1431 memcpy( req->info_hash[req->info_hash_count++], hash, SHA_DIGEST_LENGTH ); 1432 tier->isScraping = true; 1433 tier->lastScrapeStartTime = now; 1434 } 1435 } 1436 1437 /* send the requests we just built */ 1438 for( i=0; i<request_count; ++i ) 1439 scrape_request_delegate( announcer, &requests[i], on_scrape_done, announcer->session ); 1440 1441 /* cleanup */ 1442 tr_free( requests ); 1443} 1444 1445static void 1446flushCloseMessages( tr_announcer * announcer ) 1447{ 1448 int i; 1449 const int n = tr_ptrArraySize( &announcer->stops ); 1450 1451 for( i=0; i<n; ++i ) 1452 announce_request_delegate( announcer, tr_ptrArrayNth( &announcer->stops, i ), NULL, NULL ); 1453 1454 tr_ptrArrayClear( &announcer->stops ); 1455} 1456 1457static bool 1458tierNeedsToAnnounce( const tr_tier * tier, const time_t now ) 1459{ 1460 return !tier->isAnnouncing 1461 && !tier->isScraping 1462 && ( tier->announceAt != 0 ) 1463 && ( tier->announceAt <= now ) 1464 && ( tier->announce_event_count > 0 ); 1465} 1466 1467static bool 1468tierNeedsToScrape( const tr_tier * tier, const time_t now ) 1469{ 1470 return ( !tier->isScraping ) 1471 && ( tier->scrapeAt != 0 ) 1472 && ( tier->scrapeAt <= now ) 1473 && ( tier->currentTracker != NULL ) 1474 && ( tier->currentTracker->scrape != NULL ); 1475} 1476 1477static int 1478compareTiers( const void * va, const void * vb ) 1479{ 1480 int ret; 1481 const tr_tier * a = *(const tr_tier**)va; 1482 const tr_tier * b = *(const tr_tier**)vb; 1483 1484 /* primary key: larger stats come before smaller */ 1485 ret = compareTransfer( a->byteCounts[TR_ANN_UP], a->byteCounts[TR_ANN_DOWN], 1486 b->byteCounts[TR_ANN_UP], b->byteCounts[TR_ANN_DOWN] ); 1487 1488 /* secondary key: announcements that have been waiting longer go first */ 1489 if( !ret && ( a->announceAt != b->announceAt ) ) 1490 ret = a->announceAt < b->announceAt ? -1 : 1; 1491 1492 return ret; 1493} 1494 1495static void 1496announceMore( tr_announcer * announcer ) 1497{ 1498 int i; 1499 int n; 1500 tr_torrent * tor; 1501 tr_ptrArray announceMe = TR_PTR_ARRAY_INIT; 1502 tr_ptrArray scrapeMe = TR_PTR_ARRAY_INIT; 1503 const time_t now = tr_time( ); 1504 1505 dbgmsg( NULL, "announceMore: slotsAvailable is %d", announcer->slotsAvailable ); 1506 1507 if( announcer->slotsAvailable < 1 ) 1508 return; 1509 1510 /* build a list of tiers that need to be announced */ 1511 tor = NULL; 1512 while(( tor = tr_torrentNext( announcer->session, tor ))) { 1513 struct tr_torrent_tiers * tt = tor->tiers; 1514 for( i=0; tt && i<tt->tier_count; ++i ) { 1515 tr_tier * tier = &tt->tiers[i]; 1516 if( tierNeedsToAnnounce( tier, now ) ) 1517 tr_ptrArrayAppend( &announceMe, tier ); 1518 else if( tierNeedsToScrape( tier, now ) ) 1519 tr_ptrArrayAppend( &scrapeMe, tier ); 1520 } 1521 } 1522 1523 /* if there are more tiers than slots available, prioritize */ 1524 n = tr_ptrArraySize( &announceMe ); 1525 if( n > announcer->slotsAvailable ) 1526 qsort( tr_ptrArrayBase(&announceMe), n, sizeof(tr_tier*), compareTiers ); 1527 1528 /* announce some */ 1529 n = MIN( tr_ptrArraySize( &announceMe ), announcer->slotsAvailable ); 1530 for( i=0; i<n; ++i ) { 1531 tr_tier * tier = tr_ptrArrayNth( &announceMe, i ); 1532 tr_tordbg( tier->tor, "%s", "Announcing to tracker" ); 1533 dbgmsg( tier, "announcing tier %d of %d", i, n ); 1534 tierAnnounce( announcer, tier ); 1535 } 1536 1537 /* scrape some */ 1538 multiscrape( announcer, &scrapeMe ); 1539 1540 /* cleanup */ 1541 tr_ptrArrayDestruct( &scrapeMe, NULL ); 1542 tr_ptrArrayDestruct( &announceMe, NULL ); 1543} 1544 1545static void 1546onUpkeepTimer( int foo UNUSED, short bar UNUSED, void * vannouncer ) 1547{ 1548 tr_announcer * announcer = vannouncer; 1549 tr_session * session = announcer->session; 1550 const bool is_closing = session->isClosed; 1551 const time_t now = tr_time( ); 1552 1553 tr_sessionLock( session ); 1554 1555 /* maybe send out some "stopped" messages for closed torrents */ 1556 flushCloseMessages( announcer ); 1557 1558 /* maybe send out some announcements to trackers */ 1559 if( !is_closing ) 1560 announceMore( announcer ); 1561 1562 /* TAU upkeep */ 1563 if( announcer->tauUpkeepAt <= now ) { 1564 announcer->tauUpkeepAt = now + TAU_UPKEEP_INTERVAL_SECS; 1565 tr_tracker_udp_upkeep( session ); 1566 } 1567 1568 /* set up the next timer */ 1569 tr_timerAdd( announcer->upkeepTimer, UPKEEP_INTERVAL_SECS, 0 ); 1570 1571 tr_sessionUnlock( session ); 1572} 1573 1574/*** 1575**** 1576***/ 1577 1578tr_tracker_stat * 1579tr_announcerStats( const tr_torrent * torrent, int * setmeTrackerCount ) 1580{ 1581 int i; 1582 int out = 0; 1583 tr_tracker_stat * ret; 1584 struct tr_torrent_tiers * tt; 1585 const time_t now = tr_time( ); 1586 1587 assert( tr_isTorrent( torrent ) ); 1588 assert( tr_torrentIsLocked( torrent ) ); 1589 1590 tt = torrent->tiers; 1591 1592 /* alloc the stats */ 1593 *setmeTrackerCount = tt->tracker_count; 1594 ret = tr_new0( tr_tracker_stat, tt->tracker_count ); 1595 1596 /* populate the stats */ 1597 for( i=0; i<tt->tier_count; ++i ) 1598 { 1599 int j; 1600 const tr_tier * const tier = &tt->tiers[i]; 1601 for( j=0; j<tier->tracker_count; ++j ) 1602 { 1603 const tr_tracker * const tracker = &tier->trackers[j]; 1604 tr_tracker_stat * st = &ret[out++]; 1605 1606 st->id = tracker->id; 1607 tr_strlcpy( st->host, tracker->key, sizeof( st->host ) ); 1608 tr_strlcpy( st->announce, tracker->announce, sizeof( st->announce ) ); 1609 st->tier = i; 1610 st->isBackup = tracker != tier->currentTracker; 1611 st->lastScrapeStartTime = tier->lastScrapeStartTime; 1612 if( tracker->scrape ) 1613 tr_strlcpy( st->scrape, tracker->scrape, sizeof( st->scrape ) ); 1614 else 1615 st->scrape[0] = '\0'; 1616 1617 st->seederCount = tracker->seederCount; 1618 st->leecherCount = tracker->leecherCount; 1619 st->downloadCount = tracker->downloadCount; 1620 1621 if( st->isBackup ) 1622 { 1623 st->scrapeState = TR_TRACKER_INACTIVE; 1624 st->announceState = TR_TRACKER_INACTIVE; 1625 st->nextScrapeTime = 0; 1626 st->nextAnnounceTime = 0; 1627 } 1628 else 1629 { 1630 if(( st->hasScraped = tier->lastScrapeTime != 0 )) { 1631 st->lastScrapeTime = tier->lastScrapeTime; 1632 st->lastScrapeSucceeded = tier->lastScrapeSucceeded; 1633 st->lastScrapeTimedOut = tier->lastScrapeTimedOut; 1634 tr_strlcpy( st->lastScrapeResult, tier->lastScrapeStr, 1635 sizeof( st->lastScrapeResult ) ); 1636 } 1637 1638 if( tier->isScraping ) 1639 st->scrapeState = TR_TRACKER_ACTIVE; 1640 else if( !tier->scrapeAt ) 1641 st->scrapeState = TR_TRACKER_INACTIVE; 1642 else if( tier->scrapeAt > now ) 1643 { 1644 st->scrapeState = TR_TRACKER_WAITING; 1645 st->nextScrapeTime = tier->scrapeAt; 1646 } 1647 else 1648 st->scrapeState = TR_TRACKER_QUEUED; 1649 1650 st->lastAnnounceStartTime = tier->lastAnnounceStartTime; 1651 1652 if(( st->hasAnnounced = tier->lastAnnounceTime != 0 )) { 1653 st->lastAnnounceTime = tier->lastAnnounceTime; 1654 tr_strlcpy( st->lastAnnounceResult, tier->lastAnnounceStr, 1655 sizeof( st->lastAnnounceResult ) ); 1656 st->lastAnnounceSucceeded = tier->lastAnnounceSucceeded; 1657 st->lastAnnounceTimedOut = tier->lastAnnounceTimedOut; 1658 st->lastAnnouncePeerCount = tier->lastAnnouncePeerCount; 1659 } 1660 1661 if( tier->isAnnouncing ) 1662 st->announceState = TR_TRACKER_ACTIVE; 1663 else if( !torrent->isRunning || !tier->announceAt ) 1664 st->announceState = TR_TRACKER_INACTIVE; 1665 else if( tier->announceAt > now ) 1666 { 1667 st->announceState = TR_TRACKER_WAITING; 1668 st->nextAnnounceTime = tier->announceAt; 1669 } 1670 else 1671 st->announceState = TR_TRACKER_QUEUED; 1672 } 1673 } 1674 } 1675 1676 return ret; 1677} 1678 1679void 1680tr_announcerStatsFree( tr_tracker_stat * trackers, 1681 int trackerCount UNUSED ) 1682{ 1683 tr_free( trackers ); 1684} 1685 1686/*** 1687**** 1688***/ 1689 1690static void 1691copy_tier_attributes_impl( struct tr_tier * tgt, int trackerIndex, const tr_tier * src ) 1692{ 1693 const tr_tier keep = *tgt; 1694 1695 /* sanity clause */ 1696 assert( trackerIndex < tgt->tracker_count ); 1697 assert( !tr_strcmp0( tgt->trackers[trackerIndex].announce, src->currentTracker->announce ) ); 1698 1699 /* bitwise copy will handle most of tr_tier's fields... */ 1700 *tgt = *src; 1701 1702 /* ...fix the fields that can't be cleanly bitwise-copied */ 1703 tgt->wasCopied = true; 1704 tgt->trackers = keep.trackers; 1705 tgt->tracker_count = keep.tracker_count; 1706 tgt->announce_events = tr_memdup( src->announce_events, sizeof( tr_announce_event ) * src->announce_event_count ); 1707 tgt->announce_event_count = src->announce_event_count; 1708 tgt->announce_event_alloc = src->announce_event_count; 1709 tgt->currentTrackerIndex = trackerIndex; 1710 tgt->currentTracker = &tgt->trackers[trackerIndex]; 1711 tgt->currentTracker->seederCount = src->currentTracker->seederCount; 1712 tgt->currentTracker->leecherCount = src->currentTracker->leecherCount; 1713 tgt->currentTracker->downloadCount = src->currentTracker->downloadCount; 1714 tgt->currentTracker->downloaderCount = src->currentTracker->downloaderCount; 1715} 1716 1717static void 1718copy_tier_attributes( struct tr_torrent_tiers * tt, const tr_tier * src ) 1719{ 1720 int i, j; 1721 bool found = false; 1722 1723 /* find a tier (if any) which has a match for src->currentTracker */ 1724 for( i=0; !found && i<tt->tier_count; ++i ) 1725 for( j=0; !found && j<tt->tiers[i].tracker_count; ++j ) 1726 if(( found = !tr_strcmp0( src->currentTracker->announce, tt->tiers[i].trackers[j].announce ))) 1727 copy_tier_attributes_impl( &tt->tiers[i], j, src ); 1728} 1729 1730void 1731tr_announcerResetTorrent( tr_announcer * announcer UNUSED, tr_torrent * tor ) 1732{ 1733 int i; 1734 const time_t now = tr_time( ); 1735 struct tr_torrent_tiers * tt = tor->tiers; 1736 tr_torrent_tiers old = *tt; 1737 1738 assert( tt != NULL ); 1739 1740 /* remove the old tiers / trackers */ 1741 tt->tiers = NULL; 1742 tt->trackers = NULL; 1743 tt->tier_count = 0; 1744 tt->tracker_count = 0; 1745 1746 /* create the new tiers / trackers */ 1747 addTorrentToTier( tt, tor ); 1748 1749 /* copy the old tiers' states into their replacements */ 1750 for( i=0; i<old.tier_count; ++i ) 1751 if( old.tiers[i].currentTracker != NULL ) 1752 copy_tier_attributes( tt, &old.tiers[i] ); 1753 1754 /* kickstart any tiers that didn't get started */ 1755 if( tor->isRunning ) 1756 for( i=0; i<tt->tier_count; ++i ) 1757 if( !tt->tiers[i].wasCopied ) 1758 tier_announce_event_push( &tt->tiers[i], TR_ANNOUNCE_EVENT_STARTED, now ); 1759 1760 /* cleanup */ 1761 tiersDestruct( &old ); 1762} 1763