3 Copyright (c) 2003, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
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"
39 #pragma warning(push, 1)
42 #include <boost/optional.hpp>
48 #include "libtorrent/identify_client.hpp"
49 #include "libtorrent/fingerprint.hpp"
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
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')
74 return boost::optional
<fingerprint
>();
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
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')
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]);
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];
117 return boost::optional
<fingerprint
>(ret
);
120 // checks if a peer id can possibly contain a mainline-style
122 boost::optional
<fingerprint
> parse_mainline_style(const peer_id
& id
)
125 std::copy(id
.begin(), id
.end(), ids
);
127 fingerprint
ret("..", 0, 0, 0, 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
);
144 // only support BitTorrentSpecification
145 // must be ordered alphabetically
146 map_entry name_map
[] =
150 , {"AR", "Arctic Torrent"}
159 , {"BR", "BitRocket"}
161 , {"BX", "BittorrentX"}
162 , {"CD", "Enhanced CTorrent"}
164 , {"DE", "Deluge Torrent"}
166 , {"ES", "electric sheep"}
168 , {"HN", "Hydranode"}
172 , {"LT", "libtorrent"}
175 , {"MO", "Mono Torrent"}
176 , {"MP", "MooPolice"}
177 , {"MT", "Moonlight Torrent"}
178 , {"O", "Osprey Permaseed"}
186 , {"SS", "SwarmScope"}
188 , {"S~", "Shareaza (beta)"}
189 , {"T", "BitTornado"}
190 , {"TN", "Torrent.NET"}
191 , {"TR", "Transmission"}
192 , {"TS", "TorrentStorm"}
196 , {"UT", "MicroTorrent"}
197 , {"XT", "XanTorrent"}
199 , {"ZT", "ZipTorrent"}
200 , {"lt", "libTorrent (libtorrent.rakshasa.no/}"}
202 , {"qB", "qBittorrent"}
205 bool compare_id(map_entry
const& lhs
, map_entry
const& rhs
)
207 return lhs
.id
[0] < rhs
.id
[0]
208 || ((lhs
.id
[0] == rhs
.id
[0]) && (lhs
.id
[1] < rhs
.id
[1]));
211 std::string
lookup(fingerprint
const& f
)
213 std::stringstream identity
;
215 const int size
= sizeof(name_map
)/sizeof(name_map
[0]);
216 map_entry tmp
= {f
.name
, ""};
218 std::lower_bound(name_map
, name_map
+ size
222 for (int i
= 1; i
< size
; ++i
)
224 assert(compare_id(name_map
[i
-1]
229 if (i
< name_map
+ size
&& std::equal(f
.name
, f
.name
+ 2, i
->id
))
233 identity
<< f
.name
[0];
234 if (f
.name
[1] != 0) identity
<< f
.name
[1];
237 identity
<< " " << (int)f
.major_version
238 << "." << (int)f
.minor_version
239 << "." << (int)f
.revision_version
;
242 identity
<< "." << (int)f
.tag_version
;
244 return identity
.str();
247 bool find_string(unsigned char const* id
, char const* search
)
249 return std::equal(search
, search
+ std::strlen(search
), id
);
256 boost::optional
<fingerprint
> client_fingerprint(peer_id
const& p
)
258 // look for azureus style id
259 boost::optional
<fingerprint
> f
;
260 f
= parse_az_style(p
);
263 // look for shadow style id
264 f
= parse_shadow_style(p
);
267 // look for mainline style id
268 f
= parse_mainline_style(p
);
273 std::string
identify_client(peer_id
const& p
)
275 peer_id::const_iterator PID
= p
.begin();
276 boost::optional
<fingerprint
> f
;
278 if (p
.is_all_zeros()) return "Unknown";
280 // ----------------------
281 // non standard encodings
282 // ----------------------
284 if (find_string(PID
, "Deadman Walking-")) return "Deadman";
285 if (find_string(PID
+ 5, "Azureus")) return "Azureus 2.0.3.2";
286 if (find_string(PID
, "DansClient")) return "XanTorrent";
287 if (find_string(PID
+ 4, "btfans")) return "SimpleBT";
288 if (find_string(PID
, "PRC.P---")) return "Bittorrent Plus! II";
289 if (find_string(PID
, "P87.P---")) return "Bittorrent Plus!";
290 if (find_string(PID
, "S587Plus")) return "Bittorrent Plus!";
291 if (find_string(PID
, "martini")) return "Martini Man";
292 if (find_string(PID
, "Plus---")) return "Bittorrent Plus";
293 if (find_string(PID
, "turbobt")) return "TurboBT";
294 if (find_string(PID
, "a00---0")) return "Swarmy";
295 if (find_string(PID
, "a02---0")) return "Swarmy";
296 if (find_string(PID
, "T00---0")) return "Teeweety";
297 if (find_string(PID
, "BTDWV-")) return "Deadman Walking";
298 if (find_string(PID
+ 2, "BS")) return "BitSpirit";
299 if (find_string(PID
, "btuga")) return "BTugaXP";
300 if (find_string(PID
, "oernu")) return "BTugaXP";
301 if (find_string(PID
, "Mbrst")) return "Burst!";
302 if (find_string(PID
, "Plus")) return "Plus!";
303 if (find_string(PID
, "-Qt-")) return "Qt";
304 if (find_string(PID
, "exbc")) return "BitComet";
305 if (find_string(PID
, "-G3")) return "G3 Torrent";
306 if (find_string(PID
, "XBT")) return "XBT";
307 if (find_string(PID
, "OP")) return "Opera";
309 if (find_string(PID
, "-BOW") && PID
[7] == '-')
310 return "Bits on Wheels " + std::string(PID
+ 4, PID
+ 7);
313 if (find_string(PID
, "eX"))
315 std::string
user(PID
+ 2, PID
+ 14);
316 return std::string("eXeem ('") + user
.c_str() + "')";
319 if (std::equal(PID
, PID
+ 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97"))
320 return "Experimental 3.2.1b2";
322 if (std::equal(PID
, PID
+ 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0"))
323 return "Experimental 3.1";
326 // look for azureus style id
327 f
= parse_az_style(p
);
328 if (f
) return lookup(*f
);
330 // look for shadow style id
331 f
= parse_shadow_style(p
);
332 if (f
) return lookup(*f
);
334 // look for mainline style id
335 f
= parse_mainline_style(p
);
336 if (f
) return lookup(*f
);
339 if (std::equal(PID
, PID
+ 12, "\0\0\0\0\0\0\0\0\0\0\0\0"))
342 std::string
unknown("Unknown [");
343 for (peer_id::const_iterator i
= p
.begin(); i
!= p
.end(); ++i
)
345 unknown
+= std::isprint(*i
)?*i
:'.';