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 | } |
Copyright © 2010 Geeknet, Inc. All rights reserved. Terms of Use