diff --git a/src/base/bittorrent/peerinfo.cpp b/src/base/bittorrent/peerinfo.cpp index 7b754bf24993..900e177cf0f3 100644 --- a/src/base/bittorrent/peerinfo.cpp +++ b/src/base/bittorrent/peerinfo.cpp @@ -196,6 +196,11 @@ PeerAddress PeerInfo::address() const m_nativeInfo.ip.port()); } +int PeerInfo::port() const +{ + return m_nativeInfo.ip.port(); +} + QString PeerInfo::client() const { return Utils::String::fromStdString(m_nativeInfo.client); diff --git a/src/base/bittorrent/peerinfo.h b/src/base/bittorrent/peerinfo.h index a30be4239df9..cf13d21fc75c 100644 --- a/src/base/bittorrent/peerinfo.h +++ b/src/base/bittorrent/peerinfo.h @@ -87,6 +87,7 @@ namespace BitTorrent bool isPlaintextEncrypted() const; PeerAddress address() const; + int port() const; QString client() const; QString pid() const; QString pidtoclient() const; diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index c666e5f5b797..62ca9617fd97 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -258,6 +258,7 @@ Session::Session(QObject *parent) , m_isBandwidthSchedulerEnabled(BITTORRENT_SESSION_KEY("BandwidthSchedulerEnabled"), false) , m_saveResumeDataInterval(BITTORRENT_SESSION_KEY("SaveResumeDataInterval"), 3) , m_autoBanUnknownPeer(BITTORRENT_SESSION_KEY("AutoBanUnknownPeer"), false) + , m_showTrackerAuthWindow(BITTORRENT_SESSION_KEY("ShowTrackerAuthWindow"), true) , m_port(BITTORRENT_SESSION_KEY("Port"), 8999) , m_useRandomPort(BITTORRENT_SESSION_KEY("UseRandomPort"), false) , m_networkInterface(BITTORRENT_SESSION_KEY("Interface")) @@ -390,8 +391,11 @@ Session::Session(QObject *parent) if (isIPFilteringEnabled()) enableIPFilter(); - // Add the banned IPs - processBannedIPs(); + else { + // Add the banned IPs + processBannedIPs(); + loadOfflineFilter(); + } m_categories = map_cast(m_storedCategories); if (isSubcategoriesEnabled()) { @@ -2288,6 +2292,18 @@ void Session::setAutoBanUnknownPeer(bool value) } } +bool Session::isShowTrackerAuthWindow() const +{ + return m_showTrackerAuthWindow; +} + +void Session::setShowTrackerAuthWindow(bool value) +{ + if (value != isShowTrackerAuthWindow()) { + m_showTrackerAuthWindow = value; + } +} + int Session::port() const { static int randomPort = Utils::Random::rand(1024, 65535); @@ -3079,6 +3095,186 @@ void Session::disableIPFilter() // which creates an empty filter and overrides all previously // applied bans. processBannedIPs(); + loadOfflineFilter(); +} + +// Handle ipfilter.dat +int trim(char* const data, int start, int end) +{ + if (start >= end) return start; + int newStart = start; + + for (int i = start; i <= end; ++i) { + if (isspace(data[i]) != 0) { + data[i] = '\0'; + } + else { + newStart = i; + break; + } + } + + for (int i = end; i >= start; --i) { + if (isspace(data[i]) != 0) + data[i] = '\0'; + else + break; + } + + return newStart; +} + +int findAndNullDelimiter(char *const data, char delimiter, int start, int end) +{ + for (int i = start; i <= end; ++i) { + if (data[i] == delimiter) { + data[i] = '\0'; + return i; + } + } + + return -1; +} + +int Session::parseOfflineFilterFile(QString ipDat, libt::ip_filter &filter) +{ + int ruleCount = 0; + QFile file(ipDat); + if (!file.exists()) return ruleCount; + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + LogMsg(tr("I/O Error: Could not open IP filter file in read mode."), Log::CRITICAL); + return ruleCount; + } + + std::vector buffer(2 * 1024 * 1024, 0); // seems a bit faster than QVector + qint64 bytesRead = 0; + int offset = 0; + int start = 0; + int endOfLine = -1; + int nbLine = 0; + + while (true) { + bytesRead = file.read(buffer.data() + offset, 2 * 1024 * 1024 - offset - 1); + if (bytesRead < 0) + break; + int dataSize = bytesRead + offset; + if (bytesRead == 0 && dataSize == 0) + break; + + for (start = 0; start < dataSize; ++start) { + endOfLine = -1; + // The file might have ended without the last line having a newline + if (!(bytesRead == 0 && dataSize > 0)) { + for (int i = start; i < dataSize; ++i) { + if (buffer[i] == '\n') { + endOfLine = i; + // We need to NULL the newline in case the line has only an IP range. + // In that case the parser won't work for the end IP, because it ends + // with the newline and not with a number. + buffer[i] = '\0'; + break; + } + } + } + else { + endOfLine = dataSize; + buffer[dataSize] = '\0'; + } + + if (endOfLine == -1) { + // read the next chunk from file + // but first move(copy) the leftover data to the front of the buffer + offset = dataSize - start; + memmove(buffer.data(), buffer.data() + start, offset); + break; + } + else { + ++nbLine; + } + + if ((buffer[start] == '#') + || ((buffer[start] == '/') && ((start + 1 < dataSize) && (buffer[start + 1] == '/')))) { + start = endOfLine; + continue; + } + + // Each line should follow this format: + // 001.009.096.105 - 001.009.096.105 , 000 , Some organization + // The 3rd entry is access level and if above 127 the IP range isn't blocked. + int firstComma = findAndNullDelimiter(buffer.data(), ',', start, endOfLine); + if (firstComma != -1) + findAndNullDelimiter(buffer.data(), ',', firstComma + 1, endOfLine); + + // Check if there is an access value (apparently not mandatory) + if (firstComma != -1) { + // There is possibly one + const long int nbAccess = strtol(buffer.data() + firstComma + 1, nullptr, 10); + // Ignoring this rule because access value is too high + if (nbAccess > 127L) { + start = endOfLine; + continue; + } + } + + // IP Range should be split by a dash + int endOfIPRange = ((firstComma == -1) ? (endOfLine - 1) : (firstComma - 1)); + int delimIP = findAndNullDelimiter(buffer.data(), '-', start, endOfIPRange); + if (delimIP == -1) { + start = endOfLine; + continue; + } + + boost::system::error_code ec; + int newStart = trim(buffer.data(), start, delimIP - 1); + libt::address startAddr = libt::address::from_string(buffer.data() + newStart, ec); + Q_ASSERT(!ec); + if (ec) { + start = endOfLine; + continue; + } + + newStart = trim(buffer.data(), delimIP + 1, endOfIPRange); + libt::address endAddr = libt::address::from_string(buffer.data() + newStart, ec); + Q_ASSERT(!ec); + if (ec) { + start = endOfLine; + continue; + } + + if ((startAddr.is_v4() != endAddr.is_v4()) + || (startAddr.is_v6() != endAddr.is_v6())) { + start = endOfLine; + continue; + } + + start = endOfLine; + + filter.add_rule(startAddr, endAddr, libt::ip_filter::blocked); + ++ruleCount; + } + + if (start >= dataSize) + offset = 0; + } + + return ruleCount; +} + +void Session::loadOfflineFilter() { + int Count = 0; + libt::ip_filter offlineFilter = m_nativeSession->get_ip_filter(); + +#if defined(Q_OS_WIN) + Count = parseOfflineFilterFile("./ipfilter.dat", offlineFilter); +#endif + +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) + Count = parseOfflineFilterFile(QDir::home().absoluteFilePath(".config")+"/qBittorrent/ipfilter.dat", offlineFilter); +#endif + + m_nativeSession->set_ip_filter(offlineFilter); + Logger::instance()->addMessage(tr("Successfully parsed the offline downloader IP filter: %1 rules were applied.", "%1 is a number").arg(Count)); } void Session::recursiveTorrentDownload(const InfoHash &hash) @@ -3217,12 +3413,14 @@ void Session::handleIPFilterParsed(int ruleCount) { Logger::instance()->addMessage(tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount)); emit IPFilterParsed(false, ruleCount); + loadOfflineFilter(); } void Session::handleIPFilterError() { Logger::instance()->addMessage(tr("Error: Failed to parse the provided IP filter."), Log::CRITICAL); emit IPFilterParsed(true, 0); + loadOfflineFilter(); } #if LIBTORRENT_VERSION_NUM < 10100 diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 5a2f9abb17b5..26c8b4ed7d35 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -250,9 +251,11 @@ namespace BitTorrent void setBandwidthSchedulerEnabled(bool enabled); uint saveResumeDataInterval() const; - bool isAutoBanUnknownPeerEnabled() const; void setSaveResumeDataInterval(uint value); + bool isAutoBanUnknownPeerEnabled() const; void setAutoBanUnknownPeer(bool value); + bool isShowTrackerAuthWindow() const; + void setShowTrackerAuthWindow(bool value); int port() const; void setPort(int port); bool useRandomPort() const; @@ -463,6 +466,8 @@ namespace BitTorrent void populateAdditionalTrackers(); void enableIPFilter(); void disableIPFilter(); + int parseOfflineFilterFile(QString ipDat, libtorrent::ip_filter &filter); + void loadOfflineFilter(); bool addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri, TorrentInfo torrentInfo = TorrentInfo(), @@ -554,6 +559,7 @@ namespace BitTorrent CachedSettingValue m_isBandwidthSchedulerEnabled; CachedSettingValue m_saveResumeDataInterval; CachedSettingValue m_autoBanUnknownPeer; + CachedSettingValue m_showTrackerAuthWindow; CachedSettingValue m_port; CachedSettingValue m_useRandomPort; CachedSettingValue m_networkInterface; diff --git a/src/base/bittorrent/torrenthandle.cpp b/src/base/bittorrent/torrenthandle.cpp index 01b77ad50ee3..589935973292 100644 --- a/src/base/bittorrent/torrenthandle.cpp +++ b/src/base/bittorrent/torrenthandle.cpp @@ -1401,7 +1401,8 @@ void TorrentHandle::handleTrackerErrorAlert(libtorrent::tracker_error_alert *p) m_trackerInfos[trackerUrl].lastMessage = message; if (p->status_code == 401) - m_session->handleTorrentTrackerAuthenticationRequired(this, trackerUrl); + if (Preferences::instance()->getShowTrackerAuthWindow()) + m_session->handleTorrentTrackerAuthenticationRequired(this, trackerUrl); m_session->handleTorrentTrackerError(this, trackerUrl); } diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index a6f093865988..57cf5aeae32d 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -1084,6 +1084,16 @@ void Preferences::setAutoBanUnknownPeer(const bool checked) setValue("Preferences/Advanced/AutoBanUnknownPeer", checked); } +bool Preferences::getShowTrackerAuthWindow() const +{ + return value("Preferences/Advanced/ShowTrackerAuthWindow", true).toBool(); +} + +void Preferences::setShowTrackerAuthWindow(const bool checked) +{ + setValue("Preferences/Advanced/ShowTrackerAuthWindow", checked); +} + // Stuff that don't appear in the Options GUI but are saved // in the same file. diff --git a/src/base/preferences.h b/src/base/preferences.h index 83556c7d3290..83cccd13943d 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -263,6 +263,8 @@ class Preferences: public QObject void setTrayIconStyle(TrayIcon::Style style); bool getAutoBanUnknownPeer() const; void setAutoBanUnknownPeer(const bool checked); + bool getShowTrackerAuthWindow() const; + void setShowTrackerAuthWindow(const bool checked); // Stuff that don't appear in the Options GUI but are saved // in the same file. diff --git a/src/base/settingsstorage.cpp b/src/base/settingsstorage.cpp index 2b5442dac7ae..50fe756e8825 100644 --- a/src/base/settingsstorage.cpp +++ b/src/base/settingsstorage.cpp @@ -118,6 +118,7 @@ namespace {"BitTorrent/Session/InterfaceAddress", "Preferences/Connection/InterfaceAddress"}, {"BitTorrent/Session/SaveResumeDataInterval", "Preferences/Downloads/SaveResumeDataInterval"}, {"BitTorrent/Session/AutoBanUnknownPeer", "Preferences/Advanced/AutoBanUnknownPeer"}, + {"BitTorrent/Session/ShowTrackerAuthWindow", "Preferences/Advanced/ShowTrackerAuthWindow"}, {"BitTorrent/Session/Encryption", "Preferences/Bittorrent/Encryption"}, {"BitTorrent/Session/ForceProxy", "Preferences/Connection/ProxyForce"}, {"BitTorrent/Session/ProxyPeerConnections", "Preferences/Connection/ProxyPeerConnections"}, diff --git a/src/gui/advancedsettings.cpp b/src/gui/advancedsettings.cpp index 83cf7af3cc00..637bb14563e9 100644 --- a/src/gui/advancedsettings.cpp +++ b/src/gui/advancedsettings.cpp @@ -56,6 +56,7 @@ enum AdvSettingsRows // behavior SAVE_RESUME_DATA_INTERVAL, CONFIRM_AUTO_BAN, + SHOW_TRACKER_AUTH_WINDOW, CONFIRM_RECHECK_TORRENT, RECHECK_COMPLETED, #if defined(Q_OS_WIN) || defined(Q_OS_MAC) @@ -169,6 +170,8 @@ void AdvancedSettings::saveAdvancedSettings() session->setAnnounceIP(addr.isNull() ? "" : addr.toString()); // Enable Auto ban Unknown Peer session->setAutoBanUnknownPeer(cb_auto_ban_unknown_peer.isChecked()); + // Show Tracker Authenticaion Window + session->setShowTrackerAuthWindow(cb_show_tracker_auth_window.isChecked()); // Program notification MainWindow * const mainWindow = static_cast(QCoreApplication::instance())->mainWindow(); @@ -353,6 +356,9 @@ void AdvancedSettings::loadAdvancedSettings() // Auto Ban Unknown Peer from China cb_auto_ban_unknown_peer.setChecked(session->isAutoBanUnknownPeerEnabled()); addRow(CONFIRM_AUTO_BAN, tr("Auto Ban Unknown Peer from China"), &cb_auto_ban_unknown_peer); + // Show Tracker Authenticaion Window + cb_show_tracker_auth_window.setChecked(session->isShowTrackerAuthWindow()); + addRow(SHOW_TRACKER_AUTH_WINDOW, tr("Show Tracker Authenticaion Window"), &cb_show_tracker_auth_window); // Program notifications const MainWindow * const mainWindow = static_cast(QCoreApplication::instance())->mainWindow(); diff --git a/src/gui/advancedsettings.h b/src/gui/advancedsettings.h index 3d7b6dbb4326..97d09e9b7824 100644 --- a/src/gui/advancedsettings.h +++ b/src/gui/advancedsettings.h @@ -79,7 +79,7 @@ private slots: QSpinBox spin_cache, spin_save_resume_data_interval, outgoing_ports_min, outgoing_ports_max, spin_list_refresh, spin_maxhalfopen, spin_tracker_port, spin_cache_ttl; QCheckBox cb_os_cache, cb_recheck_completed, cb_resolve_countries, cb_resolve_hosts, cb_super_seeding, cb_program_notifications, cb_torrent_added_notifications, cb_tracker_favicon, cb_tracker_status, - cb_confirm_torrent_recheck, cb_listen_ipv6, cb_announce_all_trackers, cb_auto_ban_unknown_peer; + cb_confirm_torrent_recheck, cb_listen_ipv6, cb_announce_all_trackers, cb_auto_ban_unknown_peer, cb_show_tracker_auth_window; QComboBox combo_iface, combo_iface_address; QLineEdit txtAnnounceIP; diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index c34490cc94e9..97ff359131f8 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1394,12 +1394,13 @@ void MainWindow::updateGUI() BitTorrent::PeerAddress addr = peer.address(); if (addr.ip.isNull()) continue; QString ip = addr.ip.toString(); + int port = peer.port(); QString client = peer.client(); QString ptoc = peer.pidtoclient(); QString pid = peer.pid().left(8); QString country = peer.country(); - QRegExp filter("-(XL\\d+|SD\\d+|XF\\d+|QD\\d+|BN\\d+)-"); + QRegExp filter("-(XL|SD|XF|QD|BN)(\\d+)-"); if (filter.exactMatch(pid)) { qDebug("Auto Banning bad Peer %s...", ip.toLocal8Bit().data()); Logger::instance()->addMessage(tr("Auto banning bad Peer '%1'...'%2'...'%3'...'%4'").arg(ip).arg(pid).arg(ptoc).arg(country)); @@ -1409,11 +1410,18 @@ void MainWindow::updateGUI() } if(m_AutoBan) { - if(client.contains("Unknown") && country == "CN") { + if (client.contains("Unknown") && country == "CN") { qDebug("Auto Banning Unknown Peer %s...", ip.toLocal8Bit().data()); Logger::instance()->addMessage(tr("Auto banning Unknown Peer '%1'...'%2'...'%3'...'%4'").arg(ip).arg(pid).arg(ptoc).arg(country)); BitTorrent::Session::instance()->tempblockIP(ip); insertQueue(ip); + continue; + } + if (port >= 65000 && country == "CN" && client.contains("Transmission")) { + qDebug("Auto Banning Offline Downloader %s...", ip.toLocal8Bit().data()); + Logger::instance()->addMessage(tr("Auto banning Offline Downloader '%1:%2'...'%3'...'%4'...'%5'").arg(ip).arg(port).arg(pid).arg(ptoc).arg(country)); + BitTorrent::Session::instance()->tempblockIP(ip); + insertQueue(ip); } } } diff --git a/version.pri b/version.pri index aaa94ee0d7b1..8a4dc13513b3 100644 --- a/version.pri +++ b/version.pri @@ -5,7 +5,7 @@ PROJECT_NAME = qbittorrent VER_MAJOR = 3 VER_MINOR = 3 VER_BUGFIX = 16 -VER_BUILD = 2 +VER_BUILD = 3 VER_STATUS = # Should be empty for stable releases! # Don't touch the rest part