There are no available options for this view.

Parent Directory Parent Directory | Revision Log Revision Log

Revision 1.84 - (show annotations) (download) (as text)
Thu Jul 2 16:33:53 2009 UTC (5 years, 10 months ago) by parg
Branch: MAIN
CVS Tags: RELEASE-4_2_0_8a, RELEASE-4_3_0_0a, RELEASE-4_3_1_0, RELEASE-4_3_1_2, RELEASE-4_3_0_4, RELEASE-4_3_0_6, RELEASE-4_3_0_0, RELEASE-4_2_0_6a, RELEASE-4_3_0_2, RELEASE-4_2_0_8, RELEASE-4_2_0_4, RELEASE-4_2_0_6, HEAD
Branch point for: BRANCH_4314
Changes since 1.83: +1 -0 lines
File MIME type: text/x-java
fixed up cache logic decode
1 /*
2 * Created on Nov 12, 2003
3 * Created by Alon Rohter
4 * Copyright (C) 2003-2004 Alon Rohter, All Rights Reserved.
5 * Copyright (C) 2003, 2004, 2005, 2006 Aelitis, All Rights Reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 * AELITIS, SAS au capital de 46,603.30 euros
20 * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
21 *
22 */
23 package com.aelitis.azureus.core.peermanager.utils;
24
25 import org.gudy.azureus2.core3.internat.MessageText;
26 import org.gudy.azureus2.core3.util.AEDiagnostics;
27 import org.gudy.azureus2.core3.util.AEDiagnosticsLogger;
28 import org.gudy.azureus2.core3.util.ByteFormatter;
29 import org.gudy.azureus2.core3.util.Constants;
30 import org.gudy.azureus2.core3.util.Debug;
31
32 import java.io.*;
33 import java.util.HashSet;
34
35
36 /**
37 * Used for identifying clients by their peerID.
38 */
39 public class BTPeerIDByteDecoder {
40
41 final static boolean LOG_UNKNOWN;
42
43 static {
44 String prop = System.getProperty("log.unknown.peerids");
45 LOG_UNKNOWN = prop == null || prop.equals("1");
46 }
47
48 private static String logUnknownClient0(byte[] peer_id_bytes) throws IOException {
49 String text = new String(peer_id_bytes, 0, 20, Constants.BYTE_ENCODING);
50 text = text.replace((char)12, (char)32);
51 text = text.replace((char)10, (char)32);
52
53 return "[" + text + "] " + ByteFormatter.encodeString(peer_id_bytes) + " ";
54 }
55
56 private static String asUTF8ByteString(String text) {
57 try {
58 byte[] utf_bytes = text.getBytes(Constants.DEFAULT_ENCODING);
59 return ByteFormatter.encodeString(utf_bytes);
60 }
61 catch (UnsupportedEncodingException uee) {return "";}
62 }
63
64 private static HashSet logged_discrepancies = new HashSet();
65 public static void logClientDiscrepancy(String peer_id_name, String handshake_name, String discrepancy, String protocol, byte[] peer_id) {
66 if (!client_logging_allowed) {return;}
67
68 // Generate the string used that we will log.
69 String line_to_log = discrepancy + " [" + protocol + "]: ";
70 line_to_log += "\"" + peer_id_name + "\" / \"" + handshake_name + "\" ";
71
72 // We'll encode the name in byte form to help us decode it.
73 line_to_log += "[" + asUTF8ByteString(handshake_name) + "]";
74
75 // Avoid logging the same combination of things again.
76 boolean log_to_debug_out = Constants.isCVSVersion();
77 if (log_to_debug_out || LOG_UNKNOWN) {
78 // If this text has been recorded before, then avoid doing it again.
79 if (!logged_discrepancies.add(line_to_log)) {return;}
80 }
81
82 // Add peer ID bytes.
83 if (peer_id != null) {
84 line_to_log += ", Peer ID: " + ByteFormatter.encodeString(peer_id);
85 }
86
87 // Enable this block for now - just until we get more feedback about
88 // problematic clients.
89 if (log_to_debug_out) {
90 Debug.outNoStack("Conflicting peer identification: " + line_to_log);
91 }
92
93 if (!LOG_UNKNOWN) {return;}
94 logClientDiscrepancyToFile(line_to_log);
95 }
96
97 private static AEDiagnosticsLogger logger = null;
98 private synchronized static void logClientDiscrepancyToFile(String line_to_log) {
99 if (logger == null) {logger = AEDiagnostics.getLogger("clientid");}
100 try {logger.log(line_to_log);}
101 catch (Throwable e) {Debug.printStackTrace(e);}
102 }
103
104 static boolean client_logging_allowed = true;
105
106 // I don't expect this to grow too big, and it won't grow if there's no logging going on.
107 private static HashSet logged_ids = new HashSet();
108 static void logUnknownClient(byte[] peer_id_bytes) {logUnknownClient(peer_id_bytes, true);}
109 static void logUnknownClient(byte[] peer_id_bytes, boolean to_debug_out) {
110
111 if (!client_logging_allowed) {return;}
112
113 // Avoid logging the same client ID multiple times.
114 boolean log_to_debug_out = to_debug_out && Constants.isCVSVersion();
115 if (log_to_debug_out || LOG_UNKNOWN) {
116 // If the ID has been recorded before, then avoid doing it again.
117 if (!logged_ids.add(makePeerIDReadableAndUsable(peer_id_bytes))) {return;}
118 }
119
120 // Enable this block for now - just until we get more feedback about
121 // unknown clients.
122 if (log_to_debug_out) {
123 Debug.outNoStack("Unable to decode peer correctly - peer ID bytes: " + makePeerIDReadableAndUsable(peer_id_bytes));
124 }
125
126 if (!LOG_UNKNOWN) {return;}
127 try {logClientDiscrepancyToFile(logUnknownClient0(peer_id_bytes));}
128 catch (Throwable t) {Debug.printStackTrace(t);}
129
130 }
131
132 static void logUnknownClient(String peer_id) {
133 try {logUnknownClient(peer_id.getBytes(Constants.BYTE_ENCODING));}
134 catch (UnsupportedEncodingException uee) {}
135 }
136
137 public static String decode0(byte[] peer_id_bytes) {
138
139 final String UNKNOWN = MessageText.getString("PeerSocket.unknown");
140 final String FAKE = MessageText.getString("PeerSocket.fake_client");
141 final String BAD_PEER_ID = MessageText.getString("PeerSocket.bad_peer_id");
142
143 String peer_id = null;
144 try {peer_id = new String(peer_id_bytes, Constants.BYTE_ENCODING);}
145 catch (UnsupportedEncodingException uee) {return "";}
146
147 // We store the result here.
148 String client = null;
149
150 /**
151 * If the client reuses parts of the peer ID of other peers, then try to determine this
152 * first (before we misidentify the client).
153 */
154 if (BTPeerIDByteDecoderUtils.isPossibleSpoofClient(peer_id)) {
155 client = decodeBitSpiritClient(peer_id, peer_id_bytes);
156 if (client != null) {return client;}
157 client = decodeBitCometClient(peer_id, peer_id_bytes);
158 if (client != null) {return client;}
159 return "BitSpirit? (" + BAD_PEER_ID + ")";
160 }
161
162 /**
163 * See if the client uses Az style identification.
164 */
165 if (BTPeerIDByteDecoderUtils.isAzStyle(peer_id)) {
166 client = BTPeerIDByteDecoderDefinitions.getAzStyleClientName(peer_id);
167 if (client != null) {
168 String client_with_version = BTPeerIDByteDecoderDefinitions.getAzStyleClientVersion(client, peer_id);
169
170 /**
171 * Hack for fake ZipTorrent clients - there seems to be some clients
172 * which use the same identifier, but they aren't valid ZipTorrent clients.
173 */
174 if (client.startsWith("ZipTorrent") && peer_id.startsWith("bLAde", 8)) {
175 String client_name = (client_with_version == null) ? client : client_with_version;
176 return UNKNOWN + " [" + FAKE + ": " + client_name + "]";
177 }
178
179 /**
180 * BitTorrent 6.0 Beta currently misidentifies itself.
181 */
182 if ("\u00B5Torrent 6.0.0 Beta".equals(client_with_version)) {
183 return "Mainline 6.0 Beta";
184 }
185
186 /**
187 * If it's the rakshasa libtorrent, then it's probably rTorrent.
188 */
189 if (client.startsWith("libTorrent (Rakshasa)")) {
190 String client_name = (client_with_version == null) ? client : client_with_version;
191 return client_name + " / rTorrent*";
192 }
193
194 if (client_with_version != null) {return client_with_version;}
195
196 return client;
197 }
198
199 }
200
201 /**
202 * See if the client uses Shadow style identification.
203 */
204 if (BTPeerIDByteDecoderUtils.isShadowStyle(peer_id)) {
205 client = BTPeerIDByteDecoderDefinitions.getShadowStyleClientName(peer_id);
206 if (client != null) {
207 String client_ver = BTPeerIDByteDecoderUtils.getShadowStyleVersionNumber(peer_id);
208 if (client_ver != null) {return client + " " + client_ver;}
209 return client;
210 }
211 }
212
213 /**
214 * See if the client uses Mainline style identification.
215 */
216 client = BTPeerIDByteDecoderDefinitions.getMainlineStyleClientName(peer_id);
217 if (client != null) {
218 /**
219 * We haven't got a good way of detecting whether this is a Mainline style
220 * version of peer ID until we start decoding peer ID information. So for
221 * that reason, we wait until we get client version information here - if
222 * we don't manage to determine a version number, then we assume that it
223 * has been misidentified and carry on with it.
224 */
225 String client_ver = BTPeerIDByteDecoderUtils.getMainlineStyleVersionNumber(peer_id);
226
227 if (client_ver != null) {
228 String result = client + " " + client_ver;
229 return result;
230 }
231 }
232
233 /**
234 * Check for BitSpirit / BitComet (non possible spoof client mode).
235 */
236 client = decodeBitSpiritClient(peer_id, peer_id_bytes);
237 if (client != null) {return client;}
238 client = decodeBitCometClient(peer_id, peer_id_bytes);
239 if (client != null) {return client;}
240
241
242 /**
243 * See if the client identifies itself using a particular substring.
244 */
245 BTPeerIDByteDecoderDefinitions.ClientData client_data = BTPeerIDByteDecoderDefinitions.getSubstringStyleClient(peer_id);
246 if (client_data != null) {
247 client = client_data.client_name;
248 String client_with_version = BTPeerIDByteDecoderDefinitions.getSubstringStyleClientVersion(client_data, peer_id, peer_id_bytes);
249 if (client_with_version != null) {return client_with_version;}
250 return client;
251 }
252
253 client = identifyAwkwardClient(peer_id_bytes);
254 if (client != null) {return client;}
255 return null;
256 }
257
258 /**
259 * Decodes the given peerID, returning an identification string.
260 */
261 public static String
262 decode(byte[] peer_id)
263 {
264 if ( peer_id.length > 0 ){
265
266 try {
267 String client = decode0(peer_id);
268
269 if (client != null ){
270
271 return client;
272 }
273 }catch (Throwable e) {
274
275 Debug.out( "Failed to decode peer id " + ByteFormatter.encodeString(peer_id) + ": " + Debug.getNestedExceptionMessageAndStack( e ));
276 }
277
278 try {
279 String peer_id_as_string = new String(peer_id, Constants.BYTE_ENCODING);
280
281 boolean is_az_style = BTPeerIDByteDecoderUtils.isAzStyle(peer_id_as_string);
282
283 boolean is_shadow_style = BTPeerIDByteDecoderUtils.isShadowStyle(peer_id_as_string);
284
285 logUnknownClient(peer_id, !(is_az_style || is_shadow_style));
286
287 if (is_az_style) {
288 return BTPeerIDByteDecoderDefinitions.formatUnknownAzStyleClient(peer_id_as_string);
289 }
290 else if (is_shadow_style) {
291 return BTPeerIDByteDecoderDefinitions.formatUnknownShadowStyleClient(peer_id_as_string);
292 }
293 }catch( Throwable e ){
294
295 Debug.out( "Failed to decode peer id " + ByteFormatter.encodeString(peer_id) + ": " + Debug.getNestedExceptionMessageAndStack( e ));
296 }
297 }
298
299 String sPeerID = getPrintablePeerID(peer_id);
300 return MessageText.getString("PeerSocket.unknown") + " [" + sPeerID + "]";
301 }
302
303 public static String identifyAwkwardClient(byte[] peer_id) {
304
305 int iFirstNonZeroPos = 0;
306
307 iFirstNonZeroPos = 20;
308 for( int i=0; i < 20; i++ ) {
309 if( peer_id[i] != (byte)0 ) {
310 iFirstNonZeroPos = i;
311 break;
312 }
313 }
314
315 //Shareaza check
316 if( iFirstNonZeroPos == 0 ) {
317 boolean bShareaza = true;
318 for( int i=0; i < 16; i++ ) {
319 if( peer_id[i] == (byte)0 ) {
320 bShareaza = false;
321 break;
322 }
323 }
324 if( bShareaza ) {
325 for( int i=16; i < 20; i++ ) {
326 if( peer_id[i] != ( peer_id[i % 16] ^ peer_id[15 - (i % 16)] ) ) {
327 bShareaza = false;
328 break;
329 }
330 }
331 if( bShareaza ) return "Shareaza";
332 }
333 }
334
335 byte three = (byte)3;
336 if ((iFirstNonZeroPos == 9)
337 && (peer_id[9] == three)
338 && (peer_id[10] == three)
339 && (peer_id[11] == three)) {
340 return "Snark";
341 }
342
343 if ((iFirstNonZeroPos == 12) && (peer_id[12] == (byte)97) && (peer_id[13] == (byte)97)) {
344 return "Experimental 3.2.1b2";
345 }
346 if ((iFirstNonZeroPos == 12) && (peer_id[12] == (byte)0) && (peer_id[13] == (byte)0)) {
347 return "Experimental 3.1";
348 }
349 if (iFirstNonZeroPos == 12) return "Mainline";
350
351 return null;
352
353 }
354
355 private static String decodeBitSpiritClient(String peer_id, byte[] peer_id_bytes) {
356 if (!peer_id.substring(2, 4).equals("BS")) {return null;}
357 String version = BTPeerIDByteDecoderUtils.decodeNumericValueOfByte(peer_id_bytes[1]);
358 if ("0".equals(version)) {version = "1";}
359 return "BitSpirit v" + version;
360 }
361
362 private static String decodeBitCometClient(String peer_id, byte[] peer_id_bytes) {
363 String mod_name = null;
364 if (peer_id.startsWith("exbc")) {mod_name = "";}
365 else if (peer_id.startsWith("FUTB")) {mod_name = "(Solidox Mod) ";}
366 else if (peer_id.startsWith("xUTB")) {mod_name = "(Mod 2) ";}
367 else {return null;}
368
369 boolean is_bitlord = (peer_id.substring(6, 10).equals("LORD"));
370
371 /**
372 * Older versions of BitLord are of the form x.yy, whereas new versions (1 and onwards),
373 * are of the form x.y. BitComet is of the form x.yy.
374 */
375 String client_name = (is_bitlord) ? "BitLord " : "BitComet ";
376 String maj_version = BTPeerIDByteDecoderUtils.decodeNumericValueOfByte(peer_id_bytes[4]);
377 int min_version_length = (is_bitlord && !maj_version.equals("0")) ? 1 : 2;
378
379 return client_name + mod_name + maj_version + "." +
380 BTPeerIDByteDecoderUtils.decodeNumericValueOfByte(peer_id_bytes[5], min_version_length);
381 }
382
383
384 protected static String getPrintablePeerID(byte[] peer_id) {
385 return getPrintablePeerID(peer_id, '-');
386 }
387
388 protected static String getPrintablePeerID(byte[] peer_id, char fallback_char) {
389 String sPeerID = "";
390 byte[] peerID = new byte[ peer_id.length ];
391 System.arraycopy( peer_id, 0, peerID, 0, peer_id.length );
392
393 try {
394 for (int i = 0; i < peerID.length; i++) {
395 int b = (0xFF & peerID[i]);
396 if (b < 32 || b > 127)
397 peerID[i] = (byte)fallback_char;
398 }
399 sPeerID = new String(peerID, Constants.BYTE_ENCODING);
400 }
401 catch (UnsupportedEncodingException ignore) {}
402 catch (Throwable e) {}
403
404 return( sPeerID );
405 }
406
407 private static String makePeerIDReadableAndUsable(byte[] peer_id) {
408 boolean as_ascii = true;
409 for (int i=0; i<peer_id.length; i++) {
410 int b = 0xFF & peer_id[i];
411 if (b < 32 || b > 127 || b == 10 || b == 9 || b==13) {
412 as_ascii = false;
413 break;
414 }
415 }
416 if (as_ascii) {
417 try {return new String(peer_id, Constants.BYTE_ENCODING);}
418 catch (UnsupportedEncodingException uee) {return "";}
419 }
420 else {return ByteFormatter.encodeString(peer_id);}
421 }
422
423 static byte[] peerIDStringToBytes(String peer_id) throws Exception {
424 if (peer_id.length() > 40) {
425 peer_id = peer_id.replaceAll("[ ]", "");
426 }
427
428 byte[] byte_peer_id = null;
429 if (peer_id.length() == 40) {
430 byte_peer_id = ByteFormatter.decodeString(peer_id);
431 String readable_peer_id = makePeerIDReadableAndUsable(byte_peer_id);
432 if (!peer_id.equals(readable_peer_id)) {
433 throw new RuntimeException("Use alternative format for peer ID - from " + peer_id + " to " + readable_peer_id);
434 }
435 }
436 else if (peer_id.length() == 20) {
437 byte_peer_id = peer_id.getBytes(Constants.BYTE_ENCODING);
438 }
439 else {
440 throw new IllegalArgumentException(peer_id);
441 }
442 return byte_peer_id;
443 }
444
445 private static void assertDecode(String client_result, String peer_id) throws Exception {
446 assertDecode(client_result, peerIDStringToBytes(peer_id));
447 }
448
449 private static void assertDecode(String client_result, byte[] peer_id) throws Exception {
450 String peer_id_as_string = getPrintablePeerID(peer_id, '*');
451 System.out.println(" Peer ID: " + peer_id_as_string + " Client: " + client_result);
452
453 // Do not log any clients.
454 String decoded_result = decode(peer_id);
455 if (client_result.equals(decoded_result)) {return;}
456 throw new RuntimeException("assertion failure - expected \"" + client_result + "\", got \"" + decoded_result + "\": " + peer_id_as_string);
457 }
458
459 public static void main(String[] args) throws Exception {
460 client_logging_allowed = false;
461
462 final String FAKE = MessageText.getString("PeerSocket.fake_client");
463 final String UNKNOWN = MessageText.getString("PeerSocket.unknown");
464 final String BAD_PEER_ID = MessageText.getString("PeerSocket.bad_peer_id");
465
466 System.out.println("Testing AZ style clients...");
467 assertDecode("Ares 2.0.5.3", "-AG2053-Em6o1EmvwLtD");
468 assertDecode("Ares 1.6.7.0", "-AR1670-3Ql6wM3hgtCc");
469 assertDecode("Artemis 2.5.2.0", "-AT2520-vEEt0wO6v0cr");
470 assertDecode("Azureus 2.2.0.0", "-AZ2200-6wfG2wk6wWLc");
471 assertDecode("BT Next Evolution 1.0.9", "-NE1090002IKyMn4g7Ko");
472 assertDecode("BitRocket 0.3(32)", "-BR0332-!XVceSn(*KIl");
473 assertDecode("Mainline 6.0 Beta", "2D555436 3030422D A78DC290 C3F7BDE0 15EC3CC7");
474 assertDecode("FlashGet 1.80", "2D464730 31383075 F8005782 1359D64B B3DFD265");
475 assertDecode("GetRight 6.3", "-GR6300-13s3iFKmbArc");
476 assertDecode("Halite 0.2.9", "-HL0290-xUO*9ugvENUE");
477 assertDecode("KTorrent 1.1 RC1", "-KT11R1-693649213030");
478 assertDecode("KTorrent 3.0", "2D4B543330302D006A7139727958377731756A4B");
479 assertDecode("libTorrent (Rakshasa) 0.11.2 / rTorrent*", "2D6C74304232302D0D739B93E6BE21FEBB557B20");
480 assertDecode("libtorrent (Rasterbar) 0.13.0", "-LT0D00-eZ0PwaDDr-~v"); // The latest version at time of writing is v0.12, but I'll assume this is valid.
481 assertDecode("linkage 0.1.4", "-LK0140-ATIV~nbEQAMr");
482 assertDecode("LimeWire", "2D4C57303030312D31E0B3A0B46F7D4E954F4103");
483 assertDecode("Lphant 3.02", "2D4C5030 3330322D 00383336 35363935 37373030");
484 assertDecode("Shareaza 2.1.3.2", "2D535A323133322D000000000000000000000000");
485 assertDecode("SymTorrent 1.17", "-ST0117-01234567890!");
486 assertDecode("Transmission 0.6", "-TR0006-01234567890!");
487 assertDecode("Transmission 0.72 (Dev)", "-TR072Z-zihst5yvg22f");
488 assertDecode("Transmission 0.72", "-TR0072-8vd6hrmp04an");
489 assertDecode("TuoTu 2.1.0", "-TT210w-dq!nWf~Qcext");
490 assertDecode("\u00B5Torrent 1.7.0 Beta", "2D555431 3730422D 92844644 1DB0A094 A01C01E5");
491 assertDecode("\u54c7\u560E (Vagaa) 2.6.4.4", "2D5647323634342D4FD62CDA69E235717E3BB94B");
492 assertDecode("Wyzo 0.3.0.0", "-WY0300-6huHF5Pr7Vde");
493 assertDecode("CacheLogic 25.1-26", "-PC251Q-6huHF5Pr7Vde");
494 System.out.println();
495
496 // Shadow style clients.
497 System.out.println("Testing Shadow style clients...");
498 assertDecode("ABC", "A--------YMyoBPXYy2L"); // Seen this quite a bit - not sure that it is ABC, but I guess we should default to that...
499 assertDecode("ABC 2.6.9", "413236392D2D2D2D345077199FAEC4A673BECA01");
500 assertDecode("ABC 3.1", "A310--001v5Gysr4NxNK");
501 assertDecode("BitTornado 0.3.12", "T03C-----6tYolxhVUFS");
502 assertDecode("BitTornado 0.3.18", "T03I--008gY6iB6Aq27C");
503 assertDecode("BitTornado 0.3.9", "T0390----5uL5NvjBe2z");
504 assertDecode("Tribler 1", "R100--003hR6s07XWcov"); // Seen recently - is this really Tribler?
505 assertDecode("Tribler 3.7", "R37---003uApHy851-Pq");
506 System.out.println();
507
508 // Simple substring style clients.
509 System.out.println("Testing simple substring clients...");
510 assertDecode("Azureus 1", "417A7572 65757300 00000000 000000A0 76F0AEF7");
511 assertDecode("Azureus 2.0.3.2", "2D2D2D2D2D417A757265757354694E7A2A6454A7");
512 assertDecode("G3 Torrent", "2D473341 6E6F6E79 6D6F7573 70E8D9CB 30250AD4");
513 assertDecode("Hurricane Electric", "6172636C696768742E68652EA5860C157A5ADC35");
514 assertDecode("Pando", "Pando-6B511B691CAC2E"); // Seen recently, have they changed peer ID format?
515 assertDecode("\u00B5Torrent 1.7.0 RC", "2D55543137302D00AF8BC5ACCC4631481EB3EB60");
516 System.out.println();
517
518 // Version substring style clients.
519 System.out.println("Testing versioned substring clients...");
520 assertDecode("Bitlet 0.1", "4269744C657430319AEA4E02A09E318D70CCF47D");
521 assertDecode("BitsOnWheels", "-BOWP05-EPICNZOGQPHP"); // Seen in the wild - no idea what version that's meant to be - a pre-release?
522 assertDecode("Burst! 1.1.3", "Mbrst1-1-32e3c394b43");
523 assertDecode("Opera (Build 7685)", "OP7685f2c1495b1680bf");
524 assertDecode("Opera (Build 10063)", "O100634008270e29150a");
525 assertDecode("Rufus 0.6.9", "00455253 416E6F6E 796D6F75 7382BE42 75024AE3");
526 assertDecode("BitTorrent DNA 1.0", "444E413031303030DD01C9B2DA689E6E02803E91");
527 assertDecode("BTuga Revolution 2.1", "BTM21abcdefghijklmno");
528 assertDecode("AllPeers 0.70rc30", "4150302E3730726333302D3E3EB87B31F241DBFE"); // AP0.70rc30->>-{1-A--]"
529 assertDecode("External Webseed", "45787420EC7CC30033D7801FEEB713FBB0557AC4");
530 assertDecode("QVOD (Build 0054)", "QVOD00541234567890AB"); // Based on description on wiki.theory.org.
531 assertDecode("Top-BT 1.0.0", "TB100----abcdefghijk");
532 System.out.println();
533
534 // BitComet/Lord/Spirit
535 System.out.println("Testing BitComet/Lord/Spirit clients...");
536 assertDecode("BitComet 0.56", "6578626300387A4463102D6E9AD6723B339F35A9");
537 assertDecode("BitLord 0.56", "6578626300384C4F52443200048ECED57BD71028");
538 assertDecode("BitSpirit? (" + BAD_PEER_ID + ")", "4D342D302D322D2D6898D9D0CAF25E4555445030");
539 assertDecode("BitSpirit v2", "000242539B7ED3E058A8384AA748485454504254");
540 assertDecode("BitSpirit v3", "00034253 07248896 44C59530 8A5FF2CA 55445030");
541 System.out.println();
542
543 // Mainline style clients.
544 System.out.println("Testing new mainline style clients...");
545 assertDecode("Mainline 5.0.7", "M5-0-7--9aa757efd5be");
546 assertDecode("Amazon AWS S3", "S3-1-0-0--0123456789"); // Not currently decoded as mainline style...
547 System.out.println();
548
549 // Various specialised clients.
550 System.out.println("Testing various specialised clients...");
551 assertDecode("Mainline", "0000000000000000000000004C53441933104277");
552 assertDecode(UNKNOWN + " [" + FAKE + ": ZipTorrent 1.6.0.0]", "-ZT1600-bLAdeY9rdjbe");
553 System.out.println();
554
555 // Unknown clients - may be random bytes.
556 System.out.println("Testing unknown (random byte?) clients...");
557 assertDecode(UNKNOWN + " [--------1}-/---A---<]", "0000000000000000317DA32F831FF041A515FE3C");
558 assertDecode(UNKNOWN + " [------- -- ------@(]", "000000DF05020020100020200008000000004028");
559 assertDecode(UNKNOWN + " [-----------D-y-I--aO]", "0000000000000000F106CE44F179A2498FAC614F");
560 assertDecode(UNKNOWN + " [--c--_-5-\\----t-#---]", "E7F163BB0E5FCD35005C09A11BC274C42385A1A0");
561 System.out.println();
562
563 // Unknown AZ style clients.
564 System.out.println("Testing unknown AZ style clients...");
565 String unknown_az;
566 unknown_az = MessageText.getString("PeerSocket.unknown_az_style", new String[]{"BD", "0.3.0.0"});
567 assertDecode(unknown_az, "-BD0300-1SGiRZ8uWpWH");
568 unknown_az = MessageText.getString("PeerSocket.unknown_az_style", new String[]{"wF", "2.2.0.0"});
569 assertDecode(unknown_az, "2D7746323230302D9DFF296B56AFC2DF751C609C");
570 unknown_az = MessageText.getString("PeerSocket.unknown_az_style", new String[]{"X1", "0.0.6.4"});
571 assertDecode(unknown_az, "2D5831303036342D12FB8A5B954153A114267F1F");
572 unknown_az = MessageText.getString("PeerSocket.unknown_az_style", new String[]{"bF", "2q00"}); // I made this one up.
573 assertDecode(unknown_az, "2D6246327130302D9DFF296B56AFC2DF751C609C");
574 System.out.println();
575
576 // Unknown Shadow style clients.
577 System.out.println("Testing unknown Shadow style clients...");
578 String unknown_shadow;
579 unknown_shadow = MessageText.getString("PeerSocket.unknown_shadow_style", new String[]{"B", "1.2"});
580 assertDecode(unknown_shadow, "B12------xgTofhetSVQ");
581 System.out.println();
582
583 // TODO
584 //assertDecode("KTorrent 2.2", "-KT22B1-695754334315"); // We could use the B1 information...
585 //assertDecode("KTorrent 2.1.4", "-KT2140-584815613993"); // Currently shows as 2.1.
586 //assertDecode("", "C8F2D9CD3A90455354426578626300362D2D2D92"); // Looks like a BitLord client - ESTBexbc?
587 //assertDecode("", "303030302D2D0000005433585859684B59584C72"); // Seen in the wild, appears to be a modified version of Azureus 2.5.0.0 (that's what was in the AZMP handshake)?
588 //assertDecode("", "B5546F7272656E742F3330323520202020202020");
589
590 System.out.println("Done.");
591 }
592 }