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