PageRenderTime 65ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/AZ-SVN/azureus2/src/com/aelitis/azureus/core/peermanager/utils/ClientIdentifier.java

https://bitbucket.org/crunchiness/vuze
Java | 357 lines | 209 code | 63 blank | 85 comment | 82 complexity | a957260086aba630a0a9cf96e6bb818c MD5 | raw file
  1. package com.aelitis.azureus.core.peermanager.utils;
  2. import org.gudy.azureus2.core3.internat.MessageText;
  3. import org.gudy.azureus2.core3.util.ByteFormatter;
  4. public class ClientIdentifier {
  5. public static String identifyBTOnly(String peer_id_client, byte[] handshake_bytes) {
  6. // BitThief check.
  7. if (peer_id_client.equals("Mainline 4.4.0") && (handshake_bytes[7] & (byte)1) == 0) {
  8. return asDiscrepancy("BitThief*", peer_id_client, "fake_client");
  9. }
  10. // We do care if something is claiming to be Azureus when it isn't. If
  11. // it's a recent version of Azureus, but doesn't support advanced messaging, we
  12. // know it's a fake.
  13. if (!peer_id_client.startsWith("Azureus ")) {return peer_id_client;}
  14. // Older versions of Azureus won't have support, so discount these first.
  15. String version = peer_id_client.substring(8);
  16. if (version.startsWith("1") || version.startsWith("2.0") ||
  17. version.startsWith("2.1") || version.startsWith("2.2")) {
  18. return peer_id_client;
  19. }
  20. // Must be a fake.
  21. return asDiscrepancy(null, peer_id_client, "fake_client");
  22. }
  23. public static String identifyAZMP(String peer_id_client_name, String az_msg_client_name, String az_msg_client_version, byte[] peer_id) {
  24. /**
  25. * Hack for BitTyrant - the handshake resembles this:
  26. * Client: AzureusBitTyrant
  27. * ClientVersion: 2.5.0.0BitTyrant
  28. *
  29. * Yuck - let's format it so it resembles something pleasant.
  30. */
  31. if (az_msg_client_name.endsWith("BitTyrant")) {
  32. return "BitTyrant " + az_msg_client_version.replaceAll("BitTyrant", "") + " (Azureus Mod)";
  33. }
  34. String msg_client_name = az_msg_client_name + " " + az_msg_client_version;
  35. /**
  36. * Do both names seem to match?
  37. */
  38. if (msg_client_name.equals(peer_id_client_name)) {return msg_client_name;}
  39. /**
  40. * There may be some discrepancy - a different version number perhaps.
  41. * If the main client name still seems to be the same, then return the one
  42. * given to us in the AZ handshake.
  43. */
  44. String peer_id_client = peer_id_client_name.split(" ", 2)[0];
  45. String az_client_name = az_msg_client_name.split(" ", 2)[0];
  46. if (peer_id_client.equals(az_client_name)) {
  47. /**
  48. * If both are Azureus, the version numbers shouldn't differ. This is what
  49. * we should have - 15 characters both the same (sometimes beta version
  50. * is included in the version number but not in the peer ID, but we can deal
  51. * with that.
  52. * "Azureus a.b.c.d"
  53. */
  54. if (az_client_name.equals("Azureus") && peer_id_client.equals("Azureus")) {
  55. if (msg_client_name.length()<15 || peer_id_client_name.length() < 15 || !msg_client_name.substring(0, 15).equals(peer_id_client_name.substring(0, 15))) {
  56. return asDiscrepancy("Azureus (Hacked)", peer_id_client_name, msg_client_name, "fake_client", "AZMP", peer_id);
  57. }
  58. }
  59. return msg_client_name;
  60. }
  61. // Transmission and XTorrent.
  62. String res = checkForTransmissionBasedClients(msg_client_name, peer_id_client, peer_id_client_name, msg_client_name, peer_id, "AZMP");
  63. if (res != null) {return res;}
  64. // There is an inconsistency. Let's try figuring out what we can.
  65. String client_displayed_name = null;
  66. boolean is_peer_id_azureus = peer_id_client_name.startsWith("Azureus ");
  67. boolean is_msg_client_azureus = az_msg_client_name.equals("Azureus");
  68. boolean is_fake = false;
  69. boolean is_mismatch = true;
  70. boolean is_peer_id_unknown = peer_id_client_name.startsWith(MessageText.getString("PeerSocket.unknown"));
  71. if (is_peer_id_azureus) {
  72. // Shouldn't happen.
  73. if (is_msg_client_azureus) {
  74. throw new RuntimeException("logic error in getExtendedClientName - both clients are Azureus");
  75. }
  76. else {
  77. // We've got a peer ID that says Azureus, but it doesn't say Azureus in the handshake.
  78. // It's definitely fake.
  79. is_fake = true;
  80. // It might be XTorrent - it does use AZ2504 in the peer ID and "Transmission 0.7-svn"
  81. // in the handshake.
  82. if (msg_client_name.equals("Transmission 0.7-svn")) {client_displayed_name = "XTorrent";}
  83. }
  84. }
  85. else {
  86. if (is_msg_client_azureus) {is_fake = true;}
  87. else if (is_peer_id_unknown) {
  88. // Our peer ID decoding can't decode it, but the client identifies itself anyway.
  89. // In that case, we won't say that it is a mismatch, and we'll just use the name
  90. // provided to us.
  91. client_displayed_name = msg_client_name;
  92. is_mismatch = false;
  93. // Log it though.
  94. BTPeerIDByteDecoder.logClientDiscrepancy(peer_id_client_name, msg_client_name, "unknown_client", "AZMP", peer_id);
  95. }
  96. else {
  97. // We've got a general mismatch, we don't know what client it is - in most cases.
  98. // Ares Galaxy sometimes uses the same peer ID as Arctic Torrent, so allow it to be
  99. // overridden.
  100. if (msg_client_name.startsWith("Ares") && peer_id_client.equals("ArcticTorrent")) {
  101. return msg_client_name;
  102. }
  103. }
  104. }
  105. String discrepancy_type;
  106. if (is_fake) {discrepancy_type = "fake_client";}
  107. else if (is_mismatch) {discrepancy_type = "mismatch_id";}
  108. else {discrepancy_type = null;}
  109. if (discrepancy_type != null) {
  110. return asDiscrepancy(null, peer_id_client_name, msg_client_name, discrepancy_type, "AZMP", peer_id);
  111. }
  112. return client_displayed_name;
  113. }
  114. public static String identifyLTEP(String peer_id_name, String handshake_name, byte[] peer_id) {
  115. if (handshake_name == null) {return peer_id_name;}
  116. /**
  117. * Official BitTorrent clients should still be shown as Mainline.
  118. * This is to be consistent with previous Azureus behaviour.
  119. */
  120. String handshake_name_to_process = handshake_name;
  121. if (handshake_name.startsWith("BitTorrent ")) {
  122. handshake_name_to_process = handshake_name.replaceFirst("BitTorrent", "Mainline");
  123. }
  124. if (peer_id_name.startsWith("\u00B5Torrent")) {
  125. // 1.6.0 misidentifies itself as 1.5 in the handshake.
  126. if (peer_id_name.equals("\u00B5Torrent 1.6.0")) {
  127. return peer_id_name;
  128. }
  129. // Older �Torrent versions will not always use the appropriate character for the
  130. // first letter, so compensate here.
  131. if (!handshake_name.startsWith("\u00B5Torrent") && handshake_name.startsWith("Torrent", 1)) {
  132. handshake_name_to_process = "\u00B5" + handshake_name.substring(1);
  133. }
  134. // Some versions indicate they are the beta version in the peer ID, but not in the
  135. // handshake - we prefer to keep the beta identifier.
  136. if (peer_id_name.endsWith("Beta") && peer_id_name.startsWith(handshake_name_to_process)) {
  137. return peer_id_name;
  138. }
  139. }
  140. // Some Mainline 4.x versions identify themselves as �Torrent - according to alus,
  141. // this was a bug, so just identify as Mainline.
  142. if (peer_id_name.startsWith("Mainline 4.") && handshake_name.startsWith("Torrent", 1)) {
  143. return peer_id_name;
  144. }
  145. // Azureus should never be using LTEP when connected to another Azureus client!
  146. if (peer_id_name.startsWith("Azureus") && handshake_name.startsWith("Azureus")) {
  147. return asDiscrepancy(null, peer_id_name, handshake_name, "fake_client", "LTEP", peer_id);
  148. }
  149. // We allow a client to have a different version number than the one decoded from
  150. // the peer ID. Some clients separate version and client name using a forward slash,
  151. // so we split on that as well.
  152. String client_type_peer = peer_id_name.split(" ", 2)[0];
  153. String client_type_handshake = handshake_name_to_process.split(" ", 2)[0].split("/", 2)[0];
  154. // Transmission and XTorrent.
  155. String res = checkForTransmissionBasedClients(handshake_name_to_process, client_type_peer, peer_id_name, handshake_name, peer_id, "LTEP");
  156. if (res != null) {return res;}
  157. if (client_type_peer.toLowerCase().equals(client_type_handshake.toLowerCase())) {return handshake_name_to_process;}
  158. // Like we do with AZMP peers, allow the handshake to define the client even if we can't extract the
  159. // name from the peer ID, but log it so we can possibly identify it in future.
  160. if (peer_id_name.startsWith(MessageText.getString("PeerSocket.unknown"))) {
  161. BTPeerIDByteDecoder.logClientDiscrepancy(peer_id_name, handshake_name, "unknown_client", "LTEP", peer_id);
  162. return handshake_name_to_process;
  163. }
  164. /**
  165. * libtorrent is... unsurprisingly... a torrent library. Many clients use it, so cope with clients
  166. * which don't identify themselves through the peer ID, but *do* identify themselves through the
  167. * handshake.
  168. */
  169. if (peer_id_name.startsWith("libtorrent (Rasterbar)")) {
  170. if (handshake_name_to_process.toLowerCase().indexOf("libtorrent") == -1) {
  171. handshake_name_to_process += " (" + peer_id_name + ")";
  172. }
  173. return handshake_name_to_process;
  174. }
  175. /**
  176. * And some clients do things the other way round - they don't bother with the handshake name,
  177. * but do remember to change the peer ID name.
  178. */
  179. if (client_type_handshake.startsWith("libtorrent")) {
  180. // Peer ID doesn't mention libtorrent (just the client name) and the handshake name doesn't
  181. // mention the client name (just "libtorrent"), then combine them together.
  182. if (client_type_peer.toLowerCase().indexOf("libtorrent") == -1 && client_type_handshake.toLowerCase().indexOf(client_type_peer.toLowerCase()) == -1) {
  183. return peer_id_name + " (" + handshake_name_to_process + ")";
  184. }
  185. }
  186. // Can't determine what the client is.
  187. return asDiscrepancy(null, peer_id_name, handshake_name, "mismatch_id", "LTEP", peer_id);
  188. }
  189. private static String checkForTransmissionBasedClients(String handshake_name_to_process, String client_type_peer, String peer_id_name, String handshake_name, byte[] peer_id, String protocol) {
  190. // Bloody XTorrent.
  191. if (handshake_name_to_process.equals("Transmission 0.7-svn") && client_type_peer.equals("Azureus")) {
  192. return asDiscrepancy("XTorrent", peer_id_name, handshake_name, "fake_client", protocol, peer_id);
  193. }
  194. // Bloody XTorrent!
  195. if (handshake_name_to_process.startsWith("Transmission") && client_type_peer.startsWith("XTorrent")) {
  196. return asDiscrepancy(client_type_peer, handshake_name_to_process, "fake_client");
  197. }
  198. // Transmission 0.96 still uses 0.95 in the LT handshake, so cope with that and just display
  199. // 0.96.
  200. if (peer_id_name.equals("Transmission 0.96") && handshake_name.equals("Transmission 0.95")) {
  201. return peer_id_name;
  202. }
  203. return null;
  204. }
  205. private static String asDiscrepancy(String client_name, String peer_id_name, String handshake_name, String discrepancy_type, String protocol_type, byte[] peer_id) {
  206. if (client_name == null) {
  207. BTPeerIDByteDecoder.logClientDiscrepancy(peer_id_name, handshake_name, discrepancy_type, protocol_type, peer_id);
  208. }
  209. // Use this form as it is shorter.
  210. if (peer_id_name.equals(handshake_name)) {return asDiscrepancy(client_name, peer_id_name, discrepancy_type);}
  211. return asDiscrepancy(client_name, peer_id_name + "\" / \"" + handshake_name, discrepancy_type);
  212. }
  213. private static String asDiscrepancy(String real_client, String dodgy_client, String discrepancy_type) {
  214. if (real_client == null) {
  215. real_client = MessageText.getString("PeerSocket.unknown");
  216. }
  217. return real_client + " [" +
  218. MessageText.getString("PeerSocket." + discrepancy_type) + ": \"" + dodgy_client + "\"]";
  219. }
  220. private static int test_count = 1;
  221. private static void assertDecode(String client_name, String peer_id, String handshake_name, String handshake_version, byte[] handshake_reserved, String type) throws Exception {
  222. byte[] byte_peer_id = BTPeerIDByteDecoder.peerIDStringToBytes(peer_id);
  223. String peer_id_client = BTPeerIDByteDecoder.decode(byte_peer_id);
  224. String decoded_client;
  225. if (type.equals("AZMP")) {decoded_client = identifyAZMP(peer_id_client, handshake_name, handshake_version, byte_peer_id);}
  226. else if (type.equals("LTEP")) {decoded_client = identifyLTEP(peer_id_client, handshake_name, byte_peer_id);}
  227. else if (type.equals("BT")) {decoded_client = identifyBTOnly(peer_id_client, handshake_reserved);}
  228. else {throw new RuntimeException("invalid extension type: " + type);}
  229. boolean passed = client_name.equals(decoded_client);
  230. System.out.println(" Test " + test_count++ + ": \"" + client_name + "\" - " + (passed ? "PASSED" : "FAILED"));
  231. if (!passed) {
  232. throw new Exception("\n" +
  233. "Decoded : " + decoded_client + "\n" +
  234. "Peer ID name : " + peer_id_client + "\n" +
  235. "Extended name: " + handshake_name + "\n");
  236. //throw new Exception("Client name decoded - " + decoded_client);
  237. }
  238. }
  239. private static void assertDecodeAZMP(String client_name, String peer_id, String handshake_name, String handshake_version) throws Exception {
  240. assertDecode(client_name, peer_id, handshake_name, handshake_version, null, "AZMP");
  241. }
  242. private static void assertDecodeLTEP(String client_name, String peer_id, String handshake_name) throws Exception {
  243. assertDecode(client_name, peer_id, handshake_name, null, null, "LTEP");
  244. }
  245. private static void assertDecodeExtProtocol(String client_name, String peer_id, String handshake_name, String handshake_version) throws Exception {
  246. assertDecodeAZMP(client_name, peer_id, handshake_name, handshake_version);
  247. assertDecodeLTEP(client_name, peer_id, handshake_name + " " + handshake_version);
  248. }
  249. private static void assertDecodeBT(String client_name, String peer_id, String handshake_reserved) throws Exception {
  250. if (handshake_reserved == null) {handshake_reserved = "0000000000000000";}
  251. handshake_reserved = handshake_reserved.replaceAll("[ ]", "");
  252. byte[] handshake_reserved_bytes = ByteFormatter.decodeString(handshake_reserved);
  253. if (handshake_reserved_bytes.length != 8) {throw new RuntimeException("invalid handshake reserved bytes");}
  254. assertDecode(client_name, peer_id, null, null, handshake_reserved_bytes, "BT");
  255. }
  256. public static void main(String[] args) throws Exception {
  257. System.setProperty("transitory.startup", "1");
  258. BTPeerIDByteDecoder.client_logging_allowed = false;
  259. System.out.println("Testing simple BT clients:");
  260. assertDecodeBT("BitThief* [FAKE: \"Mainline 4.4.0\"]", "M4-4-0--9aa757efd5be", "0000000000000000");
  261. assertDecodeBT("Mainline 4.4.0", "M4-4-0--9aa757efd5be", "0000000000000001");
  262. assertDecodeBT("Unknown [FAKE: \"Azureus 3.0.3.4\"]", "-AZ3034-6wfG2wk6wWLc", "0000000000000000");
  263. System.out.println("");
  264. System.out.println("Testing AZMP clients:");
  265. assertDecodeAZMP("Azureus 3.0.4.2", "-AZ3042-6ozMq5q6Q3NX", "Azureus", "3.0.4.2");
  266. assertDecodeAZMP("Azureus 3.0.4.3_B02", "-AZ3043-6ozMq5q6Q3NX", "Azureus", "3.0.4.3_B02");
  267. assertDecodeAZMP("BitTyrant 2.5.0.0 (Azureus Mod)", "AZ2500BTeyuzyabAfo6U", "AzureusBitTyrant", "2.5.0.0BitTyrant");
  268. //assertDecodeAZMP("", "-BS5820-oy4La2MWGEFj", "Bearshare Premium P2P", "5.8.2.0");
  269. //assertDecodeAZMP("", "-AR6360-6oZyyMWoOOBe", "Imesh Turbo", "6.3.6.0");
  270. assertDecodeAZMP("Azureus (Hacked) [FAKE: \"Azureus 2.4.0.2\" / \"Azureus 2.3.0.6\"]", "2D415A32 3430322D 2E414794 2C57D644 4989CA58", "Azureus", "2.3.0.6");
  271. //assertDecodeAZMP("", "-AG2083-s1hiF8vGAAg0", "Ares", "2.0.8.3029");
  272. //assertDecodeAZMP("", "-AG3003-lEl2Mm4NEO4n", "Ares Destiny", "3.0.0.3805");
  273. System.out.println("");
  274. System.out.println("Testing LTEP clients:");
  275. assertDecodeLTEP("\u00B5Torrent 1.7.6", "2D555431 3736302D B39EC7AD F6B94610 AA4ACD4A", "\u00B5Torrent 1.7.6");
  276. assertDecodeLTEP("\u00B5Torrent 1.6.1", "2D5554313631302DEA818D43F5E5EC3D67BF8D67", "\uFDFFTorrent 1.6.1");
  277. assertDecodeLTEP("Unknown [FAKE: \"Azureus 3.0.4.2\"]", "-AZ3042-6ozMq5q6Q3NX", "Azureus 3.0.4.2");
  278. assertDecodeLTEP("Mainline 6.0", "4D362D30 2D302D2D 8B92860D 05055DF5 B01C2D94", "BitTorrent 6.0");
  279. //assertDecodeLTEP("libTorrent 0.11.9", "2D6C7430 4239302D 11F3EB39 5D44EEFD CEA07E79", "libTorrent 0.11.9");
  280. assertDecodeLTEP("\u00B5Torrent 1.8.0 Beta", "2D555431 3830422D E69C9942 D1A5A6C2 0BE2E4BD", "\u00B5Torrent 1.8");
  281. assertDecodeLTEP("Miro 1.1.0.0 (libtorrent/0.13.0.0)", "-MR1100-00HS~T7*65rm", "libtorrent/0.13.0.0");
  282. assertDecodeLTEP("linkage/0.1.4 libtorrent/0.12.0.0", "-LK0140-ATIV~nbEQAMr", "linkage/0.1.4 libtorrent/0.12.0.0");
  283. assertDecodeLTEP("KTorrent 2.2.2", "-KT2210-347143496631", "KTorrent 2.2.2");
  284. //assertDecodeLTEP("", "B5546F72 72656E74 2F333037 36202020 20202020", "\uFDFFTorrent/3.0.7.6");
  285. assertDecodeLTEP("Transmission 0.96", "-TR0960-6ep6svaa61r4", "Transmission 0.95");
  286. assertDecodeLTEP("Opera 9.50", "O100634008270e29150a", "Opera 9.50");
  287. System.out.println("");
  288. System.out.println("Testing common clients:");
  289. //assertDecodeExtProtocol("", "-XX1150-dv220cotgj4d", "Transmission", "0.72Z");
  290. assertDecodeExtProtocol("XTorrent [FAKE: \"Azureus 2.5.0.4\" / \"Transmission 0.7-svn\"]", "-AZ2504-192gwethivju", "Transmission", "0.7-svn");
  291. System.out.println("");
  292. System.out.println("Done.");
  293. }
  294. }