📄 trtrackerservertorrentimpl.java
字号:
/*
* File : TRTrackerServerTorrent.java
* Created : 26-Oct-2003
* By : stuff
*
* Azureus - a Java Bittorrent client
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details ( see the LICENSE file ).
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.gudy.azureus2.core3.tracker.server.impl;
/**
* @author parg
*
*/
import java.util.*;
import java.io.*;
import java.net.URL;
import java.net.UnknownHostException;
import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.tracker.server.*;
import org.gudy.azureus2.core3.util.*;
import com.aelitis.azureus.core.dht.netcoords.DHTNetworkPosition;
public class
TRTrackerServerTorrentImpl
implements TRTrackerServerTorrent
{
private static final LogIDs LOGID = LogIDs.TRACKER;
// no point in caching replies smaller than that below
public static final int MIN_CACHE_ENTRY_SIZE = 10;
public static final int MAX_UPLOAD_BYTES_PER_SEC = 3*1024*1024; //3MBs
public static final int MAX_DOWNLOAD_BYTES_PER_SEC = MAX_UPLOAD_BYTES_PER_SEC;
public static final boolean USE_LIGHTWEIGHT_SEEDS = true;
public static final int MAX_IP_OVERRIDE_PEERS = 64;
public static final byte COMPACT_MODE_NONE = 0;
public static final byte COMPACT_MODE_NORMAL = 1;
public static final byte COMPACT_MODE_AZ = 2;
public static final byte COMPACT_MODE_AZ_2 = 3;
private static final int QUEUED_PEERS_MAX_SWARM_SIZE = 32;
private static final int QUEUED_PEERS_MAX = 32;
private static final int QUEUED_PEERS_ADD_MAX = 3;
private TRTrackerServerImpl server;
private HashWrapper hash;
private Map peer_map = new HashMap();
private Map peer_reuse_map = new HashMap();
private List peer_list = new ArrayList();
private int peer_list_hole_count;
private boolean peer_list_compaction_suspended;
private List biased_peers = null;
private Map lightweight_seed_map = new HashMap();
private int seed_count;
private int removed_count;
private int ip_override_count;
private int bad_NAT_count; // calculated periodically
private Random random = new Random( SystemTime.getCurrentTime());
private long last_scrape_calc_time;
private Map last_scrape;
private LinkedHashMap announce_cache = new LinkedHashMap();
private TRTrackerServerTorrentStatsImpl stats;
private List listeners = new ArrayList();
private List peer_listeners;
private boolean deleted;
private boolean enabled;
private boolean map_size_diff_reported;
private boolean ip_override_limit_exceeded_reported;
private byte duplicate_peer_checker_index = 0;
private byte[] duplicate_peer_checker = new byte[0];
private URL[] redirects;
private boolean caching_enabled = true;
private LinkedList queued_peers;
protected AEMonitor this_mon = new AEMonitor( "TRTrackerServerTorrent" );
public
TRTrackerServerTorrentImpl(
TRTrackerServerImpl _server,
HashWrapper _hash,
boolean _enabled )
{
server = _server;
hash = _hash;
enabled = _enabled;
stats = new TRTrackerServerTorrentStatsImpl( this );
}
public void
setEnabled(
boolean _enabled )
{
enabled = _enabled;
}
public boolean
isEnabled()
{
return( enabled );
}
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 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 )
throws TRTrackerServerException
{
if ( !enabled ){
throw( new TRTrackerServerException( "Torrent temporarily disabled" ));
}
// we can safely resolve the client_ip_address here as it is either already resolved or that of
// the tracker. We need it resolved so we canonically store peers, otherwise we can get two
// entries for a dns name and the corresponding ip
if ( !HostNameToIPResolver.isNonDNSName( ip_address )){
try{
ip_address = HostNameToIPResolver.syncResolve( ip_address ).getHostAddress();
}catch( UnknownHostException e ){
Debug.printStackTrace( e );
}
}
TRTrackerServerException deferred_failure = null;
try{
this_mon.enter();
handleRedirects( url_parameters, ip_address, false );
// System.out.println( "TRTrackerServerTorrent: peerContact, ip = " + ip_address );
int event_type = TRTrackerServerTorrentPeerListener.ET_UPDATED;
if ( event != null && event.length() > 2){
char c = event.charAt(2);
if ( c == 'm' ){ // "coMpleted"
event_type = TRTrackerServerTorrentPeerListener.ET_COMPLETE;
}else if ( c == 'o' ){ // "stOpped"
event_type = TRTrackerServerTorrentPeerListener.ET_STOPPED;
}else{
event_type = TRTrackerServerTorrentPeerListener.ET_STARTED;
}
}
long now = SystemTime.getCurrentTime();
int tracker_key_hash_code = tracker_key==null?0:tracker_key.hashCode();
TRTrackerServerPeerImpl peer = (TRTrackerServerPeerImpl)peer_map.get( peer_id );
boolean new_peer = false;
boolean peer_already_removed = false;
boolean already_completed = false;
long last_contact_time = 0;
long ul_diff = 0;
long dl_diff = 0;
long le_diff = 0;
byte[] ip_address_bytes = ip_address.getBytes( Constants.BYTE_ENCODING );
if ( peer == null ){
String reuse_key = new String( ip_address_bytes, Constants.BYTE_ENCODING ) + ":" + tcp_port;
byte last_NAT_status = loopback?TRTrackerServerPeer.NAT_CHECK_OK:TRTrackerServerPeer.NAT_CHECK_UNKNOWN;
new_peer = true;
// check to see if this peer already has an entry against this torrent
// and if so delete it (assumption is that the client has quit and
// restarted with new peer id
//System.out.println( "new peer" );
TRTrackerServerPeerImpl old_peer = (TRTrackerServerPeerImpl)peer_reuse_map.get( reuse_key );
if ( old_peer != null ){
// don't allow an ip_override to grab a non-override entry as this is a way for
// a malicious client to remove, say, a seed (simply send in a "stopped" command
// with override set to the seed you want to remove)
if ( ip_override && !old_peer.isIPOverride()){
throw( new TRTrackerServerException( "IP Override denied" ));
}
last_contact_time = old_peer.getLastContactTime();
already_completed = old_peer.getDownloadCompleted();
removePeer( old_peer, TRTrackerServerTorrentPeerListener.ET_REPLACED, null );
lightweight_seed_map.remove( old_peer.getPeerId());
}else{
lightweightSeed lws = (lightweightSeed)lightweight_seed_map.remove( peer_id );
if ( lws != null ){
last_contact_time = lws.getLastContactTime();
ul_diff = uploaded - lws.getUploaded();
if ( ul_diff < 0 ){
ul_diff = 0;
}
last_NAT_status = lws.getNATStatus();
}else{
last_contact_time = now;
}
}
if ( event_type != TRTrackerServerTorrentPeerListener.ET_STOPPED ){
if ( ip_override && ip_override_count >= MAX_IP_OVERRIDE_PEERS && !loopback ){
// bail out - the peer will still get an announce response but we don't
// want too many override peers on a torrent as these can be spoofed
// to cause trouble
if ( !ip_override_limit_exceeded_reported ){
ip_override_limit_exceeded_reported = true;
Debug.out( "Too many ip-override peers for " + ByteFormatter.encodeString( hash.getBytes()));
}
return( null );
}
peer = new TRTrackerServerPeerImpl(
peer_id,
tracker_key_hash_code,
ip_address_bytes,
ip_override,
tcp_port,
udp_port,
http_port,
crypto_level,
az_ver,
last_contact_time,
already_completed,
last_NAT_status,
up_speed,
network_position );
if ( ip_override ){
ip_override_count++;
}
peer_map.put( peer_id, peer );
peer_list.add( peer );
peer_reuse_map.put( reuse_key, peer );
Set b = server.getBiasedPeers();
if ( b != null && b.contains( peer.getIPRaw())){
peer.setBiased( true );
if ( biased_peers == null ){
biased_peers = new ArrayList();
}
biased_peers.add( peer );
}
if ( queued_peers != null ){
if ( peer_map.size() > QUEUED_PEERS_MAX_SWARM_SIZE ){
queued_peers = null;
}else{
// peer has become active, remove the queued peer info
Iterator it = queued_peers.iterator();
while( it.hasNext()){
QueuedPeer qp = (QueuedPeer)it.next();
if ( qp.sameAs( peer )){
it.remove();
break;
}
}
}
}
}
}else{
int existing_tracker_key_hash_code = peer.getKeyHashCode();
// System.out.println( "tracker_key:" + existing_tracker_key + "/" + tracker_key );
if ( existing_tracker_key_hash_code != tracker_key_hash_code ){
throw( new TRTrackerServerException( "Unauthorised: key mismatch "));
}
// prevent an ip_override peer from affecting a non-override entry
if ( ip_override && !peer.isIPOverride()){
throw( new TRTrackerServerException( "IP Override denied" ));
}
already_completed = peer.getDownloadCompleted();
last_contact_time = peer.getLastContactTime();
if ( event_type == TRTrackerServerTorrentPeerListener.ET_STOPPED ){
removePeer( peer, event_type, url_parameters );
peer_already_removed = true;
}else{
// IP may have changed - update if required
// it is possible for two az clients to have the same peer id. Unlikely but possible
// or indeed some hacked versions could do it on purpose. If this is the case then all we
// will see here is address/port changes as each peer announces
byte[] old_ip = peer.getIPAsRead();
int old_port = peer.getTCPPort();
if ( peer.update( ip_address_bytes, tcp_port, udp_port, http_port, crypto_level, az_ver, up_speed, network_position )){
// same peer id so same port
String old_key = new String( old_ip, Constants.BYTE_ENCODING ) + ":" + old_port;
String new_key = new String( ip_address_bytes, Constants.BYTE_ENCODING ) + ":" + tcp_port;
// it is possible, on address change, that the target address already exists and is
// (was) being used by another peer. Given that this peer has taken over its address
// the assumption is that the other peer has also had an address change and has yet
// to report it. The only action here is to delete the other peer
TRTrackerServerPeerImpl old_peer = (TRTrackerServerPeerImpl)peer_reuse_map.get( new_key );
if ( old_peer != null ){
removePeer( old_peer, TRTrackerServerTorrentPeerListener.ET_REPLACED, null );
}
// now swap the keys
if ( peer_reuse_map.remove( old_key ) == null ){
Debug.out( "TRTrackerServerTorrent: IP address change: '" + old_key + "' -> '" + new_key + "': old key not found" );
}
peer_reuse_map.put( new_key, peer );
}
}
}
// a null peer here signifies a new peer whose first state was "stopped"
long new_timeout = now + ( interval_requested * 1000 * TRTrackerServerImpl.CLIENT_TIMEOUT_MULTIPLIER );
if ( peer != null ){
// if the peer has already been removed above then it will have reported the
// event already
if ( !peer_already_removed ){
try{
peerEvent( peer, event_type, url_parameters );
}catch( TRTrackerServerException e ){
deferred_failure = e;
}
}
peer.setTimeout( now, new_timeout );
// if this is the first time we've heard from this peer then we don't want to
// use existing ul/dl value diffs as they will have been reported previously
// (either the client's changed peer id by stop/start (in which case the values
// should be 0 anyway as its a per-session total), or the tracker's been
// stopped and started).
if ( !new_peer ){
ul_diff = uploaded - peer.getUploaded();
dl_diff = downloaded - peer.getDownloaded();
}
// simple rate control
long elapsed_time = now - last_contact_time;
if ( elapsed_time == 0 ){
elapsed_time = SystemTime.TIME_GRANULARITY_MILLIS;
}
long ul_rate = (ul_diff*1000)/elapsed_time; // bytes per second
long dl_rate = (dl_diff*1000)/elapsed_time;
if ( ul_rate > MAX_UPLOAD_BYTES_PER_SEC ){
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, "TRTrackerPeer: peer "
+ peer.getIPRaw() + "/"
+ new String(peer.getPeerId().getHash())
+ " reported an upload rate of " + ul_rate / 1024
+ " KiB/s per second"));
ul_diff = 0;
}
if ( dl_rate > MAX_DOWNLOAD_BYTES_PER_SEC ){
if (Logger.isEnabled())
Logger.log(new LogEvent(LOGID, "TRTrackerPeer: peer "
+ peer.getIPRaw() + "/"
+ new String(peer.getPeerId().getHash())
+ " reported a download rate of " + dl_rate / 1024
+ " KiB/s per second"));
dl_diff = 0;
}
// when the peer is removed its "left" amount will dealt with
le_diff = (event_type==TRTrackerServerTorrentPeerListener.ET_STOPPED)?0:(left - peer.getAmountLeft());
boolean was_seed = new_peer?false:peer.isSeed();
peer.setStats( uploaded, downloaded, left );
boolean is_seed = peer.isSeed();
if (!(event_type == TRTrackerServerTorrentPeerListener.ET_STOPPED || was_seed || !is_seed )){
seed_count++;
}
}
stats.addAnnounce( ul_diff, dl_diff, le_diff, peer != null && peer.isBiased());
if ( event_type==TRTrackerServerTorrentPeerListener.ET_COMPLETE && !already_completed ){
peer.setDownloadCompleted();
stats.addCompleted();
}
if ( peer != null && peer.isSeed()){
int seed_limit = TRTrackerServerImpl.getSeedLimit();
if ( seed_limit != 0 && seed_count > seed_limit && !loopback ){
if ( !peer_already_removed ){
removePeer( peer, TRTrackerServerTorrentPeerListener.ET_TOO_MANY_PEERS, null );
}
// this is picked up by AZ client removal rules and causes the torrent to
// be removed
throw( new TRTrackerServerException( "too many seeds" ));
}
int seed_retention = TRTrackerServerImpl.getMaxSeedRetention();
if ( seed_retention != 0 && seed_count > seed_retention ){
// remove 5% of the seeds
int to_remove = (seed_retention/20)+1;
try{
peer_list_compaction_suspended = true;
// remove bad NAT ones in preference to others
for (int bad_nat_loop=TRTrackerServerNATChecker.getSingleton().isEnabled()?0:1;bad_nat_loop<2;bad_nat_loop++){
for (int i=0;i<peer_list.size();i++){
TRTrackerServerPeerImpl this_peer = (TRTrackerServerPeerImpl)peer_list.get(i);
if ( this_peer != null && this_peer.isSeed()){
boolean bad_nat = this_peer.isNATStatusBad();
if ( ( bad_nat_loop == 0 && bad_nat ) ||
( bad_nat_loop == 1 )){
if ( USE_LIGHTWEIGHT_SEEDS ){
lightweight_seed_map.put(
this_peer.getPeerId(),
new lightweightSeed(
now,
new_timeout,
this_peer.getUploaded(),
this_peer.getNATStatus()));
}
removePeer( this_peer, i, TRTrackerServerTorrentPeerListener.ET_TOO_MANY_PEERS, null );
if ( --to_remove == 0 ){
break;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -