aboutsummaryrefslogtreecommitdiff
path: root/src/Protocols
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/Protocols
parentc784240d1af68dbd8d0466822b34fd05d6ccdda1 (diff)
downloadMasterserver-Qt5-60a301a93b6057bb2c54ac04a7c38c38389037b3.tar.gz
Masterserver-Qt5-60a301a93b6057bb2c54ac04a7c38c38389037b3.zip
Add files via upload
Diffstat (limited to 'src/Protocols')
-rw-r--r--src/Protocols/GameSpy0/algorithm.cpp96
-rw-r--r--src/Protocols/GameSpy0/gamespy0.cpp51
-rw-r--r--src/Protocols/GameSpy0/gamespy0.h10
-rw-r--r--src/Protocols/GameSpy0/securevalidate.cpp90
-rw-r--r--src/Protocols/GameSpy0/securevalidate.h62
-rw-r--r--src/Protocols/overrides.cpp44
-rw-r--r--src/Protocols/overrides.h70
7 files changed, 423 insertions, 0 deletions
diff --git a/src/Protocols/GameSpy0/algorithm.cpp b/src/Protocols/GameSpy0/algorithm.cpp
new file mode 100644
index 0000000..5250079
--- /dev/null
+++ b/src/Protocols/GameSpy0/algorithm.cpp
@@ -0,0 +1,96 @@
+#include "securevalidate.h"
+
+/* This algorithm is based on Luigi Auriemma's gsmsalg 0.3.3,
+ * https://aluigi.altervista.org/papers/gsmsalg.h
+ * under GNU General Public License v2.
+ */
+QString generateValidateString(const QByteArray &cipher,
+ const QByteArray &secure,
+ const int &enctype)
+{
+ unsigned char cipherLength = static_cast<unsigned char>( cipher.length() );
+ unsigned char secureLength = static_cast<unsigned char>( secure.length() );
+
+ // fill array with ascii characters
+ unsigned char enc[256];
+ for (unsigned short j = 0; j < 256; j++)
+ {
+ enc[j] = static_cast<unsigned char>(j);
+ }
+
+ // cipher shuffle
+ unsigned char a = 0, x;
+ for (unsigned short j = 0; j < 256; j++)
+ {
+ a += enc[j] + cipher[j % cipherLength];
+ x = enc[a];
+ enc[a] = enc[j];
+ enc[j] = x;
+ }
+
+ // secure shuffle
+ unsigned char tmp[66];
+ unsigned char i = 0,
+ y = 0,
+ b = 0;
+ a = 0; // reset a
+ for (i = 0; i < cipherLength; i++)
+ {
+ a += secure.at(i) + 1;
+ x = enc[a];
+ b += x;
+ y = enc[b];
+ enc[b] = x;
+ enc[a] = y;
+ tmp[i] = static_cast<unsigned char>( secure.at(i) ^ enc[ (x+y) & 0xff ] );
+ }
+
+ // part of the enctype 1-2 process (uses i from previous loop)
+ for (secureLength = i; secureLength % 3; secureLength++)
+ {
+ tmp[secureLength] = 0;
+ }
+
+ // enctype 1 shuffle
+ if (enctype == 1)
+ {
+ for (i = 0; i < secureLength; i++)
+ {
+ tmp[i] = enctype1_data[ tmp[i] ];
+ }
+ }
+ else if (enctype == 2 )
+ {
+ for (i = 0; i < secureLength; i++)
+ {
+ tmp[i] = static_cast<unsigned char>(tmp[i] ^ cipher[i % cipherLength] );
+ }
+ }
+
+ // final shuffle and stitch validate response together
+ unsigned char z = 0;
+ QString validate;
+ for (i = 0; i < secureLength; i += 3)
+ {
+ x = (tmp[i]);
+ y = (tmp[i+1]);
+ z = (tmp[i+2]);
+
+ validate.append( charshift (x >> 2) );
+ validate.append( charshift (static_cast<unsigned char>(((x & 3) << 4) | (y >> 4)) ));
+ validate.append( charshift (static_cast<unsigned char>(((y & 15) << 2) | (z >> 6)) ));
+ validate.append( charshift (z & 63));
+ }
+ return validate;
+}
+
+// part of gsmsalg 0.3.3 and license
+unsigned char charshift ( const unsigned char &reg )
+{
+ if (reg < 26) return (reg + 'A');
+ if (reg < 52) return (reg + 'G');
+ if (reg < 62) return (reg - 4);
+ if (reg == 62) return ('+');
+ if (reg == 63) return ('/');
+ return (0);
+}
diff --git a/src/Protocols/GameSpy0/gamespy0.cpp b/src/Protocols/GameSpy0/gamespy0.cpp
new file mode 100644
index 0000000..6595d4a
--- /dev/null
+++ b/src/Protocols/GameSpy0/gamespy0.cpp
@@ -0,0 +1,51 @@
+#include "gamespy0.h"
+
+QMultiHash<QString, QString> parseGameSpy0Buffer(const QString &bufferString)
+{
+ // initialise output hash
+ QMultiHash<QString, QString> queryStringHash;
+
+ // split on backslash
+ QStringList bufferStringList = bufferString.split('\\', QString::KeepEmptyParts);
+
+ // iterate through all items
+ QListIterator<QString> property (bufferStringList);
+
+ // the first element is always empty -- skip it
+ if ( property.hasNext() )
+ {
+ property.next();
+ }
+
+ // store as key -> value
+ while ( property.hasNext() )
+ {
+ // unify valid keys
+ QString key = overrideKey( property.next().trimmed() );
+
+ // see if a value for this key exists
+ if ( ! property.hasNext() )
+ break;
+
+ // get value
+ QString value = property.next().trimmed();
+
+ // insert to return hash
+ queryStringHash.insert(key, value);
+ }
+
+ // override gamename
+ if ( queryStringHash.contains("gamename") )
+ {
+ QList<QString> gn = queryStringHash.values("gamename");
+ queryStringHash.remove("gamename");
+
+ // read backwards to preserve element order
+ for (int i = gn.size()-1; i >= 0; i--)
+ {
+ queryStringHash.insert("gamename", overrideGamename( gn.value(i) ) );
+ }
+ }
+
+ return queryStringHash;
+}
diff --git a/src/Protocols/GameSpy0/gamespy0.h b/src/Protocols/GameSpy0/gamespy0.h
new file mode 100644
index 0000000..9a28fa8
--- /dev/null
+++ b/src/Protocols/GameSpy0/gamespy0.h
@@ -0,0 +1,10 @@
+#ifndef GAMESPY0_H
+#define GAMESPY0_H
+
+#include <QHash>
+#include <QMultiHash>
+#include "Protocols/overrides.h"
+
+QMultiHash<QString, QString> parseGameSpy0Buffer(const QString &bufferString);
+
+#endif // GAMESPY0_H
diff --git a/src/Protocols/GameSpy0/securevalidate.cpp b/src/Protocols/GameSpy0/securevalidate.cpp
new file mode 100644
index 0000000..0532640
--- /dev/null
+++ b/src/Protocols/GameSpy0/securevalidate.cpp
@@ -0,0 +1,90 @@
+#include "securevalidate.h"
+
+AuthResult validateGamename(const bool &isBeacon,
+ const QString &gamename,
+ const QString &validate,
+ const QString &cipherIn,
+ const QString &secureIn,
+ const int &enctypeIn)
+{
+ // output result
+ AuthResult authResult;
+
+ // override certain cases for UDP beacon
+ if ( isBeacon and _overrideValidateBeacon.contains(gamename) )
+ {
+ authResult.auth = true;
+ authResult.validate = "override";
+ return authResult;
+ }
+
+ // override certain cases for TCP client
+ if ( ! isBeacon and _overrideValidateClient.contains(gamename) )
+ {
+ authResult.auth = true;
+ authResult.validate = "override";
+ return authResult;
+ }
+
+ // inputs + checks
+ QByteArray secure = secureIn.toLatin1();
+ QByteArray cipher = cipherIn.toLatin1();
+
+ // get validate value
+ if ( 6 <= cipher.length() and cipher.length() < 16 and
+ 6 <= secure.length() and secure.length() < 16 )
+ {
+ // safe to call validation. proceed.
+ authResult.validate = generateValidateString(cipher, secure, enctypeIn);
+ }
+ else
+ {
+ // incorrect input, not safe to calculate validation
+ authResult.auth = false;
+ authResult.validate = "invalid!";
+ }
+
+ // correct validation provided?
+ authResult.auth = (authResult.validate.compare(validate) == 0);
+
+ // return result as boolean and string
+ return authResult;
+}
+
+QString returnValidate(const QByteArray &cipher,
+ const QByteArray &secure,
+ const int &enctype)
+{
+ // get validate value
+ if ( 6 <= cipher.length() and cipher.length() < 16 and
+ 6 <= secure.length() and secure.length() < 16 )
+ {
+ return generateValidateString(cipher, secure, enctype);
+ }
+ else
+ {
+ return "invalid!";
+ }
+}
+
+QString genChallengeString(const int len, const bool moreChars)
+{
+ QString randomString;
+ if (moreChars)
+ {
+ // use A-Za-z0-9
+ for(unsigned char i = 0; i < len; ++i)
+ {
+ randomString += moreCharacters[qrand() % moreCharacters.length()];
+ }
+ }
+ else
+ {
+ // use A-Z only
+ for(unsigned char i = 0; i < len; ++i)
+ {
+ randomString += possibleCharacters[qrand() % possibleCharacters.length()];
+ }
+ }
+ return randomString;
+}
diff --git a/src/Protocols/GameSpy0/securevalidate.h b/src/Protocols/GameSpy0/securevalidate.h
new file mode 100644
index 0000000..b767bda
--- /dev/null
+++ b/src/Protocols/GameSpy0/securevalidate.h
@@ -0,0 +1,62 @@
+#ifndef SECUREVALIDATE_H
+#define SECUREVALIDATE_H
+
+#include <QString>
+#include "Protocols/overrides.h"
+
+// return both status and result string
+struct AuthResult {
+ bool auth = false;
+ QString validate = "";
+};
+
+// authenticate the beacon/client and return the validate string
+AuthResult validateGamename(const bool &isBeacon,
+ const QString &gamename,
+ const QString &validate,
+ const QString &cipherIn,
+ const QString &secureIn,
+ const int &enctypeIn);
+
+// return validate string (acccepts unsanitised inputs)
+QString returnValidate(const QByteArray &cipher,
+ const QByteArray &secure,
+ const int &enctype);
+
+// generate random challenge strings
+QString genChallengeString(const int len, const bool moreChars);
+
+
+// algorithm for the secure/validate challenge
+unsigned char charshift ( const unsigned char &reg );
+QString generateValidateString(const QByteArray &cipher,
+ const QByteArray &secure,
+ const int &enctype);
+
+// pre-built characters for generating validate string
+const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+const QString moreCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+
+/* Pre-built algorithm data for enctype 1 from Luigi Auriemma's
+ * gsmsalg 0.3.3, https://aluigi.altervista.org/papers/gsmsalg.h
+ * under GNU General Public License v2.
+ */
+const unsigned char enctype1_data[] = (
+"\x01\xba\xfa\xb2\x51\x00\x54\x80\x75\x16\x8e\x8e\x02\x08\x36\xa5"
+"\x2d\x05\x0d\x16\x52\x07\xb4\x22\x8c\xe9\x09\xd6\xb9\x26\x00\x04"
+"\x06\x05\x00\x13\x18\xc4\x1e\x5b\x1d\x76\x74\xfc\x50\x51\x06\x16"
+"\x00\x51\x28\x00\x04\x0a\x29\x78\x51\x00\x01\x11\x52\x16\x06\x4a"
+"\x20\x84\x01\xa2\x1e\x16\x47\x16\x32\x51\x9a\xc4\x03\x2a\x73\xe1"
+"\x2d\x4f\x18\x4b\x93\x4c\x0f\x39\x0a\x00\x04\xc0\x12\x0c\x9a\x5e"
+"\x02\xb3\x18\xb8\x07\x0c\xcd\x21\x05\xc0\xa9\x41\x43\x04\x3c\x52"
+"\x75\xec\x98\x80\x1d\x08\x02\x1d\x58\x84\x01\x4e\x3b\x6a\x53\x7a"
+"\x55\x56\x57\x1e\x7f\xec\xb8\xad\x00\x70\x1f\x82\xd8\xfc\x97\x8b"
+"\xf0\x83\xfe\x0e\x76\x03\xbe\x39\x29\x77\x30\xe0\x2b\xff\xb7\x9e"
+"\x01\x04\xf8\x01\x0e\xe8\x53\xff\x94\x0c\xb2\x45\x9e\x0a\xc7\x06"
+"\x18\x01\x64\xb0\x03\x98\x01\xeb\x02\xb0\x01\xb4\x12\x49\x07\x1f"
+"\x5f\x5e\x5d\xa0\x4f\x5b\xa0\x5a\x59\x58\xcf\x52\x54\xd0\xb8\x34"
+"\x02\xfc\x0e\x42\x29\xb8\xda\x00\xba\xb1\xf0\x12\xfd\x23\xae\xb6"
+"\x45\xa9\xbb\x06\xb8\x88\x14\x24\xa9\x00\x14\xcb\x24\x12\xae\xcc"
+"\x57\x56\xee\xfd\x08\x30\xd9\xfd\x8b\x3e\x0a\x84\x46\xfa\x77\xb8");
+
+#endif // SECUREVALIDATE_H
diff --git a/src/Protocols/overrides.cpp b/src/Protocols/overrides.cpp
new file mode 100644
index 0000000..957c489
--- /dev/null
+++ b/src/Protocols/overrides.cpp
@@ -0,0 +1,44 @@
+#include "overrides.h"
+
+// some games use different key/value pairs. override with known key.
+QString overrideKey(const QString &rawKey)
+{
+ // convert to lowercase
+ QString key = rawKey.toLower();
+
+ // some keys are indexed, like "player_0".
+ if ( _index_match.indexIn(key) >= 0 )
+ {
+ // find keyword without index and match key
+ if ( _validKeys.contains( _index_match.cap(1) ) ) // group starts at 1, not 0
+ {
+ // concat the index back and return found override
+ return _validKeys.value( _index_match.cap(1), key ) + _index_match.cap(2);
+ }
+ }
+ else
+ {
+ // non-indexed key
+ if ( _validKeys.contains( key ) )
+ {
+ // return found override
+ return _validKeys.value(key, key);
+ }
+ }
+
+ // no override found. return original.
+ return key;
+}
+
+// some games deviate from the gamename protocol. override with correct gamename
+QString overrideGamename(const QString &gamenameIn)
+{
+ // convert to lowercase
+ QString gamename = gamenameIn.toLower();
+
+ if ( _validGamenames.contains(gamename ) )
+ {
+ return _validGamenames.value(gamename, gamename);
+ }
+ return gamename;
+}
diff --git a/src/Protocols/overrides.h b/src/Protocols/overrides.h
new file mode 100644
index 0000000..eb6f8b2
--- /dev/null
+++ b/src/Protocols/overrides.h
@@ -0,0 +1,70 @@
+#ifndef OVERRIDES_H
+#define OVERRIDES_H
+
+#include <QHash>
+#include <QString>
+#include <QRegExp>
+
+// parse to a valid key/value pair
+QString overrideKey(const QString &rawKey);
+
+// parse to a valid gamename
+QString overrideGamename(const QString &gamenameIn);
+
+// record of gamenames that need to be overridden
+const QHash<QString, QString> _validGamenames
+{
+ {"JetFighter IV", "jetfighter4"},
+ {"igi2" , "projectigi2r"},
+};
+
+// record of query keys that need to be overridden
+const QHash<QString, QString> _validKeys
+{
+ {"version", "gamever"},
+ {"mingamever", "minnetver"},
+ {"admin", "adminname"},
+ {"adminname", "adminname"},
+ {"admin e-mail", "adminemail"},
+ {"friendly fire", "friendlyfire"},
+ {"friendly fire?", "friendlyfire"},
+ {"mapfilename", "mapname"},
+ {"mapid", "mapname"},
+ {"skill", "botskill"},
+ {"num teams", "maxteams"},
+ {"time limit", "timelimit"},
+ {"time_limit", "timelimit"},
+ {"timetowin", "timelimit"},
+ {"roundtime", "timelimit"},
+ {"active_mods", "mutators"},
+ {"activemod", "mutators"},
+ {"Mutator", "mutators"},
+ {"Mutator101", "mutators"},
+ {"Mutator102", "mutators"},
+ {"Mutator103", "mutators"},
+ {"Mutator104", "mutators"},
+ {"playername", "player"},
+ {"teamname", "team"},
+ {"score", "score"},
+ {"kills", "score"},
+};
+
+// some games do not (fully) support secure/validate
+const QStringList _overrideValidateBeacon
+{
+ "deusex",
+ "rune",
+ "wot",
+};
+
+const QStringList _overrideValidateClient
+{
+ "deusex",
+ "wot",
+};
+
+// matching regexes
+// TODO: replace with QRegularExpression
+const QRegExp _index_match{"^(\\w+)(_\\d+)$"};
+
+#endif // OVERRIDES_H