diff options
| author | Dark1-dev <shansarkar272@gmail.com> | 2023-03-01 21:30:57 +0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-01 21:30:57 +0600 |
| commit | 60a301a93b6057bb2c54ac04a7c38c38389037b3 (patch) | |
| tree | b09c5f8bc0045828c660654d8ed6744663856202 /src/TcpTasks/SyncClient | |
| parent | c784240d1af68dbd8d0466822b34fd05d6ccdda1 (diff) | |
| download | Masterserver-Qt5-60a301a93b6057bb2c54ac04a7c38c38389037b3.tar.gz Masterserver-Qt5-60a301a93b6057bb2c54ac04a7c38c38389037b3.zip | |
Add files via upload
Diffstat (limited to 'src/TcpTasks/SyncClient')
| -rw-r--r-- | src/TcpTasks/SyncClient/onsyncconnect.cpp | 10 | ||||
| -rw-r--r-- | src/TcpTasks/SyncClient/onsyncdisconnect.cpp | 49 | ||||
| -rw-r--r-- | src/TcpTasks/SyncClient/onsyncread.cpp | 111 | ||||
| -rw-r--r-- | src/TcpTasks/SyncClient/syncclient.cpp | 27 | ||||
| -rw-r--r-- | src/TcpTasks/SyncClient/syncclient.h | 50 | ||||
| -rw-r--r-- | src/TcpTasks/SyncClient/syncreplyquery.cpp | 49 | ||||
| -rw-r--r-- | src/TcpTasks/SyncClient/updatesyncedserver.cpp | 27 |
7 files changed, 323 insertions, 0 deletions
diff --git a/src/TcpTasks/SyncClient/onsyncconnect.cpp b/src/TcpTasks/SyncClient/onsyncconnect.cpp new file mode 100644 index 0000000..3e05864 --- /dev/null +++ b/src/TcpTasks/SyncClient/onsyncconnect.cpp @@ -0,0 +1,10 @@ +#include "syncclient.h" + +void SyncClient::onSyncConnect() +{ + // reset timeout + _timeOut.start(); + + // log + _coreObject->Log.logEvent("tcp", QStringLiteral("connected to %1").arg(_clientLabel) ); +} diff --git a/src/TcpTasks/SyncClient/onsyncdisconnect.cpp b/src/TcpTasks/SyncClient/onsyncdisconnect.cpp new file mode 100644 index 0000000..4bc8eac --- /dev/null +++ b/src/TcpTasks/SyncClient/onsyncdisconnect.cpp @@ -0,0 +1,49 @@ +#include "syncclient.h" + +void SyncClient::onSyncDisconnect() +{ + // remote host closed the connection (typically occurs after receiving the list from remote host) + if (_tcpSocket.error() == QAbstractSocket::RemoteHostClosedError ) + { + _coreObject->Log.logEvent("tcp", QStringLiteral("disconnected from %1").arg(_clientLabel) ); + } + + // timer already stopped or a timeout occurred + if ( ! _timeOut.isActive() or _tcpSocket.error() == QAbstractSocket::SocketTimeoutError) + { + _coreObject->Log.logEvent("warning", QStringLiteral("timeout while attempting to sync with %1 (1)").arg(_clientLabel)); + } + + // an error occured and is reported, excluding... + if ( _tcpSocket.error() != QAbstractSocket::RemoteHostClosedError and // ...regular disconnect caught above + _tcpSocket.error() != QAbstractSocket::SocketTimeoutError and // ...timeout caught above + _tcpSocket.error() != QAbstractSocket::UnknownSocketError ) // ...QTimer timeout does not leave an errorcode (defaults to unknown error) + { + _coreObject->Log.logEvent("warning", QStringLiteral("error while syncing with %1: %2").arg(_clientLabel, _tcpSocket.errorString())); + } + + // stop timer if necessary and delete this client + _timeOut.stop(); + this->deleteLater(); +} + +void SyncClient::onSyncTimeOut() +{ + // if no error was specified while timer expired, there was a connection timeout + if ( _tcpSocket.error() == QAbstractSocket::UnknownSocketError ) + { + _coreObject->Log.logEvent("warning", QStringLiteral("timeout while attempting to sync with %1 (2)").arg(_clientLabel)); + } + + // other errors, like establishing connection/refused + if ( _tcpSocket.error() != QAbstractSocket::UnknownSocketError ) + { + _coreObject->Log.logEvent("warning", QStringLiteral("error while syncing with %1: %2").arg(_clientLabel, _tcpSocket.errorString())); + } + + // stop timer and close socket + _timeOut.stop(); + _coreObject->Log.logEvent("tcp", QStringLiteral("%1 scheduled for deletion").arg(_clientLabel) ); + _tcpSocket.disconnectFromHost(); + this->deleteLater(); +} diff --git a/src/TcpTasks/SyncClient/onsyncread.cpp b/src/TcpTasks/SyncClient/onsyncread.cpp new file mode 100644 index 0000000..c0f0b73 --- /dev/null +++ b/src/TcpTasks/SyncClient/onsyncread.cpp @@ -0,0 +1,111 @@ +#include "syncclient.h" + +void SyncClient::onSyncRead() +{ + // reset timeout after receiving (any) data + _timeOut.start(); + + // read from tcp connection and append to buffer + QByteArray receiveBuffer = _tcpSocket.readAll(); + _rxBuffer.append( receiveBuffer ); + + // prevent large heaps of text -- log only relevant message, not masterserver data + if ( receiveBuffer.length() > 30 ) + { + // log size of data + _coreObject->Log.logEvent("tcp", QStringLiteral("%1 sent %2 characters") + .arg(_clientLabel, QString::number(receiveBuffer.length()) ) ); + } + else + { + // log message + _coreObject->Log.logEvent("tcp", QStringLiteral("%1 sent '%2'") + .arg(_clientLabel, receiveBuffer.data()) ); + } + + // remote masterserver opens with secure challenge + if ( _rxBuffer.contains("secure") ) + { + // parse to hash + QMultiHash<QString, QString> receiveData = parseGameSpy0Buffer(_rxBuffer.toLatin1()); + + // generate response + QStringList response = replyQuery(receiveData); + + // return response + _tcpSocket.write(response.join("").toLatin1()); + + // sync request + QString request = QStringLiteral("\\sync\\%1\\msid\\%2") + .arg(_coreObject->Settings.SyncerSettings.syncGames, + _coreObject->masterserverIdentity); + _tcpSocket.write(request.toLatin1()); + + // all relevant information received. clear buffer for next interaction + _rxBuffer = ""; + return; + } + + if ( _rxBuffer.contains("final") ) + { + // parse to hash: receivedData format is {gamename} => {string of addresses} + QMultiHash<QString, QString> receiveData = parseGameSpy0Buffer(_rxBuffer.toLatin1()); + receiveData.remove("final"); // prevent "final" from registering as gamename + + // count number of addresses for logging + int totalServerCount = 0; + + // use transaction for SQLite + QSqlDatabase::database().transaction(); + + // parse to list of list of <ServerInfo> + QHashIterator<QString,QString> receiveDataIterator(receiveData); + while ( receiveDataIterator.hasNext() ) + { + // {gamename} => {string of addresses} + receiveDataIterator.next(); + QString gamename = receiveDataIterator.key(); + QString addressBufferList = receiveDataIterator.value(); + + // split address list in single addresses + QStringListIterator listIterator( addressBufferList.split(" ", QString::SkipEmptyParts) ); + while ( listIterator.hasNext() ) + { + // address (ip:port) + QString addr = listIterator.next(); + + // older Qt5 masterservers sync in ::ffff:127.0.0.1 format, trim the '::ffff:' + addr.remove("::ffff:"); + + // (address cont.) + QStringList address = addr.split(':'); + unsigned short remotePort = address.takeLast().toUShort(); + QString remoteAddr = address.join(":"); // IPv4 has only 1 element, IPv6 has 4 that need joining with ':' + + // valid address? + if ( ! QHostAddress(remoteAddr).isNull() and remotePort > 0 ) + { + // if it does not exist in the db, insert + if ( ! updateSyncedServer(remoteAddr, remotePort) ) + { + // add + insertServer(remoteAddr, remotePort, gamename, false); + } + } + totalServerCount++; + } // has next address + } // has next game + + // commit SQLite + QSqlDatabase::database().commit(); + + // report in log + _coreObject->Log.logEvent("sync", QStringLiteral("received %1 servers in %2 games from %3") + .arg( QString::number(totalServerCount), + QString::number(receiveData.count()), + _clientLabel) + ); + } // if final + + // else keep appending data until \\final\\ is received +} diff --git a/src/TcpTasks/SyncClient/syncclient.cpp b/src/TcpTasks/SyncClient/syncclient.cpp new file mode 100644 index 0000000..a5a1531 --- /dev/null +++ b/src/TcpTasks/SyncClient/syncclient.cpp @@ -0,0 +1,27 @@ +#include "syncclient.h" + +SyncClient::SyncClient(const QSharedPointer<CoreObject> &coreObject, + const QString &remoteHost, + const unsigned short int &remotePort) +{ + // create local access + this->_coreObject = coreObject; + + // connect events and functions + connect(&_tcpSocket, &QTcpSocket::connected, this, &SyncClient::onSyncConnect); + connect(&_tcpSocket, &QTcpSocket::readyRead, this, &SyncClient::onSyncRead); + connect(&_tcpSocket, &QTcpSocket::disconnected, this, &SyncClient::onSyncDisconnect); + connect(&_timeOut, &QTimer::timeout, this, &SyncClient::onSyncTimeOut); + + // convenience label to found + _clientLabel = QStringLiteral("%1:%2") + .arg(remoteHost, QString::number(remotePort)); + + // set timeout + _timeOut.setInterval( _timeOutTime_ms ); + _timeOut.start(); // connection timout time === read timeout time + + // connect to remote masterserver + _tcpSocket.connectToHost(remoteHost, remotePort); + _coreObject->Log.logEvent("tcp", QStringLiteral("connecting to %1").arg(_clientLabel)); +} diff --git a/src/TcpTasks/SyncClient/syncclient.h b/src/TcpTasks/SyncClient/syncclient.h new file mode 100644 index 0000000..8cf253f --- /dev/null +++ b/src/TcpTasks/SyncClient/syncclient.h @@ -0,0 +1,50 @@ +#ifndef SYNCCLIENT_H +#define SYNCCLIENT_H + +#include <QTimer> +#include <QTcpSocket> +#include <QHostAddress> + +#include "Core/CoreObject/coreobject.h" +#include "Database/Common/commonactions.h" +#include "Protocols/GameSpy0/gamespy0.h" +#include "Protocols/GameSpy0/securevalidate.h" + +class SyncClient: public QObject +{ + Q_OBJECT +public: + SyncClient(const QSharedPointer<CoreObject> &coreObject, + const QString &remoteHost, + const unsigned short int &remotePort); + +private: + QSharedPointer<CoreObject> _coreObject; + const int _timeOutTime_ms = 7500; + + // tcp client handles + QTcpSocket _tcpSocket; + QTimer _timeOut; + QString _rxBuffer = ""; + QString _clientLabel; + + // helpers + int _queryId = 0; + + // functions + QStringList replyQuery(const QMultiHash<QString, QString> &query); + +private: // update sync time in database + bool updateSyncedServer(const QString &serverAddress, + const unsigned short &serverPort); + +private slots: + void onSyncConnect(); + void onSyncRead(); + void onSyncDisconnect(); + void onSyncTimeOut(); + +signals: +}; + +#endif // SYNCCLIENT_H diff --git a/src/TcpTasks/SyncClient/syncreplyquery.cpp b/src/TcpTasks/SyncClient/syncreplyquery.cpp new file mode 100644 index 0000000..d36637f --- /dev/null +++ b/src/TcpTasks/SyncClient/syncreplyquery.cpp @@ -0,0 +1,49 @@ +#include "syncclient.h" + +QStringList SyncClient::replyQuery(const QMultiHash<QString, QString> &query) +{ + // initialise output + QStringList queryResponse; + + // gamespy uses incrementing query ids in the messages + _queryId = ( (_queryId > 99) ? 1 : _queryId + 1 ); + int querySubId = 1; + + // secure response + if ( query.contains("secure") and _coreObject->SupportedGames.contains(TYPE_GAMENAME)) + { + // sanity checks + QByteArray secure = query.value("secure", "").toLatin1(); + QByteArray cipher = _coreObject->SupportedGames.value(TYPE_GAMENAME).cipher.toLatin1(); + int enctype = query.value("enctype", "0").toInt(); + QString validate = returnValidate(cipher, secure, enctype); + + queryResponse.append( + QStringLiteral("\\validate\\%1\\queryid\\%2.%3") + .arg(validate, QString::number(_queryId), QString::number(querySubId++)) + ); + } + + // basic + if ( query.contains("basic") ) + { + queryResponse.append( + QStringLiteral("\\gamename\\%1" + "\\gamever\\%2" + "\\location\\0" + "\\queryid\\%3.%4") + .arg(TYPE_GAMENAME, + SHORT_VER, + QString::number(_queryId), + QString::number(querySubId++) + ) + ); + } + + + + // end query with final + queryResponse.append("\\final\\"); + + return queryResponse; +} diff --git a/src/TcpTasks/SyncClient/updatesyncedserver.cpp b/src/TcpTasks/SyncClient/updatesyncedserver.cpp new file mode 100644 index 0000000..354c6a5 --- /dev/null +++ b/src/TcpTasks/SyncClient/updatesyncedserver.cpp @@ -0,0 +1,27 @@ +#include "syncclient.h" + +bool SyncClient::updateSyncedServer(const QString &serverAddress, + const unsigned short &serverPort) +{ + // update existing entry, but do not insert. + QSqlQuery q; + QString updateString; + + // update with available values + updateString = "UPDATE serverlist SET " + "dt_sync = :timestamp " + "WHERE ip = :ip " + "AND queryport = :queryport"; + + // bind values and execute + q.prepare(updateString); + q.bindValue(":ip", serverAddress); + q.bindValue(":queryport", serverPort); + q.bindValue(":timestamp", QDateTime::currentSecsSinceEpoch() ); + + if ( ! q.exec() ) + return reportQuery(q); + + // was a row updated? + return (q.numRowsAffected() > 0); +} |
