Skip to content

Commit

Permalink
Implement SSL (#1146) (#1858)
Browse files Browse the repository at this point in the history
  • Loading branch information
jerzerisz authored and vadi2 committed Jan 6, 2019
1 parent 4886489 commit 2f11360
Show file tree
Hide file tree
Showing 17 changed files with 2,725 additions and 244 deletions.
1 change: 0 additions & 1 deletion src/Host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,6 @@ bool Host::killTrigger(const QString& name)
return mTriggerUnit.killTrigger(name);
}


void Host::connectToServer()
{
mTelnet.connectIt(mUrl, mPort);
Expand Down
8 changes: 8 additions & 0 deletions src/Host.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class Host : public QObject
void setUserDefinedName(const QString& s ) { QMutexLocker locker(& mLock); mUserDefinedName = s; }
int getPort() { QMutexLocker locker(& mLock); return mPort; }
void setPort( int p ) { QMutexLocker locker(& mLock); mPort = p; }
void setAutoReconnect(bool b) { mTelnet.setAutoReconnect(b); }
QString & getLogin() { QMutexLocker locker(& mLock); return mLogin; }
void setLogin(const QString& s ) { QMutexLocker locker(& mLock); mLogin = s; }
QString & getPass() { QMutexLocker locker(& mLock); return mPass; }
Expand Down Expand Up @@ -238,6 +239,13 @@ class Host : public QObject
bool mFORCE_NO_COMPRESSION;
bool mFORCE_SAVE_ON_EXIT;
bool mInsertedMissingLF;

bool mSslTsl;
bool mAutoReconnect;
bool mSslIgnoreExpired;
bool mSslIgnoreSelfSigned;
bool mSslIgnoreAll;

bool mIsGoingDown;
bool mIsProfileLoadingSequence;

Expand Down
2 changes: 1 addition & 1 deletion src/TLuaInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9691,7 +9691,7 @@ int TLuaInterpreter::downloadFile(lua_State* L)
#endif
// This should fix: https://bugs.launchpad.net/mudlet/+bug/1366781
request.setRawHeader(QByteArray("User-Agent"), QByteArray(QStringLiteral("Mozilla/5.0 (Mudlet/%1%2)").arg(APP_VERSION, APP_BUILD).toUtf8().constData()));
#ifndef QT_NO_OPENSSL
#ifndef QT_NO_SSL
if (url.scheme() == QStringLiteral("https")) {
QSslConfiguration config(QSslConfiguration::defaultConfiguration());
request.setSslConfiguration(config);
Expand Down
2 changes: 1 addition & 1 deletion src/TMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2223,7 +2223,7 @@ void TMap::downloadMap(const QString& remoteUrl, const QString& localFileName)
// placed this code here:
request.setRawHeader(QByteArray("User-Agent"), QByteArray(QStringLiteral("Mozilla/5.0 (Mudlet/%1%2)").arg(APP_VERSION, APP_BUILD).toUtf8().constData()));

#ifndef QT_NO_OPENSSL
#ifndef QT_NO_SSL
if (url.scheme() == QStringLiteral("https")) {
QSslConfiguration config(QSslConfiguration::defaultConfiguration());
request.setSslConfiguration(config);
Expand Down
5 changes: 5 additions & 0 deletions src/XMLexport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,11 @@ void XMLexport::writeHost(Host* pHost, pugi::xml_node mudletPackage)
host.append_attribute("mThemePreviewType") = pHost->mThemePreviewType.toUtf8().constData();
host.append_attribute("mSearchEngineName") = pHost->mSearchEngineName.toUtf8().constData();
host.append_attribute("mTimerSupressionInterval") = pHost->mTimerDebugOutputSuppressionInterval.toString(QLatin1String("HH:mm:ss.zzz")).toUtf8().constData();
host.append_attribute("mSslTsl") = pHost->mSslTsl ? "yes" : "no";
host.append_attribute("mAutoReconnect") = pHost->mAutoReconnect ? "yes" : "no";
host.append_attribute("mSslIgnoreExpired") = pHost->mSslIgnoreExpired ? "yes" : "no";
host.append_attribute("mSslIgnoreSelfSigned") = pHost->mSslIgnoreSelfSigned ? "yes" : "no";
host.append_attribute("mSslIgnoreAll") = pHost->mSslIgnoreAll ? "yes" : "no";
host.append_attribute("mDiscordAccessFlags") = QString::number(pHost->mDiscordAccessFlags).toUtf8().constData();
host.append_attribute("mRequiredDiscordUserName") = pHost->mRequiredDiscordUserName.toUtf8().constData();
host.append_attribute("mRequiredDiscordUserDiscriminator") = pHost->mRequiredDiscordUserDiscriminator.toUtf8().constData();
Expand Down
7 changes: 7 additions & 0 deletions src/XMLimport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,11 @@ void XMLimport::readHostPackage(Host* pHost)
for (auto character : ignore) {
pHost->mDoubleClickIgnore.insert(character);
}
pHost->mSslTsl = (attributes().value("mSslTsl") == "yes");
pHost->mAutoReconnect = (attributes().value("mAutoReconnect") == "yes");
pHost->mSslIgnoreExpired = (attributes().value("mSslIgnoreExpired") == "yes");
pHost->mSslIgnoreSelfSigned = (attributes().value("mSslIgnoreSelfSigned") == "yes");
pHost->mSslIgnoreAll = (attributes().value("mSslIgnoreAll") == "yes");

while (!atEnd()) {
readNext();
Expand Down Expand Up @@ -1041,6 +1046,8 @@ void XMLimport::readHostPackage(Host* pHost)
}
}
}


}

// returns the ID of the root imported trigger/group
Expand Down
152 changes: 141 additions & 11 deletions src/ctelnet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <QProgressDialog>
#include <QTextCodec>
#include <QTextEncoder>
#include <QSslError>
#include "post_guard.h"

#define DEBUG
Expand Down Expand Up @@ -86,6 +87,7 @@ cTelnet::cTelnet(Host* pH)
{
mIsTimerPosting = false;
mNeedDecompression = false;
mDontReconnect = false;

// initialize encoding to a sensible default - needs to be a different value
// than that in the initialisation list so that it is processed as a change
Expand Down Expand Up @@ -120,10 +122,21 @@ cTelnet::cTelnet(Host* pH)
mFriendlyEncodings << TBuffer::getFriendlyEncodingNames();
}

// initialize the socket
connect(&socket, &QAbstractSocket::connected, this, &cTelnet::handle_socket_signal_connected);
connect(&socket, &QAbstractSocket::disconnected, this, &cTelnet::handle_socket_signal_disconnected);
connect(&socket, &QIODevice::readyRead, this, &cTelnet::handle_socket_signal_readyRead);
// initialize the socket after the Host initialisation is complete so we can access mSslTsl
QTimer::singleShot(0, this, [this]() {
qDebug() << mpHost->getName();
if (mpHost->mSslTsl) {
connect(&socket, &QSslSocket::encrypted, this, &cTelnet::handle_socket_signal_connected);
} else {
connect(&socket, &QAbstractSocket::connected, this, &cTelnet::handle_socket_signal_connected);
}
connect(&socket, &QAbstractSocket::disconnected, this, &cTelnet::handle_socket_signal_disconnected);
connect(&socket, &QIODevice::readyRead, this, &cTelnet::handle_socket_signal_readyRead);
#if !defined(QT_NO_SSL)
connect(&socket, qOverload<const QList<QSslError>&>(&QSslSocket::sslErrors), this, &cTelnet::handle_socket_signal_sslError);
#endif
});


// initialize telnet session
reset();
Expand Down Expand Up @@ -237,6 +250,28 @@ const QString& cTelnet::getComputerEncoding(const QString& encoding)
return TBuffer::getComputerEncoding(encoding);
}

#if !defined(QT_NO_SSL)
QSslCertificate cTelnet::getPeerCertificate()
{
return socket.peerCertificate();
}

QList<QSslError> cTelnet::getSslErrors()
{
return socket.sslErrors();
}
#endif

QAbstractSocket::SocketError cTelnet::error()
{
return socket.error();
}

QString cTelnet::errorString()
{
return socket.errorString();
}

// returns the human-friendly one ("ISO 8859-5 (Cyrillic)") given a computer encoding name ("ISO 8859-5")
const QString& cTelnet::getFriendlyEncoding()
{
Expand Down Expand Up @@ -334,7 +369,9 @@ void cTelnet::connectIt(const QString& address, int port)

void cTelnet::disconnect()
{
mDontReconnect = true;
socket.disconnectFromHost();

}

void cTelnet::handle_socket_signal_error()
Expand All @@ -355,11 +392,20 @@ void cTelnet::slot_send_pass()

void cTelnet::handle_socket_signal_connected()
{
reset();
QString msg;

qDebug() << "MODE:" << socket.mode();

reset();
setKeepAlive(socket.socketDescriptor());

QString msg = tr("[ INFO ] - A connection has been established successfully.") + "\n \n ";
if (mpHost->mSslTsl)
{
msg = tr("[ INFO ] - A secure connection has been established successfully.");
} else {
msg = tr("[ INFO ] - A connection has been established successfully.");
}
msg.append(QStringLiteral("\n \n "));
postMessage(msg);
QString func = "onConnect";
QString nothing = "";
Expand All @@ -380,31 +426,110 @@ void cTelnet::handle_socket_signal_connected()

void cTelnet::handle_socket_signal_disconnected()
{
QString msg;
TEvent event;
QString reason;
QString spacer = " ";
bool sslerr = false;

postData();

emit signal_disconnected(mpHost);

TEvent event;
event.mArgumentList.append(QStringLiteral("sysDisconnectionEvent"));
event.mArgumentTypeList.append(ARGUMENT_TYPE_STRING);
mpHost->raiseEvent(event);

QString msg;
QTime timeDiff(0, 0, 0, 0);
msg = QString(tr("[ INFO ] - Connection time: %1\n ")).arg(timeDiff.addMSecs(mConnectionTime.elapsed()).toString("hh:mm:ss.zzz"));
mNeedDecompression = false;
reset();
QString err = tr("[ ALERT ] - Socket got disconnected.\nReason: ") % socket.errorString();
QString spacer = " ";

if (!mpHost->mIsGoingDown) {
postMessage(spacer);
postMessage(err);

#if !defined(QT_NO_SSL)

QList<QSslError> sslErrors = getSslErrors();
QSslCertificate cert = socket.peerCertificate();

if (mpHost->mSslIgnoreExpired) {
sslErrors.removeAll(QSslError(QSslError::CertificateExpired, cert));
}

if (mpHost->mSslIgnoreSelfSigned) {
sslErrors.removeAll(QSslError(QSslError::SelfSignedCertificate, cert));
}

sslerr = (sslErrors.count() > 0 && !mpHost->mSslIgnoreAll && mpHost->mSslTsl);

if (sslerr) {
mDontReconnect = true;

for (int a = 0; a < sslErrors.count(); a++) {
reason.append(QStringLiteral(" %1\n").arg(QString(sslErrors.at(a).errorString())));
}
QString err = "[ ALERT ] - Socket got disconnected.\nReason: \n" % reason;
postMessage(err);
} else
#endif
{
if (mDontReconnect) {
reason = QStringLiteral("User Disconnected");
} else {
reason = socket.errorString();
}
if (reason == QStringLiteral("Error during SSL handshake: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol")) {
reason = tr("Secure connections aren't supported by this game on this port - try turning the option off.");
}
QString err = "[ ALERT ] - Socket got disconnected.\nReason: " % reason;
postMessage(err);
}
postMessage(msg);
}

#if !defined(QT_NO_SSL)
if (sslerr) {
mudlet::self()->show_options_dialog(QStringLiteral("Security"));
}
#endif

if (mAutoReconnect && !mDontReconnect) {
connectIt(hostName, hostPort);
}
mDontReconnect = false;
}

#if !defined(QT_NO_SSL)
void cTelnet::handle_socket_signal_sslError(const QList<QSslError>& errors)
{
QSslCertificate cert = socket.peerCertificate();
QList<QSslError> ignoreErrorList;

if (mpHost->mSslIgnoreExpired) {
ignoreErrorList << QSslError(QSslError::CertificateExpired, cert);
}
if (mpHost->mSslIgnoreSelfSigned) {
ignoreErrorList << QSslError(QSslError::SelfSignedCertificate, cert);
}

if (mpHost->mSslIgnoreAll) {
socket.ignoreSslErrors(errors);
} else {
socket.ignoreSslErrors(ignoreErrorList);
}
}
#endif

void cTelnet::handle_socket_signal_hostFound(QHostInfo hostInfo)
{
#if !defined(QT_NO_SSL)
if (mpHost->mSslTsl) {
postMessage(tr("[ INFO ] - Trying secure connection to %1: %2 ...\n").arg(hostInfo.hostName(), QString::number(hostPort)));
socket.connectToHostEncrypted(hostInfo.hostName(), hostPort, QIODevice::ReadWrite);

} else
#endif
if (!hostInfo.addresses().isEmpty()) {
mHostAddress = hostInfo.addresses().constFirst();
postMessage(tr("[ INFO ] - The IP address of %1 has been found. It is: %2\n").arg(hostName, mHostAddress.toString()));
Expand Down Expand Up @@ -1434,6 +1559,11 @@ void cTelnet::setChannel102Variables(const QString& msg)
}
}

void cTelnet::setAutoReconnect(bool status)
{
mAutoReconnect = status;
}

void cTelnet::atcpComposerCancel()
{
if (!mpComposer) {
Expand Down
24 changes: 24 additions & 0 deletions src/ctelnet.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@
#include <QHostInfo>
#include <QPointer>
#include <QStringList>
#if defined(QT_NO_SSL)
#include <QTcpSocket>
#else
#include <QSslSocket>
#endif
#include <QTime>
#include "post_guard.h"

Expand Down Expand Up @@ -133,6 +137,7 @@ class cTelnet : public QObject
void atcpComposerCancel();
void atcpComposerSave(QString);
void setDisplayDimensions();
void setAutoReconnect(bool status);
void encodingChanged(const QString&);
void set_USE_IRE_DRIVER_BUGFIX(bool b) { mUSE_IRE_DRIVER_BUGFIX = b; }
void set_LF_ON_GA(bool b) { mLF_ON_GA = b; }
Expand All @@ -149,6 +154,12 @@ class cTelnet : public QObject
const QStringList & getFriendlyEncodingsList() const { return mFriendlyEncodings; }
const QString& getComputerEncoding(const QString& encoding);
const QString& getFriendlyEncoding();
QAbstractSocket::SocketError error();
QString errorString();
#if !defined(QT_NO_SSL)
QSslCertificate getPeerCertificate();
QList<QSslError> getSslErrors();
#endif
QByteArray decodeBytes(const char*);
std::string encodeAndCookBytes(const std::string&);
bool isATCPEnabled() const { return enableATCP; }
Expand Down Expand Up @@ -207,7 +218,11 @@ public slots:


QPointer<Host> mpHost;
#if defined(QT_NO_SSL)
QTcpSocket socket;
#else
QSslSocket socket;
#endif
QHostAddress mHostAddress;
// QTextCodec* incomingDataCodec;
QTextCodec* mpOutOfBandDataIncomingCodec;
Expand All @@ -216,6 +231,7 @@ public slots:
QTextEncoder* outgoingDataEncoder;
QString hostName;
int hostPort;
bool hostSslTsl;
double networkLatencyMin;
double networkLatencyMax;
bool mWaitingForResponse;
Expand Down Expand Up @@ -261,6 +277,8 @@ public slots:
bool enableATCP;
bool enableGMCP;
bool enableChannel102;
bool mDontReconnect;
bool mAutoReconnect;
QStringList messageStack;
// True if THIS profile is playing a replay, does not know about any OTHER
// active profile...
Expand All @@ -273,6 +291,12 @@ public slots:
// encoding (when the user wants to use characters that cannot be encoded in
// the current Server Encoding) - gets reset when the encoding is changed:
bool mEncodingWarningIssued;

private slots:
#if !defined(QT_NO_SSL)
void handle_socket_signal_sslError(const QList<QSslError> &errors);
#endif

};

#endif // MUDLET_CTELNET_H
Loading

0 comments on commit 2f11360

Please sign in to comment.