1 /*
2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 * In addition, as a special exception, the copyright holders give permission to
20 * link this program with the OpenSSL project's "OpenSSL" library (or with
21 * modified versions of it that use the same license as the "OpenSSL" library),
22 * and distribute the linked executables. You must obey the GNU General Public
23 * License in all respects for all of the code used other than "OpenSSL". If you
24 * modify file(s), you may extend this exception to your version of the file(s),
25 * but you are not obligated to do so. If you do not wish to do so, delete this
26 * exception statement from your version.
27 */
28
29 #include "peerinfo.h"
30
31 #include <QBitArray>
32
33 #include "base/bittorrent/torrent.h"
34 #include "base/net/geoipmanager.h"
35 #include "base/unicodestrings.h"
36 #include "peeraddress.h"
37
38 using namespace BitTorrent;
39
PeerInfo(const Torrent * torrent,const lt::peer_info & nativeInfo)40 PeerInfo::PeerInfo(const Torrent *torrent, const lt::peer_info &nativeInfo)
41 : m_nativeInfo(nativeInfo)
42 {
43 calcRelevance(torrent);
44 determineFlags();
45 }
46
fromDHT() const47 bool PeerInfo::fromDHT() const
48 {
49 return static_cast<bool>(m_nativeInfo.source & lt::peer_info::dht);
50 }
51
fromPeX() const52 bool PeerInfo::fromPeX() const
53 {
54 return static_cast<bool>(m_nativeInfo.source & lt::peer_info::pex);
55 }
56
fromLSD() const57 bool PeerInfo::fromLSD() const
58 {
59 return static_cast<bool>(m_nativeInfo.source & lt::peer_info::lsd);
60 }
61
country() const62 QString PeerInfo::country() const
63 {
64 if (m_country.isEmpty())
65 m_country = Net::GeoIPManager::instance()->lookup(address().ip);
66 return m_country;
67 }
68
isInteresting() const69 bool PeerInfo::isInteresting() const
70 {
71 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::interesting);
72 }
73
isChocked() const74 bool PeerInfo::isChocked() const
75 {
76 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::choked);
77 }
78
isRemoteInterested() const79 bool PeerInfo::isRemoteInterested() const
80 {
81 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::remote_interested);
82 }
83
isRemoteChocked() const84 bool PeerInfo::isRemoteChocked() const
85 {
86 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::remote_choked);
87 }
88
isSupportsExtensions() const89 bool PeerInfo::isSupportsExtensions() const
90 {
91 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::supports_extensions);
92 }
93
isLocalConnection() const94 bool PeerInfo::isLocalConnection() const
95 {
96 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::local_connection);
97 }
98
isHandshake() const99 bool PeerInfo::isHandshake() const
100 {
101 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::handshake);
102 }
103
isConnecting() const104 bool PeerInfo::isConnecting() const
105 {
106 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::connecting);
107 }
108
isOnParole() const109 bool PeerInfo::isOnParole() const
110 {
111 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::on_parole);
112 }
113
isSeed() const114 bool PeerInfo::isSeed() const
115 {
116 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::seed);
117 }
118
optimisticUnchoke() const119 bool PeerInfo::optimisticUnchoke() const
120 {
121 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::optimistic_unchoke);
122 }
123
isSnubbed() const124 bool PeerInfo::isSnubbed() const
125 {
126 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::snubbed);
127 }
128
isUploadOnly() const129 bool PeerInfo::isUploadOnly() const
130 {
131 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::upload_only);
132 }
133
isEndgameMode() const134 bool PeerInfo::isEndgameMode() const
135 {
136 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::endgame_mode);
137 }
138
isHolepunched() const139 bool PeerInfo::isHolepunched() const
140 {
141 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::holepunched);
142 }
143
useI2PSocket() const144 bool PeerInfo::useI2PSocket() const
145 {
146 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::i2p_socket);
147 }
148
useUTPSocket() const149 bool PeerInfo::useUTPSocket() const
150 {
151 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::utp_socket);
152 }
153
useSSLSocket() const154 bool PeerInfo::useSSLSocket() const
155 {
156 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::ssl_socket);
157 }
158
isRC4Encrypted() const159 bool PeerInfo::isRC4Encrypted() const
160 {
161 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::rc4_encrypted);
162 }
163
isPlaintextEncrypted() const164 bool PeerInfo::isPlaintextEncrypted() const
165 {
166 return static_cast<bool>(m_nativeInfo.flags & lt::peer_info::plaintext_encrypted);
167 }
168
address() const169 PeerAddress PeerInfo::address() const
170 {
171 // fast path for platforms which boost.asio internal struct maps to `sockaddr`
172 return {QHostAddress(m_nativeInfo.ip.data()), m_nativeInfo.ip.port()};
173 // slow path for the others
174 //return {QHostAddress(QString::fromStdString(m_nativeInfo.ip.address().to_string()))
175 // , m_nativeInfo.ip.port()};
176 }
177
client() const178 QString PeerInfo::client() const
179 {
180 return QString::fromStdString(m_nativeInfo.client);
181 }
182
progress() const183 qreal PeerInfo::progress() const
184 {
185 return m_nativeInfo.progress;
186 }
187
payloadUpSpeed() const188 int PeerInfo::payloadUpSpeed() const
189 {
190 return m_nativeInfo.payload_up_speed;
191 }
192
payloadDownSpeed() const193 int PeerInfo::payloadDownSpeed() const
194 {
195 return m_nativeInfo.payload_down_speed;
196 }
197
totalUpload() const198 qlonglong PeerInfo::totalUpload() const
199 {
200 return m_nativeInfo.total_upload;
201 }
202
totalDownload() const203 qlonglong PeerInfo::totalDownload() const
204 {
205 return m_nativeInfo.total_download;
206 }
207
pieces() const208 QBitArray PeerInfo::pieces() const
209 {
210 QBitArray result(m_nativeInfo.pieces.size());
211 for (int i = 0; i < result.size(); ++i)
212 {
213 if (m_nativeInfo.pieces[lt::piece_index_t {i}])
214 result.setBit(i, true);
215 }
216 return result;
217 }
218
connectionType() const219 QString PeerInfo::connectionType() const
220 {
221 if (m_nativeInfo.flags & lt::peer_info::utp_socket)
222 return QString::fromUtf8(C_UTP);
223
224 return (m_nativeInfo.connection_type == lt::peer_info::standard_bittorrent)
225 ? QLatin1String {"BT"}
226 : QLatin1String {"Web"};
227 }
228
calcRelevance(const Torrent * torrent)229 void PeerInfo::calcRelevance(const Torrent *torrent)
230 {
231 const QBitArray allPieces = torrent->pieces();
232 const QBitArray peerPieces = pieces();
233
234 int localMissing = 0;
235 int remoteHaves = 0;
236
237 for (int i = 0; i < allPieces.size(); ++i)
238 {
239 if (!allPieces[i])
240 {
241 ++localMissing;
242 if (peerPieces[i])
243 ++remoteHaves;
244 }
245 }
246
247 if (localMissing == 0)
248 m_relevance = 0.0;
249 else
250 m_relevance = static_cast<qreal>(remoteHaves) / localMissing;
251 }
252
relevance() const253 qreal PeerInfo::relevance() const
254 {
255 return m_relevance;
256 }
257
determineFlags()258 void PeerInfo::determineFlags()
259 {
260 if (isInteresting())
261 {
262 // d = Your client wants to download, but peer doesn't want to send (interested and choked)
263 if (isRemoteChocked())
264 {
265 m_flags += "d ";
266 m_flagsDescription += ("d = "
267 + tr("Interested(local) and Choked(peer)") + '\n');
268 }
269 else
270 {
271 // D = Currently downloading (interested and not choked)
272 m_flags += "D ";
273 m_flagsDescription += ("D = "
274 + tr("interested(local) and unchoked(peer)") + '\n');
275 }
276 }
277
278 if (isRemoteInterested())
279 {
280 // u = Peer wants your client to upload, but your client doesn't want to (interested and choked)
281 if (isChocked())
282 {
283 m_flags += "u ";
284 m_flagsDescription += ("u = "
285 + tr("interested(peer) and choked(local)") + '\n');
286 }
287 else
288 {
289 // U = Currently uploading (interested and not choked)
290 m_flags += "U ";
291 m_flagsDescription += ("U = "
292 + tr("interested(peer) and unchoked(local)") + '\n');
293 }
294 }
295
296 // O = Optimistic unchoke
297 if (optimisticUnchoke())
298 {
299 m_flags += "O ";
300 m_flagsDescription += ("O = " + tr("optimistic unchoke") + '\n');
301 }
302
303 // S = Peer is snubbed
304 if (isSnubbed())
305 {
306 m_flags += "S ";
307 m_flagsDescription += ("S = " + tr("peer snubbed") + '\n');
308 }
309
310 // I = Peer is an incoming connection
311 if (!isLocalConnection())
312 {
313 m_flags += "I ";
314 m_flagsDescription += ("I = " + tr("incoming connection") + '\n');
315 }
316
317 // K = Peer is unchoking your client, but your client is not interested
318 if (!isRemoteChocked() && !isInteresting())
319 {
320 m_flags += "K ";
321 m_flagsDescription += ("K = "
322 + tr("not interested(local) and unchoked(peer)") + '\n');
323 }
324
325 // ? = Your client unchoked the peer but the peer is not interested
326 if (!isChocked() && !isRemoteInterested())
327 {
328 m_flags += "? ";
329 m_flagsDescription += ("? = "
330 + tr("not interested(peer) and unchoked(local)") + '\n');
331 }
332
333 // X = Peer was included in peerlists obtained through Peer Exchange (PEX)
334 if (fromPeX())
335 {
336 m_flags += "X ";
337 m_flagsDescription += ("X = " + tr("peer from PEX") + '\n');
338 }
339
340 // H = Peer was obtained through DHT
341 if (fromDHT())
342 {
343 m_flags += "H ";
344 m_flagsDescription += ("H = " + tr("peer from DHT") + '\n');
345 }
346
347 // E = Peer is using Protocol Encryption (all traffic)
348 if (isRC4Encrypted())
349 {
350 m_flags += "E ";
351 m_flagsDescription += ("E = " + tr("encrypted traffic") + '\n');
352 }
353
354 // e = Peer is using Protocol Encryption (handshake)
355 if (isPlaintextEncrypted())
356 {
357 m_flags += "e ";
358 m_flagsDescription += ("e = " + tr("encrypted handshake") + '\n');
359 }
360
361 // P = Peer is using uTorrent uTP
362 if (useUTPSocket())
363 {
364 m_flags += "P ";
365 m_flagsDescription += ("P = " + QString::fromUtf8(C_UTP) + '\n');
366 }
367
368 // L = Peer is local
369 if (fromLSD())
370 {
371 m_flags += "L ";
372 m_flagsDescription += ("L = " + tr("peer from LSD") + '\n');
373 }
374
375 m_flags = m_flags.trimmed();
376 m_flagsDescription = m_flagsDescription.trimmed();
377 }
378
flags() const379 QString PeerInfo::flags() const
380 {
381 return m_flags;
382 }
383
flagsDescription() const384 QString PeerInfo::flagsDescription() const
385 {
386 return m_flagsDescription;
387 }
388
downloadingPieceIndex() const389 int PeerInfo::downloadingPieceIndex() const
390 {
391 return static_cast<int>(m_nativeInfo.downloading_piece_index);
392 }
393