00001
00002
00003
00004
00005
00006
00007
00008 #include <cctype>
00009 #include <algorithm>
00010
00011 #ifdef _MSC_VER
00012 #pragma warning(push, 1)
00013 #endif
00014
00015 #include <boost/optional.hpp>
00016
00017 #ifdef _MSC_VER
00018 #pragma warning(pop)
00019 #endif
00020
00021 #include "libtorrent/identify_client.hpp"
00022 #include "libtorrent/fingerprint.hpp"
00023
00024 namespace
00025 {
00026
00027 using namespace libtorrent;
00028
00029 int decode_digit(char c)
00030 {
00031 if (std::isdigit(c)) return c - '0';
00032 return unsigned(c) - 'A' + 10;
00033 }
00034
00035
00036
00037
00038
00039 boost::optional<fingerprint> parse_az_style(const peer_id& id)
00040 {
00041 fingerprint ret("..", 0, 0, 0, 0);
00042
00043 if (id[0] != '-' || !std::isprint(id[1]) || (id[2] < '0')
00044 || (id[3] < '0') || (id[4] < '0')
00045 || (id[5] < '0') || (id[6] < '0')
00046 || id[7] != '-')
00047 return boost::optional<fingerprint>();
00048
00049 ret.name[0] = id[1];
00050 ret.name[1] = id[2];
00051 ret.major_version = decode_digit(id[3]);
00052 ret.minor_version = decode_digit(id[4]);
00053 ret.revision_version = decode_digit(id[5]);
00054 ret.tag_version = decode_digit(id[6]);
00055
00056 return boost::optional<fingerprint>(ret);
00057 }
00058
00059
00060
00061 boost::optional<fingerprint> parse_shadow_style(const peer_id& id)
00062 {
00063 fingerprint ret("..", 0, 0, 0, 0);
00064
00065 if (!std::isalnum(id[0]))
00066 return boost::optional<fingerprint>();
00067
00068 if (std::equal(id.begin()+4, id.begin()+6, "--"))
00069 {
00070 if ((id[1] < '0') || (id[2] < '0')
00071 || (id[3] < '0'))
00072 return boost::optional<fingerprint>();
00073 ret.major_version = decode_digit(id[1]);
00074 ret.minor_version = decode_digit(id[2]);
00075 ret.revision_version = decode_digit(id[3]);
00076 }
00077 else
00078 {
00079 if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127)
00080 return boost::optional<fingerprint>();
00081 ret.major_version = id[1];
00082 ret.minor_version = id[2];
00083 ret.revision_version = id[3];
00084 }
00085
00086 ret.name[0] = id[0];
00087 ret.name[1] = 0;
00088
00089 ret.tag_version = 0;
00090 return boost::optional<fingerprint>(ret);
00091 }
00092
00093
00094
00095 boost::optional<fingerprint> parse_mainline_style(const peer_id& id)
00096 {
00097 char ids[21];
00098 std::copy(id.begin(), id.end(), ids);
00099 ids[20] = 0;
00100 fingerprint ret("..", 0, 0, 0, 0);
00101 ret.name[1] = 0;
00102 ret.tag_version = 0;
00103 if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version
00104 , &ret.revision_version) != 4
00105 || !std::isprint(ret.name[0]))
00106 return boost::optional<fingerprint>();
00107
00108 return boost::optional<fingerprint>(ret);
00109 }
00110
00111 typedef std::pair<char const*, char const*> map_entry;
00112
00113
00114
00115 map_entry name_map[] =
00116 {
00117 map_entry("A", "ABC")
00118 , map_entry("AR", "Arctic Torrent")
00119 , map_entry("AX", "BitPump")
00120 , map_entry("AZ", "Azureus")
00121 , map_entry("BB", "BitBuddy")
00122 , map_entry("BC", "BitComet")
00123 , map_entry("BF", "Bitflu")
00124 , map_entry("BG", "btgdaemon")
00125 , map_entry("BS", "BTSlave")
00126 , map_entry("BX", "BittorrentX")
00127 , map_entry("CD", "Enhanced CTorrent")
00128 , map_entry("CT", "CTorrent")
00129 , map_entry("DE", "Deluge")
00130 , map_entry("ES", "electric sheep")
00131 , map_entry("HL", "Halite")
00132 , map_entry("KT", "KTorrent")
00133 , map_entry("LK", "Linkage")
00134 , map_entry("LP", "lphant")
00135 , map_entry("LT", "libtorrent")
00136 , map_entry("M", "Mainline")
00137 , map_entry("ML", "MLDonkey")
00138 , map_entry("MO", "Mono Torrent")
00139 , map_entry("MP", "MooPolice")
00140 , map_entry("MT", "Moonlight Torrent")
00141 , map_entry("O", "Osprey Permaseed")
00142 , map_entry("QT", "Qt 4")
00143 , map_entry("R", "Tribler")
00144 , map_entry("S", "Shadow")
00145 , map_entry("SB", "Swiftbit")
00146 , map_entry("SN", "ShareNet")
00147 , map_entry("SS", "SwarmScope")
00148 , map_entry("SZ", "Shareaza")
00149 , map_entry("T", "BitTornado")
00150 , map_entry("TN", "Torrent.NET")
00151 , map_entry("TR", "Transmission")
00152 , map_entry("TS", "TorrentStorm")
00153 , map_entry("U", "UPnP")
00154 , map_entry("UL", "uLeecher")
00155 , map_entry("UT", "MicroTorrent")
00156 , map_entry("XT", "XanTorrent")
00157 , map_entry("XX", "Xtorrent")
00158 , map_entry("ZT", "ZipTorrent")
00159 , map_entry("lt", "libTorrent (libtorrent.rakshasa.no/)")
00160 , map_entry("pX", "pHoeniX")
00161 , map_entry("qB", "qBittorrent")
00162 };
00163
00164 bool compare_first_string(map_entry const& lhs, map_entry const& rhs)
00165 {
00166 return lhs.first[0] < rhs.first[0]
00167 || ((lhs.first[0] == rhs.first[0]) && (lhs.first[1] < rhs.first[1]));
00168 }
00169
00170 std::string lookup(fingerprint const& f)
00171 {
00172 std::stringstream identity;
00173
00174 const int size = sizeof(name_map)/sizeof(name_map[0]);
00175 map_entry* i =
00176 std::lower_bound(name_map, name_map + size
00177 , map_entry(f.name, ""), &compare_first_string);
00178
00179 #ifndef NDEBUG
00180 for (int i = 1; i < size; ++i)
00181 {
00182 assert(compare_first_string(name_map[i-1]
00183 , name_map[i]));
00184 }
00185 #endif
00186
00187 if (i < name_map + size && std::equal(f.name, f.name + 2, i->first))
00188 identity << i->second;
00189 else
00190 {
00191 identity << f.name[0];
00192 if (f.name[1] != 0) identity << f.name[1];
00193 }
00194
00195 identity << " " << (int)f.major_version
00196 << "." << (int)f.minor_version
00197 << "." << (int)f.revision_version;
00198
00199 if (f.name[1] != 0)
00200 identity << "." << (int)f.tag_version;
00201
00202 return identity.str();
00203 }
00204
00205 bool find_string(unsigned char const* id, char const* search)
00206 {
00207 return std::equal(search, search + std::strlen(search), id);
00208 }
00209 }
00210
00211 namespace libtorrent
00212 {
00213
00214 boost::optional<fingerprint> client_fingerprint(peer_id const& p)
00215 {
00216
00217 boost::optional<fingerprint> f;
00218 f = parse_az_style(p);
00219 if (f) return f;
00220
00221
00222 f = parse_shadow_style(p);
00223 if (f) return f;
00224
00225
00226 f = parse_mainline_style(p);
00227 if (f) return f;
00228 return f;
00229 }
00230
00231 std::string identify_client(peer_id const& p)
00232 {
00233 peer_id::const_iterator PID = p.begin();
00234 boost::optional<fingerprint> f;
00235
00236 if (p.is_all_zeros()) return "Unknown";
00237
00238
00239
00240
00241
00242 if (find_string(PID, "Deadman Walking-")) return "Deadman";
00243 if (find_string(PID + 5, "Azureus")) return "Azureus 2.0.3.2";
00244 if (find_string(PID, "DansClient")) return "XanTorrent";
00245 if (find_string(PID + 4, "btfans")) return "SimpleBT";
00246 if (find_string(PID, "PRC.P---")) return "Bittorrent Plus! II";
00247 if (find_string(PID, "P87.P---")) return "Bittorrent Plus!";
00248 if (find_string(PID, "S587Plus")) return "Bittorrent Plus!";
00249 if (find_string(PID, "martini")) return "Martini Man";
00250 if (find_string(PID, "Plus---")) return "Bittorrent Plus";
00251 if (find_string(PID, "turbobt")) return "TurboBT";
00252 if (find_string(PID, "a00---0")) return "Swarmy";
00253 if (find_string(PID, "a02---0")) return "Swarmy";
00254 if (find_string(PID, "T00---0")) return "Teeweety";
00255 if (find_string(PID, "BTDWV-")) return "Deadman Walking";
00256 if (find_string(PID + 2, "BS")) return "BitSpirit";
00257 if (find_string(PID, "btuga")) return "BTugaXP";
00258 if (find_string(PID, "oernu")) return "BTugaXP";
00259 if (find_string(PID, "Mbrst")) return "Burst!";
00260 if (find_string(PID, "Plus")) return "Plus!";
00261 if (find_string(PID, "-Qt-")) return "Qt";
00262 if (find_string(PID, "exbc")) return "BitComet";
00263 if (find_string(PID, "-G3")) return "G3 Torrent";
00264 if (find_string(PID, "XBT")) return "XBT";
00265 if (find_string(PID, "OP")) return "Opera";
00266
00267 if (find_string(PID, "-BOW") && PID[7] == '-')
00268 return "Bits on Wheels " + std::string(PID + 4, PID + 7);
00269
00270
00271 if (find_string(PID, "eX"))
00272 {
00273 std::string user(PID + 2, PID + 14);
00274 return std::string("eXeem ('") + user.c_str() + "')";
00275 }
00276
00277 if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97"))
00278 return "Experimental 3.2.1b2";
00279
00280 if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0"))
00281 return "Experimental 3.1";
00282
00283
00284
00285 f = parse_az_style(p);
00286 if (f) return lookup(*f);
00287
00288
00289 f = parse_shadow_style(p);
00290 if (f) return lookup(*f);
00291
00292
00293 f = parse_mainline_style(p);
00294 if (f) return lookup(*f);
00295
00296
00297 if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0"))
00298 return "Generic";
00299
00300 std::string unknown("Unknown [");
00301 for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i)
00302 {
00303 unknown += std::isprint(*i)?*i:'.';
00304 }
00305 unknown += "]";
00306 return unknown;
00307 }
00308
00309 }