excluded mapped_storage from build
[libtorrent.git] / src / identify_client.cpp
blob73221de66a93eb45d2b9be30a46dfeb8ab32ad82
1 /*
3 Copyright (c) 2003, Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/pch.hpp"
35 #include <cctype>
36 #include <algorithm>
38 #ifdef _MSC_VER
39 #pragma warning(push, 1)
40 #endif
42 #include <boost/optional.hpp>
44 #ifdef _MSC_VER
45 #pragma warning(pop)
46 #endif
48 #include "libtorrent/identify_client.hpp"
49 #include "libtorrent/fingerprint.hpp"
51 namespace
54 using namespace libtorrent;
56 int decode_digit(char c)
58 if (std::isdigit(c)) return c - '0';
59 return unsigned(c) - 'A' + 10;
62 // takes a peer id and returns a valid boost::optional
63 // object if the peer id matched the azureus style encoding
64 // the returned fingerprint contains information about the
65 // client's id
66 boost::optional<fingerprint> parse_az_style(const peer_id& id)
68 fingerprint ret("..", 0, 0, 0, 0);
70 if (id[0] != '-' || !std::isprint(id[1]) || (id[2] < '0')
71 || (id[3] < '0') || (id[4] < '0')
72 || (id[5] < '0') || (id[6] < '0')
73 || id[7] != '-')
74 return boost::optional<fingerprint>();
76 ret.name[0] = id[1];
77 ret.name[1] = id[2];
78 ret.major_version = decode_digit(id[3]);
79 ret.minor_version = decode_digit(id[4]);
80 ret.revision_version = decode_digit(id[5]);
81 ret.tag_version = decode_digit(id[6]);
83 return boost::optional<fingerprint>(ret);
86 // checks if a peer id can possibly contain a shadow-style
87 // identification
88 boost::optional<fingerprint> parse_shadow_style(const peer_id& id)
90 fingerprint ret("..", 0, 0, 0, 0);
92 if (!std::isalnum(id[0]))
93 return boost::optional<fingerprint>();
95 if (std::equal(id.begin()+4, id.begin()+6, "--"))
97 if ((id[1] < '0') || (id[2] < '0')
98 || (id[3] < '0'))
99 return boost::optional<fingerprint>();
100 ret.major_version = decode_digit(id[1]);
101 ret.minor_version = decode_digit(id[2]);
102 ret.revision_version = decode_digit(id[3]);
104 else
106 if (id[8] != 0 || id[1] > 127 || id[2] > 127 || id[3] > 127)
107 return boost::optional<fingerprint>();
108 ret.major_version = id[1];
109 ret.minor_version = id[2];
110 ret.revision_version = id[3];
113 ret.name[0] = id[0];
114 ret.name[1] = 0;
116 ret.tag_version = 0;
117 return boost::optional<fingerprint>(ret);
120 // checks if a peer id can possibly contain a mainline-style
121 // identification
122 boost::optional<fingerprint> parse_mainline_style(const peer_id& id)
124 char ids[21];
125 std::copy(id.begin(), id.end(), ids);
126 ids[20] = 0;
127 fingerprint ret("..", 0, 0, 0, 0);
128 ret.name[1] = 0;
129 ret.tag_version = 0;
130 if (sscanf(ids, "%c%d-%d-%d--", &ret.name[0], &ret.major_version, &ret.minor_version
131 , &ret.revision_version) != 4
132 || !std::isprint(ret.name[0]))
133 return boost::optional<fingerprint>();
135 return boost::optional<fingerprint>(ret);
138 struct map_entry
140 char const* id;
141 char const* name;
144 // only support BitTorrentSpecification
145 // must be ordered alphabetically
146 map_entry name_map[] =
148 {"A", "ABC"}
149 , {"AG", "Ares"}
150 , {"AR", "Arctic Torrent"}
151 , {"AV", "Avicora"}
152 , {"AX", "BitPump"}
153 , {"AZ", "Azureus"}
154 , {"A~", "Ares"}
155 , {"BB", "BitBuddy"}
156 , {"BC", "BitComet"}
157 , {"BF", "Bitflu"}
158 , {"BG", "BTG"}
159 , {"BR", "BitRocket"}
160 , {"BS", "BTSlave"}
161 , {"BX", "BittorrentX"}
162 , {"CD", "Enhanced CTorrent"}
163 , {"CT", "CTorrent"}
164 , {"DE", "Deluge Torrent"}
165 , {"EB", "EBit"}
166 , {"ES", "electric sheep"}
167 , {"HL", "Halite"}
168 , {"HN", "Hydranode"}
169 , {"KT", "KTorrent"}
170 , {"LK", "Linkage"}
171 , {"LP", "lphant"}
172 , {"LT", "libtorrent"}
173 , {"M", "Mainline"}
174 , {"ML", "MLDonkey"}
175 , {"MO", "Mono Torrent"}
176 , {"MP", "MooPolice"}
177 , {"MR", "Miro"}
178 , {"MT", "Moonlight Torrent"}
179 , {"O", "Osprey Permaseed"}
180 , {"PD", "Pando"}
181 , {"Q", "BTQueue"}
182 , {"QT", "Qt 4"}
183 , {"R", "Tribler"}
184 , {"S", "Shadow"}
185 , {"SB", "Swiftbit"}
186 , {"SN", "ShareNet"}
187 , {"SS", "SwarmScope"}
188 , {"ST", "SymTorrent"}
189 , {"SZ", "Shareaza"}
190 , {"S~", "Shareaza (beta)"}
191 , {"T", "BitTornado"}
192 , {"TN", "Torrent.NET"}
193 , {"TR", "Transmission"}
194 , {"TS", "TorrentStorm"}
195 , {"TT", "TuoTu"}
196 , {"U", "UPnP"}
197 , {"UL", "uLeecher"}
198 , {"UT", "uTorrent"}
199 , {"XL", "Xunlei"}
200 , {"XT", "XanTorrent"}
201 , {"XX", "Xtorrent"}
202 , {"ZT", "ZipTorrent"}
203 , {"lt", "rTorrent"}
204 , {"pX", "pHoeniX"}
205 , {"qB", "qBittorrent"}
206 , {"st", "SharkTorrent"}
209 struct generic_map_entry
211 int offset;
212 char const* id;
213 char const* name;
215 // non-standard names
216 generic_map_entry generic_mappings[] =
218 {0, "Deadman Walking-", "Deadman"}
219 , {5, "Azureus", "Azureus 2.0.3.2"}
220 , {0, "DansClient", "XanTorrent"}
221 , {4, "btfans", "SimpleBT"}
222 , {0, "PRC.P---", "Bittorrent Plus! II"}
223 , {0, "P87.P---", "Bittorrent Plus!"}
224 , {0, "S587Plus", "Bittorrent Plus!"}
225 , {0, "martini", "Martini Man"}
226 , {0, "Plus---", "Bittorrent Plus"}
227 , {0, "turbobt", "TurboBT"}
228 , {0, "a00---0", "Swarmy"}
229 , {0, "a02---0", "Swarmy"}
230 , {0, "T00---0", "Teeweety"}
231 , {0, "BTDWV-", "Deadman Walking"}
232 , {2, "BS", "BitSpirit"}
233 , {0, "Pando-", "Pando"}
234 , {0, "LIME", "LimeWire"}
235 , {0, "btuga", "BTugaXP"}
236 , {0, "oernu", "BTugaXP"}
237 , {0, "Mbrst", "Burst!"}
238 , {0, "PEERAPP", "PeerApp"}
239 , {0, "Plus", "Plus!"}
240 , {0, "-Qt-", "Qt"}
241 , {0, "exbc", "BitComet"}
242 , {0, "DNA", "BitTorrent DNA"}
243 , {0, "-G3", "G3 Torrent"}
244 , {0, "-FG", "FlashGet"}
245 , {0, "-ML", "MLdonkey"}
246 , {0, "XBT", "XBT"}
247 , {0, "OP", "Opera"}
248 , {2, "RS", "Rufus"}
249 , {0, "AZ2500BT", "BitTyrant"}
252 bool compare_id(map_entry const& lhs, map_entry const& rhs)
254 return lhs.id[0] < rhs.id[0]
255 || ((lhs.id[0] == rhs.id[0]) && (lhs.id[1] < rhs.id[1]));
258 std::string lookup(fingerprint const& f)
260 std::stringstream identity;
262 const int size = sizeof(name_map)/sizeof(name_map[0]);
263 map_entry tmp = {f.name, ""};
264 map_entry* i =
265 std::lower_bound(name_map, name_map + size
266 , tmp, &compare_id);
268 #ifndef NDEBUG
269 for (int i = 1; i < size; ++i)
271 TORRENT_ASSERT(compare_id(name_map[i-1]
272 , name_map[i]));
274 #endif
276 if (i < name_map + size && std::equal(f.name, f.name + 2, i->id))
277 identity << i->name;
278 else
280 identity << f.name[0];
281 if (f.name[1] != 0) identity << f.name[1];
284 identity << " " << (int)f.major_version
285 << "." << (int)f.minor_version
286 << "." << (int)f.revision_version;
288 if (f.name[1] != 0)
289 identity << "." << (int)f.tag_version;
291 return identity.str();
294 bool find_string(unsigned char const* id, char const* search)
296 return std::equal(search, search + std::strlen(search), id);
300 namespace libtorrent
303 boost::optional<fingerprint> client_fingerprint(peer_id const& p)
305 // look for azureus style id
306 boost::optional<fingerprint> f;
307 f = parse_az_style(p);
308 if (f) return f;
310 // look for shadow style id
311 f = parse_shadow_style(p);
312 if (f) return f;
314 // look for mainline style id
315 f = parse_mainline_style(p);
316 if (f) return f;
317 return f;
320 std::string identify_client(peer_id const& p)
322 peer_id::const_iterator PID = p.begin();
323 boost::optional<fingerprint> f;
325 if (p.is_all_zeros()) return "Unknown";
327 // ----------------------
328 // non standard encodings
329 // ----------------------
331 int num_generic_mappings = sizeof(generic_mappings) / sizeof(generic_mappings[0]);
333 for (int i = 0; i < num_generic_mappings; ++i)
335 generic_map_entry const& e = generic_mappings[i];
336 if (find_string(PID + e.offset, e.id)) return e.name;
339 if (find_string(PID, "-BOW") && PID[7] == '-')
340 return "Bits on Wheels " + std::string(PID + 4, PID + 7);
343 if (find_string(PID, "eX"))
345 std::string user(PID + 2, PID + 14);
346 return std::string("eXeem ('") + user.c_str() + "')";
349 if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97"))
350 return "Experimental 3.2.1b2";
352 if (std::equal(PID, PID + 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0"))
353 return "Experimental 3.1";
356 // look for azureus style id
357 f = parse_az_style(p);
358 if (f) return lookup(*f);
360 // look for shadow style id
361 f = parse_shadow_style(p);
362 if (f) return lookup(*f);
364 // look for mainline style id
365 f = parse_mainline_style(p);
366 if (f) return lookup(*f);
369 if (std::equal(PID, PID + 12, "\0\0\0\0\0\0\0\0\0\0\0\0"))
370 return "Generic";
372 std::string unknown("Unknown [");
373 for (peer_id::const_iterator i = p.begin(); i != p.end(); ++i)
375 unknown += std::isprint(*i)?*i:'.';
377 unknown += "]";
378 return unknown;