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;
64 return c
>= 32 && c
< 127;
67 // takes a peer id and returns a valid boost::optional
68 // object if the peer id matched the azureus style encoding
69 // the returned fingerprint contains information about the
71 boost::optional
<fingerprint
> parse_az_style(const peer_id
& id
)
73 fingerprint
ret("..", 0, 0, 0, 0);
75 if (id
[0] != '-' || !isprint(id
[1]) || (id
[2] < '0')
76 || (id
[3] < '0') || (id
[4] < '0')
77 || (id
[5] < '0') || (id
[6] < '0')
79 return boost::optional
<fingerprint
>();
83 ret
.major_version
= decode_digit(id
[3]);
84 ret
.minor_version
= decode_digit(id
[4]);
85 ret
.revision_version
= decode_digit(id
[5]);
86 ret
.tag_version
= decode_digit(id
[6]);
88 return boost::optional
<fingerprint
>(ret
);
91 // checks if a peer id can possibly contain a shadow-style
93 boost::optional
<fingerprint
> parse_shadow_style(const peer_id
& id
)
95 fingerprint
ret("..", 0, 0, 0, 0);
97 if (!std::isalnum(id
[0]))
98 return boost::optional
<fingerprint
>();
100 if (std::equal(id
.begin()+4, id
.begin()+6, "--"))
102 if ((id
[1] < '0') || (id
[2] < '0')
104 return boost::optional
<fingerprint
>();
105 ret
.major_version
= decode_digit(id
[1]);
106 ret
.minor_version
= decode_digit(id
[2]);
107 ret
.revision_version
= decode_digit(id
[3]);
111 if (id
[8] != 0 || id
[1] > 127 || id
[2] > 127 || id
[3] > 127)
112 return boost::optional
<fingerprint
>();
113 ret
.major_version
= id
[1];
114 ret
.minor_version
= id
[2];
115 ret
.revision_version
= id
[3];
122 return boost::optional
<fingerprint
>(ret
);
125 // checks if a peer id can possibly contain a mainline-style
127 boost::optional
<fingerprint
> parse_mainline_style(const peer_id
& id
)
130 std::copy(id
.begin(), id
.end(), ids
);
132 fingerprint
ret("..", 0, 0, 0, 0);
135 if (sscanf(ids
, "%c%d-%d-%d--", &ret
.name
[0], &ret
.major_version
, &ret
.minor_version
136 , &ret
.revision_version
) != 4
137 || !isprint(ret
.name
[0]))
138 return boost::optional
<fingerprint
>();
140 return boost::optional
<fingerprint
>(ret
);
149 // only support BitTorrentSpecification
150 // must be ordered alphabetically
151 map_entry name_map
[] =
155 , {"AR", "Arctic Torrent"}
164 , {"BR", "BitRocket"}
166 , {"BX", "BittorrentX"}
167 , {"CD", "Enhanced CTorrent"}
169 , {"DE", "Deluge Torrent"}
171 , {"ES", "electric sheep"}
173 , {"HN", "Hydranode"}
175 , {"LC", "LeechCraft"}
178 , {"LT", "libtorrent"}
181 , {"MO", "Mono Torrent"}
182 , {"MP", "MooPolice"}
184 , {"MT", "Moonlight Torrent"}
185 , {"O", "Osprey Permaseed"}
193 , {"SS", "SwarmScope"}
194 , {"ST", "SymTorrent"}
196 , {"S~", "Shareaza (beta)"}
197 , {"T", "BitTornado"}
198 , {"TN", "Torrent.NET"}
199 , {"TR", "Transmission"}
200 , {"TS", "TorrentStorm"}
206 , {"XT", "XanTorrent"}
208 , {"ZT", "ZipTorrent"}
211 , {"qB", "qBittorrent"}
212 , {"st", "SharkTorrent"}
215 struct generic_map_entry
221 // non-standard names
222 generic_map_entry generic_mappings
[] =
224 {0, "Deadman Walking-", "Deadman"}
225 , {5, "Azureus", "Azureus 2.0.3.2"}
226 , {0, "DansClient", "XanTorrent"}
227 , {4, "btfans", "SimpleBT"}
228 , {0, "PRC.P---", "Bittorrent Plus! II"}
229 , {0, "P87.P---", "Bittorrent Plus!"}
230 , {0, "S587Plus", "Bittorrent Plus!"}
231 , {0, "martini", "Martini Man"}
232 , {0, "Plus---", "Bittorrent Plus"}
233 , {0, "turbobt", "TurboBT"}
234 , {0, "a00---0", "Swarmy"}
235 , {0, "a02---0", "Swarmy"}
236 , {0, "T00---0", "Teeweety"}
237 , {0, "BTDWV-", "Deadman Walking"}
238 , {2, "BS", "BitSpirit"}
239 , {0, "Pando-", "Pando"}
240 , {0, "LIME", "LimeWire"}
241 , {0, "btuga", "BTugaXP"}
242 , {0, "oernu", "BTugaXP"}
243 , {0, "Mbrst", "Burst!"}
244 , {0, "PEERAPP", "PeerApp"}
245 , {0, "Plus", "Plus!"}
247 , {0, "exbc", "BitComet"}
248 , {0, "DNA", "BitTorrent DNA"}
249 , {0, "-G3", "G3 Torrent"}
250 , {0, "-FG", "FlashGet"}
251 , {0, "-ML", "MLdonkey"}
255 , {0, "AZ2500BT", "BitTyrant"}
258 bool compare_id(map_entry
const& lhs
, map_entry
const& rhs
)
260 return lhs
.id
[0] < rhs
.id
[0]
261 || ((lhs
.id
[0] == rhs
.id
[0]) && (lhs
.id
[1] < rhs
.id
[1]));
264 std::string
lookup(fingerprint
const& f
)
266 std::stringstream identity
;
268 const int size
= sizeof(name_map
)/sizeof(name_map
[0]);
269 map_entry tmp
= {f
.name
, ""};
271 std::lower_bound(name_map
, name_map
+ size
275 for (int i
= 1; i
< size
; ++i
)
277 TORRENT_ASSERT(compare_id(name_map
[i
-1]
282 if (i
< name_map
+ size
&& std::equal(f
.name
, f
.name
+ 2, i
->id
))
286 identity
<< f
.name
[0];
287 if (f
.name
[1] != 0) identity
<< f
.name
[1];
290 identity
<< " " << (int)f
.major_version
291 << "." << (int)f
.minor_version
292 << "." << (int)f
.revision_version
;
295 identity
<< "." << (int)f
.tag_version
;
297 return identity
.str();
300 bool find_string(unsigned char const* id
, char const* search
)
302 return std::equal(search
, search
+ std::strlen(search
), id
);
309 boost::optional
<fingerprint
> client_fingerprint(peer_id
const& p
)
311 // look for azureus style id
312 boost::optional
<fingerprint
> f
;
313 f
= parse_az_style(p
);
316 // look for shadow style id
317 f
= parse_shadow_style(p
);
320 // look for mainline style id
321 f
= parse_mainline_style(p
);
326 std::string
identify_client(peer_id
const& p
)
328 peer_id::const_iterator PID
= p
.begin();
329 boost::optional
<fingerprint
> f
;
331 if (p
.is_all_zeros()) return "Unknown";
333 // ----------------------
334 // non standard encodings
335 // ----------------------
337 int num_generic_mappings
= sizeof(generic_mappings
) / sizeof(generic_mappings
[0]);
339 for (int i
= 0; i
< num_generic_mappings
; ++i
)
341 generic_map_entry
const& e
= generic_mappings
[i
];
342 if (find_string(PID
+ e
.offset
, e
.id
)) return e
.name
;
345 if (find_string(PID
, "-BOW") && PID
[7] == '-')
346 return "Bits on Wheels " + std::string(PID
+ 4, PID
+ 7);
349 if (find_string(PID
, "eX"))
351 std::string
user(PID
+ 2, PID
+ 14);
352 return std::string("eXeem ('") + user
.c_str() + "')";
355 if (std::equal(PID
, PID
+ 13, "\0\0\0\0\0\0\0\0\0\0\0\0\x97"))
356 return "Experimental 3.2.1b2";
358 if (std::equal(PID
, PID
+ 13, "\0\0\0\0\0\0\0\0\0\0\0\0\0"))
359 return "Experimental 3.1";
362 // look for azureus style id
363 f
= parse_az_style(p
);
364 if (f
) return lookup(*f
);
366 // look for shadow style id
367 f
= parse_shadow_style(p
);
368 if (f
) return lookup(*f
);
370 // look for mainline style id
371 f
= parse_mainline_style(p
);
372 if (f
) return lookup(*f
);
375 if (std::equal(PID
, PID
+ 12, "\0\0\0\0\0\0\0\0\0\0\0\0"))
378 std::string
unknown("Unknown [");
379 for (peer_id::const_iterator i
= p
.begin(); i
!= p
.end(); ++i
)
381 unknown
+= isprint(*i
)?*i
:'.';