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