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"}
178 , {"MT", "Moonlight Torrent"}
179 , {"O", "Osprey Permaseed"}
187 , {"SS", "SwarmScope"}
188 , {"ST", "SymTorrent"}
190 , {"S~", "Shareaza (beta)"}
191 , {"T", "BitTornado"}
192 , {"TN", "Torrent.NET"}
193 , {"TR", "Transmission"}
194 , {"TS", "TorrentStorm"}
200 , {"XT", "XanTorrent"}
202 , {"ZT", "ZipTorrent"}
205 , {"qB", "qBittorrent"}
206 , {"st", "SharkTorrent"}
209 struct generic_map_entry
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!"}
241 , {0, "exbc", "BitComet"}
242 , {0, "DNA", "BitTorrent DNA"}
243 , {0, "-G3", "G3 Torrent"}
244 , {0, "-FG", "FlashGet"}
245 , {0, "-ML", "MLdonkey"}
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
, ""};
265 std::lower_bound(name_map
, name_map
+ size
269 for (int i
= 1; i
< size
; ++i
)
271 TORRENT_ASSERT(compare_id(name_map
[i
-1]
276 if (i
< name_map
+ size
&& std::equal(f
.name
, f
.name
+ 2, i
->id
))
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
;
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
);
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
);
310 // look for shadow style id
311 f
= parse_shadow_style(p
);
314 // look for mainline style id
315 f
= parse_mainline_style(p
);
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"))
372 std::string
unknown("Unknown [");
373 for (peer_id::const_iterator i
= p
.begin(); i
!= p
.end(); ++i
)
375 unknown
+= std::isprint(*i
)?*i
:'.';