From 60a301a93b6057bb2c54ac04a7c38c38389037b3 Mon Sep 17 00:00:00 2001 From: Dark1-dev Date: Wed, 1 Mar 2023 21:30:57 +0600 Subject: Add files via upload --- .../BeaconServer/Receive/heartbeatgamespy0.cpp | 186 +++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 src/UdpTasks/BeaconServer/Receive/heartbeatgamespy0.cpp (limited to 'src/UdpTasks/BeaconServer/Receive/heartbeatgamespy0.cpp') diff --git a/src/UdpTasks/BeaconServer/Receive/heartbeatgamespy0.cpp b/src/UdpTasks/BeaconServer/Receive/heartbeatgamespy0.cpp new file mode 100644 index 0000000..657330d --- /dev/null +++ b/src/UdpTasks/BeaconServer/Receive/heartbeatgamespy0.cpp @@ -0,0 +1,186 @@ +#include "../beaconserver.h" + +// heartbeat processing for different protocol types +void BeaconServer::processHeartbeatGamespy0(const QNetworkDatagram &datagram, + const QString &senderAddress, + const unsigned short &senderPort, + const QString &receiveBuffer) +{ + // parse key/value pairs and create a readable server label + QMultiHash receiveData = parseGameSpy0Buffer(receiveBuffer); + QString senderAddressLabel = QStringLiteral("%1:%2").arg(senderAddress, QString::number(senderPort)); + + /* + * Receive a heartbeat. + * Update or insert the server in the database. If not authenticated, send a secure/validate challenge. + */ + if ( receiveData.contains("heartbeat") ) + { + // store heartbeat and request authentication + UdpData newBeacon; + newBeacon.ip = senderAddress; + newBeacon.port = receiveData.value("heartbeat", "0").toUShort(); + newBeacon.gamename = receiveData.value("gamename", "unknown"); + + // sanity check: known game(name) + if ( _coreObject->SupportedGames.contains( newBeacon.gamename ) ) + { + // valid port and/or default port available? + if (newBeacon.port == 0) + { + // override with default port if possible + if ( _coreObject->SupportedGames.value(newBeacon.gamename).port > 0 ) + { + newBeacon.port = _coreObject->SupportedGames.value( newBeacon.gamename ).port; + } + else // no valid port available. log and abort. + { + _coreObject->Log.logEvent("heartbeat", QStringLiteral("%1 invalid port for %2") + .arg(newBeacon.ip, newBeacon.gamename) ); + return; + } + } + } + else // unknown game. log and abort. + { + _coreObject->Log.logEvent("unsupported", QStringLiteral("%1:%2 for unsupported game %3") + .arg(newBeacon.ip, QString::number(newBeacon.port), newBeacon.gamename) ); + return; + } + + + // if this server already exists, update it + if ( updateServer(newBeacon.ip, newBeacon.port, newBeacon.gamename, true, false) ) + { + // log update + _coreObject->Log.logEvent("heartbeat", QStringLiteral("%1:%2 for %3") + .arg(newBeacon.ip, QString::number(newBeacon.port), newBeacon.gamename) ); + + // no further tasks for this datagram + return; + } + + // else: + + // add to database + insertServer(newBeacon.ip, newBeacon.port, newBeacon.gamename, true); + + // log type "uplink" for first heartbeat. from now on, this server is logged with type "heartbeat" + _coreObject->Log.logEvent("new", QStringLiteral("%1:%2 for %3") + .arg(newBeacon.ip, QString::number(newBeacon.port),newBeacon.gamename) ); + + /* + * Set up secure/validate challenge + */ + + // some games are incompatible with secure/validate (within this protocol) + if ( _overrideValidateBeacon.contains(newBeacon.gamename) ) + return; + + // generate new challenge + newBeacon.secure = genChallengeString(6, false); + + // store heartbeat in temporary list + _beaconList.remove(senderAddressLabel); // remove potential old challenges first + _beaconList.insert(senderAddressLabel, newBeacon); + + // request authentication of remote server + _udpSocket.writeDatagram( datagram.makeReply( QStringLiteral("\\secure\\%1").arg(newBeacon.secure).toLatin1() ) ); + + // no further tasks for this datagram + return; + } + + /* + * received response to authentication request + */ + if ( receiveData.contains("validate") ) + { + // load existing information + UdpData valBeacon = _beaconList.value(senderAddressLabel); + + // empty heartbeat UdpData? then received validate timed out and previous UdpData was removed + if (QHostAddress(valBeacon.ip).isNull() || valBeacon.gamename.length() <= 0) + { + _coreObject->Log.logEvent("secure", QStringLiteral("unexpected validate from %1").arg(senderAddress)); + return; + } + + // get response + AuthResult authResult = validateGamename(true, // this is a beacon + valBeacon.gamename, + receiveData.value("validate",""), + _coreObject->SupportedGames.value(valBeacon.gamename).cipher, + valBeacon.secure, + receiveData.value("enctype", "0").toInt() ); + + // compare with received response + if ( authResult.auth ) + { + // server authenticated - log and add to database + _coreObject->Log.logEvent("secure", QStringLiteral("successful validate from %1:%2 for %3") + .arg(valBeacon.ip, QString::number(valBeacon.port),valBeacon.gamename) ); + + // update the existing entry that was already added in the initial heartbeat + updateServer(valBeacon.ip, valBeacon.port, valBeacon.gamename, false, true); + + // remove from temporary/pending list + _beaconList.remove(senderAddressLabel); + } + else // log failed validate + { + // set validate false (but update last response time) + updateServer(valBeacon.ip, valBeacon.port, valBeacon.gamename, false, false); + _coreObject->Log.logEvent("secure", QStringLiteral("failed validate from %1:%2 for %3") + .arg(valBeacon.ip, QString::number(valBeacon.port),valBeacon.gamename) ); + _coreObject->Log.logEvent("secure", QStringLiteral("secure: '%1', gamename: '%2', validate: '%3', expected: '%4'") + .arg(valBeacon.secure, valBeacon.gamename, receiveData.value("validate", "null"), authResult.validate )); + } + + return; + } + + /* + * status queries directed at masterserver + */ + if (receiveData.contains("secure") or + receiveData.contains("basic") or + receiveData.contains("info") or + receiveData.contains("rules") or + receiveData.contains("status") or + receiveData.contains("echo") ) + { + // parse response query + QStringList response = replyQuery(receiveData); + + // return response + _udpSocket.writeDatagram( datagram.makeReply( response.join("").toLatin1() ) ); + + // log incoming query + _coreObject->Log.logEvent("query", QStringLiteral("%1 queried us with %2") + .arg(senderAddress, receiveData.keys().join(", ") ) ); + + // log echo separately + if ( receiveData.contains("echo") ) + _coreObject->Log.logEvent("echo", QStringLiteral("%1: '%2'") + .arg(senderAddress, receiveData.value("echo") ) ); + + return; + } + + // ignore trailing queryid+final + if (receiveData.size() > 0) + { + // receive queryid and final from the query + receiveData.remove("queryid"); + receiveData.remove("final"); + + // nothing remains? then ignore. otherwise, proceed to "unknown query" + if ( receiveData.size() <= 0) + return; + } + + // received another type of query? + _coreObject->Log.logEvent("unknown", QStringLiteral("received unknown udp uplink %1 from %2") + .arg(receiveBuffer, senderAddress) ); +} -- cgit v1.2.3