1 /* 2 * File : TRTrackerServerTorrent.java 3 * Created : 26-Oct-2003 4 * By : stuff 5 * 6 * Azureus - a Java Bittorrent client 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details ( see the LICENSE file ). 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23 package org.gudy.azureus2.core3.tracker.server.impl; 24 25 /** 26 * @author parg 27 * 28 */ 29 30 import java.util.*; 31 import java.io.*; 32 import java.net.InetAddress; 33 import java.net.URL; 34 import java.net.UnknownHostException; 35 36 import org.gudy.azureus2.core3.logging.*; 37 import org.gudy.azureus2.core3.tracker.server.*; 38 import org.gudy.azureus2.core3.util.*; 39 40 import com.aelitis.azureus.core.dht.netcoords.DHTNetworkPosition; 41 42 43 public class 44 TRTrackerServerTorrentImpl 45 implements TRTrackerServerTorrent 46 { 47 private static final LogIDs LOGID = LogIDs.TRACKER; 48 // no point in caching replies smaller than that below 49 50 public static final int MIN_CACHE_ENTRY_SIZE = 10; 51 52 public static final int MAX_UPLOAD_BYTES_PER_SEC = 3*1024*1024; //3MBs 53 public static final int MAX_DOWNLOAD_BYTES_PER_SEC = MAX_UPLOAD_BYTES_PER_SEC; 54 55 public static final boolean USE_LIGHTWEIGHT_SEEDS = true; 56 57 public static final int MAX_IP_OVERRIDE_PEERS = 64; 58 59 public static final byte COMPACT_MODE_NONE = 0; 60 public static final byte COMPACT_MODE_NORMAL = 1; 61 public static final byte COMPACT_MODE_AZ = 2; 62 public static final byte COMPACT_MODE_AZ_2 = 3; 63 public static final byte COMPACT_MODE_XML = 16; 64 65 private static final int QUEUED_PEERS_MAX_SWARM_SIZE = 32; 66 private static final int QUEUED_PEERS_MAX = 32; 67 private static final int QUEUED_PEERS_ADD_MAX = 3; 68 69 70 private TRTrackerServerImpl server; 71 private HashWrapper hash; 72 73 private Map<HashWrapper,TRTrackerServerPeerImpl> peer_map = new HashMap<HashWrapper,TRTrackerServerPeerImpl>(); 74 75 private Map<String,TRTrackerServerPeerImpl> peer_reuse_map = new HashMap<String,TRTrackerServerPeerImpl>(); 76 77 private List<TRTrackerServerPeerImpl> peer_list = new ArrayList<TRTrackerServerPeerImpl>(); 78 79 private int peer_list_hole_count; 80 private boolean peer_list_compaction_suspended; 81 82 private List biased_peers = null; 83 private int min_biased_peers = 0; 84 85 private Map lightweight_seed_map = new HashMap(); 86 87 private int seed_count; 88 private int removed_count; 89 90 private int ip_override_count; 91 92 private int bad_NAT_count; // calculated periodically 93 94 private Random random = new Random( SystemTime.getCurrentTime()); 95 96 private long last_scrape_calc_time; 97 private Map last_scrape; 98 99 private LinkedHashMap announce_cache = new LinkedHashMap(); 100 101 private TRTrackerServerTorrentStatsImpl stats; 102 103 private List listeners = new ArrayList(); 104 private List peer_listeners; 105 private boolean deleted; 106 private boolean enabled; 107 108 private boolean map_size_diff_reported; 109 private boolean ip_override_limit_exceeded_reported; 110 111 private byte duplicate_peer_checker_index = 0; 112 private byte[] duplicate_peer_checker = new byte[0]; 113 114 private URL[] redirects; 115 116 private boolean caching_enabled = true; 117 118 private LinkedList queued_peers; 119 120 protected AEMonitor this_mon = new AEMonitor( "TRTrackerServerTorrent" ); 121 122 private List explicit_manual_biased_peers; 123 124 private int explicit_next_peer; 125 126 public TRTrackerServerTorrentImpl( TRTrackerServerImpl _server, HashWrapper _hash, boolean _enabled )127 TRTrackerServerTorrentImpl( 128 TRTrackerServerImpl _server, 129 HashWrapper _hash, 130 boolean _enabled ) 131 { 132 server = _server; 133 hash = _hash; 134 enabled = _enabled; 135 136 stats = new TRTrackerServerTorrentStatsImpl( this ); 137 } 138 139 public void setEnabled( boolean _enabled )140 setEnabled( 141 boolean _enabled ) 142 { 143 enabled = _enabled; 144 } 145 146 public boolean isEnabled()147 isEnabled() 148 { 149 return( enabled ); 150 } 151 152 public void setMinBiasedPeers( int num )153 setMinBiasedPeers( 154 int num ) 155 { 156 min_biased_peers = num; 157 } 158 159 public void importPeers( List peers )160 importPeers( 161 List peers ) 162 { 163 try{ 164 this_mon.enter(); 165 166 // only currently support import when torrent "empty" 167 168 if ( peer_map.size() > 0 ){ 169 170 System.out.println( "TRTrackerServerTorrent: ignoring peer import as torrent already active" ); 171 172 return; 173 } 174 175 for (int i=0;i<peers.size();i++){ 176 177 TRTrackerServerPeerImpl peer = TRTrackerServerPeerImpl.importPeer((Map)peers.get(i)); 178 179 if ( peer != null ){ 180 181 try{ 182 String reuse_key = new String( peer.getIPAsRead(), Constants.BYTE_ENCODING ) + ":" + peer.getTCPPort(); 183 184 peer_map.put( peer.getPeerId(), peer ); 185 186 peer_list.add( peer ); 187 188 peer_reuse_map.put( reuse_key, peer ); 189 190 if ( peer.isSeed()){ 191 192 seed_count++; 193 } 194 195 if ( peer.isBiased()){ 196 197 if ( biased_peers == null ){ 198 199 biased_peers = new ArrayList(); 200 } 201 202 biased_peers.add( peer ); 203 } 204 }catch( Throwable e ){ 205 } 206 } 207 } 208 }finally{ 209 210 this_mon.exit(); 211 } 212 } 213 214 public TRTrackerServerPeerImpl peerContact( String url_parameters, String event, HashWrapper peer_id, int tcp_port, int udp_port, int http_port, byte crypto_level, byte az_ver, String original_address, String ip_address, boolean ip_override, boolean loopback, String tracker_key, long uploaded, long downloaded, long left, long interval_requested, int up_speed, DHTNetworkPosition network_position )215 peerContact( 216 String url_parameters, 217 String event, 218 HashWrapper peer_id, 219 int tcp_port, 220 int udp_port, 221 int http_port, 222 byte crypto_level, 223 byte az_ver, 224 String original_address, 225 String ip_address, 226 boolean ip_override, 227 boolean loopback, 228 String tracker_key, 229 long uploaded, 230 long downloaded, 231 long left, 232 long interval_requested, 233 int up_speed, 234 DHTNetworkPosition network_position ) 235 236 throws TRTrackerServerException 237 { 238 if ( !enabled ){ 239 240 throw( new TRTrackerServerException( "Torrent temporarily disabled" )); 241 } 242 243 // we can safely resolve the client_ip_address here as it is either already resolved or that of 244 // the tracker. We need it resolved so we canonically store peers, otherwise we can get two 245 // entries for a dns name and the corresponding ip 246 247 if ( !HostNameToIPResolver.isNonDNSName( ip_address )){ 248 249 try{ 250 ip_address = HostNameToIPResolver.syncResolve( ip_address ).getHostAddress(); 251 252 }catch( UnknownHostException e ){ 253 } 254 } 255 256 TRTrackerServerException deferred_failure = null; 257 258 try{ 259 this_mon.enter(); 260 261 handleRedirects( url_parameters, ip_address, false ); 262 263 // System.out.println( "TRTrackerServerTorrent: peerContact, ip = " + ip_address ); 264 265 int event_type = TRTrackerServerTorrentPeerListener.ET_UPDATED; 266 267 if ( event != null && event.length() > 2){ 268 269 char c = event.charAt(2); 270 271 if ( c == 'm' ){ // "coMpleted" 272 273 event_type = TRTrackerServerTorrentPeerListener.ET_COMPLETE; 274 275 }else if ( c == 'o' ){ // "stOpped" 276 277 event_type = TRTrackerServerTorrentPeerListener.ET_STOPPED; 278 279 }else{ 280 281 event_type = TRTrackerServerTorrentPeerListener.ET_STARTED; 282 } 283 } 284 285 long now = SystemTime.getCurrentTime(); 286 287 int tracker_key_hash_code = tracker_key==null?0:tracker_key.hashCode(); 288 289 TRTrackerServerPeerImpl peer = (TRTrackerServerPeerImpl)peer_map.get( peer_id ); 290 291 boolean new_peer = false; 292 boolean peer_already_removed = false; 293 294 boolean already_completed = false; 295 long last_contact_time = 0; 296 297 long ul_diff = 0; 298 long dl_diff = 0; 299 long le_diff = 0; 300 301 byte[] ip_address_bytes = ip_address.getBytes( Constants.BYTE_ENCODING ); 302 303 if ( peer == null ){ 304 305 String reuse_key = new String( ip_address_bytes, Constants.BYTE_ENCODING ) + ":" + tcp_port; 306 307 byte last_NAT_status = loopback?TRTrackerServerPeer.NAT_CHECK_OK:TRTrackerServerPeer.NAT_CHECK_UNKNOWN; 308 309 new_peer = true; 310 311 // check to see if this peer already has an entry against this torrent 312 // and if so delete it (assumption is that the client has quit and 313 // restarted with new peer id 314 315 //System.out.println( "new peer" ); 316 317 318 TRTrackerServerPeerImpl old_peer = (TRTrackerServerPeerImpl)peer_reuse_map.get( reuse_key ); 319 320 if ( old_peer != null ){ 321 322 // don't allow an ip_override to grab a non-override entry as this is a way for 323 // a malicious client to remove, say, a seed (simply send in a "stopped" command 324 // with override set to the seed you want to remove) 325 326 if ( ip_override && !old_peer.isIPOverride()){ 327 328 throw( new TRTrackerServerException( "IP Override denied (existing '" + reuse_key + "' is not override)" )); 329 } 330 331 last_contact_time = old_peer.getLastContactTime(); 332 333 already_completed = old_peer.getDownloadCompleted(); 334 335 removePeer( old_peer, TRTrackerServerTorrentPeerListener.ET_REPLACED, null ); 336 337 lightweight_seed_map.remove( old_peer.getPeerId()); 338 339 }else{ 340 341 lightweightSeed lws = (lightweightSeed)lightweight_seed_map.remove( peer_id ); 342 343 if ( lws != null ){ 344 345 last_contact_time = lws.getLastContactTime(); 346 347 ul_diff = uploaded - lws.getUploaded(); 348 349 if ( ul_diff < 0 ){ 350 351 ul_diff = 0; 352 } 353 354 last_NAT_status = lws.getNATStatus(); 355 356 }else{ 357 358 last_contact_time = now; 359 } 360 } 361 362 if ( event_type != TRTrackerServerTorrentPeerListener.ET_STOPPED ){ 363 364 Set biased_peer_set = server.getBiasedPeers(); 365 366 boolean biased = biased_peer_set != null && biased_peer_set.contains( ip_address ); 367 368 if ( ip_override && ip_override_count >= MAX_IP_OVERRIDE_PEERS && !( loopback || biased )){ 369 370 // bail out - the peer will still get an announce response but we don't 371 // want too many override peers on a torrent as these can be spoofed 372 // to cause trouble 373 374 if ( !ip_override_limit_exceeded_reported ){ 375 376 ip_override_limit_exceeded_reported = true; 377 378 Debug.out( "Too many ip-override peers for " + ByteFormatter.encodeString( hash.getBytes())); 379 } 380 381 return( null ); 382 } 383 384 peer = new TRTrackerServerPeerImpl( 385 peer_id, 386 tracker_key_hash_code, 387 ip_address_bytes, 388 ip_override, 389 tcp_port, 390 udp_port, 391 http_port, 392 crypto_level, 393 az_ver, 394 last_contact_time, 395 already_completed, 396 last_NAT_status, 397 up_speed, 398 network_position ); 399 400 if ( ip_override ){ 401 402 // never allow an ip-override to take on the guise of a biased peer 403 404 if ( biased ){ 405 406 // UNLESS the originating IP is biased too 407 408 if ( !biased_peer_set.contains( original_address )){ 409 410 throw( new TRTrackerServerException( "IP Override denied (you are " + original_address + ")" )); 411 } 412 } 413 414 ip_override_count++; 415 } 416 417 peer_map.put( peer_id, peer ); 418 419 peer_list.add( peer ); 420 421 peer_reuse_map.put( reuse_key, peer ); 422 423 if ( biased ){ 424 425 peer.setBiased( true ); 426 427 if ( biased_peers == null ){ 428 429 biased_peers = new ArrayList(); 430 } 431 432 biased_peers.add( peer ); 433 } 434 435 if ( queued_peers != null ){ 436 437 if ( peer_map.size() > QUEUED_PEERS_MAX_SWARM_SIZE ){ 438 439 queued_peers = null; 440 441 }else{ 442 443 // peer has become active, remove the queued peer info 444 445 Iterator it = queued_peers.iterator(); 446 447 while( it.hasNext()){ 448 449 QueuedPeer qp = (QueuedPeer)it.next(); 450 451 if ( qp.sameAs( peer )){ 452 453 it.remove(); 454 455 break; 456 } 457 } 458 } 459 } 460 } 461 }else{ 462 463 int existing_tracker_key_hash_code = peer.getKeyHashCode(); 464 465 // System.out.println( "tracker_key:" + existing_tracker_key + "/" + tracker_key ); 466 467 if ( existing_tracker_key_hash_code != tracker_key_hash_code ){ 468 469 if ( server.isKeyEnabled()){ 470 471 throw( new TRTrackerServerException( "Unauthorised: key mismatch ")); 472 } 473 } 474 475 if ( ip_override ){ 476 477 // biased peers are never ip-override 478 479 if ( peer.isBiased()){ 480 481 // UNLESS the originating IP is biased too 482 483 Set biased_peer_set = server.getBiasedPeers(); 484 485 if ( biased_peer_set == null || !biased_peer_set.contains( original_address )){ 486 487 throw( new TRTrackerServerException( "IP Override denied (you are " + original_address + ")")); 488 } 489 } 490 491 // prevent an ip_override peer from affecting a non-override entry 492 493 if ( !peer.isIPOverride()){ 494 495 throw( new TRTrackerServerException( "IP Override denied (existing entry not override)" )); 496 } 497 } 498 499 already_completed = peer.getDownloadCompleted(); 500 501 last_contact_time = peer.getLastContactTime(); 502 503 if ( event_type == TRTrackerServerTorrentPeerListener.ET_STOPPED ){ 504 505 removePeer( peer, event_type, url_parameters ); 506 507 peer_already_removed = true; 508 509 }else{ 510 511 // IP may have changed - update if required 512 513 // it is possible for two az clients to have the same peer id. Unlikely but possible 514 // or indeed some hacked versions could do it on purpose. If this is the case then all we 515 // will see here is address/port changes as each peer announces 516 517 byte[] old_ip = peer.getIPAsRead(); 518 int old_port = peer.getTCPPort(); 519 520 if ( peer.update( ip_address_bytes, tcp_port, udp_port, http_port, crypto_level, az_ver, up_speed, network_position )){ 521 522 // same peer id so same port 523 524 String old_key = new String( old_ip, Constants.BYTE_ENCODING ) + ":" + old_port; 525 526 String new_key = new String( ip_address_bytes, Constants.BYTE_ENCODING ) + ":" + tcp_port; 527 528 // it is possible, on address change, that the target address already exists and is 529 // (was) being used by another peer. Given that this peer has taken over its address 530 // the assumption is that the other peer has also had an address change and has yet 531 // to report it. The only action here is to delete the other peer 532 533 TRTrackerServerPeerImpl old_peer = (TRTrackerServerPeerImpl)peer_reuse_map.get( new_key ); 534 535 if ( old_peer != null ){ 536 537 removePeer( old_peer, TRTrackerServerTorrentPeerListener.ET_REPLACED, null ); 538 } 539 540 // now swap the keys 541 542 if ( peer_reuse_map.remove( old_key ) == null ){ 543 544 Debug.out( "TRTrackerServerTorrent: IP address change: '" + old_key + "' -> '" + new_key + "': old key not found" ); 545 } 546 547 peer_reuse_map.put( new_key, peer ); 548 } 549 } 550 } 551 552 // a null peer here signifies a new peer whose first state was "stopped" 553 554 long new_timeout = now + ( interval_requested * 1000 * TRTrackerServerImpl.CLIENT_TIMEOUT_MULTIPLIER ); 555 556 if ( peer != null ){ 557 558 peer.setTimeout( now, new_timeout ); 559 560 // if this is the first time we've heard from this peer then we don't want to 561 // use existing ul/dl value diffs as they will have been reported previously 562 // (either the client's changed peer id by stop/start (in which case the values 563 // should be 0 anyway as its a per-session total), or the tracker's been 564 // stopped and started). 565 566 if ( !new_peer ){ 567 568 ul_diff = uploaded - peer.getUploaded(); 569 dl_diff = downloaded - peer.getDownloaded(); 570 } 571 572 // simple rate control 573 574 long elapsed_time = now - last_contact_time; 575 576 if ( elapsed_time == 0 ){ 577 578 elapsed_time = SystemTime.TIME_GRANULARITY_MILLIS; 579 } 580 581 long ul_rate = (ul_diff*1000)/elapsed_time; // bytes per second 582 long dl_rate = (dl_diff*1000)/elapsed_time; 583 584 if ( ul_rate > MAX_UPLOAD_BYTES_PER_SEC ){ 585 586 if (Logger.isEnabled()) 587 Logger.log(new LogEvent(LOGID, "TRTrackerPeer: peer " 588 + peer.getIPRaw() + "/" 589 + new String(peer.getPeerId().getHash()) 590 + " reported an upload rate of " + ul_rate / 1024 591 + " KiB/s per second")); 592 593 ul_diff = 0; 594 } 595 596 if ( dl_rate > MAX_DOWNLOAD_BYTES_PER_SEC ){ 597 if (Logger.isEnabled()) 598 Logger.log(new LogEvent(LOGID, "TRTrackerPeer: peer " 599 + peer.getIPRaw() + "/" 600 + new String(peer.getPeerId().getHash()) 601 + " reported a download rate of " + dl_rate / 1024 602 + " KiB/s per second")); 603 604 dl_diff = 0; 605 } 606 // when the peer is removed its "left" amount will dealt with 607 608 le_diff = (event_type==TRTrackerServerTorrentPeerListener.ET_STOPPED)?0:(left - peer.getAmountLeft()); 609 610 boolean was_seed = new_peer?false:peer.isSeed(); 611 612 peer.setStats( uploaded, downloaded, left ); 613 614 boolean is_seed = peer.isSeed(); 615 616 if (!(event_type == TRTrackerServerTorrentPeerListener.ET_STOPPED || was_seed || !is_seed )){ 617 618 seed_count++; 619 } 620 621 // report event *after* updating totals above so listeners get a valid initial 622 // view of the peer (e.g. is it a seed) 623 624 // if the peer has already been removed above then it will have reported the 625 // event already 626 627 if ( !peer_already_removed ){ 628 629 try{ 630 peerEvent( peer, event_type, url_parameters ); 631 632 }catch( TRTrackerServerException e ){ 633 634 deferred_failure = e; 635 } 636 } 637 } 638 639 stats.addAnnounce( ul_diff, dl_diff, le_diff, peer != null && peer.isBiased()); 640 641 if ( event_type==TRTrackerServerTorrentPeerListener.ET_COMPLETE && !already_completed ){ 642 643 peer.setDownloadCompleted(); 644 645 stats.addCompleted(); 646 } 647 648 if ( peer != null && peer.isSeed()){ 649 650 int seed_limit = TRTrackerServerImpl.getSeedLimit(); 651 652 if ( seed_limit != 0 && seed_count > seed_limit && !loopback ){ 653 654 if ( !peer_already_removed ){ 655 656 removePeer( peer, TRTrackerServerTorrentPeerListener.ET_TOO_MANY_PEERS, null ); 657 } 658 659 // this is picked up by AZ client removal rules and causes the torrent to 660 // be removed 661 662 throw( new TRTrackerServerException( "too many seeds" )); 663 } 664 665 int seed_retention = TRTrackerServerImpl.getMaxSeedRetention(); 666 667 if ( seed_retention != 0 && seed_count > seed_retention ){ 668 669 // remove 5% of the seeds 670 671 int to_remove = (seed_retention/20)+1; 672 673 try{ 674 peer_list_compaction_suspended = true; 675 676 // remove bad NAT ones in preference to others 677 678 for (int bad_nat_loop=TRTrackerServerNATChecker.getSingleton().isEnabled()?0:1;bad_nat_loop<2;bad_nat_loop++){ 679 680 for (int i=0;i<peer_list.size();i++){ 681 682 TRTrackerServerPeerImpl this_peer = (TRTrackerServerPeerImpl)peer_list.get(i); 683 684 if ( this_peer != null && this_peer.isSeed() && !this_peer.isBiased()){ 685 686 boolean bad_nat = this_peer.isNATStatusBad(); 687 688 if ( ( bad_nat_loop == 0 && bad_nat ) || 689 ( bad_nat_loop == 1 )){ 690 691 if ( USE_LIGHTWEIGHT_SEEDS ){ 692 693 lightweight_seed_map.put( 694 this_peer.getPeerId(), 695 new lightweightSeed( 696 now, 697 new_timeout, 698 this_peer.getUploaded(), 699 this_peer.getNATStatus())); 700 } 701 702 removePeer( this_peer, i, TRTrackerServerTorrentPeerListener.ET_TOO_MANY_PEERS, null ); 703 704 if ( --to_remove == 0 ){ 705 706 break; 707 } 708 } 709 } 710 } 711 712 if ( to_remove == 0 ){ 713 714 break; 715 } 716 } 717 }finally{ 718 719 peer_list_compaction_suspended = false; 720 } 721 722 checkForPeerListCompaction( false ); 723 } 724 } 725 726 if ( deferred_failure != null ){ 727 728 if ( peer != null && !peer_already_removed ){ 729 730 removePeer( peer, TRTrackerServerTorrentPeerListener.ET_FAILED, url_parameters ); 731 } 732 733 throw( deferred_failure ); 734 } 735 736 return( peer ); 737 738 }catch( UnsupportedEncodingException e ){ 739 740 throw( new TRTrackerServerException( "Encoding fails", e )); 741 742 }finally{ 743 744 // note we can bail out here through a return when there are too many IP overrides 745 746 this_mon.exit(); 747 } 748 } 749 750 public void peerQueued( String ip, int tcp_port, int udp_port, int http_port, byte crypto_level, byte az_ver, long timeout_secs, boolean seed )751 peerQueued( 752 String ip, 753 int tcp_port, 754 int udp_port, 755 int http_port, 756 byte crypto_level, 757 byte az_ver, 758 long timeout_secs, 759 boolean seed ) 760 { 761 // System.out.println( "peerQueued: " + ip + "/" + tcp_port + "/" + udp_port + "/" + crypto_level ); 762 763 if ( peer_map.size() >= QUEUED_PEERS_MAX_SWARM_SIZE || tcp_port == 0 ){ 764 765 return; 766 } 767 768 try{ 769 this_mon.enter(); 770 771 Set biased_peer_set = server.getBiasedPeers(); 772 773 boolean biased = biased_peer_set != null && biased_peer_set.contains( ip ); 774 775 QueuedPeer new_qp = 776 new QueuedPeer( ip, tcp_port, udp_port, http_port, crypto_level, 777 az_ver, (int)timeout_secs, seed, biased ); 778 779 String reuse_key = new_qp.getIP() + ":" + tcp_port; 780 781 // if still active then drop it 782 783 if ( peer_reuse_map.containsKey( reuse_key )){ 784 785 return; 786 } 787 788 boolean add = true; 789 790 if ( queued_peers != null ){ 791 792 Iterator it = queued_peers.iterator(); 793 794 while( it.hasNext()){ 795 796 QueuedPeer qp = (QueuedPeer)it.next(); 797 798 if ( qp.sameAs( new_qp )){ 799 800 it.remove(); 801 802 queued_peers.add( new_qp ); 803 804 return; 805 } 806 } 807 808 if ( queued_peers.size() >= QUEUED_PEERS_MAX ){ 809 810 QueuedPeer oldest = null; 811 812 it = queued_peers.iterator(); 813 814 while( it.hasNext()){ 815 816 QueuedPeer qp = (QueuedPeer)it.next(); 817 818 // never drop biased peers 819 820 if ( qp.isBiased()){ 821 822 continue; 823 } 824 825 if ( oldest == null ){ 826 827 oldest = qp; 828 829 }else{ 830 831 if ( qp.getCreateTime() < oldest.getCreateTime()){ 832 833 oldest = qp; 834 } 835 } 836 } 837 838 if ( oldest == null ){ 839 840 add = false; 841 842 }else{ 843 844 queued_peers.remove( oldest ); 845 } 846 } 847 }else{ 848 849 queued_peers = new LinkedList(); 850 } 851 852 if ( add ){ 853 854 queued_peers.addFirst( new_qp ); 855 } 856 857 }finally{ 858 859 this_mon.exit(); 860 } 861 } 862 863 public void remove( TRTrackerServerPeerBase peer )864 remove( 865 TRTrackerServerPeerBase peer ) 866 { 867 try{ 868 this_mon.enter(); 869 870 if ( peer instanceof TRTrackerServerPeerImpl ){ 871 872 TRTrackerServerPeerImpl pi = (TRTrackerServerPeerImpl)peer; 873 874 if ( peer_map.containsKey( pi.getPeerId())){ 875 876 int index = peer_list.indexOf( pi ); 877 878 if ( index != -1 ){ 879 880 removePeer( pi, index, TRTrackerServerTorrentPeerListener.ET_FAILED, null ); 881 } 882 } 883 }else{ 884 885 if ( queued_peers != null ){ 886 887 queued_peers.remove( peer ); 888 889 if ( queued_peers.size() == 0 ){ 890 891 queued_peers = null; 892 } 893 } 894 } 895 }finally{ 896 897 this_mon.exit(); 898 } 899 } 900 protected void removePeer( TRTrackerServerPeerImpl peer, int reason, String url_parameters )901 removePeer( 902 TRTrackerServerPeerImpl peer, 903 int reason, 904 String url_parameters ) 905 { 906 removePeer( peer, -1, reason, url_parameters ); 907 } 908 909 protected void removePeer( TRTrackerServerPeerImpl peer, int peer_list_index, int reason, String url_parameters )910 removePeer( 911 TRTrackerServerPeerImpl peer, 912 int peer_list_index, 913 int reason, 914 String url_parameters ) // -1 if not known 915 { 916 try{ 917 this_mon.enter(); 918 919 if ( peer.isIPOverride()){ 920 921 ip_override_count--; 922 } 923 924 stats.removeLeft( peer.getAmountLeft()); 925 926 if ( peer_map.size() != peer_reuse_map.size()){ 927 928 if ( !map_size_diff_reported ){ 929 930 map_size_diff_reported = true; 931 932 Debug.out( "TRTrackerServerTorrent::removePeer: maps size different ( " + peer_map.size() + "/" + peer_reuse_map.size() +")"); 933 } 934 } 935 936 { 937 Object o = peer_map.remove( peer.getPeerId()); 938 939 if ( o == null ){ 940 941 Debug.out(" TRTrackerServerTorrent::removePeer: peer_map doesn't contain peer"); 942 }else{ 943 944 try{ 945 peerEvent( peer, reason, url_parameters ); 946 947 }catch( TRTrackerServerException e ){ 948 // ignore during peer removal 949 } 950 } 951 } 952 953 if ( peer_list_index == -1 ){ 954 955 int peer_index = peer_list.indexOf( peer ); 956 957 if ( peer_index == -1){ 958 959 Debug.out(" TRTrackerServerTorrent::removePeer: peer_list doesn't contain peer"); 960 }else{ 961 962 peer_list.set( peer_index, null ); 963 } 964 }else{ 965 966 if ( peer_list.get( peer_list_index ) == peer ){ 967 968 peer_list.set( peer_list_index, null ); 969 970 }else{ 971 972 Debug.out(" TRTrackerServerTorrent::removePeer: peer_list doesn't contain peer at index"); 973 974 } 975 } 976 977 peer_list_hole_count++; 978 979 checkForPeerListCompaction( false ); 980 981 try{ 982 Object o = peer_reuse_map.remove( new String( peer.getIPAsRead(), Constants.BYTE_ENCODING ) + ":" + peer.getTCPPort()); 983 984 if ( o == null ){ 985 986 Debug.out(" TRTrackerServerTorrent::removePeer: peer_reuse_map doesn't contain peer"); 987 } 988 989 }catch( UnsupportedEncodingException e ){ 990 } 991 992 if ( biased_peers != null ){ 993 994 biased_peers.remove( peer ); 995 } 996 997 if ( peer.isSeed()){ 998 999 seed_count--; 1000 } 1001 1002 removed_count++; 1003 1004 }finally{ 1005 1006 this_mon.exit(); 1007 } 1008 } 1009 1010 protected void updateBiasedPeers( Set biased_peers_set )1011 updateBiasedPeers( 1012 Set biased_peers_set ) 1013 { 1014 try{ 1015 this_mon.enter(); 1016 1017 Iterator it = peer_list.iterator(); 1018 1019 if ( it.hasNext() && biased_peers == null ){ 1020 1021 biased_peers = new ArrayList(); 1022 } 1023 1024 while( it.hasNext()){ 1025 1026 TRTrackerServerPeerImpl this_peer = (TRTrackerServerPeerImpl)it.next(); 1027 1028 if ( this_peer != null ){ 1029 1030 boolean biased = biased_peers_set.contains( this_peer.getIPRaw()); 1031 1032 this_peer.setBiased( biased ); 1033 1034 if ( biased ){ 1035 1036 if ( !biased_peers.contains( this_peer )){ 1037 1038 biased_peers.add( this_peer ); 1039 } 1040 }else{ 1041 1042 biased_peers.remove( this_peer ); 1043 } 1044 } 1045 } 1046 1047 if ( queued_peers != null ){ 1048 1049 it = queued_peers.iterator(); 1050 1051 while( it.hasNext()){ 1052 1053 QueuedPeer peer = (QueuedPeer)it.next(); 1054 1055 peer.setBiased( biased_peers_set.contains( peer.getIP())); 1056 } 1057 } 1058 }finally{ 1059 1060 this_mon.exit(); 1061 } 1062 } 1063 1064 public TRTrackerServerTorrent addLink( String link )1065 addLink( 1066 String link ) 1067 { 1068 return( server.addLink( link, this )); 1069 } 1070 1071 public void removeLink( String link )1072 removeLink( 1073 String link ) 1074 { 1075 server.removeLink( link, this ); 1076 } 1077 1078 public Map exportAnnounceToMap( String ip_address, HashMap preprocess_map, TRTrackerServerPeerImpl requesting_peer, boolean include_seeds, int num_want, long interval, long min_interval, boolean no_peer_id, byte compact_mode, byte crypto_level, DHTNetworkPosition network_position )1079 exportAnnounceToMap( 1080 String ip_address, 1081 HashMap preprocess_map, 1082 TRTrackerServerPeerImpl requesting_peer, // maybe null for an initial announce from a stopped peer 1083 boolean include_seeds, 1084 int num_want, 1085 long interval, 1086 long min_interval, 1087 boolean no_peer_id, 1088 byte compact_mode, 1089 byte crypto_level, 1090 DHTNetworkPosition network_position ) 1091 { 1092 try{ 1093 this_mon.enter(); 1094 1095 long now = SystemTime.getCurrentTime(); 1096 1097 // we have to force non-caching for nat_warnings responses as they include 1098 // peer-specific data 1099 1100 boolean nat_warning = requesting_peer != null && requesting_peer.getNATStatus() == TRTrackerServerPeerImpl.NAT_CHECK_FAILED; 1101 1102 int total_peers = peer_map.size(); 1103 int cache_millis = TRTrackerServerImpl.getAnnounceCachePeriod(); 1104 1105 boolean send_peer_ids = TRTrackerServerImpl.getSendPeerIds(); 1106 1107 // override if client has explicitly not requested them 1108 1109 if ( no_peer_id || compact_mode != COMPACT_MODE_NONE ){ 1110 1111 send_peer_ids = false; 1112 } 1113 1114 boolean add_to_cache = false; 1115 1116 int max_peers = TRTrackerServerImpl.getMaxPeersToSend(); 1117 1118 // num_want < 0 -> not supplied so give them max 1119 1120 if ( num_want < 0 ){ 1121 1122 num_want = total_peers; 1123 } 1124 1125 // trim back to max_peers if specified 1126 1127 if ( max_peers > 0 && num_want > max_peers ){ 1128 1129 num_want = max_peers; 1130 } 1131 1132 // if set this list contains the only peers that are to be returned. It allows a manual 1133 // external peer selection algorithm 1134 1135 List<TRTrackerServerSimplePeer> explicit_limited_peers = null; 1136 List<TRTrackerServerSimplePeer> explicit_biased_peers = null; 1137 1138 Set remove_ips = null; 1139 1140 if ( requesting_peer != null ){ 1141 1142 if ( peer_listeners != null ){ 1143 1144 for (int i=0;i<peer_listeners.size();i++){ 1145 1146 try{ 1147 Map reply = ((TRTrackerServerTorrentPeerListener)peer_listeners.get(i)).eventOccurred( this, requesting_peer, TRTrackerServerTorrentPeerListener.ET_ANNOUNCE, null ); 1148 1149 if ( reply != null ){ 1150 1151 List limited_peers = (List)reply.get( "limited_peers" ); 1152 1153 if ( limited_peers != null ){ 1154 1155 if ( explicit_limited_peers == null ){ 1156 1157 explicit_limited_peers = new ArrayList<TRTrackerServerSimplePeer>(); 1158 } 1159 1160 for (int j=0;j<limited_peers.size();j++){ 1161 1162 Map peer_map = (Map)limited_peers.get(j); 1163 1164 String ip = (String)peer_map.get("ip"); 1165 int port = ((Long)peer_map.get( "port")).intValue(); 1166 1167 String reuse_key = ip + ":" + port; 1168 1169 TRTrackerServerPeerImpl peer = (TRTrackerServerPeerImpl)peer_reuse_map.get( reuse_key ); 1170 1171 if ( peer != null && !explicit_limited_peers.contains( peer )){ 1172 1173 explicit_limited_peers.add( peer ); 1174 } 1175 } 1176 } 1177 1178 List biased_peers = (List)reply.get( "biased_peers" ); 1179 1180 if ( biased_peers != null ){ 1181 1182 if ( explicit_biased_peers == null ){ 1183 1184 explicit_biased_peers = new ArrayList<TRTrackerServerSimplePeer>(); 1185 } 1186 1187 for (int j=0;j<biased_peers.size();j++){ 1188 1189 Map peer_map = (Map)biased_peers.get(j); 1190 1191 String ip = (String)peer_map.get("ip"); 1192 int port = ((Long)peer_map.get( "port")).intValue(); 1193 1194 String reuse_key = ip + ":" + port; 1195 1196 TRTrackerServerSimplePeer peer = peer_reuse_map.get( reuse_key ); 1197 1198 if ( peer == null ){ 1199 1200 peer = new temporaryBiasedSeed( ip, port ); 1201 } 1202 1203 if ( !explicit_biased_peers.contains( peer )){ 1204 1205 explicit_biased_peers.add( peer ); 1206 } 1207 } 1208 } 1209 1210 remove_ips = (Set)reply.get( "remove_ips" ); 1211 } 1212 }catch( Throwable e ){ 1213 1214 Debug.printStackTrace(e); 1215 } 1216 } 1217 } 1218 } 1219 1220 boolean requester_is_biased; 1221 1222 if ( requesting_peer == null ){ 1223 1224 Set bp = server.getBiasedPeers(); 1225 1226 if ( bp == null ){ 1227 1228 requester_is_biased = false; 1229 1230 }else{ 1231 1232 requester_is_biased = bp.contains( ip_address ); 1233 } 1234 }else{ 1235 1236 requester_is_biased = requesting_peer.isBiased(); 1237 } 1238 1239 if ( caching_enabled && 1240 explicit_limited_peers == null && 1241 explicit_biased_peers == null && 1242 !requester_is_biased && 1243 remove_ips == null && 1244 (!nat_warning) && 1245 preprocess_map.size() == 0 && // don't cache if we've got pre-process stuff to add 1246 cache_millis > 0 && 1247 num_want >= MIN_CACHE_ENTRY_SIZE && 1248 total_peers >= TRTrackerServerImpl.getAnnounceCachePeerThreshold() && 1249 crypto_level != TRTrackerServerPeer.CRYPTO_REQUIRED ){ // no cache for crypto required peers 1250 1251 // too busy to bother with network position stuff 1252 1253 network_position = null; 1254 1255 // note that we've got to select a cache entry that is somewhat 1256 // relevant to the num_want param (but NOT greater than it) 1257 1258 // remove stuff that's too old 1259 1260 Iterator it = announce_cache.keySet().iterator(); 1261 1262 while( it.hasNext() ){ 1263 1264 Integer key = (Integer)it.next(); 1265 1266 announceCacheEntry entry = (announceCacheEntry)announce_cache.get( key ); 1267 1268 if ( now - entry.getTime() > cache_millis ){ 1269 1270 it.remove(); 1271 } 1272 } 1273 1274 // look for an entry with a reasonable num_want 1275 // e.g. for 100 look between 50 and 100 1276 1277 for (int i=num_want/10;i>num_want/20;i--){ 1278 1279 announceCacheEntry entry = (announceCacheEntry)announce_cache.get(new Integer(i)); 1280 1281 if( entry != null ){ 1282 1283 if ( now - entry.getTime() > cache_millis ){ 1284 1285 announce_cache.remove( new Integer(i)); 1286 1287 }else{ 1288 1289 // make sure this is compatible 1290 1291 if ( entry.getSendPeerIds() == send_peer_ids && 1292 entry.getCompactMode() == compact_mode ){ 1293 1294 return( entry.getData()); 1295 } 1296 } 1297 } 1298 } 1299 1300 add_to_cache = true; 1301 } 1302 1303 1304 LinkedList rep_peers = new LinkedList(); 1305 1306 1307 // System.out.println( "exportPeersToMap: num_want = " + num_want + ", max = " + max_peers ); 1308 1309 // if they want them all simply give them the set 1310 1311 if ( num_want > 0 && explicit_limited_peers == null ){ 1312 1313 if ( num_want >= total_peers){ 1314 1315 // if they want them all simply give them the set 1316 1317 for (int i=0;i<peer_list.size();i++){ 1318 1319 TRTrackerServerPeerImpl peer = (TRTrackerServerPeerImpl)peer_list.get(i); 1320 1321 if ( peer == null || peer == requesting_peer ){ 1322 1323 }else if ( now > peer.getTimeout()){ 1324 1325 // System.out.println( "removing timed out client '" + peer.getString()); 1326 1327 removePeer( peer, i, TRTrackerServerTorrentPeerListener.ET_TIMEOUT, null ); 1328 1329 }else if ( peer.getTCPPort() == 0 ){ 1330 1331 // a port of 0 means that the peer definitely can't accept incoming connections 1332 1333 }else if ( crypto_level == TRTrackerServerPeer.CRYPTO_NONE && peer.getCryptoLevel() == TRTrackerServerPeer.CRYPTO_REQUIRED ){ 1334 1335 // don't return "crypto required" peers to those that can't correctly connect to them 1336 1337 /* change this to make the explicit ones additional, not replacing 1338 }else if ( explicit_biased_peers != null && 1339 peer.isBiased()){ 1340 */ 1341 // if we have an explicit biased peer list and this peer is biased 1342 // skip here as we add them later 1343 1344 }else if ( remove_ips != null && remove_ips.contains( new String( peer.getIP()))){ 1345 1346 // skippy skippy 1347 1348 }else if ( include_seeds || !peer.isSeed()){ 1349 1350 Map rep_peer = new HashMap(3); 1351 1352 if ( send_peer_ids ){ 1353 1354 rep_peer.put( "peer id", peer.getPeerId().getHash()); 1355 } 1356 1357 if ( compact_mode != COMPACT_MODE_NONE ){ 1358 1359 byte[] peer_bytes = peer.getIPAddressBytes(); 1360 1361 if ( peer_bytes == null ){ 1362 1363 continue; 1364 } 1365 1366 rep_peer.put( "ip", peer_bytes ); 1367 1368 if ( compact_mode >= COMPACT_MODE_AZ ){ 1369 1370 rep_peer.put( "azver", new Long( peer.getAZVer())); 1371 1372 rep_peer.put( "azudp", new Long( peer.getUDPPort())); 1373 1374 if ( peer.isSeed()){ 1375 1376 rep_peer.put( "azhttp", new Long( peer.getHTTPPort())); 1377 } 1378 1379 if ( compact_mode >= COMPACT_MODE_XML ){ 1380 1381 rep_peer.put( "ip", peer.getIPAsRead() ); 1382 1383 }else{ 1384 1385 rep_peer.put( "azup", new Long( peer.getUpSpeed())); 1386 1387 if ( peer.isBiased()){ 1388 1389 rep_peer.put( "azbiased", "" ); 1390 } 1391 1392 if ( network_position != null ){ 1393 1394 DHTNetworkPosition peer_pos = peer.getNetworkPosition(); 1395 1396 if ( peer_pos != null && network_position.getPositionType() == peer_pos.getPositionType()){ 1397 1398 rep_peer.put( "azrtt", new Long( (long)peer_pos.estimateRTT(network_position ))); 1399 } 1400 } 1401 } 1402 } 1403 }else{ 1404 1405 rep_peer.put( "ip", peer.getIPAsRead() ); 1406 } 1407 1408 rep_peer.put( "port", new Long( peer.getTCPPort())); 1409 1410 if ( crypto_level != TRTrackerServerPeer.CRYPTO_NONE ){ 1411 1412 rep_peer.put( "crypto_flag", new Long( peer.getCryptoLevel() == TRTrackerServerPeer.CRYPTO_REQUIRED?1:0)); 1413 } 1414 1415 if ( peer.isBiased()){ 1416 1417 rep_peers.addFirst( rep_peer ); 1418 1419 }else{ 1420 1421 rep_peers.addLast( rep_peer ); 1422 } 1423 } 1424 } 1425 }else{ 1426 1427 int peer_list_size = peer_list.size(); 1428 1429 // to avoid returning duplicates when doing the two-loop check 1430 // for nat selection we maintain an array of markers 1431 1432 if ( duplicate_peer_checker.length < peer_list_size ){ 1433 1434 duplicate_peer_checker = new byte[peer_list_size*2]; 1435 1436 duplicate_peer_checker_index = 1; 1437 1438 }else if ( duplicate_peer_checker.length > (peer_list_size*2)){ 1439 1440 duplicate_peer_checker = new byte[(3*peer_list_size)/2]; 1441 1442 duplicate_peer_checker_index = 1; 1443 1444 }else{ 1445 1446 duplicate_peer_checker_index++; 1447 1448 if ( duplicate_peer_checker_index == 0 ){ 1449 1450 Arrays.fill( duplicate_peer_checker, (byte)0); 1451 1452 duplicate_peer_checker_index = 1; 1453 } 1454 } 1455 1456 boolean peer_removed = false; 1457 1458 try{ 1459 // got to suspend peer list compaction as we rely on the 1460 // list staying the same size during processing below 1461 1462 peer_list_compaction_suspended = true; 1463 1464 // too costly to randomise as below. use more efficient but slightly less accurate 1465 // approach 1466 1467 // two pass process if bad nat detection enabled 1468 1469 int added = 0; 1470 //int bad_nat_added = 0; 1471 1472 for (int bad_nat_loop=TRTrackerServerNATChecker.getSingleton().isEnabled()?0:1;bad_nat_loop<2;bad_nat_loop++){ 1473 1474 int limit = num_want*2; // some entries we find might not be usable 1475 // so in the limit search for more 1476 1477 if ( num_want*3 > total_peers ){ 1478 1479 limit++; 1480 } 1481 1482 int biased_peers_count = 0; 1483 1484 if ( biased_peers != null ){ // explicit are additional && explicit_biased_peers == null ){ 1485 1486 if ( biased_peers.size() > 1 ){ 1487 1488 // juggle things a bit 1489 1490 Object x = biased_peers.remove(0); 1491 1492 biased_peers.add( random.nextInt( biased_peers.size() + 1 ), x); 1493 } 1494 1495 biased_peers_count = Math.min( min_biased_peers, biased_peers.size()); 1496 } 1497 1498 for (int i=0;i<limit && added < num_want;i++){ 1499 1500 int peer_index; 1501 1502 TRTrackerServerPeerImpl peer; 1503 1504 // deal with bias up front 1505 1506 if ( bad_nat_loop == 1 && i < biased_peers_count ){ 1507 1508 peer = (TRTrackerServerPeerImpl)biased_peers.get(i); 1509 1510 peer_index = -1; // don't know actual index and don't need to as biased peers processed separately 1511 1512 }else{ 1513 1514 peer_index = random.nextInt(peer_list_size); 1515 1516 peer = (TRTrackerServerPeerImpl)peer_list.get(peer_index); 1517 1518 if ( peer == null || peer.isBiased()){ 1519 1520 continue; 1521 } 1522 } 1523 1524 if ( now > peer.getTimeout()){ 1525 1526 removePeer( peer, TRTrackerServerTorrentPeerListener.ET_TIMEOUT, null ); 1527 1528 peer_removed = true; 1529 1530 }else if ( requesting_peer == peer || peer.getTCPPort() == 0 ){ 1531 1532 // a port of 0 means that the peer definitely can't accept incoming connections 1533 1534 }else if ( crypto_level == TRTrackerServerPeer.CRYPTO_NONE && peer.getCryptoLevel() == TRTrackerServerPeer.CRYPTO_REQUIRED ){ 1535 1536 // don't return "crypto required" peers to those that can't correctly connect to them 1537 1538 }else if ( remove_ips != null && remove_ips.contains( new String( peer.getIP()))){ 1539 1540 // skippy skippy 1541 1542 }else if ( include_seeds || !peer.isSeed()){ 1543 1544 boolean bad_nat = peer.isNATStatusBad(); 1545 1546 if ( ( bad_nat_loop == 0 && !bad_nat ) || 1547 ( bad_nat_loop == 1 )){ 1548 1549 if ( peer_index == -1 || duplicate_peer_checker[peer_index] != duplicate_peer_checker_index ){ 1550 1551 if ( peer_index != -1 ){ 1552 1553 duplicate_peer_checker[peer_index] = duplicate_peer_checker_index; 1554 } 1555 1556 //if ( bad_nat ){ 1557 // 1558 // bad_nat_added++; 1559 //} 1560 1561 added++; 1562 1563 Map rep_peer = new HashMap(3); 1564 1565 if ( send_peer_ids ){ 1566 1567 rep_peer.put( "peer id", peer.getPeerId().getHash()); 1568 } 1569 1570 if ( compact_mode != COMPACT_MODE_NONE ){ 1571 1572 byte[] peer_bytes = peer.getIPAddressBytes(); 1573 1574 if ( peer_bytes == null ){ 1575 1576 continue; 1577 } 1578 1579 rep_peer.put( "ip", peer_bytes ); 1580 1581 if ( compact_mode >= COMPACT_MODE_AZ ){ 1582 1583 rep_peer.put( "azver", new Long( peer.getAZVer())); 1584 1585 rep_peer.put( "azudp", new Long( peer.getUDPPort())); 1586 1587 if ( peer.isSeed()){ 1588 1589 rep_peer.put( "azhttp", new Long( peer.getHTTPPort())); 1590 } 1591 1592 if ( compact_mode >= COMPACT_MODE_XML ){ 1593 1594 rep_peer.put( "ip", peer.getIPAsRead() ); 1595 1596 }else{ 1597 1598 rep_peer.put( "azup", new Long( peer.getUpSpeed())); 1599 1600 if ( peer.isBiased()){ 1601 1602 rep_peer.put( "azbiased", "" ); 1603 } 1604 1605 if ( network_position != null ){ 1606 1607 DHTNetworkPosition peer_pos = peer.getNetworkPosition(); 1608 1609 if ( peer_pos != null && network_position.getPositionType() == peer_pos.getPositionType()){ 1610 1611 rep_peer.put( "azrtt", new Long( (long)peer_pos.estimateRTT(network_position ))); 1612 } 1613 } 1614 } 1615 } 1616 }else{ 1617 1618 rep_peer.put( "ip", peer.getIPAsRead() ); 1619 } 1620 1621 rep_peer.put( "port", new Long( peer.getTCPPort())); 1622 1623 if ( crypto_level != TRTrackerServerPeer.CRYPTO_NONE ){ 1624 1625 rep_peer.put( "crypto_flag", new Long( peer.getCryptoLevel() == TRTrackerServerPeer.CRYPTO_REQUIRED?1:0)); 1626 } 1627 1628 if ( peer.isBiased()){ 1629 1630 rep_peers.addFirst( rep_peer ); 1631 1632 }else{ 1633 1634 rep_peers.addLast( rep_peer ); 1635 } 1636 } 1637 } 1638 } 1639 } 1640 } 1641 1642 // System.out.println( "num_want = " + num_want + ", added = " + added + ", bad_nat = " + bad_nat_added ); 1643 1644 }finally{ 1645 1646 peer_list_compaction_suspended = false; 1647 1648 if ( peer_removed ){ 1649 1650 checkForPeerListCompaction( false ); 1651 } 1652 } 1653 /* 1654 }else{ 1655 // given up on this approach for the moment as too costly 1656 1657 // randomly select the peers to return 1658 1659 LinkedList peers = new LinkedList( peer_map.keySet()); 1660 1661 int added = 0; 1662 1663 while( added < num_want && peers.size() > 0 ){ 1664 1665 String key = (String)peers.remove(random.nextInt(peers.size())); 1666 1667 TRTrackerServerPeerImpl peer = (TRTrackerServerPeerImpl)peer_map.get(key); 1668 1669 if ( now > peer.getTimeout()){ 1670 1671 removePeer( peer, TRTrackerServerTorrentPeerListener.ET_TIMEOUT, null ); 1672 1673 }else if ( peer.getTCPPort() == 0 ){ 1674 1675 // a port of 0 means that the peer definitely can't accept incoming connections 1676 1677 }else if ( crypto_level == TRTrackerServerPeer.CRYPTO_NONE && peer.getCryptoLevel() == TRTrackerServerPeer.CRYPTO_REQUIRED ){ 1678 1679 // don't return "crypto required" peers to those that can't correctly connect to them 1680 1681 }else if ( include_seeds || !peer.isSeed()){ 1682 1683 added++; 1684 1685 Map rep_peer = new HashMap(3); // don't use TreeMap as default is "compact" 1686 // so we never actually encode anyway 1687 1688 if ( send_peer_ids ){ 1689 1690 rep_peer.put( "peer id", peer.getPeerId().getHash()); 1691 } 1692 1693 if ( compact_mode != COMPACT_MODE_NONE ){ 1694 1695 byte[] peer_bytes = peer.getIPBytes(); 1696 1697 if ( peer_bytes == null ){ 1698 1699 continue; 1700 } 1701 1702 rep_peer.put( "ip", peer_bytes ); 1703 1704 if ( compact_mode >= COMPACT_MODE_AZ ){ 1705 1706 rep_peer.put( "azver", new Long( peer.getAZVer())); 1707 1708 rep_peer.put( "azudp", new Long( peer.getUDPPort())); 1709 1710 if ( peer.isSeed()){ 1711 1712 rep_peer.put( "azhttp", new Long( peer.getHTTPPort())); 1713 } 1714 } 1715 }else{ 1716 rep_peer.put( "ip", peer.getIPAsRead() ); 1717 } 1718 1719 rep_peer.put( "port", new Long( peer.getTCPPort())); 1720 1721 if ( crypto_level != TRTrackerServerPeer.CRYPTO_NONE ){ 1722 1723 rep_peer.put( "crypto_flag", new Long( peer.getCryptoLevel() == TRTrackerServerPeer.CRYPTO_REQUIRED?1:0)); 1724 } 1725 1726 rep_peers.add( rep_peer ); 1727 1728 } 1729 }*/ 1730 } 1731 } 1732 1733 if ( include_seeds && 1734 explicit_limited_peers == null && 1735 !send_peer_ids && 1736 seed_count < 3 && 1737 queued_peers != null ){ 1738 1739 Iterator it = queued_peers.iterator(); 1740 1741 List added = new ArrayList( QUEUED_PEERS_ADD_MAX ); 1742 1743 while( it.hasNext() && num_want > rep_peers.size() && added.size() < QUEUED_PEERS_ADD_MAX ){ 1744 1745 QueuedPeer peer = (QueuedPeer)it.next(); 1746 1747 if ( peer.isTimedOut( now )){ 1748 1749 it.remove(); 1750 1751 }else if ( crypto_level == TRTrackerServerPeer.CRYPTO_NONE && peer.getCryptoLevel() == TRTrackerServerPeer.CRYPTO_REQUIRED ){ 1752 1753 // don't return "crypto required" peers to those that can't correctly connect to them 1754 1755 }else if ( remove_ips != null && remove_ips.contains( peer.getIP())){ 1756 1757 // skippy skippy 1758 1759 }else{ 1760 1761 Map rep_peer = new HashMap(3); 1762 1763 if ( compact_mode != COMPACT_MODE_NONE ){ 1764 1765 byte[] peer_bytes = peer.getIPAddressBytes(); 1766 1767 if ( peer_bytes == null ){ 1768 1769 continue; 1770 } 1771 1772 rep_peer.put( "ip", peer_bytes ); 1773 1774 if ( compact_mode >= COMPACT_MODE_AZ ){ 1775 1776 rep_peer.put( "azver", new Long( peer.getAZVer())); 1777 1778 rep_peer.put( "azudp", new Long( peer.getUDPPort())); 1779 1780 if ( peer.isSeed()){ 1781 1782 rep_peer.put( "azhttp", new Long( peer.getHTTPPort())); 1783 } 1784 1785 if ( compact_mode >= COMPACT_MODE_XML ){ 1786 1787 rep_peer.put( "ip", peer.getIPAsRead()); 1788 } 1789 } 1790 1791 }else{ 1792 1793 rep_peer.put( "ip", peer.getIPAsRead()); 1794 } 1795 1796 rep_peer.put( "port", new Long( peer.getTCPPort())); 1797 1798 if ( crypto_level != TRTrackerServerPeer.CRYPTO_NONE ){ 1799 1800 rep_peer.put( "crypto_flag", new Long( peer.getCryptoLevel() == TRTrackerServerPeer.CRYPTO_REQUIRED?1:0)); 1801 } 1802 1803 // System.out.println( "added queued peer " + peer.getString()); 1804 1805 rep_peers.addLast( rep_peer ); 1806 1807 added.add( peer ); 1808 1809 // it'll be added back in below, don't worry! 1810 1811 it.remove(); 1812 } 1813 } 1814 1815 for (int i=0;i<added.size();i++){ 1816 1817 queued_peers.add( added.get(i)); 1818 } 1819 } 1820 1821 Map root = new TreeMap(); // user TreeMap to pre-sort so encoding quicker 1822 1823 if ( preprocess_map.size() > 0 ){ 1824 1825 root.putAll( preprocess_map ); 1826 } 1827 1828 if ( explicit_limited_peers != null ){ 1829 1830 for (int i=0;i<explicit_limited_peers.size();i++){ 1831 1832 num_want--; 1833 1834 TRTrackerServerSimplePeer peer = explicit_limited_peers.get(i); 1835 1836 exportPeer(rep_peers, peer, send_peer_ids, compact_mode, crypto_level, network_position); 1837 } 1838 } 1839 1840 if ( explicit_biased_peers != null ){ 1841 1842 for (int i=0;i<explicit_biased_peers.size();i++){ 1843 1844 num_want--; 1845 1846 TRTrackerServerSimplePeer peer = explicit_biased_peers.get(i); 1847 1848 exportPeer(rep_peers, peer, send_peer_ids, compact_mode, crypto_level, network_position); 1849 } 1850 } 1851 1852 if ( explicit_manual_biased_peers != null ){ 1853 1854 if ( requesting_peer != null && !requesting_peer.isSeed()){ 1855 1856 Object[] explicit_peer = (Object[])explicit_manual_biased_peers.get( explicit_next_peer++ ); 1857 1858 if ( explicit_next_peer == explicit_manual_biased_peers.size()){ 1859 1860 explicit_next_peer = 0; 1861 } 1862 1863 Map rep_peer = new HashMap(3); 1864 1865 if ( send_peer_ids ){ 1866 1867 byte[] peer_id = new byte[20]; 1868 1869 random.nextBytes( peer_id ); 1870 1871 rep_peer.put( "peer id", peer_id ); 1872 } 1873 1874 if ( compact_mode != COMPACT_MODE_NONE ){ 1875 1876 byte[] peer_bytes = (byte[])explicit_peer[1]; 1877 1878 rep_peer.put( "ip", peer_bytes ); 1879 1880 if ( compact_mode >= COMPACT_MODE_AZ ){ 1881 1882 rep_peer.put( "azver", new Long( 0 )); // non-az 1883 1884 rep_peer.put( "azudp", new Long( 0 )); 1885 1886 rep_peer.put( "azup", new Long( 0 )); 1887 1888 rep_peer.put( "azbiased", "" ); 1889 } 1890 }else{ 1891 1892 rep_peer.put( "ip", ((String)explicit_peer[0]).getBytes()); 1893 } 1894 1895 rep_peer.put( "port", new Long( ((Integer)explicit_peer[2]).intValue())); 1896 1897 if ( crypto_level != TRTrackerServerPeer.CRYPTO_NONE ){ 1898 1899 rep_peer.put( "crypto_flag", new Long( 0 )); 1900 } 1901 1902 rep_peers.addFirst( rep_peer ); 1903 } 1904 } 1905 1906 int num_peers_returned = rep_peers.size(); 1907 Iterator it = rep_peers.iterator(); 1908 1909 if ( compact_mode == COMPACT_MODE_AZ ){ 1910 1911 byte[] compact_peers = new byte[num_peers_returned*9]; 1912 1913 int index = 0; 1914 1915 while( it.hasNext()){ 1916 1917 Map rep_peer = (Map)it.next(); 1918 1919 byte[] ip = (byte[])rep_peer.get( "ip" ); 1920 int tcp_port = ((Long)rep_peer.get( "port" )).intValue(); 1921 int udp_port = ((Long)rep_peer.get( "azudp" )).intValue(); 1922 Long crypto_flag_l = (Long)rep_peer.get( "crypto_flag" ); 1923 byte crypto_flag = crypto_flag_l==null?0:crypto_flag_l.byteValue(); 1924 1925 int pos = index*9; 1926 1927 System.arraycopy( ip, 0, compact_peers, pos, 4 ); 1928 1929 pos += 4; 1930 1931 compact_peers[pos++] = (byte)(tcp_port>>8); 1932 compact_peers[pos++] = (byte)(tcp_port&0xff); 1933 compact_peers[pos++] = (byte)(udp_port>>8); 1934 compact_peers[pos++] = (byte)(udp_port&0xff); 1935 compact_peers[pos++] = crypto_flag; 1936 1937 index++; 1938 } 1939 1940 root.put( "peers", compact_peers ); 1941 1942 root.put( "azcompact", new Long(1)); 1943 1944 }else if ( compact_mode == COMPACT_MODE_AZ_2 ){ 1945 1946 List compact_peers = new ArrayList( num_peers_returned ); 1947 1948 while( it.hasNext()){ 1949 1950 Map rep_peer = (Map)it.next(); 1951 1952 Map peer = new HashMap(); 1953 1954 compact_peers.add( peer ); 1955 1956 byte[] ip = (byte[])rep_peer.get( "ip" ); 1957 1958 peer.put( "i", ip ); 1959 1960 int tcp_port = ((Long)rep_peer.get( "port" )).intValue(); 1961 1962 peer.put( "t", new byte[]{ (byte)(tcp_port>>8), (byte)(tcp_port&0xff) }); 1963 1964 int udp_port = ((Long)rep_peer.get( "azudp" )).intValue(); 1965 1966 if ( udp_port != 0 ){ 1967 1968 if ( udp_port == tcp_port ){ 1969 1970 peer.put( "u", new byte[0] ); 1971 1972 }else{ 1973 1974 peer.put( "u", new byte[]{ (byte)(udp_port>>8), (byte)(udp_port&0xff) }); 1975 } 1976 } 1977 1978 Long http_port_l = (Long)rep_peer.get( "azhttp" ); 1979 1980 if ( http_port_l != null ){ 1981 1982 int http_port = http_port_l.intValue(); 1983 1984 if ( http_port != 0 ){ 1985 1986 peer.put( "h", new byte[]{ (byte)(http_port>>8), (byte)(http_port&0xff) }); 1987 } 1988 } 1989 1990 Long crypto_flag_l = (Long)rep_peer.get( "crypto_flag" ); 1991 byte crypto_flag = crypto_flag_l==null?0:crypto_flag_l.byteValue(); 1992 1993 if ( crypto_flag != 0 ){ 1994 1995 peer.put( "c", new byte[]{ crypto_flag } ); 1996 } 1997 1998 Long az_ver_l = (Long)rep_peer.get( "azver" ); 1999 byte az_ver = az_ver_l==null?0:az_ver_l.byteValue(); 2000 2001 if ( az_ver != 0 ){ 2002 2003 peer.put( "v", new Long(az_ver)); 2004 } 2005 2006 Long up_speed = (Long)rep_peer.get( "azup" ); 2007 2008 if ( up_speed != null && up_speed.longValue() != 0 ){ 2009 2010 peer.put( "s", up_speed ); 2011 } 2012 2013 Long rtt = (Long)rep_peer.get( "azrtt" ); 2014 2015 if ( rtt != null ){ 2016 2017 peer.put( "r", rtt ); 2018 } 2019 2020 if ( rep_peer.containsKey("azbiased")){ 2021 2022 peer.put( "b", new Long(1)); 2023 } 2024 } 2025 2026 root.put( "peers", compact_peers ); 2027 2028 root.put( "azcompact", new Long(2)); 2029 2030 }else if ( compact_mode == COMPACT_MODE_XML ){ 2031 2032 List xml_peers = new ArrayList( num_peers_returned ); 2033 2034 while( it.hasNext()){ 2035 2036 Map rep_peer = (Map)it.next(); 2037 2038 Map peer = new HashMap(); 2039 2040 xml_peers.add( peer ); 2041 2042 peer.put( "ip", rep_peer.get( "ip" ) ); 2043 2044 peer.put( "tcp", rep_peer.get( "port" )); 2045 2046 int udp_port = ((Long)rep_peer.get( "azudp" )).intValue(); 2047 2048 if ( udp_port != 0 ){ 2049 2050 peer.put( "udp", new Long( udp_port )); 2051 } 2052 2053 Long http_port_l = (Long)rep_peer.get( "azhttp" ); 2054 2055 if ( http_port_l != null ){ 2056 2057 int http_port = http_port_l.intValue(); 2058 2059 if ( http_port != 0 ){ 2060 2061 peer.put( "http", new Long( http_port )); 2062 } 2063 } 2064 } 2065 2066 root.put( "peers", xml_peers ); 2067 2068 }else{ 2069 2070 byte[] crypto_flags = null; 2071 2072 if ( crypto_level != TRTrackerServerPeer.CRYPTO_NONE ){ 2073 2074 crypto_flags = new byte[num_peers_returned]; 2075 } 2076 2077 if ( compact_mode == COMPACT_MODE_NORMAL ){ 2078 2079 byte[] compact_peers = new byte[num_peers_returned*6]; 2080 2081 int index = 0; 2082 2083 int num_ipv4 = 0; 2084 int num_ipv6 = 0; 2085 2086 while( it.hasNext()){ 2087 2088 Map rep_peer = (Map)it.next(); 2089 2090 byte[] ip = (byte[])rep_peer.get( "ip" ); 2091 2092 if ( ip.length > 4 ){ 2093 2094 num_ipv6++; 2095 2096 // continue and fill in crypto return 2097 2098 }else{ 2099 2100 num_ipv4++; 2101 2102 if ( num_ipv6 == 0 ){ 2103 2104 int port = ((Long)rep_peer.get( "port" )).intValue(); 2105 2106 int pos = index*6; 2107 2108 System.arraycopy( ip, 0, compact_peers, pos, 4 ); 2109 2110 pos += 4; 2111 2112 compact_peers[pos++] = (byte)(port>>8); 2113 compact_peers[pos++] = (byte)(port&0xff); 2114 } 2115 } 2116 2117 if ( crypto_flags != null ){ 2118 2119 Long crypto_flag = (Long)rep_peer.remove( "crypto_flag" ); 2120 2121 crypto_flags[index] = crypto_flag.byteValue(); 2122 } 2123 2124 index++; 2125 } 2126 2127 // inefficient hack to support ipv6 compact for the moment 2128 2129 if ( num_ipv6 > 0 ){ 2130 2131 byte[] compact_peers_v4 = new byte[num_ipv4*6]; 2132 byte[] compact_peers_v6 = new byte[num_ipv6*18]; 2133 2134 it = rep_peers.iterator(); 2135 2136 int v4_index = 0; 2137 int v6_index = 0; 2138 2139 while( it.hasNext()){ 2140 2141 Map rep_peer = (Map)it.next(); 2142 2143 byte[] ip = (byte[])rep_peer.get( "ip" ); 2144 2145 int port = ((Long)rep_peer.get( "port" )).intValue(); 2146 2147 if ( ip.length > 4 ){ 2148 2149 int pos = v6_index*18; 2150 2151 System.arraycopy( ip, 0, compact_peers_v6, pos, 16 ); 2152 2153 pos += 16; 2154 2155 compact_peers_v6[pos++] = (byte)(port>>8); 2156 compact_peers_v6[pos++] = (byte)(port&0xff); 2157 2158 v6_index++; 2159 2160 }else{ 2161 2162 int pos = v4_index*6; 2163 2164 System.arraycopy( ip, 0, compact_peers_v4, pos, 4 ); 2165 2166 pos += 4; 2167 2168 compact_peers_v4[pos++] = (byte)(port>>8); 2169 compact_peers_v4[pos++] = (byte)(port&0xff); 2170 2171 v4_index++; 2172 } 2173 } 2174 2175 if ( compact_peers_v4.length > 0 ){ 2176 2177 root.put( "peers", compact_peers_v4 ); 2178 } 2179 2180 if ( compact_peers_v6.length > 0 ){ 2181 2182 root.put( "peers6", compact_peers_v6 ); 2183 } 2184 }else{ 2185 2186 root.put( "peers", compact_peers ); 2187 } 2188 }else{ 2189 2190 int index = 0; 2191 2192 while( it.hasNext()){ 2193 2194 Map rep_peer = (Map)it.next(); 2195 2196 if ( crypto_flags != null ){ 2197 2198 Long crypto_flag = (Long)rep_peer.remove( "crypto_flag" ); 2199 2200 crypto_flags[index] = crypto_flag.byteValue(); 2201 } 2202 2203 index++; 2204 } 2205 2206 root.put( "peers", rep_peers ); 2207 } 2208 2209 if ( crypto_flags != null ){ 2210 2211 root.put( "crypto_flags", crypto_flags ); 2212 } 2213 } 2214 2215 root.put( "interval", new Long( interval )); 2216 2217 root.put( "min interval", new Long( min_interval )); 2218 2219 if ( nat_warning ){ 2220 2221 requesting_peer.setNATStatus( TRTrackerServerPeerImpl.NAT_CHECK_FAILED_AND_REPORTED ); 2222 2223 root.put( 2224 "warning message", 2225 ("Unable to connect to your incoming data port (" + requesting_peer.getIP() + ":" + requesting_peer.getTCPPort() +"). " + 2226 "This will result in slow downloads. Please check your firewall/router settings").getBytes()); 2227 } 2228 2229 // also include scrape details 2230 2231 root.put( "complete", new Long( getSeedCountForScrape( requester_is_biased ))); 2232 root.put( "incomplete", new Long( getLeecherCount() )); 2233 root.put( "downloaded", new Long(stats.getCompletedCount())); 2234 2235 if ( add_to_cache ){ 2236 2237 announce_cache.put( new Integer((num_peers_returned+9)/10), new announceCacheEntry( root, send_peer_ids, compact_mode )); 2238 } 2239 2240 return( root ); 2241 2242 }finally{ 2243 2244 this_mon.exit(); 2245 } 2246 } 2247 2248 2249 private void exportPeer( LinkedList rep_peers, TRTrackerServerSimplePeer peer, boolean send_peer_ids, byte compact_mode, byte crypto_level, DHTNetworkPosition network_position )2250 exportPeer( 2251 LinkedList rep_peers, 2252 TRTrackerServerSimplePeer peer, 2253 boolean send_peer_ids, 2254 byte compact_mode, 2255 byte crypto_level, 2256 DHTNetworkPosition network_position ) 2257 { 2258 Map rep_peer = new HashMap(3); 2259 2260 if ( send_peer_ids ){ 2261 2262 rep_peer.put( "peer id", peer.getPeerId().getHash()); 2263 } 2264 2265 if ( compact_mode != COMPACT_MODE_NONE ){ 2266 2267 byte[] peer_bytes = peer.getIPAddressBytes(); 2268 2269 if ( peer_bytes == null ){ 2270 2271 return; 2272 } 2273 2274 rep_peer.put( "ip", peer_bytes ); 2275 2276 if ( compact_mode >= COMPACT_MODE_AZ ){ 2277 2278 rep_peer.put( "azver", new Long( peer.getAZVer())); 2279 2280 rep_peer.put( "azudp", new Long( peer.getUDPPort())); 2281 2282 if ( peer.isSeed()){ 2283 2284 rep_peer.put( "azhttp", new Long( peer.getHTTPPort())); 2285 } 2286 2287 if ( compact_mode >= COMPACT_MODE_XML ){ 2288 2289 rep_peer.put( "ip", peer.getIPAsRead() ); 2290 2291 }else{ 2292 2293 rep_peer.put( "azup", new Long( peer.getUpSpeed())); 2294 2295 if ( peer.isBiased()){ 2296 2297 rep_peer.put( "azbiased", "" ); 2298 } 2299 2300 if ( network_position != null ){ 2301 2302 DHTNetworkPosition peer_pos = peer.getNetworkPosition(); 2303 2304 if ( peer_pos != null && network_position.getPositionType() == peer_pos.getPositionType()){ 2305 2306 rep_peer.put( "azrtt", new Long( (long)peer_pos.estimateRTT(network_position ))); 2307 } 2308 } 2309 } 2310 } 2311 }else{ 2312 2313 rep_peer.put( "ip", peer.getIPAsRead() ); 2314 } 2315 2316 rep_peer.put( "port", new Long( peer.getTCPPort())); 2317 2318 if ( crypto_level != TRTrackerServerPeer.CRYPTO_NONE ){ 2319 2320 rep_peer.put( "crypto_flag", new Long( peer.getCryptoLevel() == TRTrackerServerPeer.CRYPTO_REQUIRED?1:0)); 2321 } 2322 2323 if ( peer.isBiased()){ 2324 2325 rep_peers.addFirst( rep_peer ); 2326 2327 }else{ 2328 2329 rep_peers.addLast( rep_peer ); 2330 } 2331 } 2332 2333 public Map exportScrapeToMap( String url_parameters, String ip_address, boolean allow_cache )2334 exportScrapeToMap( 2335 String url_parameters, 2336 String ip_address, 2337 boolean allow_cache ) 2338 2339 throws TRTrackerServerException 2340 { 2341 try{ 2342 this_mon.enter(); 2343 2344 handleRedirects( url_parameters, ip_address, true ); 2345 2346 stats.addScrape(); 2347 2348 long now = SystemTime.getCurrentTime(); 2349 2350 long diff = now - last_scrape_calc_time; 2351 2352 if( allow_cache && last_scrape != null && diff < TRTrackerServerImpl.getScrapeCachePeriod() && !(diff < 0) ){ 2353 2354 return( last_scrape ); 2355 } 2356 2357 last_scrape = new TreeMap(); 2358 last_scrape_calc_time = now; 2359 2360 boolean requester_is_biased; 2361 2362 Set bp = server.getBiasedPeers(); 2363 2364 if ( bp == null ){ 2365 2366 requester_is_biased = false; 2367 2368 }else{ 2369 2370 requester_is_biased = bp.contains( ip_address ); 2371 } 2372 2373 last_scrape.put( "complete", new Long( getSeedCountForScrape( requester_is_biased ))); 2374 last_scrape.put( "incomplete", new Long( getLeecherCount())); 2375 last_scrape.put( "downloaded", new Long(stats.getCompletedCount())); 2376 2377 return( last_scrape ); 2378 2379 }finally{ 2380 2381 this_mon.exit(); 2382 } 2383 } 2384 2385 protected void checkTimeouts()2386 checkTimeouts() 2387 { 2388 try{ 2389 this_mon.enter(); 2390 2391 long now = SystemTime.getCurrentTime(); 2392 2393 int new_bad_NAT_count = 0; 2394 2395 // recalc seed count as this drifts for some reason (maybe seeds switching back to leechers 2396 // on recheck fail, not sure) 2397 2398 int new_seed_count = 0; 2399 2400 try{ 2401 peer_list_compaction_suspended = true; 2402 2403 for (int i=0;i<peer_list.size();i++){ 2404 2405 TRTrackerServerPeerImpl peer = (TRTrackerServerPeerImpl)peer_list.get(i); 2406 2407 if ( peer == null ){ 2408 2409 continue; 2410 } 2411 2412 if ( now > peer.getTimeout()){ 2413 2414 removePeer( peer, i, TRTrackerServerTorrentPeerListener.ET_TIMEOUT, null ); 2415 2416 }else{ 2417 2418 if ( peer.isSeed()){ 2419 2420 new_seed_count++; 2421 } 2422 2423 if ( peer.isNATStatusBad()){ 2424 2425 new_bad_NAT_count++; 2426 } 2427 } 2428 } 2429 }finally{ 2430 2431 peer_list_compaction_suspended = false; 2432 } 2433 2434 bad_NAT_count = new_bad_NAT_count; 2435 seed_count = new_seed_count; 2436 2437 if ( removed_count > 1000 ){ 2438 2439 removed_count = 0; 2440 2441 checkForPeerListCompaction( true ); 2442 2443 // rehash 2444 2445 HashMap new_peer_map = new HashMap(peer_map); 2446 HashMap new_peer_reuse_map = new HashMap(peer_reuse_map); 2447 2448 peer_map = new_peer_map; 2449 peer_reuse_map = new_peer_reuse_map; 2450 2451 }else{ 2452 2453 checkForPeerListCompaction( false ); 2454 } 2455 2456 Iterator it = lightweight_seed_map.values().iterator(); 2457 2458 while( it.hasNext()){ 2459 2460 lightweightSeed lws = (lightweightSeed)it.next(); 2461 2462 if ( now > lws.getTimeout()){ 2463 2464 it.remove(); 2465 } 2466 } 2467 }finally{ 2468 2469 this_mon.exit(); 2470 } 2471 } 2472 2473 protected void checkForPeerListCompaction( boolean force )2474 checkForPeerListCompaction( 2475 boolean force ) 2476 { 2477 if ( peer_list_hole_count > 0 && !peer_list_compaction_suspended ){ 2478 2479 if ( force || peer_list_hole_count > peer_map.size()/10 ){ 2480 2481 ArrayList new_peer_list = new ArrayList( peer_list.size() - (peer_list_hole_count/2)); 2482 2483 int holes_found = 0; 2484 2485 for (int i=0;i<peer_list.size();i++){ 2486 2487 Object obj = peer_list.get(i); 2488 2489 if ( obj == null ){ 2490 2491 holes_found++; 2492 }else{ 2493 2494 new_peer_list.add( obj ); 2495 } 2496 } 2497 2498 if( holes_found != peer_list_hole_count ){ 2499 2500 Debug.out( "TRTrackerTorrent:compactHoles: count mismatch" ); 2501 } 2502 2503 peer_list = new_peer_list; 2504 2505 peer_list_hole_count = 0; 2506 } 2507 } 2508 } 2509 2510 protected void updateXferStats( int bytes_in, int bytes_out )2511 updateXferStats( 2512 int bytes_in, 2513 int bytes_out ) 2514 { 2515 stats.addXferStats( bytes_in, bytes_out ); 2516 } 2517 2518 public TRTrackerServerTorrentStats getStats()2519 getStats() 2520 { 2521 return( stats ); 2522 } 2523 2524 protected int getPeerCount()2525 getPeerCount() 2526 { 2527 return( peer_map.size() + lightweight_seed_map.size()); 2528 } 2529 2530 protected int getSeedCount()2531 getSeedCount() 2532 { 2533 if ( seed_count < 0 ){ 2534 2535 Debug.out( "seed count negative" ); 2536 } 2537 2538 return( seed_count + lightweight_seed_map.size()); 2539 } 2540 2541 protected int getSeedCountForScrape( boolean requester_is_biased )2542 getSeedCountForScrape( 2543 boolean requester_is_biased ) 2544 { 2545 int seeds = getSeedCount(); 2546 2547 if ( biased_peers != null && !requester_is_biased ){ 2548 2549 int bpc = 0; 2550 2551 Iterator it = biased_peers.iterator(); 2552 2553 while( it.hasNext()){ 2554 2555 TRTrackerServerPeerImpl bp = (TRTrackerServerPeerImpl)it.next(); 2556 2557 if ( bp.isSeed()){ 2558 2559 seeds--; 2560 2561 bpc++; 2562 } 2563 } 2564 2565 if ( seeds < 0 ){ 2566 2567 seeds = 0; 2568 } 2569 2570 // retain at least one biased seed 2571 2572 if ( bpc > 0 ){ 2573 2574 seeds++; 2575 } 2576 } 2577 2578 // if we have any queued then lets add at least one in to indicate potential 2579 2580 int queued = getQueuedCount(); 2581 2582 if ( queued > 0 ){ 2583 2584 seeds++; 2585 } 2586 2587 return( seeds ); 2588 } 2589 2590 protected int getLeecherCount()2591 getLeecherCount() 2592 { 2593 // this isn't synchronised so could possible end up negative 2594 2595 int res = peer_map.size() - seed_count; 2596 2597 return( res<0?0:res ); 2598 } 2599 2600 public TRTrackerServerPeer[] getPeers()2601 getPeers() 2602 { 2603 try{ 2604 this_mon.enter(); 2605 2606 TRTrackerServerPeer[] res = new TRTrackerServerPeer[peer_map.size()]; 2607 2608 peer_map.values().toArray( res ); 2609 2610 return( res ); 2611 2612 }finally{ 2613 2614 this_mon.exit(); 2615 } 2616 } 2617 2618 protected int getQueuedCount()2619 getQueuedCount() 2620 { 2621 List l = queued_peers; 2622 2623 if ( l == null ){ 2624 2625 return( 0 ); 2626 } 2627 2628 return( l.size()); 2629 } 2630 2631 public TRTrackerServerPeerBase[] getQueuedPeers()2632 getQueuedPeers() 2633 { 2634 try{ 2635 this_mon.enter(); 2636 2637 if ( queued_peers == null ){ 2638 2639 return( new TRTrackerServerPeerBase[0] ); 2640 } 2641 2642 TRTrackerServerPeerBase[] res = new TRTrackerServerPeerBase[queued_peers.size()]; 2643 2644 queued_peers.toArray( res ); 2645 2646 return( res ); 2647 2648 }finally{ 2649 2650 this_mon.exit(); 2651 } 2652 } 2653 2654 public HashWrapper getHash()2655 getHash() 2656 { 2657 return( hash ); 2658 } 2659 2660 public void addExplicitBiasedPeer( String ip, int port )2661 addExplicitBiasedPeer( 2662 String ip, 2663 int port ) 2664 { 2665 byte[] bytes = HostNameToIPResolver.hostAddressToBytes( ip ); 2666 2667 if ( bytes != null ){ 2668 2669 try{ 2670 this_mon.enter(); 2671 2672 if ( explicit_manual_biased_peers == null ){ 2673 2674 explicit_manual_biased_peers = new ArrayList(); 2675 } 2676 2677 explicit_manual_biased_peers.add( new Object[]{ ip, bytes, new Integer( port )}); 2678 2679 }finally{ 2680 2681 this_mon.exit(); 2682 } 2683 } 2684 } 2685 public void setRedirects( URL[] urls )2686 setRedirects( 2687 URL[] urls ) 2688 { 2689 try{ 2690 this_mon.enter(); 2691 2692 redirects = urls; 2693 2694 }finally{ 2695 2696 this_mon.exit(); 2697 } 2698 } 2699 2700 public URL[] getRedirects()2701 getRedirects() 2702 { 2703 return( redirects ); 2704 } 2705 2706 protected void handleRedirects( String url_parameters, String real_ip_address, boolean scrape )2707 handleRedirects( 2708 String url_parameters, 2709 String real_ip_address, 2710 boolean scrape ) 2711 2712 throws TRTrackerServerException 2713 { 2714 if ( redirects != null ){ 2715 2716 if ( url_parameters.indexOf("permredirect") != -1 ){ 2717 2718 Debug.out( "redirect recursion" ); 2719 2720 throw( new TRTrackerServerException( "redirection recursion not supported" )); 2721 } 2722 2723 URL redirect = redirects[(real_ip_address.hashCode()&0x7fffffff)%redirects.length]; 2724 2725 Map headers = new HashMap(); 2726 2727 String redirect_str = redirect.toString(); 2728 2729 if ( scrape ){ 2730 2731 int pos = redirect_str.indexOf( "/announce" ); 2732 2733 if ( pos == -1 ){ 2734 2735 return; 2736 } 2737 2738 redirect_str = redirect_str.substring( 0, pos ) + "/scrape" + redirect_str.substring( pos + 9 ); 2739 } 2740 2741 if ( redirect_str.indexOf('?' ) == -1 ){ 2742 2743 redirect_str += "?"; 2744 2745 }else{ 2746 2747 redirect_str += "&"; 2748 } 2749 2750 redirect_str += "permredirect=1"; 2751 2752 if ( url_parameters.length() > 0 ){ 2753 2754 redirect_str += "&" + url_parameters; 2755 } 2756 2757 System.out.println( "redirect -> " + redirect_str ); 2758 2759 headers.put( "Location", redirect_str); 2760 2761 throw( new TRTrackerServerException(301, "Moved Permanently", headers )); 2762 } 2763 } 2764 public void addListener( TRTrackerServerTorrentListener l )2765 addListener( 2766 TRTrackerServerTorrentListener l ) 2767 { 2768 listeners.add(l); 2769 2770 if ( deleted ){ 2771 2772 l.deleted(this); 2773 } 2774 } 2775 2776 public void removeListener( TRTrackerServerTorrentListener l )2777 removeListener( 2778 TRTrackerServerTorrentListener l ) 2779 { 2780 listeners.remove(l); 2781 } 2782 2783 protected void peerEvent( TRTrackerServerPeer peer, int event, String url_parameters )2784 peerEvent( 2785 TRTrackerServerPeer peer, 2786 int event, 2787 String url_parameters ) 2788 2789 throws TRTrackerServerException 2790 { 2791 if ( peer_listeners != null ){ 2792 2793 for (int i=0;i<peer_listeners.size();i++){ 2794 2795 try{ 2796 ((TRTrackerServerTorrentPeerListener)peer_listeners.get(i)).eventOccurred( this, peer, event, url_parameters ); 2797 2798 }catch( TRTrackerServerException e ){ 2799 2800 throw( e ); 2801 2802 }catch( Throwable e ){ 2803 2804 Debug.printStackTrace(e); 2805 } 2806 } 2807 } 2808 } 2809 2810 public void addPeerListener( TRTrackerServerTorrentPeerListener l )2811 addPeerListener( 2812 TRTrackerServerTorrentPeerListener l ) 2813 { 2814 if ( peer_listeners == null ){ 2815 2816 peer_listeners = new ArrayList(); 2817 } 2818 2819 peer_listeners.add( l ); 2820 } 2821 2822 public void removePeerListener( TRTrackerServerTorrentPeerListener l )2823 removePeerListener( 2824 TRTrackerServerTorrentPeerListener l ) 2825 { 2826 if ( peer_listeners != null ){ 2827 2828 peer_listeners.remove(l); 2829 } 2830 } 2831 2832 public void disableCaching()2833 disableCaching() 2834 { 2835 caching_enabled = false; 2836 } 2837 2838 public boolean isCachingEnabled()2839 isCachingEnabled() 2840 { 2841 return( caching_enabled ); 2842 } 2843 2844 public int getBadNATPeerCount()2845 getBadNATPeerCount() 2846 { 2847 return( bad_NAT_count ); 2848 } 2849 2850 protected void delete()2851 delete() 2852 { 2853 deleted = true; 2854 2855 for (int i=0;i<listeners.size();i++){ 2856 2857 ((TRTrackerServerTorrentListener)listeners.get(i)).deleted(this); 2858 } 2859 } 2860 2861 static class 2862 announceCacheEntry 2863 { 2864 protected Map data; 2865 protected boolean send_peer_ids; 2866 protected byte compact_mode; 2867 protected long time; 2868 2869 protected announceCacheEntry( Map _data, boolean _send_peer_ids, byte _compact_mode )2870 announceCacheEntry( 2871 Map _data, 2872 boolean _send_peer_ids, 2873 byte _compact_mode ) 2874 { 2875 data = _data; 2876 send_peer_ids = _send_peer_ids; 2877 compact_mode = _compact_mode; 2878 time = SystemTime.getCurrentTime(); 2879 } 2880 2881 protected boolean getSendPeerIds()2882 getSendPeerIds() 2883 { 2884 return( send_peer_ids ); 2885 } 2886 2887 protected byte getCompactMode()2888 getCompactMode() 2889 { 2890 return( compact_mode ); 2891 } 2892 2893 protected long getTime()2894 getTime() 2895 { 2896 return( time ); 2897 } 2898 2899 protected Map getData()2900 getData() 2901 { 2902 return( data ); 2903 } 2904 } 2905 2906 protected static class 2907 lightweightSeed 2908 { 2909 long timeout; 2910 long last_contact_time; 2911 long uploaded; 2912 byte nat_status; 2913 2914 protected lightweightSeed( long _now, long _timeout, long _uploaded, byte _nat_status )2915 lightweightSeed( 2916 long _now, 2917 long _timeout, 2918 long _uploaded, 2919 byte _nat_status ) 2920 { 2921 last_contact_time = _now; 2922 timeout = _timeout; 2923 uploaded = _uploaded; 2924 nat_status = _nat_status; 2925 } 2926 2927 protected long getTimeout()2928 getTimeout() 2929 { 2930 return( timeout ); 2931 } 2932 protected long getLastContactTime()2933 getLastContactTime() 2934 { 2935 return( last_contact_time ); 2936 } 2937 2938 protected long getUploaded()2939 getUploaded() 2940 { 2941 return( uploaded ); 2942 } 2943 2944 protected byte getNATStatus()2945 getNATStatus() 2946 { 2947 return( nat_status ); 2948 } 2949 } 2950 2951 protected static class 2952 QueuedPeer 2953 implements TRTrackerServerPeerBase 2954 { 2955 private static final byte FLAG_SEED = 0x01; 2956 private static final byte FLAG_BIASED = 0x02; 2957 2958 private short tcp_port; 2959 private short udp_port; 2960 private short http_port; 2961 private byte[] ip; 2962 private byte crypto_level; 2963 private byte az_ver; 2964 private int create_time_secs; 2965 private int timeout_secs; 2966 private byte flags; 2967 2968 protected QueuedPeer( String _ip_str, int _tcp_port, int _udp_port, int _http_port, byte _crypto_level, byte _az_ver, int _timeout_secs, boolean _seed, boolean _biased )2969 QueuedPeer( 2970 String _ip_str, 2971 int _tcp_port, 2972 int _udp_port, 2973 int _http_port, 2974 byte _crypto_level, 2975 byte _az_ver, 2976 int _timeout_secs, 2977 boolean _seed, 2978 boolean _biased ) 2979 { 2980 try{ 2981 ip = _ip_str.getBytes( Constants.BYTE_ENCODING ); 2982 2983 }catch( UnsupportedEncodingException e ){ 2984 2985 Debug.printStackTrace(e); 2986 } 2987 2988 tcp_port = (short)_tcp_port; 2989 udp_port = (short)_udp_port; 2990 http_port = (short)_http_port; 2991 crypto_level = _crypto_level; 2992 az_ver = _az_ver; 2993 2994 setFlag( FLAG_SEED, _seed ); 2995 setFlag( FLAG_BIASED, _biased ); 2996 2997 create_time_secs = (int)( SystemTime.getCurrentTime()/1000 ); 2998 2999 timeout_secs = _timeout_secs * TRTrackerServerImpl.CLIENT_TIMEOUT_MULTIPLIER; 3000 } 3001 3002 protected boolean sameAs( TRTrackerServerPeerImpl peer )3003 sameAs( 3004 TRTrackerServerPeerImpl peer ) 3005 { 3006 return( tcp_port == peer.getTCPPort() && 3007 Arrays.equals( ip, peer.getIPAsRead()) && 3008 isIPOverride() == peer.isIPOverride()); 3009 } 3010 3011 protected boolean sameAs( QueuedPeer other )3012 sameAs( 3013 QueuedPeer other ) 3014 { 3015 return( tcp_port == other.tcp_port && 3016 Arrays.equals( ip,other.ip )); 3017 } 3018 3019 protected byte[] getIPAsRead()3020 getIPAsRead() 3021 { 3022 return( ip ); 3023 } 3024 3025 public String getIP()3026 getIP() 3027 { 3028 try{ 3029 return( new String( ip, Constants.BYTE_ENCODING )); 3030 3031 }catch( UnsupportedEncodingException e ){ 3032 3033 return( new String( ip )); 3034 } 3035 } 3036 3037 protected boolean isSeed()3038 isSeed() 3039 { 3040 return( getFlag( FLAG_SEED )); 3041 } 3042 3043 protected void setBiased( boolean _biased )3044 setBiased( 3045 boolean _biased ) 3046 { 3047 setFlag( FLAG_BIASED, _biased ); 3048 } 3049 3050 protected boolean isBiased()3051 isBiased() 3052 { 3053 return( getFlag( FLAG_BIASED )); 3054 } 3055 3056 protected boolean isIPOverride()3057 isIPOverride() 3058 { 3059 // we never allow IP override queued peers 3060 3061 return( false ); 3062 } 3063 3064 protected void setFlag( byte flag, boolean value )3065 setFlag( 3066 byte flag, 3067 boolean value ) 3068 { 3069 if ( value ){ 3070 3071 flags |= flag; 3072 3073 }else{ 3074 3075 flags &= ~flag; 3076 } 3077 } 3078 3079 protected boolean getFlag( byte flag )3080 getFlag( 3081 byte flag ) 3082 { 3083 return((flags & flag ) != 0 ); 3084 } 3085 3086 protected byte[] getIPAddressBytes()3087 getIPAddressBytes() 3088 { 3089 try{ 3090 return( HostNameToIPResolver.hostAddressToBytes( new String( ip, Constants.BYTE_ENCODING ))); 3091 3092 }catch( UnsupportedEncodingException e ){ 3093 3094 Debug.printStackTrace(e); 3095 3096 return( null ); 3097 } 3098 } 3099 3100 public int getTCPPort()3101 getTCPPort() 3102 { 3103 return( tcp_port & 0xffff ); 3104 } 3105 3106 public int getUDPPort()3107 getUDPPort() 3108 { 3109 return( udp_port & 0xffff ); 3110 } 3111 3112 public int getHTTPPort()3113 getHTTPPort() 3114 { 3115 return( http_port & 0xffff ); 3116 } 3117 3118 protected byte getCryptoLevel()3119 getCryptoLevel() 3120 { 3121 return( crypto_level ); 3122 } 3123 3124 protected byte getAZVer()3125 getAZVer() 3126 { 3127 return( az_ver ); 3128 } 3129 3130 protected int getCreateTime()3131 getCreateTime() 3132 { 3133 return( create_time_secs ); 3134 } 3135 3136 protected boolean isTimedOut( long now_millis )3137 isTimedOut( 3138 long now_millis ) 3139 { 3140 int now_secs = (int)(now_millis/1000); 3141 3142 if ( now_secs < create_time_secs ){ 3143 3144 create_time_secs = now_secs; 3145 } 3146 3147 return( create_time_secs + timeout_secs < now_secs ); 3148 } 3149 3150 public int getSecsToLive()3151 getSecsToLive() 3152 { 3153 int now_secs = (int)(SystemTime.getCurrentTime()/1000); 3154 3155 if ( now_secs < create_time_secs ){ 3156 3157 create_time_secs = now_secs; 3158 } 3159 3160 return(( create_time_secs + timeout_secs ) - now_secs ); 3161 } 3162 3163 protected String getString()3164 getString() 3165 { 3166 return( new String(ip) + ":" + getTCPPort() + "/" + getUDPPort() + "/" + getCryptoLevel()); 3167 } 3168 } 3169 3170 private static class 3171 temporaryBiasedSeed 3172 implements TRTrackerServerSimplePeer 3173 { 3174 private String ip; 3175 private int tcp_port; 3176 private HashWrapper peer_id; 3177 3178 protected temporaryBiasedSeed( String _ip, int _tcp_port )3179 temporaryBiasedSeed( 3180 String _ip, 3181 int _tcp_port ) 3182 { 3183 ip = _ip; 3184 tcp_port = _tcp_port; 3185 3186 peer_id = new HashWrapper( RandomUtils.nextHash()); 3187 } 3188 3189 public byte[] getIPAsRead()3190 getIPAsRead() 3191 { 3192 try{ 3193 3194 return( ip.getBytes( Constants.BYTE_ENCODING )); 3195 3196 }catch( Throwable e ){ 3197 3198 return( ip.getBytes()); 3199 } 3200 } 3201 3202 public byte[] getIPAddressBytes()3203 getIPAddressBytes() 3204 { 3205 try{ 3206 return( InetAddress.getByName( ip ).getAddress()); 3207 3208 }catch( Throwable e ){ 3209 3210 return( null ); 3211 } 3212 } 3213 3214 public HashWrapper getPeerId()3215 getPeerId() 3216 { 3217 return( peer_id ); 3218 } 3219 3220 public int getTCPPort()3221 getTCPPort() 3222 { 3223 return( tcp_port ); 3224 } 3225 3226 public int getUDPPort()3227 getUDPPort() 3228 { 3229 return( 0 ); 3230 } 3231 3232 public int getHTTPPort()3233 getHTTPPort() 3234 { 3235 return( 0 ); 3236 } 3237 3238 public boolean isSeed()3239 isSeed() 3240 { 3241 return( true ); 3242 } 3243 3244 public boolean isBiased()3245 isBiased() 3246 { 3247 return( true ); 3248 } 3249 3250 public byte getCryptoLevel()3251 getCryptoLevel() 3252 { 3253 return( TRTrackerServerPeer.CRYPTO_NONE ); 3254 } 3255 3256 public byte getAZVer()3257 getAZVer() 3258 { 3259 return( 0 ); 3260 } 3261 3262 public int getUpSpeed()3263 getUpSpeed() 3264 { 3265 return( 0 ); 3266 } 3267 3268 public DHTNetworkPosition getNetworkPosition()3269 getNetworkPosition() 3270 { 3271 return( null ); 3272 } 3273 } 3274 3275 public String getString()3276 getString() 3277 { 3278 String redirect; 3279 3280 if ( redirects == null ){ 3281 3282 redirect = "none"; 3283 3284 }else{ 3285 3286 redirect = ""; 3287 3288 for (int i=0;i<redirects.length;i++){ 3289 3290 redirect += (i==0?"":",") + redirects[i]; 3291 } 3292 } 3293 3294 return( "seeds=" + getSeedCount() + ",leechers=" + getLeecherCount() + ", redirect=" + redirect ); 3295 } 3296 } 3297