mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-22 09:16:05 +03:00
Add support for SSL torrents
The 'SSL torrent' feature is not standardized. I.e. there are no BEP (BitTorrent Enhancement Proposals) associated with it, so we do not greatly encourage its usage as it will only work with libtorrent clients and derivatives. It will not work with other torrent clients that do not support the libtorrent specific implementation. This PR aims to provide minimal support for those who need SSL torrents. Furthermore, it is intended that there will be no UI support (nor indication) of adding/creating SSL torrents. * Prerequisites: I omit the instructions of creating those files as the intended audience (experts & advanced users) should have no problem with it. All files are as follow: 1. Root (torrent publisher) certificate 2. Root private key 3. A .torrent file created with root certificate 5. Peer certificate (signed by the root certificate) 6. Peer private key 7. Diffie-Hellman parameters file All files are stored in .pem format. * Enable SSL torrent protocol in qbt There are 2 hidden keys to put in qbt config file, under `[BitTorrent]` section: 1. `Session\SSL\Enabled`: set it to `true`. 2. `Session\SSL\Port`: set it to some unused port or omit the key entirely to let qbt pick one for you. * Add an SSL torrent to qbt The only way of adding an SSL torrent is via WebAPI. The `/api/v2/torrents/add` endpoint will support 3 additional parameters. You must provide them for an SSL torrent. 1. `ssl_certificate`: Contents of the peer certificate file (in PEM format). 2. `ssl_private_key`: Contents of the peer private key file. 3. `ssl_dh_params`: Contents of the Diffie-Hellman parameters file. * Change the SSL parameters to a torrent In case you provided wrong SSL parameters when adding a torrent, there is a new endpoint `/api/v2/torrents/setSSLParameters` that you can update the SSL parameters. The parameters (`ssl_*`) are the same as `/api/v2/torrents/add` endpoint. * Query the SSL parameters of a torrent There is a new endpoint `/api/v2/torrents/SSLParameters` that you can query the SSL parameters of a torrent. References: * https://www.libtorrent.org/manual-ref.html#ssl-torrents * https://blog.libtorrent.org/2012/01/bittorrent-over-ssl/ PR #20338. --------- Co-authored-by: Radu Carpa <radu.carpa@cern.ch>
This commit is contained in:
parent
c6ee0ff017
commit
cffd74b62a
24 changed files with 482 additions and 101 deletions
|
@ -33,6 +33,7 @@ add_library(qbt_base STATIC
|
|||
bittorrent/sessionimpl.h
|
||||
bittorrent/sessionstatus.h
|
||||
bittorrent/speedmonitor.h
|
||||
bittorrent/sslparameters.h
|
||||
bittorrent/torrent.h
|
||||
bittorrent/torrentcontenthandler.h
|
||||
bittorrent/torrentcontentlayout.h
|
||||
|
@ -105,6 +106,7 @@ add_library(qbt_base STATIC
|
|||
utils/os.h
|
||||
utils/password.h
|
||||
utils/random.h
|
||||
utils/sslkey.h
|
||||
utils/string.h
|
||||
utils/thread.h
|
||||
utils/version.h
|
||||
|
@ -135,6 +137,7 @@ add_library(qbt_base STATIC
|
|||
bittorrent/resumedatastorage.cpp
|
||||
bittorrent/sessionimpl.cpp
|
||||
bittorrent/speedmonitor.cpp
|
||||
bittorrent/sslparameters.cpp
|
||||
bittorrent/torrent.cpp
|
||||
bittorrent/torrentcontenthandler.cpp
|
||||
bittorrent/torrentcreator.cpp
|
||||
|
@ -194,6 +197,7 @@ add_library(qbt_base STATIC
|
|||
utils/os.cpp
|
||||
utils/password.cpp
|
||||
utils/random.cpp
|
||||
utils/sslkey.cpp
|
||||
utils/string.cpp
|
||||
utils/thread.cpp
|
||||
utils/version.cpp
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
|
||||
#include "base/utils/sslkey.h"
|
||||
#include "base/utils/string.h"
|
||||
|
||||
const QString PARAM_CATEGORY = u"category"_s;
|
||||
|
@ -51,6 +52,9 @@ const QString PARAM_DOWNLOADLIMIT = u"download_limit"_s;
|
|||
const QString PARAM_SEEDINGTIMELIMIT = u"seeding_time_limit"_s;
|
||||
const QString PARAM_INACTIVESEEDINGTIMELIMIT = u"inactive_seeding_time_limit"_s;
|
||||
const QString PARAM_RATIOLIMIT = u"ratio_limit"_s;
|
||||
const QString PARAM_SSL_CERTIFICATE = u"ssl_certificate"_s;
|
||||
const QString PARAM_SSL_PRIVATEKEY = u"ssl_private_key"_s;
|
||||
const QString PARAM_SSL_DHPARAMS = u"ssl_dh_params"_s;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -122,7 +126,13 @@ BitTorrent::AddTorrentParams BitTorrent::parseAddTorrentParams(const QJsonObject
|
|||
.downloadLimit = jsonObj.value(PARAM_DOWNLOADLIMIT).toInt(-1),
|
||||
.seedingTimeLimit = jsonObj.value(PARAM_SEEDINGTIMELIMIT).toInt(Torrent::USE_GLOBAL_SEEDING_TIME),
|
||||
.inactiveSeedingTimeLimit = jsonObj.value(PARAM_INACTIVESEEDINGTIMELIMIT).toInt(Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME),
|
||||
.ratioLimit = jsonObj.value(PARAM_RATIOLIMIT).toDouble(Torrent::USE_GLOBAL_RATIO)
|
||||
.ratioLimit = jsonObj.value(PARAM_RATIOLIMIT).toDouble(Torrent::USE_GLOBAL_RATIO),
|
||||
.sslParameters =
|
||||
{
|
||||
.certificate = QSslCertificate(jsonObj.value(PARAM_SSL_CERTIFICATE).toString().toLatin1()),
|
||||
.privateKey = Utils::SSLKey::load(jsonObj.value(PARAM_SSL_PRIVATEKEY).toString().toLatin1()),
|
||||
.dhParams = jsonObj.value(PARAM_SSL_DHPARAMS).toString().toLatin1()
|
||||
}
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
@ -136,13 +146,16 @@ QJsonObject BitTorrent::serializeAddTorrentParams(const AddTorrentParams ¶ms
|
|||
{PARAM_SAVEPATH, params.savePath.data()},
|
||||
{PARAM_DOWNLOADPATH, params.downloadPath.data()},
|
||||
{PARAM_OPERATINGMODE, Utils::String::fromEnum(params.addForced
|
||||
? TorrentOperatingMode::Forced : TorrentOperatingMode::AutoManaged)},
|
||||
? TorrentOperatingMode::Forced : TorrentOperatingMode::AutoManaged)},
|
||||
{PARAM_SKIPCHECKING, params.skipChecking},
|
||||
{PARAM_UPLOADLIMIT, params.uploadLimit},
|
||||
{PARAM_DOWNLOADLIMIT, params.downloadLimit},
|
||||
{PARAM_SEEDINGTIMELIMIT, params.seedingTimeLimit},
|
||||
{PARAM_INACTIVESEEDINGTIMELIMIT, params.inactiveSeedingTimeLimit},
|
||||
{PARAM_RATIOLIMIT, params.ratioLimit}
|
||||
{PARAM_RATIOLIMIT, params.ratioLimit},
|
||||
{PARAM_SSL_CERTIFICATE, QString::fromLatin1(params.sslParameters.certificate.toPem())},
|
||||
{PARAM_SSL_PRIVATEKEY, QString::fromLatin1(params.sslParameters.privateKey.toPem())},
|
||||
{PARAM_SSL_DHPARAMS, QString::fromLatin1(params.sslParameters.dhParams)}
|
||||
};
|
||||
|
||||
if (params.addToQueueTop)
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include "base/path.h"
|
||||
#include "base/tagset.h"
|
||||
#include "sslparameters.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
|
||||
|
@ -69,6 +70,7 @@ namespace BitTorrent
|
|||
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
||||
int inactiveSeedingTimeLimit = Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME;
|
||||
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
||||
SSLParameters sslParameters;
|
||||
|
||||
friend bool operator==(const AddTorrentParams &lhs, const AddTorrentParams &rhs) = default;
|
||||
};
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "base/tagset.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/io.h"
|
||||
#include "base/utils/sslkey.h"
|
||||
#include "base/utils/string.h"
|
||||
#include "infohash.h"
|
||||
#include "loadtorrentparams.h"
|
||||
|
@ -73,12 +74,22 @@ namespace BitTorrent
|
|||
|
||||
namespace
|
||||
{
|
||||
const char KEY_SSL_CERTIFICATE[] = "qBt-sslCertificate";
|
||||
const char KEY_SSL_PRIVATE_KEY[] = "qBt-sslPrivateKey";
|
||||
const char KEY_SSL_DH_PARAMS[] = "qBt-sslDhParams";
|
||||
|
||||
template <typename LTStr>
|
||||
QString fromLTString(const LTStr &str)
|
||||
{
|
||||
return QString::fromUtf8(str.data(), static_cast<qsizetype>(str.size()));
|
||||
}
|
||||
|
||||
template <typename LTStr>
|
||||
QByteArray toByteArray(const LTStr &str)
|
||||
{
|
||||
return {str.data(), static_cast<qsizetype>(str.size())};
|
||||
}
|
||||
|
||||
using ListType = lt::entry::list_type;
|
||||
|
||||
ListType setToEntryList(const TagSet &input)
|
||||
|
@ -251,6 +262,12 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
|
|||
|
||||
torrentParams.stopCondition = Utils::String::toEnum(
|
||||
fromLTString(resumeDataRoot.dict_find_string_value("qBt-stopCondition")), Torrent::StopCondition::None);
|
||||
torrentParams.sslParameters =
|
||||
{
|
||||
.certificate = QSslCertificate(toByteArray(resumeDataRoot.dict_find_string_value(KEY_SSL_CERTIFICATE))),
|
||||
.privateKey = Utils::SSLKey::load(toByteArray(resumeDataRoot.dict_find_string_value(KEY_SSL_PRIVATE_KEY))),
|
||||
.dhParams = toByteArray(resumeDataRoot.dict_find_string_value(KEY_SSL_DH_PARAMS))
|
||||
};
|
||||
|
||||
const lt::string_view ratioLimitString = resumeDataRoot.dict_find_string_value("qBt-ratioLimit");
|
||||
if (ratioLimitString.empty())
|
||||
|
@ -409,6 +426,13 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co
|
|||
data["qBt-firstLastPiecePriority"] = resumeData.firstLastPiecePriority;
|
||||
data["qBt-stopCondition"] = Utils::String::fromEnum(resumeData.stopCondition).toStdString();
|
||||
|
||||
if (!resumeData.sslParameters.certificate.isNull())
|
||||
data[KEY_SSL_CERTIFICATE] = resumeData.sslParameters.certificate.toPem().toStdString();
|
||||
if (!resumeData.sslParameters.privateKey.isNull())
|
||||
data[KEY_SSL_PRIVATE_KEY] = resumeData.sslParameters.privateKey.toPem().toStdString();
|
||||
if (!resumeData.sslParameters.dhParams.isEmpty())
|
||||
data[KEY_SSL_DH_PARAMS] = resumeData.sslParameters.dhParams.toStdString();
|
||||
|
||||
if (!resumeData.useAutoTMM)
|
||||
{
|
||||
data["qBt-savePath"] = Profile::instance()->toPortablePath(resumeData.savePath).data().toStdString();
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include "base/preferences.h"
|
||||
#include "base/profile.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/sslkey.h"
|
||||
#include "base/utils/string.h"
|
||||
#include "infohash.h"
|
||||
#include "loadtorrentparams.h"
|
||||
|
@ -66,7 +67,7 @@ namespace
|
|||
{
|
||||
const QString DB_CONNECTION_NAME = u"ResumeDataStorage"_s;
|
||||
|
||||
const int DB_VERSION = 5;
|
||||
const int DB_VERSION = 6;
|
||||
|
||||
const QString DB_TABLE_META = u"meta"_s;
|
||||
const QString DB_TABLE_TORRENTS = u"torrents"_s;
|
||||
|
@ -121,7 +122,8 @@ namespace
|
|||
|
||||
Column makeColumn(const char *columnName)
|
||||
{
|
||||
return {QString::fromLatin1(columnName), (u':' + QString::fromLatin1(columnName))};
|
||||
const QString name = QString::fromLatin1(columnName);
|
||||
return {.name = name, .placeholder = (u':' + name)};
|
||||
}
|
||||
|
||||
const Column DB_COLUMN_ID = makeColumn("id");
|
||||
|
@ -141,6 +143,9 @@ namespace
|
|||
const Column DB_COLUMN_OPERATING_MODE = makeColumn("operating_mode");
|
||||
const Column DB_COLUMN_STOPPED = makeColumn("stopped");
|
||||
const Column DB_COLUMN_STOP_CONDITION = makeColumn("stop_condition");
|
||||
const Column DB_COLUMN_SSL_CERTIFICATE = makeColumn("ssl_certificate");
|
||||
const Column DB_COLUMN_SSL_PRIVATE_KEY = makeColumn("ssl_private_key");
|
||||
const Column DB_COLUMN_SSL_DH_PARAMS = makeColumn("ssl_dh_params");
|
||||
const Column DB_COLUMN_RESUMEDATA = makeColumn("libtorrent_resume_data");
|
||||
const Column DB_COLUMN_METADATA = makeColumn("metadata");
|
||||
const Column DB_COLUMN_VALUE = makeColumn("value");
|
||||
|
@ -154,7 +159,6 @@ namespace
|
|||
QString quoted(const QString &name)
|
||||
{
|
||||
const QChar quote = u'`';
|
||||
|
||||
return (quote + name + quote);
|
||||
}
|
||||
|
||||
|
@ -237,6 +241,12 @@ namespace
|
|||
resumeData.stopped = query.value(DB_COLUMN_STOPPED.name).toBool();
|
||||
resumeData.stopCondition = Utils::String::toEnum(
|
||||
query.value(DB_COLUMN_STOP_CONDITION.name).toString(), Torrent::StopCondition::None);
|
||||
resumeData.sslParameters =
|
||||
{
|
||||
.certificate = QSslCertificate(query.value(DB_COLUMN_SSL_CERTIFICATE.name).toByteArray()),
|
||||
.privateKey = Utils::SSLKey::load(query.value(DB_COLUMN_SSL_PRIVATE_KEY.name).toByteArray()),
|
||||
.dhParams = query.value(DB_COLUMN_SSL_DH_PARAMS.name).toByteArray()
|
||||
};
|
||||
|
||||
resumeData.savePath = Profile::instance()->fromPortablePath(
|
||||
Path(query.value(DB_COLUMN_TARGET_SAVE_PATH.name).toString()));
|
||||
|
@ -535,6 +545,9 @@ void BitTorrent::DBResumeDataStorage::createDB() const
|
|||
makeColumnDefinition(DB_COLUMN_OPERATING_MODE, "TEXT NOT NULL"),
|
||||
makeColumnDefinition(DB_COLUMN_STOPPED, "INTEGER NOT NULL"),
|
||||
makeColumnDefinition(DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`"),
|
||||
makeColumnDefinition(DB_COLUMN_SSL_CERTIFICATE, "TEXT"),
|
||||
makeColumnDefinition(DB_COLUMN_SSL_PRIVATE_KEY, "TEXT"),
|
||||
makeColumnDefinition(DB_COLUMN_SSL_DH_PARAMS, "TEXT"),
|
||||
makeColumnDefinition(DB_COLUMN_RESUMEDATA, "BLOB NOT NULL"),
|
||||
makeColumnDefinition(DB_COLUMN_METADATA, "BLOB")
|
||||
};
|
||||
|
@ -574,31 +587,22 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
|||
|
||||
try
|
||||
{
|
||||
if (fromVersion == 1)
|
||||
const auto addColumn = [&query](const QString &table, const Column &column, const char *definition)
|
||||
{
|
||||
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_s
|
||||
.arg(quoted(DB_COLUMN_DOWNLOAD_PATH.name), quoted(DB_TABLE_TORRENTS));
|
||||
if (!query.exec(testQuery))
|
||||
{
|
||||
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_s
|
||||
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, "TEXT"));
|
||||
if (!query.exec(alterTableTorrentsQuery))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
}
|
||||
}
|
||||
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_s.arg(quoted(column.name), quoted(table));
|
||||
if (query.exec(testQuery))
|
||||
return;
|
||||
|
||||
const auto alterTableQuery = u"ALTER TABLE %1 ADD %2"_s.arg(quoted(table), makeColumnDefinition(column, definition));
|
||||
if (!query.exec(alterTableQuery))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
};
|
||||
|
||||
if (fromVersion <= 1)
|
||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_DOWNLOAD_PATH, "TEXT");
|
||||
|
||||
if (fromVersion <= 2)
|
||||
{
|
||||
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_s
|
||||
.arg(quoted(DB_COLUMN_STOP_CONDITION.name), quoted(DB_TABLE_TORRENTS));
|
||||
if (!query.exec(testQuery))
|
||||
{
|
||||
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_s
|
||||
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`"));
|
||||
if (!query.exec(alterTableTorrentsQuery))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
}
|
||||
}
|
||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_STOP_CONDITION, "TEXT NOT NULL DEFAULT `None`");
|
||||
|
||||
if (fromVersion <= 3)
|
||||
{
|
||||
|
@ -610,16 +614,13 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const
|
|||
}
|
||||
|
||||
if (fromVersion <= 4)
|
||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, "INTEGER NOT NULL DEFAULT -2");
|
||||
|
||||
if (fromVersion <= 5)
|
||||
{
|
||||
const auto testQuery = u"SELECT COUNT(%1) FROM %2;"_s
|
||||
.arg(quoted(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT.name), quoted(DB_TABLE_TORRENTS));
|
||||
if (!query.exec(testQuery))
|
||||
{
|
||||
const auto alterTableTorrentsQuery = u"ALTER TABLE %1 ADD %2"_s
|
||||
.arg(quoted(DB_TABLE_TORRENTS), makeColumnDefinition(DB_COLUMN_INACTIVE_SEEDING_TIME_LIMIT, "INTEGER NOT NULL DEFAULT -2"));
|
||||
if (!query.exec(alterTableTorrentsQuery))
|
||||
throw RuntimeError(query.lastError().text());
|
||||
}
|
||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_CERTIFICATE, "TEXT");
|
||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_PRIVATE_KEY, "TEXT");
|
||||
addColumn(DB_TABLE_TORRENTS, DB_COLUMN_SSL_DH_PARAMS, "TEXT");
|
||||
}
|
||||
|
||||
const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE});
|
||||
|
@ -805,6 +806,9 @@ namespace
|
|||
DB_COLUMN_OPERATING_MODE,
|
||||
DB_COLUMN_STOPPED,
|
||||
DB_COLUMN_STOP_CONDITION,
|
||||
DB_COLUMN_SSL_CERTIFICATE,
|
||||
DB_COLUMN_SSL_PRIVATE_KEY,
|
||||
DB_COLUMN_SSL_DH_PARAMS,
|
||||
DB_COLUMN_RESUMEDATA
|
||||
};
|
||||
|
||||
|
@ -864,6 +868,9 @@ namespace
|
|||
query.bindValue(DB_COLUMN_OPERATING_MODE.placeholder, Utils::String::fromEnum(m_resumeData.operatingMode));
|
||||
query.bindValue(DB_COLUMN_STOPPED.placeholder, m_resumeData.stopped);
|
||||
query.bindValue(DB_COLUMN_STOP_CONDITION.placeholder, Utils::String::fromEnum(m_resumeData.stopCondition));
|
||||
query.bindValue(DB_COLUMN_SSL_CERTIFICATE.placeholder, QString::fromLatin1(m_resumeData.sslParameters.certificate.toPem()));
|
||||
query.bindValue(DB_COLUMN_SSL_PRIVATE_KEY.placeholder, QString::fromLatin1(m_resumeData.sslParameters.privateKey.toPem()));
|
||||
query.bindValue(DB_COLUMN_SSL_DH_PARAMS.placeholder, QString::fromLatin1(m_resumeData.sslParameters.dhParams));
|
||||
|
||||
if (!m_resumeData.useAutoTMM)
|
||||
{
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include "base/path.h"
|
||||
#include "base/tagset.h"
|
||||
#include "sslparameters.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
|
||||
|
@ -61,5 +62,6 @@ namespace BitTorrent
|
|||
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
||||
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
||||
int inactiveSeedingTimeLimit = Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME;
|
||||
SSLParameters sslParameters;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -263,6 +263,10 @@ namespace BitTorrent
|
|||
virtual void setSaveResumeDataInterval(int value) = 0;
|
||||
virtual int port() const = 0;
|
||||
virtual void setPort(int port) = 0;
|
||||
virtual bool isSSLEnabled() const = 0;
|
||||
virtual void setSSLEnabled(bool enabled) = 0;
|
||||
virtual int sslPort() const = 0;
|
||||
virtual void setSSLPort(int port) = 0;
|
||||
virtual QString networkInterface() const = 0;
|
||||
virtual void setNetworkInterface(const QString &iface) = 0;
|
||||
virtual QString networkInterfaceName() const = 0;
|
||||
|
|
|
@ -479,6 +479,8 @@ SessionImpl::SessionImpl(QObject *parent)
|
|||
, m_isPerformanceWarningEnabled(BITTORRENT_SESSION_KEY(u"PerformanceWarning"_s), false)
|
||||
, m_saveResumeDataInterval(BITTORRENT_SESSION_KEY(u"SaveResumeDataInterval"_s), 60)
|
||||
, m_port(BITTORRENT_SESSION_KEY(u"Port"_s), -1)
|
||||
, m_sslEnabled(BITTORRENT_SESSION_KEY(u"SSL/Enabled"_s), false)
|
||||
, m_sslPort(BITTORRENT_SESSION_KEY(u"SSL/Port"_s), -1)
|
||||
, m_networkInterface(BITTORRENT_SESSION_KEY(u"Interface"_s))
|
||||
, m_networkInterfaceName(BITTORRENT_SESSION_KEY(u"InterfaceName"_s))
|
||||
, m_networkInterfaceAddress(BITTORRENT_SESSION_KEY(u"InterfaceAddress"_s))
|
||||
|
@ -529,6 +531,12 @@ SessionImpl::SessionImpl(QObject *parent)
|
|||
|
||||
if (port() < 0)
|
||||
m_port = Utils::Random::rand(1024, 65535);
|
||||
if (sslPort() < 0)
|
||||
{
|
||||
m_sslPort = Utils::Random::rand(1024, 65535);
|
||||
while (m_sslPort == port())
|
||||
m_sslPort = Utils::Random::rand(1024, 65535);
|
||||
}
|
||||
|
||||
m_recentErroredTorrentsTimer->setSingleShot(true);
|
||||
m_recentErroredTorrentsTimer->setInterval(1s);
|
||||
|
@ -2022,7 +2030,9 @@ void SessionImpl::applyNetworkInterfacesSettings(lt::settings_pack &settingsPack
|
|||
|
||||
QStringList endpoints;
|
||||
QStringList outgoingInterfaces;
|
||||
const QString portString = u':' + QString::number(port());
|
||||
QStringList portStrings = {u':' + QString::number(port())};
|
||||
if (isSSLEnabled())
|
||||
portStrings.append(u':' + QString::number(sslPort()) + u's');
|
||||
|
||||
for (const QString &ip : asConst(getListeningIPs()))
|
||||
{
|
||||
|
@ -2034,7 +2044,8 @@ void SessionImpl::applyNetworkInterfacesSettings(lt::settings_pack &settingsPack
|
|||
? Utils::Net::canonicalIPv6Addr(addr).toString()
|
||||
: addr.toString();
|
||||
|
||||
endpoints << ((isIPv6 ? (u'[' + ip + u']') : ip) + portString);
|
||||
for (const QString &portString : asConst(portStrings))
|
||||
endpoints << ((isIPv6 ? (u'[' + ip + u']') : ip) + portString);
|
||||
|
||||
if ((ip != u"0.0.0.0") && (ip != u"::"))
|
||||
outgoingInterfaces << ip;
|
||||
|
@ -2049,7 +2060,8 @@ void SessionImpl::applyNetworkInterfacesSettings(lt::settings_pack &settingsPack
|
|||
const QString guid = convertIfaceNameToGuid(ip);
|
||||
if (!guid.isEmpty())
|
||||
{
|
||||
endpoints << (guid + portString);
|
||||
for (const QString &portString : asConst(portStrings))
|
||||
endpoints << (guid + portString);
|
||||
outgoingInterfaces << guid;
|
||||
}
|
||||
else
|
||||
|
@ -2057,11 +2069,13 @@ void SessionImpl::applyNetworkInterfacesSettings(lt::settings_pack &settingsPack
|
|||
LogMsg(tr("Could not find GUID of network interface. Interface: \"%1\"").arg(ip), Log::WARNING);
|
||||
// Since we can't get the GUID, we'll pass the interface name instead.
|
||||
// Otherwise an empty string will be passed to outgoing_interface which will cause IP leak.
|
||||
endpoints << (ip + portString);
|
||||
for (const QString &portString : asConst(portStrings))
|
||||
endpoints << (ip + portString);
|
||||
outgoingInterfaces << ip;
|
||||
}
|
||||
#else
|
||||
endpoints << (ip + portString);
|
||||
for (const QString &portString : asConst(portStrings))
|
||||
endpoints << (ip + portString);
|
||||
outgoingInterfaces << ip;
|
||||
#endif
|
||||
}
|
||||
|
@ -2640,6 +2654,7 @@ LoadTorrentParams SessionImpl::initLoadTorrentParams(const AddTorrentParams &add
|
|||
loadTorrentParams.ratioLimit = addTorrentParams.ratioLimit;
|
||||
loadTorrentParams.seedingTimeLimit = addTorrentParams.seedingTimeLimit;
|
||||
loadTorrentParams.inactiveSeedingTimeLimit = addTorrentParams.inactiveSeedingTimeLimit;
|
||||
loadTorrentParams.sslParameters = addTorrentParams.sslParameters;
|
||||
|
||||
const QString category = addTorrentParams.category;
|
||||
if (!category.isEmpty() && !m_categories.contains(category) && !addCategory(category))
|
||||
|
@ -3525,6 +3540,40 @@ void SessionImpl::setPort(const int port)
|
|||
}
|
||||
}
|
||||
|
||||
bool SessionImpl::isSSLEnabled() const
|
||||
{
|
||||
return m_sslEnabled;
|
||||
}
|
||||
|
||||
void SessionImpl::setSSLEnabled(const bool enabled)
|
||||
{
|
||||
if (enabled == isSSLEnabled())
|
||||
return;
|
||||
|
||||
m_sslEnabled = enabled;
|
||||
configureListeningInterface();
|
||||
|
||||
if (isReannounceWhenAddressChangedEnabled())
|
||||
reannounceToAllTrackers();
|
||||
}
|
||||
|
||||
int SessionImpl::sslPort() const
|
||||
{
|
||||
return m_sslPort;
|
||||
}
|
||||
|
||||
void SessionImpl::setSSLPort(const int port)
|
||||
{
|
||||
if (port == sslPort())
|
||||
return;
|
||||
|
||||
m_sslPort = port;
|
||||
configureListeningInterface();
|
||||
|
||||
if (isReannounceWhenAddressChangedEnabled())
|
||||
reannounceToAllTrackers();
|
||||
}
|
||||
|
||||
QString SessionImpl::networkInterface() const
|
||||
{
|
||||
return m_networkInterface;
|
||||
|
@ -5449,6 +5498,9 @@ void SessionImpl::handleAlert(const lt::alert *a)
|
|||
case lt::torrent_delete_failed_alert::alert_type:
|
||||
handleTorrentDeleteFailedAlert(static_cast<const lt::torrent_delete_failed_alert *>(a));
|
||||
break;
|
||||
case lt::torrent_need_cert_alert::alert_type:
|
||||
handleTorrentNeedCertAlert(static_cast<const lt::torrent_need_cert_alert *>(a));
|
||||
break;
|
||||
case lt::portmap_error_alert::alert_type:
|
||||
handlePortmapWarningAlert(static_cast<const lt::portmap_error_alert *>(a));
|
||||
break;
|
||||
|
@ -5652,6 +5704,26 @@ void SessionImpl::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed
|
|||
m_removingTorrents.erase(removingTorrentDataIter);
|
||||
}
|
||||
|
||||
void SessionImpl::handleTorrentNeedCertAlert(const lt::torrent_need_cert_alert *a)
|
||||
{
|
||||
#ifdef QBT_USES_LIBTORRENT2
|
||||
const InfoHash infoHash {a->handle.info_hashes()};
|
||||
#else
|
||||
const InfoHash infoHash {a->handle.info_hash()};
|
||||
#endif
|
||||
const auto torrentID = TorrentID::fromInfoHash(infoHash);
|
||||
|
||||
TorrentImpl *const torrent = m_torrents.value(torrentID);
|
||||
if (!torrent) [[unlikely]]
|
||||
return;
|
||||
|
||||
if (!torrent->applySSLParameters())
|
||||
{
|
||||
LogMsg(tr("Torrent is missing SSL parameters. Torrent: \"%1\". Message: \"%2\"").arg(torrent->name(), QString::fromStdString(a->message()))
|
||||
, Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
void SessionImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
|
||||
{
|
||||
const TorrentID torrentID {p->handle.info_hash()};
|
||||
|
|
|
@ -230,6 +230,10 @@ namespace BitTorrent
|
|||
void setSaveResumeDataInterval(int value) override;
|
||||
int port() const override;
|
||||
void setPort(int port) override;
|
||||
bool isSSLEnabled() const override;
|
||||
void setSSLEnabled(bool enabled) override;
|
||||
int sslPort() const override;
|
||||
void setSSLPort(int port) override;
|
||||
QString networkInterface() const override;
|
||||
void setNetworkInterface(const QString &iface) override;
|
||||
QString networkInterfaceName() const override;
|
||||
|
@ -543,6 +547,7 @@ namespace BitTorrent
|
|||
void handleTorrentRemovedAlert(const lt::torrent_removed_alert *p);
|
||||
void handleTorrentDeletedAlert(const lt::torrent_deleted_alert *p);
|
||||
void handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_alert *p);
|
||||
void handleTorrentNeedCertAlert(const lt::torrent_need_cert_alert *a);
|
||||
void handlePortmapWarningAlert(const lt::portmap_error_alert *p);
|
||||
void handlePortmapAlert(const lt::portmap_alert *p);
|
||||
void handlePeerBlockedAlert(const lt::peer_blocked_alert *p);
|
||||
|
@ -676,6 +681,8 @@ namespace BitTorrent
|
|||
CachedSettingValue<bool> m_isPerformanceWarningEnabled;
|
||||
CachedSettingValue<int> m_saveResumeDataInterval;
|
||||
CachedSettingValue<int> m_port;
|
||||
CachedSettingValue<bool> m_sslEnabled;
|
||||
CachedSettingValue<int> m_sslPort;
|
||||
CachedSettingValue<QString> m_networkInterface;
|
||||
CachedSettingValue<QString> m_networkInterfaceName;
|
||||
CachedSettingValue<QString> m_networkInterfaceAddress;
|
||||
|
|
34
src/base/bittorrent/sslparameters.cpp
Normal file
34
src/base/bittorrent/sslparameters.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2024 Mike Tzou (Chocobo1)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "sslparameters.h"
|
||||
|
||||
bool BitTorrent::SSLParameters::isValid() const
|
||||
{
|
||||
return (!certificate.isNull() && !privateKey.isNull() && !dhParams.isEmpty());
|
||||
}
|
47
src/base/bittorrent/sslparameters.h
Normal file
47
src/base/bittorrent/sslparameters.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2024 Mike Tzou (Chocobo1)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QSslCertificate>
|
||||
#include <QSslKey>
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
struct SSLParameters
|
||||
{
|
||||
QSslCertificate certificate {{}};
|
||||
QSslKey privateKey;
|
||||
QByteArray dhParams;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
friend bool operator==(const SSLParameters &left, const SSLParameters &right) = default;
|
||||
};
|
||||
}
|
|
@ -53,6 +53,7 @@ namespace BitTorrent
|
|||
class TorrentID;
|
||||
class TorrentInfo;
|
||||
struct PeerAddress;
|
||||
struct SSLParameters;
|
||||
struct TrackerEntry;
|
||||
|
||||
// Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised
|
||||
|
@ -306,6 +307,8 @@ namespace BitTorrent
|
|||
|
||||
virtual StopCondition stopCondition() const = 0;
|
||||
virtual void setStopCondition(StopCondition stopCondition) = 0;
|
||||
virtual SSLParameters getSSLParameters() const = 0;
|
||||
virtual void setSSLParameters(const SSLParameters &sslParams) = 0;
|
||||
|
||||
virtual QString createMagnetURI() const = 0;
|
||||
virtual nonstd::expected<QByteArray, QString> exportToBuffer() const = 0;
|
||||
|
|
|
@ -304,6 +304,7 @@ TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession
|
|||
, m_hasFirstLastPiecePriority(params.firstLastPiecePriority)
|
||||
, m_useAutoTMM(params.useAutoTMM)
|
||||
, m_isStopped(params.stopped)
|
||||
, m_sslParams(params.sslParameters)
|
||||
, m_ltAddTorrentParams(params.ltAddTorrentParams)
|
||||
, m_downloadLimit(cleanLimitValue(m_ltAddTorrentParams.download_limit))
|
||||
, m_uploadLimit(cleanLimitValue(m_ltAddTorrentParams.upload_limit))
|
||||
|
@ -2123,26 +2124,27 @@ void TorrentImpl::prepareResumeData(const lt::add_torrent_params ¶ms)
|
|||
// We shouldn't save upload_mode flag to allow torrent operate normally on next run
|
||||
m_ltAddTorrentParams.flags &= ~lt::torrent_flags::upload_mode;
|
||||
|
||||
LoadTorrentParams resumeData;
|
||||
resumeData.name = m_name;
|
||||
resumeData.category = m_category;
|
||||
resumeData.tags = m_tags;
|
||||
resumeData.contentLayout = m_contentLayout;
|
||||
resumeData.ratioLimit = m_ratioLimit;
|
||||
resumeData.seedingTimeLimit = m_seedingTimeLimit;
|
||||
resumeData.inactiveSeedingTimeLimit = m_inactiveSeedingTimeLimit;
|
||||
resumeData.firstLastPiecePriority = m_hasFirstLastPiecePriority;
|
||||
resumeData.hasFinishedStatus = m_hasFinishedStatus;
|
||||
resumeData.stopped = m_isStopped;
|
||||
resumeData.stopCondition = m_stopCondition;
|
||||
resumeData.operatingMode = m_operatingMode;
|
||||
resumeData.ltAddTorrentParams = m_ltAddTorrentParams;
|
||||
resumeData.useAutoTMM = m_useAutoTMM;
|
||||
if (!resumeData.useAutoTMM)
|
||||
const LoadTorrentParams resumeData
|
||||
{
|
||||
resumeData.savePath = m_savePath;
|
||||
resumeData.downloadPath = m_downloadPath;
|
||||
}
|
||||
.ltAddTorrentParams = m_ltAddTorrentParams,
|
||||
.name = m_name,
|
||||
.category = m_category,
|
||||
.tags = m_tags,
|
||||
.savePath = (!m_useAutoTMM ? m_savePath : Path()),
|
||||
.downloadPath = (!m_useAutoTMM ? m_downloadPath : Path()),
|
||||
.contentLayout = m_contentLayout,
|
||||
.operatingMode = m_operatingMode,
|
||||
.useAutoTMM = m_useAutoTMM,
|
||||
.firstLastPiecePriority = m_hasFirstLastPiecePriority,
|
||||
.hasFinishedStatus = m_hasFinishedStatus,
|
||||
.stopped = m_isStopped,
|
||||
.stopCondition = m_stopCondition,
|
||||
.addToQueueTop = false,
|
||||
.ratioLimit = m_ratioLimit,
|
||||
.seedingTimeLimit = m_seedingTimeLimit,
|
||||
.inactiveSeedingTimeLimit = m_inactiveSeedingTimeLimit,
|
||||
.sslParameters = m_sslParams
|
||||
};
|
||||
|
||||
m_session->handleTorrentResumeDataReady(this, resumeData);
|
||||
}
|
||||
|
@ -2476,6 +2478,32 @@ void TorrentImpl::setStopCondition(const StopCondition stopCondition)
|
|||
m_stopCondition = stopCondition;
|
||||
}
|
||||
|
||||
SSLParameters TorrentImpl::getSSLParameters() const
|
||||
{
|
||||
return m_sslParams;
|
||||
}
|
||||
|
||||
void TorrentImpl::setSSLParameters(const SSLParameters &sslParams)
|
||||
{
|
||||
if (sslParams == getSSLParameters())
|
||||
return;
|
||||
|
||||
m_sslParams = sslParams;
|
||||
applySSLParameters();
|
||||
|
||||
deferredRequestResumeData();
|
||||
}
|
||||
|
||||
bool TorrentImpl::applySSLParameters()
|
||||
{
|
||||
if (!m_sslParams.isValid())
|
||||
return false;
|
||||
|
||||
m_nativeHandle.set_ssl_certificate_buffer(m_sslParams.certificate.toPem().toStdString()
|
||||
, m_sslParams.privateKey.toPem().toStdString(), m_sslParams.dhParams.toStdString());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TorrentImpl::isMoveInProgress() const
|
||||
{
|
||||
return m_storageIsMoving;
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "base/tagset.h"
|
||||
#include "infohash.h"
|
||||
#include "speedmonitor.h"
|
||||
#include "sslparameters.h"
|
||||
#include "torrent.h"
|
||||
#include "torrentcontentlayout.h"
|
||||
#include "torrentinfo.h"
|
||||
|
@ -243,6 +244,9 @@ namespace BitTorrent
|
|||
|
||||
StopCondition stopCondition() const override;
|
||||
void setStopCondition(StopCondition stopCondition) override;
|
||||
SSLParameters getSSLParameters() const override;
|
||||
void setSSLParameters(const SSLParameters &sslParams) override;
|
||||
bool applySSLParameters();
|
||||
|
||||
QString createMagnetURI() const override;
|
||||
nonstd::expected<QByteArray, QString> exportToBuffer() const override;
|
||||
|
@ -363,6 +367,7 @@ namespace BitTorrent
|
|||
bool m_useAutoTMM;
|
||||
bool m_isStopped;
|
||||
StopCondition m_stopCondition = StopCondition::None;
|
||||
SSLParameters m_sslParams;
|
||||
|
||||
bool m_unchecked = false;
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "base/algorithm.h"
|
||||
#include "base/global.h"
|
||||
#include "base/utils/net.h"
|
||||
#include "base/utils/sslkey.h"
|
||||
#include "connection.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
@ -161,7 +162,7 @@ void Server::dropTimedOutConnection()
|
|||
bool Server::setupHttps(const QByteArray &certificates, const QByteArray &privateKey)
|
||||
{
|
||||
const QList<QSslCertificate> certs {Utils::Net::loadSSLCertificate(certificates)};
|
||||
const QSslKey key {Utils::Net::loadSSLKey(privateKey)};
|
||||
const QSslKey key {Utils::SSLKey::load(privateKey)};
|
||||
|
||||
if (certs.isEmpty() || key.isNull())
|
||||
{
|
||||
|
|
|
@ -136,19 +136,5 @@ namespace Utils
|
|||
{
|
||||
return !loadSSLCertificate(data).isEmpty();
|
||||
}
|
||||
|
||||
QSslKey loadSSLKey(const QByteArray &data)
|
||||
{
|
||||
// try different formats
|
||||
const QSslKey key {data, QSsl::Rsa};
|
||||
if (!key.isNull())
|
||||
return key;
|
||||
return {data, QSsl::Ec};
|
||||
}
|
||||
|
||||
bool isSSLKeyValid(const QByteArray &data)
|
||||
{
|
||||
return !loadSSLKey(data).isNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,4 @@ namespace Utils::Net
|
|||
inline const int MAX_SSL_FILE_SIZE = 1024 * 1024;
|
||||
QList<QSslCertificate> loadSSLCertificate(const QByteArray &data);
|
||||
bool isSSLCertificatesValid(const QByteArray &data);
|
||||
QSslKey loadSSLKey(const QByteArray &data);
|
||||
bool isSSLKeyValid(const QByteArray &data);
|
||||
}
|
||||
|
|
40
src/base/utils/sslkey.cpp
Normal file
40
src/base/utils/sslkey.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2024 Mike Tzou (Chocobo1)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "sslkey.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QSslKey>
|
||||
|
||||
QSslKey Utils::SSLKey::load(const QByteArray &data)
|
||||
{
|
||||
// try different formats
|
||||
if (const QSslKey key {data, QSsl::Ec}; !key.isNull())
|
||||
return key;
|
||||
return {data, QSsl::Rsa};
|
||||
}
|
37
src/base/utils/sslkey.h
Normal file
37
src/base/utils/sslkey.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2024 Mike Tzou (Chocobo1)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class QByteArray;
|
||||
class QSslKey;
|
||||
|
||||
namespace Utils::SSLKey
|
||||
{
|
||||
QSslKey load(const QByteArray &data);
|
||||
}
|
|
@ -61,6 +61,7 @@
|
|||
#include "base/utils/os.h"
|
||||
#include "base/utils/password.h"
|
||||
#include "base/utils/random.h"
|
||||
#include "base/utils/sslkey.h"
|
||||
#include "addnewtorrentdialog.h"
|
||||
#include "advancedsettings.h"
|
||||
#include "banlistoptionsdialog.h"
|
||||
|
@ -1865,7 +1866,7 @@ Path OptionsDialog::getFilter() const
|
|||
void OptionsDialog::webUIHttpsCertChanged(const Path &path)
|
||||
{
|
||||
const auto readResult = Utils::IO::readFile(path, Utils::Net::MAX_SSL_FILE_SIZE);
|
||||
const bool isCertValid = Utils::Net::isSSLCertificatesValid(readResult.value_or(QByteArray()));
|
||||
const bool isCertValid = !Utils::SSLKey::load(readResult.value_or(QByteArray())).isNull();
|
||||
|
||||
m_ui->textWebUIHttpsCert->setSelectedPath(path);
|
||||
m_ui->lblSslCertStatus->setPixmap(UIThemeManager::instance()->getScaledPixmap(
|
||||
|
@ -1875,7 +1876,7 @@ void OptionsDialog::webUIHttpsCertChanged(const Path &path)
|
|||
void OptionsDialog::webUIHttpsKeyChanged(const Path &path)
|
||||
{
|
||||
const auto readResult = Utils::IO::readFile(path, Utils::Net::MAX_SSL_FILE_SIZE);
|
||||
const bool isKeyValid = Utils::Net::isSSLKeyValid(readResult.value_or(QByteArray()));
|
||||
const bool isKeyValid = !Utils::SSLKey::load(readResult.value_or(QByteArray())).isNull();
|
||||
|
||||
m_ui->textWebUIHttpsKey->setSelectedPath(path);
|
||||
m_ui->lblSslKeyStatus->setPixmap(UIThemeManager::instance()->getScaledPixmap(
|
||||
|
|
|
@ -201,6 +201,8 @@ void AppController::preferencesAction()
|
|||
// Connection
|
||||
// Listening Port
|
||||
data[u"listen_port"_s] = session->port();
|
||||
data[u"ssl_enabled"_s] = session->isSSLEnabled();
|
||||
data[u"ssl_listen_port"_s] = session->sslPort();
|
||||
data[u"random_port"_s] = (session->port() == 0); // deprecated
|
||||
data[u"upnp"_s] = Net::PortForwarder::instance()->isEnabled();
|
||||
// Connections Limits
|
||||
|
@ -654,6 +656,11 @@ void AppController::setPreferencesAction()
|
|||
{
|
||||
session->setPort(it.value().toInt());
|
||||
}
|
||||
// SSL Torrents
|
||||
if (hasKey(u"ssl_enabled"_s))
|
||||
session->setSSLEnabled(it.value().toBool());
|
||||
if (hasKey(u"ssl_listen_port"_s))
|
||||
session->setSSLPort(it.value().toInt());
|
||||
if (hasKey(u"upnp"_s))
|
||||
Net::PortForwarder::instance()->setEnabled(it.value().toBool());
|
||||
// Connections Limits
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "base/bittorrent/peeraddress.h"
|
||||
#include "base/bittorrent/peerinfo.h"
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "base/bittorrent/sslparameters.h"
|
||||
#include "base/bittorrent/torrent.h"
|
||||
#include "base/bittorrent/torrentdescriptor.h"
|
||||
#include "base/bittorrent/trackerentry.h"
|
||||
|
@ -55,6 +56,7 @@
|
|||
#include "base/torrentfilter.h"
|
||||
#include "base/utils/datetime.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/sslkey.h"
|
||||
#include "base/utils/string.h"
|
||||
#include "apierror.h"
|
||||
#include "serialize/serialize_torrent.h"
|
||||
|
@ -108,6 +110,9 @@ const QString KEY_PROP_SAVE_PATH = u"save_path"_s;
|
|||
const QString KEY_PROP_DOWNLOAD_PATH = u"download_path"_s;
|
||||
const QString KEY_PROP_COMMENT = u"comment"_s;
|
||||
const QString KEY_PROP_ISPRIVATE = u"is_private"_s;
|
||||
const QString KEY_PROP_SSL_CERTIFICATE = u"ssl_certificate"_s;
|
||||
const QString KEY_PROP_SSL_PRIVATEKEY = u"ssl_private_key"_s;
|
||||
const QString KEY_PROP_SSL_DHPARAMS = u"ssl_dh_params"_s;
|
||||
|
||||
// File keys
|
||||
const QString KEY_FILE_INDEX = u"index"_s;
|
||||
|
@ -721,27 +726,38 @@ void TorrentsController::addAction()
|
|||
}
|
||||
}
|
||||
|
||||
BitTorrent::AddTorrentParams addTorrentParams;
|
||||
// TODO: Check if destination actually exists
|
||||
addTorrentParams.skipChecking = skipChecking;
|
||||
addTorrentParams.sequential = seqDownload;
|
||||
addTorrentParams.firstLastPiecePriority = firstLastPiece;
|
||||
addTorrentParams.addToQueueTop = addToQueueTop;
|
||||
addTorrentParams.addPaused = addPaused;
|
||||
addTorrentParams.stopCondition = stopCondition;
|
||||
addTorrentParams.contentLayout = contentLayout;
|
||||
addTorrentParams.savePath = Path(savepath);
|
||||
addTorrentParams.downloadPath = Path(downloadPath);
|
||||
addTorrentParams.useDownloadPath = useDownloadPath;
|
||||
addTorrentParams.category = category;
|
||||
addTorrentParams.tags.insert(tags.cbegin(), tags.cend());
|
||||
addTorrentParams.name = torrentName;
|
||||
addTorrentParams.uploadLimit = upLimit;
|
||||
addTorrentParams.downloadLimit = dlLimit;
|
||||
addTorrentParams.seedingTimeLimit = seedingTimeLimit;
|
||||
addTorrentParams.inactiveSeedingTimeLimit = inactiveSeedingTimeLimit;
|
||||
addTorrentParams.ratioLimit = ratioLimit;
|
||||
addTorrentParams.useAutoTMM = autoTMM;
|
||||
const BitTorrent::AddTorrentParams addTorrentParams
|
||||
{
|
||||
// TODO: Check if destination actually exists
|
||||
.name = torrentName,
|
||||
.category = category,
|
||||
.tags = {tags.cbegin(), tags.cend()},
|
||||
.savePath = Path(savepath),
|
||||
.useDownloadPath = useDownloadPath,
|
||||
.downloadPath = Path(downloadPath),
|
||||
.sequential = seqDownload,
|
||||
.firstLastPiecePriority = firstLastPiece,
|
||||
.addForced = false,
|
||||
.addToQueueTop = addToQueueTop,
|
||||
.addPaused = addPaused,
|
||||
.stopCondition = stopCondition,
|
||||
.filePaths = {},
|
||||
.filePriorities = {},
|
||||
.skipChecking = skipChecking,
|
||||
.contentLayout = contentLayout,
|
||||
.useAutoTMM = autoTMM,
|
||||
.uploadLimit = upLimit,
|
||||
.downloadLimit = dlLimit,
|
||||
.seedingTimeLimit = seedingTimeLimit,
|
||||
.inactiveSeedingTimeLimit = inactiveSeedingTimeLimit,
|
||||
.ratioLimit = ratioLimit,
|
||||
.sslParameters =
|
||||
{
|
||||
.certificate = QSslCertificate(params()[KEY_PROP_SSL_CERTIFICATE].toLatin1()),
|
||||
.privateKey = Utils::SSLKey::load(params()[KEY_PROP_SSL_PRIVATEKEY].toLatin1()),
|
||||
.dhParams = params()[KEY_PROP_SSL_DHPARAMS].toLatin1()
|
||||
}
|
||||
};
|
||||
|
||||
bool partialSuccess = false;
|
||||
for (QString url : asConst(urls.split(u'\n')))
|
||||
|
@ -1444,3 +1460,43 @@ void TorrentsController::exportAction()
|
|||
|
||||
setResult(result.value(), u"application/x-bittorrent"_s, (id.toString() + u".torrent"));
|
||||
}
|
||||
|
||||
void TorrentsController::SSLParametersAction()
|
||||
{
|
||||
requireParams({u"hash"_s});
|
||||
|
||||
const auto id = BitTorrent::TorrentID::fromString(params()[u"hash"_s]);
|
||||
const BitTorrent::Torrent *torrent = BitTorrent::Session::instance()->getTorrent(id);
|
||||
if (!torrent)
|
||||
throw APIError(APIErrorType::NotFound);
|
||||
|
||||
const BitTorrent::SSLParameters sslParams = torrent->getSSLParameters();
|
||||
const QJsonObject ret
|
||||
{
|
||||
{KEY_PROP_SSL_CERTIFICATE, QString::fromLatin1(sslParams.certificate.toPem())},
|
||||
{KEY_PROP_SSL_PRIVATEKEY, QString::fromLatin1(sslParams.privateKey.toPem())},
|
||||
{KEY_PROP_SSL_DHPARAMS, QString::fromLatin1(sslParams.dhParams)}
|
||||
};
|
||||
setResult(ret);
|
||||
}
|
||||
|
||||
void TorrentsController::setSSLParametersAction()
|
||||
{
|
||||
requireParams({u"hash"_s, KEY_PROP_SSL_CERTIFICATE, KEY_PROP_SSL_PRIVATEKEY, KEY_PROP_SSL_DHPARAMS});
|
||||
|
||||
const auto id = BitTorrent::TorrentID::fromString(params()[u"hash"_s]);
|
||||
BitTorrent::Torrent *const torrent = BitTorrent::Session::instance()->getTorrent(id);
|
||||
if (!torrent)
|
||||
throw APIError(APIErrorType::NotFound);
|
||||
|
||||
const BitTorrent::SSLParameters sslParams
|
||||
{
|
||||
.certificate = QSslCertificate(params()[KEY_PROP_SSL_CERTIFICATE].toLatin1()),
|
||||
.privateKey = Utils::SSLKey::load(params()[KEY_PROP_SSL_PRIVATEKEY].toLatin1()),
|
||||
.dhParams = params()[KEY_PROP_SSL_DHPARAMS].toLatin1()
|
||||
};
|
||||
if (!sslParams.isValid())
|
||||
throw APIError(APIErrorType::BadData);
|
||||
|
||||
torrent->setSSLParameters(sslParams);
|
||||
}
|
||||
|
|
|
@ -89,4 +89,6 @@ private slots:
|
|||
void renameFileAction();
|
||||
void renameFolderAction();
|
||||
void exportAction();
|
||||
void SSLParametersAction();
|
||||
void setSSLParametersAction();
|
||||
};
|
||||
|
|
|
@ -204,6 +204,7 @@ private:
|
|||
{{u"torrents"_s, u"setLocation"_s}, Http::METHOD_POST},
|
||||
{{u"torrents"_s, u"setSavePath"_s}, Http::METHOD_POST},
|
||||
{{u"torrents"_s, u"setShareLimits"_s}, Http::METHOD_POST},
|
||||
{{u"torrents"_s, u"setSSLParameters"_s}, Http::METHOD_POST},
|
||||
{{u"torrents"_s, u"setSuperSeeding"_s}, Http::METHOD_POST},
|
||||
{{u"torrents"_s, u"setUploadLimit"_s}, Http::METHOD_POST},
|
||||
{{u"torrents"_s, u"toggleFirstLastPiecePrio"_s}, Http::METHOD_POST},
|
||||
|
|
Loading…
Reference in a new issue