aboutsummaryrefslogtreecommitdiff
path: root/src/TcpTasks/SyncClient
diff options
context:
space:
mode:
authorDark1-dev <shansarkar272@gmail.com>2023-03-01 21:30:57 +0600
committerGitHub <noreply@github.com>2023-03-01 21:30:57 +0600
commit60a301a93b6057bb2c54ac04a7c38c38389037b3 (patch)
treeb09c5f8bc0045828c660654d8ed6744663856202 /src/TcpTasks/SyncClient
parentc784240d1af68dbd8d0466822b34fd05d6ccdda1 (diff)
downloadMasterserver-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.cpp10
-rw-r--r--src/TcpTasks/SyncClient/onsyncdisconnect.cpp49
-rw-r--r--src/TcpTasks/SyncClient/onsyncread.cpp111
-rw-r--r--src/TcpTasks/SyncClient/syncclient.cpp27
-rw-r--r--src/TcpTasks/SyncClient/syncclient.h50
-rw-r--r--src/TcpTasks/SyncClient/syncreplyquery.cpp49
-rw-r--r--src/TcpTasks/SyncClient/updatesyncedserver.cpp27
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);
+}