Updated Bnet Server.

Port From (https://github.com/TrinityCore/TrinityCore)
This commit is contained in:
hondacrx
2024-02-21 00:05:48 -05:00
parent 7960e7b192
commit 9e3a7df6a7
62 changed files with 2186 additions and 731 deletions
@@ -133,4 +133,16 @@ namespace Framework.Constants
NameTakenByThisAccount = 4,
Unknown = 5
}
public enum SrpVersion
{
v1 = 1,
v2 = 2
}
public enum SrpHashFunction
{
Sha256 = 0,
Sha512 = 1
}
}
@@ -18,4 +18,18 @@ namespace Framework.Constants
ThreadUnsafe, //packet is not thread-safe - process it in World.UpdateSessions()
ThreadSafe //packet is thread-safe - process it in Map.Update()
}
public enum RequestHandlerFlag
{
None = 0x0,
DoNotLogRequestContent = 0x1,
DoNotLogResponseContent = 0x2,
}
public enum RequestHandlerResult
{
Handled,
Error,
Async,
}
}
+377 -21
View File
@@ -2,44 +2,400 @@
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
namespace Framework.Cryptography
{
public class SRP6
public abstract class SRP6
{
static SHA1 _sha1;
static BigInteger _g;
static BigInteger _N;
public static int SaltLength = 32;
static SRP6()
public byte[] s = new byte[SaltLength]; // s - the user's password salt, random, used to calculate v on registration
protected BigInteger I; // H(I) - the username, all uppercase
protected BigInteger b; // b - randomly chosen by the server, same length as N, never given out
protected BigInteger v; // v - the user's password verifier, derived from s + H(USERNAME || ":" || PASSWORD)
public BigInteger B; // B = k*v + g^b
bool _used; // a single instance can only be used to verify once
public SRP6() { }
public SRP6(BigInteger i, byte[] salt, byte[] verifier, BigInteger N, BigInteger g, BigInteger k)
{
_sha1 = SHA1.Create();
_g = new BigInteger(7);
_N = new BigInteger(new byte[]
{
0x89, 0x4B, 0x64, 0x5E, 0x89, 0xE1, 0x53, 0x5B, 0xBD, 0xAD, 0x5B, 0x8B, 0x29, 0x06, 0x50, 0x53,
0x08, 0x01, 0xB1, 0x8E, 0xBF, 0xBF, 0x5E, 0x8F, 0xAB, 0x3C, 0x82, 0x87, 0x2A, 0x3E, 0x9B, 0xB7,
}, true, true);
s = salt;
I = i;
b = CalculatePrivateB(N);
v = new BigInteger(verifier, true);
B = CalculatePublicB(N, g, k);
}
public static (byte[] Salt, byte[] Verifier) MakeRegistrationData(string username, string password)
public BigInteger? VerifyClientEvidence(BigInteger A, BigInteger clientM1)
{
var salt = new byte[0].GenerateRandomKey(32); // random salt
return (salt, CalculateVerifier(username, password, salt));
Cypher.Assert(!_used, "A single SRP6 object must only ever be used to verify ONCE!");
_used = true;
return DoVerifyClientEvidence(A, clientM1);
}
public static byte[] CalculateVerifier(string username, string password, byte[] salt)
public bool CheckCredentials(string username, string password)
{
return v == new BigInteger(CalculateVerifier(username, password, s), true);
}
BigInteger CalculatePrivateB(BigInteger N)
{
BigInteger b = new BigInteger(RandomHelper.GetRandomBytes((int)N.GetBitLength()), true);
b = b % (N - 1);
return b;
}
BigInteger CalculatePublicB(BigInteger N, BigInteger g, BigInteger k)
{
return (BigInteger.ModPow(g, b, N) + (v * k)) % N;
}
byte[] CalculateVerifier(string username, string password, byte[] salt)
{
// v = g ^ H(s || H(u || ':' || p)) mod N
return BigInteger.ModPow(_g, new BigInteger(_sha1.ComputeHash(salt.Combine(_sha1.ComputeHash(Encoding.UTF8.GetBytes(username.ToUpperInvariant() + ":" + password.ToUpperInvariant())))), true), _N).ToByteArray();
return BigInteger.ModPow(Getg(), CalculateX(username, password, salt), GetN()).ToByteArray();
}
public static bool CheckLogin(string username, string password, byte[] salt, byte[] verifier)
public abstract BigInteger GetN();
public abstract BigInteger Getg();
public abstract BigInteger CalculateServerEvidence(BigInteger A, BigInteger clientM1, BigInteger K);
public static (byte[], byte[]) MakeAccountRegistrationData<T>(string username, string password) where T : new()
{
return verifier.Compare(CalculateVerifier(username, password, salt));
GruntSRP6 impl = new();
return (impl.s, impl.CalculateVerifier(username, password, impl.s));
}
public static (byte[], byte[]) MakeBNetRegistrationData<T>(string username, string password) where T : new()
{
BnetSRP6v2Hash256 impl = new();
return (impl.s, impl.CalculateVerifier(username, password, impl.s));
}
public abstract BigInteger CalculateX(string username, string password, byte[] salt);
public abstract BigInteger? DoVerifyClientEvidence(BigInteger A, BigInteger clientM1);
}
public class GruntSRP6 : SRP6
{
static BigInteger N;// the modulus, an algorithm parameter; all operations are mod this
static BigInteger g;// a [g]enerator for the ring of integers mod N, algorithm parameter
static SHA1 _sha1 = SHA1.Create();
static GruntSRP6()
{
N = new BigInteger("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7".ToByteArray(), true, true);
g = new BigInteger(7u);
}
public GruntSRP6() : base() { }
public GruntSRP6(string username, byte[] salt, byte[] verifier) : base(new BigInteger(_sha1.ComputeHash(Encoding.UTF8.GetBytes(username)), true, true), salt, verifier, N, g, 3) { }
public override BigInteger CalculateServerEvidence(BigInteger A, BigInteger clientM1, BigInteger K)
{
return new BigInteger(_sha1.ComputeHash(A.ToByteArray().Combine(clientM1.ToByteArray().Combine(K.ToByteArray()))), true, true);
}
public override BigInteger CalculateX(string username, string password, byte[] salt)
{
return new BigInteger(_sha1.ComputeHash(salt.Combine(_sha1.ComputeHash(Encoding.UTF8.GetBytes(username + ":" + password)))), true, true);
}
public override BigInteger? DoVerifyClientEvidence(BigInteger A, BigInteger clientM1)
{
if ((A % N).IsZero)
return null;
BigInteger u = new(_sha1.ComputeHash(A.ToByteArray().Combine(B.ToByteArray())), false, true);
byte[] S = BigInteger.ModPow(A * BigInteger.ModPow(v, u, N), b, N).ToByteArray();
byte[] K = SHA1Interleave(S);
// NgHash = H(N) xor H(g)
byte[] NHash = _sha1.ComputeHash(N.ToByteArray());
byte[] gHash = _sha1.ComputeHash(g.ToByteArray());
byte[] NgHash = NHash.Select((x, i) => (byte)(x ^ gHash[i])).ToArray();
BigInteger ourM = new BigInteger(_sha1.ComputeHash(NgHash.Combine(I.ToByteArray().Combine(s.Combine(A.ToByteArray().Combine(B.ToByteArray().Combine(K)))))), true, true);
if (ourM == clientM1)
return new BigInteger(K, true, true);
return null;
}
byte[] SHA1Interleave(byte[] S)
{
// split S into two buffers
byte[] buf0 = new byte[32 / 2];
byte[] buf1 = new byte[32 / 2];
for (int i = 0; i < S.Length / 2; ++i)
{
buf0[i] = S[2 * i + 0];
buf1[i] = S[2 * i + 1];
}
// find position of first nonzero byte
int p = 0;
while (p < S.Length && S[p] == 0)
++p;
if ((p & 1) != 0)
++p; // skip one extra byte if p is odd
p /= 2; // offset into buffers
// hash each of the halves, starting at the first nonzero byte
byte[] hash0 = _sha1.ComputeHash(buf0[p..].Combine(S[(S.Length / 2 - p)..]));
byte[] hash1 = _sha1.ComputeHash(buf1[p..].Combine(S[(S.Length / 2 - p)..]));
// stick the two hashes back together
byte[] K = new byte[SHA1.HashSizeInBytes * 2];
for (int i = 0; i < SHA1.HashSizeInBytes; ++i)
{
K[2 * i + 0] = hash0[i];
K[2 * i + 1] = hash1[i];
}
return K;
}
public override BigInteger GetN() { return N; }
public override BigInteger Getg() { return g; }
}
public abstract class BnetSRP6Base : SRP6
{
protected static SHA256 _sha256 = SHA256.Create();
protected static SHA512 _sha512 = SHA512.Create();
public BnetSRP6Base() : base() { }
public BnetSRP6Base(BigInteger i, byte[] salt, byte[] verifier, BigInteger N, BigInteger g, BigInteger k) : base(i, salt, verifier, N, g, k) { }
public override BigInteger CalculateServerEvidence(BigInteger A, BigInteger clientM1, BigInteger K)
{
return DoCalculateEvidence(A, clientM1, K);
}
public override BigInteger? DoVerifyClientEvidence(BigInteger A, BigInteger clientM1)
{
BigInteger N = GetN();
if ((A % N).IsZero)
return null;
BigInteger u = CalculateU(A);
if ((u % N).IsZero)
return null;
BigInteger S = BigInteger.ModPow(A * BigInteger.ModPow(v, u, N), b, N);
BigInteger ourM = DoCalculateEvidence(A, B, S);
if (ourM != clientM1)
return null;
return S;
}
byte[] GetBrokenEvidenceVector(BigInteger bn)
{
int bytes = (int)(bn.GetBitLength() + 8) >> 3;
var byteArray = bn.ToByteArray(true, true);
return new byte[bytes - byteArray.Length].Combine(byteArray);
}
public abstract byte GetVersion();
public abstract uint GetXIterations();
public virtual BigInteger CalculateU(BigInteger A) { return default; }
public virtual BigInteger DoCalculateEvidence(params BigInteger[] bns) { return default; }
public BigInteger DoCalculateEvidenceHash256(params BigInteger[] bns)
{
for (var i = 0; i < bns.Length; ++i)
{
var bytes = GetBrokenEvidenceVector(bns[i]);
if (i == bns.Length - 1)
{
_sha256.TransformFinalBlock(bytes, 0, bytes.Length);
break;
}
_sha256.TransformBlock(bytes, 0, bytes.Length, null, 0);
}
return new BigInteger(_sha256.Hash, true, true);
}
public BigInteger DoCalculateEvidenceHash512(params BigInteger[] bns)
{
for (var i = 0; i < bns.Length; ++i)
{
var bytes = GetBrokenEvidenceVector(bns[i]);
if (i == bns.Length - 1)
{
_sha512.TransformFinalBlock(bytes, 0, bytes.Length);
break;
}
_sha512.TransformBlock(bytes, 0, bytes.Length, null, 0);
}
return new BigInteger(_sha512.Hash, true, true);
}
}
}
public class BnetSRP6v1Base : BnetSRP6Base
{
/// <summary>
/// // the modulus, an algorithm parameter; all operations are mod this
/// </summary>
public static BigInteger N;
/// <summary>
/// // a [g]enerator for the ring of integers mod N, algorithm parameter
/// </summary>
public static BigInteger g;
static BnetSRP6v1Base()
{
g = new BigInteger(2u);
N = new BigInteger("86A7F6DEEB306CE519770FE37D556F29944132554DED0BD68205E27F3231FEF5A10108238A3150C59CAF7B0B6478691C13A6ACF5E1B5ADAFD4A943D4A21A142B800E8A55F8BFBAC700EB77A7235EE5A609E350EA9FC19F10D921C2FA832E4461B7125D38D254A0BE873DFC27858ACB3F8B9F258461E4373BC3A6C2A9634324AB".ToByteArray(), true, true);
}
public BnetSRP6v1Base() : base() { }
public BnetSRP6v1Base(string username, byte[] salt, byte[] verifier, BigInteger k) : base(new BigInteger(SHA256.HashData(Encoding.UTF8.GetBytes(username)), true), salt, verifier, N, g, k) { }
public override BigInteger CalculateX(string username, string password, byte[] salt)
{
return new BigInteger(SHA256.HashData(salt.Combine(SHA256.HashData(Encoding.UTF8.GetBytes(username + ":" + password)))), true);
}
public override BigInteger GetN() { return N; }
public override BigInteger Getg() { return g; }
public override byte GetVersion() { return 1; }
public override uint GetXIterations() { return 1; }
}
public class BnetSRP6v2Base : BnetSRP6Base
{
/// <summary>
/// // the modulus, an algorithm parameter; all operations are mod this
/// </summary>
protected static BigInteger N;
/// <summary>
/// // a [g]enerator for the ring of integers mod N, algorithm parameter
/// </summary>
protected static BigInteger g;
static BnetSRP6v2Base()
{
N = new BigInteger("AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73".ToByteArray(), true, true);
g = new BigInteger(2u);
}
public BnetSRP6v2Base() : base() { }
public BnetSRP6v2Base(string username, byte[] salt, byte[] verifier, BigInteger k) : base(new BigInteger(SHA256.HashData(Encoding.UTF8.GetBytes(username)), true), salt, verifier, N, g, k) { }
public override BigInteger CalculateX(string username, string password, byte[] salt)
{
string tmp = username + ":" + password;
Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes(tmp, salt, (int)GetXIterations(), HashAlgorithmName.SHA512);
byte[] xBytes = rfc.GetBytes(SHA512.HashSizeInBytes);
BigInteger x = new(xBytes, true, true);
if ((xBytes[0] & 0x80) != 0)
{
byte[] fix = new byte[65];
fix[64] = 1;
x -= new BigInteger(fix, true);
}
return x % (N - 1);
}
public override BigInteger GetN() { return N; }
public override BigInteger Getg() { return g; }
public override byte GetVersion() { return 2; }
public override uint GetXIterations() { return 15000; }
}
public class BnetSRP6v1Hash256 : BnetSRP6v1Base
{
static byte[] dummyBytes = new byte[127];
public BnetSRP6v1Hash256() : base() { }
public BnetSRP6v1Hash256(string username, byte[] salt, byte[] verifier) : base(username, salt, verifier, new BigInteger(SHA256.HashData(N.ToByteArray(true, true).Combine(dummyBytes.Combine(g.ToByteArray(true, true)))), true, true)) { }
public override BigInteger CalculateU(BigInteger A)
{
return new BigInteger(SHA256.HashData(A.ToByteArray(true, true).Combine(B.ToByteArray(true, true))), true, true);
}
public override BigInteger DoCalculateEvidence(params BigInteger[] bns)
{
return DoCalculateEvidenceHash256(bns);
}
}
public class BnetSRP6v1Hash512 : BnetSRP6v1Base
{
public BnetSRP6v1Hash512() : base() { }
public BnetSRP6v1Hash512(string username, byte[] salt, byte[] verifier) : base(username, salt, verifier, new BigInteger(SHA512.HashData(N.ToByteArray(true, true).Combine(g.ToByteArray(true, true))), true, true)) { }
public override BigInteger CalculateU(BigInteger A)
{
return new BigInteger(_sha512.ComputeHash(A.ToByteArray(true, true).Combine(B.ToByteArray(true, true))), true, true);
}
public override BigInteger DoCalculateEvidence(params BigInteger[] bns)
{
return DoCalculateEvidenceHash512(bns);
}
}
public class BnetSRP6v2Hash256 : BnetSRP6v2Base
{
public BnetSRP6v2Hash256() : base() { }
public BnetSRP6v2Hash256(string username, byte[] salt, byte[] verifier) : base(username, salt, verifier, new BigInteger(_sha256.ComputeHash(N.ToByteArray(true, false).Combine(g.ToByteArray(true, false))), true)) { }
public override BigInteger CalculateU(BigInteger A)
{
return new BigInteger(_sha256.ComputeHash(A.ToByteArray(true, true).Combine(B.ToByteArray(true, true))), true, true);
}
public override BigInteger DoCalculateEvidence(params BigInteger[] bns)
{
return DoCalculateEvidenceHash256(bns);
}
}
public class BnetSRP6v2Hash512 : BnetSRP6v2Base
{
public BnetSRP6v2Hash512() : base() { }
public BnetSRP6v2Hash512(string username, byte[] salt, byte[] verifier) : base(username, salt, verifier, new BigInteger(_sha512.ComputeHash(N.ToByteArray(true, true).Combine(g.ToByteArray(true, true))), true, true)) { }
public override BigInteger CalculateU(BigInteger A)
{
return new BigInteger(_sha512.ComputeHash(A.ToByteArray(true, true).Combine(B.ToByteArray(true, true))), true, true);
}
public override BigInteger DoCalculateEvidence(params BigInteger[] bns)
{
return DoCalculateEvidenceHash512(bns);
}
}
}
@@ -7,15 +7,14 @@ namespace Framework.Database
{
public override void PreparedStatements()
{
const string BnetAccountInfo = "ba.id, ba.email, ba.locked, ba.lock_country, ba.last_ip, ba.LoginTicketExpiry, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, bab.unbandate = bab.bandate";
const string BnetAccountInfo = "ba.id, UPPER(ba.email), ba.locked, ba.lock_country, ba.last_ip, ba.LoginTicketExpiry, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, bab.unbandate = bab.bandate";
const string BnetGameAccountInfo = "a.id, a.username, ab.unbandate, ab.unbandate = ab.bandate, aa.SecurityLevel";
PrepareStatement(LoginStatements.SEL_REALMLIST, "SELECT id, name, address, localAddress, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild, Region, Battlegroup FROM realmlist WHERE flag <> 3 ORDER BY name");
PrepareStatement(LoginStatements.DelExpiredIpBans, "DELETE FROM ip_banned WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()");
PrepareStatement(LoginStatements.UpdExpiredAccountBans, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()");
PrepareStatement(LoginStatements.SelIpInfo, "SELECT unbandate > UNIX_TIMESTAMP() OR unbandate = bandate AS banned, NULL as country FROM ip_banned WHERE ip = ?");
PrepareStatement(LoginStatements.InsIpAutoBanned, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Cypher Auth', 'Failed login autoban')");
PrepareStatement(LoginStatements.DEL_EXPIRED_IP_BANS, "DELETE FROM ip_banned WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()");
PrepareStatement(LoginStatements.UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()");
PrepareStatement(LoginStatements.SEL_IP_INFO, "SELECT unbandate > UNIX_TIMESTAMP() OR unbandate = bandate AS banned, NULL as country FROM ip_banned WHERE ip = ?");
PrepareStatement(LoginStatements.INS_IP_AUTO_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban')");
PrepareStatement(LoginStatements.SEL_IP_BANNED_ALL, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) ORDER BY unbandate");
PrepareStatement(LoginStatements.SEL_IP_BANNED_BY_IP, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) AND ip LIKE CONCAT('%%', ?, '%%') ORDER BY unbandate");
PrepareStatement(LoginStatements.SEL_ACCOUNT_BANNED_ALL, "SELECT account.id, username FROM account, account_banned WHERE account.id = account_banned.id AND active = 1 GROUP BY account.id");
@@ -27,12 +26,11 @@ namespace Framework.Database
PrepareStatement(LoginStatements.UPD_LOGON, "UPDATE account SET salt = ?, verifier = ? WHERE id = ?");
PrepareStatement(LoginStatements.SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?");
PrepareStatement(LoginStatements.SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?");
PrepareStatement(LoginStatements.SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.session_key_bnet, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, a.os, ba.id, aa.SecurityLevel, " +
"bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id " +
PrepareStatement(LoginStatements.SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.session_key_bnet, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, a.os, a.timezone_offset, ba.id, aa.SecurityLevel, " +
"bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id " +
"FROM account a LEFT JOIN account r ON a.id = r.recruiter LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id " +
"LEFT JOIN account_access aa ON a.id = aa.AccountID AND aa.RealmID IN (-1, ?) LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 " +
"WHERE a.username = ? AND LENGTH(a.session_key_bnet) = 64 ORDER BY aa.RealmID DESC LIMIT 1");
PrepareStatement(LoginStatements.SEL_ACCOUNT_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?");
PrepareStatement(LoginStatements.SEL_ACCOUNT_BY_IP, "SELECT id, username FROM account WHERE last_ip = ?");
PrepareStatement(LoginStatements.SEL_ACCOUNT_BY_ID, "SELECT 1 FROM account WHERE id = ?");
@@ -43,7 +41,7 @@ namespace Framework.Database
PrepareStatement(LoginStatements.DEL_REALM_CHARACTERS, "DELETE FROM realmcharacters WHERE acctid = ?");
PrepareStatement(LoginStatements.REP_REALM_CHARACTERS, "REPLACE INTO realmcharacters (numchars, acctid, realmid) VALUES (?, ?, ?)");
PrepareStatement(LoginStatements.SEL_SUM_REALM_CHARACTERS, "SELECT SUM(numchars) FROM realmcharacters WHERE acctid = ?");
PrepareStatement(LoginStatements.INS_ACCOUNT, "INSERT INTO account (username, salt, verifier, reg_mail, email, joindate, battlenet_account, battlenet_index) VALUES(?, ?, ?, ?, ?, NOW(), ?, ?)");
PrepareStatement(LoginStatements.INS_ACCOUNT, "INSERT INTO account(username, salt, verifier, reg_mail, email, joindate, battlenet_account, battlenet_index) VALUES(?, ?, ?, ?, ?, NOW(), ?, ?)");
PrepareStatement(LoginStatements.INS_REALM_CHARACTERS_INIT, "INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist, account LEFT JOIN realmcharacters ON acctid = account.id WHERE acctid IS NULL");
PrepareStatement(LoginStatements.UPD_EXPANSION, "UPDATE account SET expansion = ? WHERE id = ?");
PrepareStatement(LoginStatements.UPD_ACCOUNT_LOCK, "UPDATE account SET locked = ? WHERE id = ?");
@@ -80,14 +78,14 @@ namespace Framework.Database
PrepareStatement(LoginStatements.DEL_ACCOUNT, "DELETE FROM account WHERE id = ?");
PrepareStatement(LoginStatements.SEL_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1");
PrepareStatement(LoginStatements.GET_EMAIL_BY_ID, "SELECT email FROM account WHERE id = ?");
// 0: uint32, 1: uint32, 2: uint8, 3: uint32, 4: string // Complete name: "Login_Insert_AccountLoginDeLete_IP_Logging"
PrepareStatement(LoginStatements.INS_ALDL_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, (SELECT last_ip FROM account WHERE id = ?), ?, unix_timestamp(NOW()), NOW())");
// 0: uint32, 1: uint32, 2: uint8, 3: uint32, 4: string // Complete name: "Login_Insert_FailedAccountLogin_IP_Logging"
PrepareStatement(LoginStatements.INS_FACL_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, (SELECT last_attempt_ip FROM account WHERE id = ?), ?, unix_timestamp(NOW()), NOW())");
// 0: uint32, 1: uint32, 2: uint8, 3: string, 4: string // Complete name: "Login_Insert_CharacterDelete_IP_Logging"
PrepareStatement(LoginStatements.INS_CHAR_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, ?, ?, unix_timestamp(NOW()), NOW())");
// 0: string, 1: string, 2: string // Complete name: "Login_Insert_Failed_Account_Login_due_password_IP_Logging"
PrepareStatement(LoginStatements.INS_FALP_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, 0, 1, ?, ?, unix_timestamp(NOW()), NOW())");
// 0: uint32, 1: uint32, 2: uint32, 3: uint8, 4: uint32, 5: string // Complete name: "Insert_AccountLoginDeLete_IP_Logging"
PrepareStatement(LoginStatements.INS_ALDL_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id, character_guid, realm_id, type, ip, systemnote, unixtime, time) VALUES (?, ?, ?, ?, (SELECT last_ip FROM account WHERE id = ?), ?, unix_timestamp(NOW()), NOW())");
// 0: uint32, 1: uint32, 2: uint32, 3: uint8, 4: uint32, 5: string // Complete name: "Insert_FailedAccountIP_Logging"
PrepareStatement(LoginStatements.INS_FACL_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id, character_guid, realm_id, type, ip, systemnote, unixtime, time) VALUES (?, ?, ?, ?, (SELECT last_attempt_ip FROM account WHERE id = ?), ?, unix_timestamp(NOW()), NOW())");
// 0: uint32, 1: uint32, 2: uint32, 3: uint8, 4: string, 5: string // Complete name: "Insert_CharacterDelete_IP_Logging"
PrepareStatement(LoginStatements.INS_CHAR_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id, character_guid, realm_id, type, ip, systemnote, unixtime, time) VALUES (?, ?, ?, ?, ?, ?, unix_timestamp(NOW()), NOW())");
// 0: uint32, 1: string, 2: string // Complete name: "Insert_Failed_Account_due_password_IP_Logging"
PrepareStatement(LoginStatements.INS_FALP_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id, character_guid, realm_id, type, ip, systemnote, unixtime, time) VALUES (?, 0, 0, 1, ?, ?, unix_timestamp(NOW()), NOW())");
PrepareStatement(LoginStatements.SEL_ACCOUNT_ACCESS_BY_ID, "SELECT SecurityLevel, RealmID FROM account_access WHERE AccountID = ? and (RealmID = ? OR RealmID = -1) ORDER BY SecurityLevel desc");
PrepareStatement(LoginStatements.SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId");
@@ -98,35 +96,46 @@ namespace Framework.Database
PrepareStatement(LoginStatements.SEL_ACCOUNT_MUTE_INFO, "SELECT mutedate, mutetime, mutereason, mutedby FROM account_muted WHERE guid = ? ORDER BY mutedate ASC");
PrepareStatement(LoginStatements.DEL_ACCOUNT_MUTED, "DELETE FROM account_muted WHERE guid = ?");
PrepareStatement(LoginStatements.SelBnetAuthentication, "SELECT ba.id, ba.sha_pass_hash, ba.failed_logins, ba.LoginTicket, ba.LoginTicketExpiry, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id WHERE email = ?");
PrepareStatement(LoginStatements.UpdBnetAuthentication, "UPDATE battlenet_accounts SET LoginTicket = ?, LoginTicketExpiry = ? WHERE id = ?");
PrepareStatement(LoginStatements.SEL_SECRET_DIGEST, "SELECT digest FROM secret_digest WHERE id = ?");
PrepareStatement(LoginStatements.INS_SECRET_DIGEST, "INSERT INTO secret_digest (id, digest) VALUES (?,?)");
PrepareStatement(LoginStatements.DEL_SECRET_DIGEST, "DELETE FROM secret_digest WHERE id = ?");
PrepareStatement(LoginStatements.SEL_ACCOUNT_TOTP_SECRET, "SELECT totp_secret FROM account WHERE id = ?");
PrepareStatement(LoginStatements.UPD_ACCOUNT_TOTP_SECRET, "UPDATE account SET totp_secret = ? WHERE id = ?");
PrepareStatement(LoginStatements.SEL_BNET_AUTHENTICATION, "SELECT ba.id, ba.srp_version, COALESCE(ba.salt, 0x0000000000000000000000000000000000000000000000000000000000000000), ba.verifier, ba.failed_logins, ba.LoginTicket, ba.LoginTicketExpiry, bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id WHERE email = ?");
PrepareStatement(LoginStatements.UPD_BNET_AUTHENTICATION, "UPDATE battlenet_accounts SET LoginTicket = ?, LoginTicketExpiry = ? WHERE id = ?");
PrepareStatement(LoginStatements.SEL_BNET_EXISTING_AUTHENTICATION, "SELECT LoginTicketExpiry FROM battlenet_accounts WHERE LoginTicket = ?");
PrepareStatement(LoginStatements.SEL_BNET_EXISTING_AUTHENTICATION_BY_ID, "SELECT LoginTicket FROM battlenet_accounts WHERE id = ?");
PrepareStatement(LoginStatements.SelBnetAccountInfo, "SELECT " + BnetAccountInfo + ", " + BnetGameAccountInfo +
" FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account" +
PrepareStatement(LoginStatements.UPD_BNET_EXISTING_AUTHENTICATION, "UPDATE battlenet_accounts SET LoginTicketExpiry = ? WHERE LoginTicket = ?");
PrepareStatement(LoginStatements.SEL_BNET_ACCOUNT_INFO, $"SELECT {BnetAccountInfo}, {BnetGameAccountInfo}" +
" FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account" +
" LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.AccountID AND aa.RealmID = -1 WHERE ba.LoginTicket = ? ORDER BY a.id");
PrepareStatement(LoginStatements.UpdBnetLastLoginInfo, "UPDATE battlenet_accounts SET last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE id = ?");
PrepareStatement(LoginStatements.UPD_BNET_GAME_ACCOUNT_LOGIN_INFO, "UPDATE account SET session_key_bnet = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?");
PrepareStatement(LoginStatements.SelBnetCharacterCountsByAccountId, "SELECT rc.acctid, rc.numchars, r.id, r.Region, r.Battlegroup FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id WHERE rc.acctid = ?");
PrepareStatement(LoginStatements.UPD_BNET_LAST_LOGIN_INFO, "UPDATE battlenet_accounts SET last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE id = ?");
PrepareStatement(LoginStatements.UPD_BNET_GAME_ACCOUNT_LOGIN_INFO, "UPDATE account SET session_key_bnet = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ?, timezone_offset = ? WHERE username = ?");
PrepareStatement(LoginStatements.SEL_BNET_CHARACTER_COUNTS_BY_ACCOUNT_ID, "SELECT rc.acctid, rc.numchars, r.id, r.Region, r.Battlegroup FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id WHERE rc.acctid = ?");
PrepareStatement(LoginStatements.SEL_BNET_CHARACTER_COUNTS_BY_BNET_ID, "SELECT rc.acctid, rc.numchars, r.id, r.Region, r.Battlegroup FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id LEFT JOIN account a ON rc.acctid = a.id WHERE a.battlenet_account = ?");
PrepareStatement(LoginStatements.SelBnetLastPlayerCharacters, "SELECT lpc.accountId, lpc.region, lpc.battlegroup, lpc.realmId, lpc.characterName, lpc.characterGUID, lpc.lastPlayedTime FROM account_last_played_character lpc LEFT JOIN account a ON lpc.accountId = a.id WHERE a.battlenet_account = ?");
PrepareStatement(LoginStatements.SEL_BNET_LAST_PLAYER_CHARACTERS, "SELECT lpc.accountId, lpc.region, lpc.battlegroup, lpc.realmId, lpc.characterName, lpc.characterGUID, lpc.lastPlayedTime FROM account_last_played_character lpc LEFT JOIN account a ON lpc.accountId = a.id WHERE a.battlenet_account = ?");
PrepareStatement(LoginStatements.DEL_BNET_LAST_PLAYER_CHARACTERS, "DELETE FROM account_last_played_character WHERE accountId = ? AND region = ? AND battlegroup = ?");
PrepareStatement(LoginStatements.INS_BNET_LAST_PLAYER_CHARACTERS, "INSERT INTO account_last_played_character (accountId, region, battlegroup, realmId, characterName, characterGUID, lastPlayedTime) VALUES (?,?,?,?,?,?,?)");
PrepareStatement(LoginStatements.INS_BNET_ACCOUNT, "INSERT INTO battlenet_accounts (`email`,`sha_pass_hash`) VALUES (?, ?)");
PrepareStatement(LoginStatements.INS_BNET_ACCOUNT, "INSERT INTO battlenet_accounts (`email`,`srp_version`,`salt`,`verifier`) VALUES (?, ?, ?, ?)");
PrepareStatement(LoginStatements.SEL_BNET_ACCOUNT_EMAIL_BY_ID, "SELECT email FROM battlenet_accounts WHERE id = ?");
PrepareStatement(LoginStatements.SEL_BNET_ACCOUNT_ID_BY_EMAIL, "SELECT id FROM battlenet_accounts WHERE email = ?");
PrepareStatement(LoginStatements.UPD_BNET_PASSWORD, "UPDATE battlenet_accounts SET sha_pass_hash = ? WHERE id = ?");
PrepareStatement(LoginStatements.SEL_BNET_CHECK_PASSWORD, "SELECT 1 FROM battlenet_accounts WHERE id = ? AND sha_pass_hash = ?");
PrepareStatement(LoginStatements.UPD_BNET_LOGON, "UPDATE battlenet_accounts SET srp_version = ?, salt = ?, verifier = ? WHERE id = ?");
PrepareStatement(LoginStatements.SEL_BNET_CHECK_PASSWORD, "SELECT srp_version, COALESCE(salt, 0x0000000000000000000000000000000000000000000000000000000000000000), verifier FROM battlenet_accounts WHERE id = ?");
PrepareStatement(LoginStatements.SEL_BNET_CHECK_PASSWORD_BY_EMAIL, "SELECT srp_version, COALESCE(salt, 0x0000000000000000000000000000000000000000000000000000000000000000), verifier FROM battlenet_accounts WHERE email = ?");
PrepareStatement(LoginStatements.UPD_BNET_ACCOUNT_LOCK, "UPDATE battlenet_accounts SET locked = ? WHERE id = ?");
PrepareStatement(LoginStatements.UPD_BNET_ACCOUNT_LOCK_CONTRY, "UPDATE battlenet_accounts SET lock_country = ? WHERE id = ?");
PrepareStatement(LoginStatements.SEL_BNET_ACCOUNT_ID_BY_GAME_ACCOUNT, "SELECT battlenet_account FROM account WHERE id = ?");
PrepareStatement(LoginStatements.UPD_BNET_GAME_ACCOUNT_LINK, "UPDATE account SET battlenet_account = ?, battlenet_index = ? WHERE id = ?");
PrepareStatement(LoginStatements.SEL_BNET_MAX_ACCOUNT_INDEX, "SELECT MAX(battlenet_index) FROM account WHERE battlenet_account = ?");
PrepareStatement(LoginStatements.SEL_BNET_GAME_ACCOUNT_LIST, "SELECT a.id, a.username FROM account a LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id WHERE ba.email = ?");
PrepareStatement(LoginStatements.SEL_BNET_GAME_ACCOUNT_LIST_SMALL, "SELECT a.id, a.username FROM account a LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id WHERE ba.email = ?");
PrepareStatement(LoginStatements.SEL_BNET_GAME_ACCOUNT_LIST, "SELECT a.username, a.expansion, ab.bandate, ab.unbandate, ab.banreason FROM account AS a LEFT JOIN account_banned AS ab ON a.id = ab.id AND ab.active = 1 INNER JOIN battlenet_accounts AS ba ON a.battlenet_account = ba.id WHERE ba.LoginTicket = ? ORDER BY a.id");
PrepareStatement(LoginStatements.UpdBnetFailedLogins, "UPDATE battlenet_accounts SET failed_logins = failed_logins + 1 WHERE id = ?");
PrepareStatement(LoginStatements.InsBnetAccountAutoBanned, "INSERT INTO battlenet_account_bans(id, bandate, unbandate, bannedby, banreason) VALUES(?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban')");
PrepareStatement(LoginStatements.DelBnetExpiredAccountBanned, "DELETE FROM battlenet_account_bans WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()");
PrepareStatement(LoginStatements.UpdBnetResetFailedLogins, "UPDATE battlenet_accounts SET failed_logins = 0 WHERE id = ?");
PrepareStatement(LoginStatements.UPD_BNET_FAILED_LOGINS, "UPDATE battlenet_accounts SET failed_logins = failed_logins + 1 WHERE id = ?");
PrepareStatement(LoginStatements.INS_BNET_ACCOUNT_AUTO_BANNED, "INSERT INTO battlenet_account_bans(id, bandate, unbandate, bannedby, banreason) VALUES(?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban')");
PrepareStatement(LoginStatements.DEL_BNET_EXPIRED_ACCOUNT_BANNED, "DELETE FROM battlenet_account_bans WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()");
PrepareStatement(LoginStatements.UPD_BNET_RESET_FAILED_LOGINS, "UPDATE battlenet_accounts SET failed_logins = 0 WHERE id = ?");
PrepareStatement(LoginStatements.SEL_LAST_CHAR_UNDELETE, "SELECT LastCharacterUndelete FROM battlenet_accounts WHERE Id = ?");
PrepareStatement(LoginStatements.UPD_LAST_CHAR_UNDELETE, "UPDATE battlenet_accounts SET LastCharacterUndelete = UNIX_TIMESTAMP() WHERE Id = ?");
@@ -157,22 +166,24 @@ namespace Framework.Database
// Transmog collection
PrepareStatement(LoginStatements.SEL_BNET_ITEM_APPEARANCES, "SELECT blobIndex, appearanceMask FROM battlenet_item_appearances WHERE battlenetAccountId = ? ORDER BY blobIndex DESC");
PrepareStatement(LoginStatements.INS_BNET_ITEM_APPEARANCES, "INSERT INTO battlenet_item_appearances (battlenetAccountId, blobIndex, appearanceMask) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE appearanceMask = appearanceMask | VALUES(appearanceMask)");
PrepareStatement(LoginStatements.INS_BNET_ITEM_APPEARANCES, "INSERT INTO battlenet_item_appearances (battlenetAccountId, blobIndex, appearanceMask) VALUES (?, ?, ?) " +
"ON DUPLICATE KEY UPDATE appearanceMask = appearanceMask | VALUES(appearanceMask)");
PrepareStatement(LoginStatements.SEL_BNET_ITEM_FAVORITE_APPEARANCES, "SELECT itemModifiedAppearanceId FROM battlenet_item_favorite_appearances WHERE battlenetAccountId = ?");
PrepareStatement(LoginStatements.INS_BNET_ITEM_FAVORITE_APPEARANCE, "INSERT INTO battlenet_item_favorite_appearances (battlenetAccountId, itemModifiedAppearanceId) VALUES (?, ?)");
PrepareStatement(LoginStatements.DEL_BNET_ITEM_FAVORITE_APPEARANCE, "DELETE FROM battlenet_item_favorite_appearances WHERE battlenetAccountId = ? AND itemModifiedAppearanceId = ?");
PrepareStatement(LoginStatements.SEL_BNET_TRANSMOG_ILLUSIONS, "SELECT blobIndex, illusionMask FROM battlenet_account_transmog_illusions WHERE battlenetAccountId = ? ORDER BY blobIndex DESC");
PrepareStatement(LoginStatements.INS_BNET_TRANSMOG_ILLUSIONS, "INSERT INTO battlenet_account_transmog_illusions (battlenetAccountId, blobIndex, illusionMask) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE illusionMask = illusionMask | VALUES(illusionMask)");
PrepareStatement(LoginStatements.INS_BNET_TRANSMOG_ILLUSIONS, "INSERT INTO battlenet_account_transmog_illusions (battlenetAccountId, blobIndex, illusionMask) VALUES (?, ?, ?) " +
"ON DUPLICATE KEY UPDATE illusionMask = illusionMask | VALUES(illusionMask)");
}
}
public enum LoginStatements
{
SEL_REALMLIST,
DelExpiredIpBans,
UpdExpiredAccountBans,
SelIpInfo,
InsIpAutoBanned,
DEL_EXPIRED_IP_BANS,
UPD_EXPIRED_ACCOUNT_BANS,
SEL_IP_INFO,
INS_IP_AUTO_BANNED,
SEL_ACCOUNT_BANNED_ALL,
SEL_ACCOUNT_BANNED_BY_FILTER,
SEL_ACCOUNT_BANNED_BY_USERNAME,
@@ -225,12 +236,9 @@ namespace Framework.Database
SEL_ACCOUNT_INFO,
SEL_ACCOUNT_ACCESS_SECLEVEL_TEST,
SEL_ACCOUNT_ACCESS,
SEL_ACCOUNT_RECRUITER,
SEL_BANS,
SEL_ACCOUNT_WHOIS,
SEL_REALMLIST_SECURITY_LEVEL,
DEL_ACCOUNT,
SEL_IP2NATION_COUNTRY,
SEL_AUTOBROADCAST,
SEL_LAST_ATTEMPT_IP,
SEL_LAST_IP,
@@ -249,34 +257,44 @@ namespace Framework.Database
SEL_ACCOUNT_MUTE_INFO,
DEL_ACCOUNT_MUTED,
SelBnetAuthentication,
UpdBnetAuthentication,
SEL_SECRET_DIGEST,
INS_SECRET_DIGEST,
DEL_SECRET_DIGEST,
SEL_ACCOUNT_TOTP_SECRET,
UPD_ACCOUNT_TOTP_SECRET,
SEL_BNET_AUTHENTICATION,
UPD_BNET_AUTHENTICATION,
SEL_BNET_EXISTING_AUTHENTICATION,
SEL_BNET_EXISTING_AUTHENTICATION_BY_ID,
SelBnetAccountInfo,
UpdBnetLastLoginInfo,
UPD_BNET_EXISTING_AUTHENTICATION,
SEL_BNET_ACCOUNT_INFO,
UPD_BNET_LAST_LOGIN_INFO,
UPD_BNET_GAME_ACCOUNT_LOGIN_INFO,
SelBnetCharacterCountsByAccountId,
SEL_BNET_CHARACTER_COUNTS_BY_ACCOUNT_ID,
SEL_BNET_CHARACTER_COUNTS_BY_BNET_ID,
SelBnetLastPlayerCharacters,
SEL_BNET_LAST_PLAYER_CHARACTERS,
DEL_BNET_LAST_PLAYER_CHARACTERS,
INS_BNET_LAST_PLAYER_CHARACTERS,
INS_BNET_ACCOUNT,
SEL_BNET_ACCOUNT_EMAIL_BY_ID,
SEL_BNET_ACCOUNT_ID_BY_EMAIL,
UPD_BNET_PASSWORD,
SEL_BNET_ACCOUNT_SALT_BY_ID,
UPD_BNET_LOGON,
SEL_BNET_CHECK_PASSWORD,
SEL_BNET_CHECK_PASSWORD_BY_EMAIL,
UPD_BNET_ACCOUNT_LOCK,
UPD_BNET_ACCOUNT_LOCK_CONTRY,
SEL_BNET_ACCOUNT_ID_BY_GAME_ACCOUNT,
UPD_BNET_GAME_ACCOUNT_LINK,
SEL_BNET_MAX_ACCOUNT_INDEX,
SEL_BNET_GAME_ACCOUNT_LIST_SMALL,
SEL_BNET_GAME_ACCOUNT_LIST,
UpdBnetFailedLogins,
InsBnetAccountAutoBanned,
DelBnetExpiredAccountBanned,
UpdBnetResetFailedLogins,
UPD_BNET_FAILED_LOGINS,
INS_BNET_ACCOUNT_AUTO_BANNED,
DEL_BNET_EXPIRED_ACCOUNT_BANNED,
UPD_BNET_RESET_FAILED_LOGINS,
SEL_LAST_CHAR_UNDELETE,
UPD_LAST_CHAR_UNDELETE,
@@ -30,6 +30,8 @@ namespace Framework.Database
{
commands.Add(new MySqlCommand(string.Format(sql, args)));
}
public int GetSize() { return commands.Count; }
}
class TransactionTask : ISqlOperation
+2 -1
View File
@@ -382,7 +382,6 @@ public enum LogLevel
public enum LogFilter
{
Misc,
Achievement,
Addon,
Ahbot,
@@ -404,11 +403,13 @@ public enum LogFilter
Garrison,
Gameevent,
Guild,
Http,
Instance,
Lfg,
Loot,
MapsScript,
Maps,
Misc,
Movement,
Network,
Outdoorpvp,
+1 -1
View File
@@ -64,7 +64,7 @@ namespace Framework.Networking
if (socket != null)
{
T newSocket = (T)Activator.CreateInstance(typeof(T), socket);
newSocket.Accept();
newSocket.Start();
if (!_closed)
AsyncAccept<T>();
@@ -0,0 +1,118 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Framework.Constants;
using Framework.Database;
using Framework.Web;
using System;
using System.Net.Sockets;
namespace Framework.Networking.Http
{
public interface IAbstractSocket : ISocket
{
public void SendResponse(RequestContext context);
public void QueueQuery(QueryCallback queryCallback);
public string GetClientInfo();
public Guid? GetSessionId();
}
public abstract class BaseSocket<Derived> : SSLSocket, IAbstractSocket
{
public BaseSocket(Socket socket) : base(socket) { }
public async override void ReadHandler(byte[] data, int receivedLength)
{
if (!IsOpen())
return;
if (!HttpHelper.ParseRequest(data, receivedLength, out var httpRequest))
{
CloseSocket();
return;
}
if (!HandleMessage(httpRequest))
{
CloseSocket();
return;
}
await AsyncRead();
}
bool HandleMessage(HttpHeader httpRequest)
{
RequestContext context = new() { request = httpRequest };
if (_state == null)
_state = ObtainSessionState(context);
RequestHandlerResult status = RequestHandler(context);
if (status != RequestHandlerResult.Async)
SendResponse(context);
return status != RequestHandlerResult.Error;
}
public virtual RequestHandlerResult RequestHandler(RequestContext context) { return 0; }
public async void SendResponse(RequestContext context)
{
Log.outDebug(LogFilter.Http, $"{GetClientInfo()} Request {context.request.Method} {context.request.Path} done, status {context.response.Status}");
{
bool canLogRequestContent = context.handler == null || !context.handler.Flags.HasFlag(RequestHandlerFlag.DoNotLogRequestContent);
bool canLogResponseContent = context.handler == null || !context.handler.Flags.HasFlag(RequestHandlerFlag.DoNotLogResponseContent);
Log.outTrace(LogFilter.Http, $"{GetClientInfo()} Request: {(canLogRequestContent ? context.request.Content : "<REDACTED>")}");
Log.outTrace(LogFilter.Http, $"{GetClientInfo()} Response: {(canLogResponseContent ? context.response.Content : "<REDACTED>")}");
}
await AsyncWrite(HttpHelper.CreateResponse(context));
if (!context.response.KeepAlive)
CloseSocket();
}
public void QueueQuery(QueryCallback queryCallback)
{
_queryProcessor.AddCallback(queryCallback);
}
public override bool Update()
{
if (!base.Update())
return false;
this._queryProcessor.ProcessReadyCallbacks();
return true;
}
public string GetClientInfo()
{
string info = GetRemoteIpAddress().ToString();
if (_state != null)
info += $", Session Id: {_state.Id}";
info += "]";
return info;
}
public Guid? GetSessionId()
{
if (_state != null)
return _state.Id;
return null;
}
public virtual SessionState ObtainSessionState(RequestContext context) { return null; }
protected AsyncCallbackProcessor<QueryCallback> _queryProcessor = new();
protected SessionState _state;
}
}
@@ -0,0 +1,321 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Framework.Constants;
using Framework.Web;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
namespace Framework.Networking.Http
{
public class DispatcherService
{
public DispatcherService(LogFilter logFilter)
{
_logger = logFilter;
}
public RequestHandlerResult HandleRequest(IAbstractSocket session, RequestContext context)
{
Log.outDebug(_logger, $"{session.GetClientInfo()} Starting request {context.request.Method} {context.request.Path}");
string path = new Func<string>(() =>
{
string path = context.request.Path;
int queryIndex = path.IndexOf('?');
if (queryIndex != -1)
path = path.Substring(0, queryIndex);
return path;
})();
context.handler = new Func<RequestHandler>(() =>
{
switch (context.request.Method)
{
case "GET":
case "HEAD":
return _getHandlers.LookupByKey(path);
case "POST":
return _postHandlers.LookupByKey(path);
default:
return null;
}
})();
DateTime responseDate = DateTime.Now;
//context.response.Date = responseDate - Timezone.GetSystemZoneOffsetAt(responseDate);
//context.response.Server.Add(BOOST_BEAST_VERSION_STRING);
context.response.KeepAlive = context.request.KeepAlive;
if (context.handler == null)
return HandlePathNotFound(session, context);
return context.handler.Func(session, context);
}
RequestHandlerResult HandleBadRequest(IAbstractSocket session, RequestContext context)
{
context.response.Status = HttpStatusCode.BadRequest;
return RequestHandlerResult.Handled;
}
public RequestHandlerResult HandleUnauthorized(IAbstractSocket session, RequestContext context)
{
context.response.Status = HttpStatusCode.Unauthorized;
return RequestHandlerResult.Handled;
}
RequestHandlerResult HandlePathNotFound(IAbstractSocket session, RequestContext context)
{
context.response.Status = HttpStatusCode.NotFound;
return RequestHandlerResult.Handled;
}
public void RegisterHandler(HttpMethod method, string path, Func<IAbstractSocket, RequestContext, RequestHandlerResult> handler, RequestHandlerFlag flags = RequestHandlerFlag.None)
{
var handlerMap = new Func<Dictionary<string, RequestHandler>>(() =>
{
switch (method.Method)
{
case "GET":
return _getHandlers;
case "POST":
return _postHandlers;
default:
//ABORT_MSG($"Tried to register a handler for unsupported HTTP method {method}");
return null;
}
})();
handlerMap[path] = new RequestHandler() { Func = handler, Flags = flags };
Log.outInfo(_logger, $"Registered new handler for {method} {path}");
}
Dictionary<string, RequestHandler> _getHandlers = new();
Dictionary<string, RequestHandler> _postHandlers = new();
LogFilter _logger;
}
public class SessionService
{
public SessionService(LogFilter logFilter)
{
_logger = logFilter;
}
public void InitAndStoreSessionState(SessionState state, IPAddress address)
{
state.RemoteAddress = address;
// Generate session id
lock (_sessionsMutex)
{
while (state.Id == Guid.Empty || _sessions.ContainsKey(state.Id))
state.Id = new(new byte[0].GenerateRandomKey(16));
Log.outDebug(_logger, $"Client at {address} created new session {state.Id}");
_sessions[state.Id] = state;
}
}
public void Start()
{
_inactiveSessionsKillTimer = new System.Timers.Timer(TimeSpan.FromMinutes(1));
_inactiveSessionsKillTimer.Elapsed += (_, _) => KillInactiveSessions();
_inactiveSessionsKillTimer.Start();
}
public void Stop()
{
_inactiveSessionsKillTimer = null;
lock (_sessionsMutex)
_sessions.Clear();
lock (_inactiveSessionsMutex)
_inactiveSessions.Clear();
}
public SessionState FindAndRefreshSessionState(string id, IPAddress address)
{
SessionState state;
lock (_sessionsMutex)
{
if (!_sessions.TryGetValue(Guid.Parse(id), out state))
{
Log.outDebug(_logger, $"Client at {address} attempted to use a session {id} that was expired");
return null; // no session
}
}
if (!state.RemoteAddress.Equals(address))
{
Log.outError(_logger, $"Client at {address} attempted to use a session {id} that was last accessed from {state.RemoteAddress}, denied access");
return null;
}
lock (_inactiveSessionsMutex)
{
_inactiveSessions.Remove(state.Id);
}
return state;
}
public void MarkSessionInactive(Guid id)
{
bool wasActive = true;
lock (_inactiveSessionsMutex)
{
wasActive = !_inactiveSessions.Contains(id);
if (wasActive)
_inactiveSessions.Add(id);
}
if (wasActive)
{
lock (_sessionsMutex)
{
var itr = _sessions.LookupByKey(id);
if (itr != null)
{
itr.InactiveTimestamp = DateTime.Now + TimeSpan.FromMinutes(5);
Log.outTrace(_logger, $"Session {id} marked as inactive");
}
}
}
}
void KillInactiveSessions()
{
lock (_inactiveSessionsMutex)
{
List<Guid> inactiveSessions = new(_inactiveSessions);
_inactiveSessions.Clear();
DateTime now = DateTime.Now;
int inactiveSessionsCount = inactiveSessions.Count;
lock (_sessionsMutex)
{
foreach (var guid in inactiveSessions.ToList())
{
if (!_sessions.TryGetValue(guid, out var sessionState) || sessionState.InactiveTimestamp < now)
{
_sessions.Remove(guid);
inactiveSessions.Remove(guid);
}
}
}
Log.outDebug(_logger, $"Killed {inactiveSessionsCount - inactiveSessions.Count} inactive sessions");
// restore sessions not killed to inactive queue
foreach (var guid in inactiveSessions.ToList())
{
_inactiveSessions.Add(guid);
inactiveSessions.Remove(guid);
}
}
}
object _sessionsMutex = new();
Dictionary<Guid, SessionState> _sessions = new();
object _inactiveSessionsMutex = new();
List<Guid> _inactiveSessions = new();
System.Timers.Timer _inactiveSessionsKillTimer;
LogFilter _logger;
}
public class HttpService<SessionImpl> : SocketManager<SessionImpl> where SessionImpl : IAbstractSocket
{
protected DispatcherService dispatcherService;
protected SessionService sessionService;
public HttpService(LogFilter logFilter) : base()
{
dispatcherService = new(logFilter);
sessionService = new(logFilter);
_logger = logFilter;
}
public override bool StartNetwork(string bindIp, int port, int threadCount = 1)
{
if (!base.StartNetwork(bindIp, port, threadCount))
return false;
sessionService.Start();
return true;
}
public override void StopNetwork()
{
sessionService.Stop();
base.StopNetwork();
}
// http handling
public delegate RequestHandlerResult HttpRequestHandler(SessionImpl session, RequestContext context);
public void RegisterHandler(HttpMethod method, string path, HttpRequestHandler handler, RequestHandlerFlag flags = RequestHandlerFlag.None)
{
dispatcherService.RegisterHandler(method, path, (session, context) =>
{
return handler((SessionImpl)session, context);
}, flags);
}
// session tracking
public virtual SessionState CreateNewSessionState(IPAddress address)
{
var state = new SessionState();
sessionService.InitAndStoreSessionState(state, address);
return state;
}
class Thread : NetworkThread<SessionImpl>
{
protected override void SocketRemoved(SessionImpl session)
{
var id = session.GetSessionId();
if (id.HasValue)
_service.MarkSessionInactive(id.Value);
}
public SessionService _service;
}
public NetworkThread<SessionImpl>[] CreateThreads()
{
Thread[] threads = new Thread[GetNetworkThreadCount()];
for (int i = 0; i < GetNetworkThreadCount(); ++i)
threads[i]._service = sessionService;
return threads;
}
public DispatcherService GetDispatcherService() { return dispatcherService; }
public SessionService GetSessionService() { return sessionService; }
protected LogFilter _logger;
}
public class RequestHandler
{
public Func<IAbstractSocket, RequestContext, RequestHandlerResult> Func;
public RequestHandlerFlag Flags;
}
public class RequestContext
{
public HttpHeader request = new();
public HttpHeader response = new();
public RequestHandler handler;
}
}
@@ -0,0 +1,15 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System;
using System.Net;
namespace Framework.Networking.Http
{
public class SessionState
{
public Guid Id;
public IPAddress RemoteAddress;
public DateTime InactiveTimestamp = DateTime.MaxValue;
}
}
@@ -0,0 +1,39 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using Framework.Configuration;
namespace Framework.Networking.Http
{
public class SslSocket<Derived> : BaseSocket<Derived>
{
X509Certificate2 _certificate;
public SslSocket(Socket socket) : base(socket)
{
_certificate = new X509Certificate2(ConfigMgr.GetDefaultValue("CertificatesFile", "./BNetServer.pfx"));
}
public async override void Start()
{
await AsyncHandshake(_certificate);
}
public async override Task HandshakeHandler(Exception exception = null)
{
if (exception != null)
{
Log.outError(LogFilter.Http, $"{GetClientInfo()} SSL Handshake failed {exception.Message}");
CloseSocket();
return;
}
await AsyncRead();
}
}
}
+10 -9
View File
@@ -33,16 +33,16 @@ namespace Framework.Networking
_stream.Dispose();
}
public abstract void Accept();
public abstract void Start();
public virtual bool Update()
{
return _socket.Connected;
}
public IPEndPoint GetRemoteIpEndPoint()
public IPAddress GetRemoteIpAddress()
{
return _remoteEndPoint;
return _remoteEndPoint.Address;
}
public async Task AsyncRead()
@@ -63,7 +63,7 @@ namespace Framework.Networking
}
catch (Exception ex)
{
Log.outException(ex);
Log.outDebug(LogFilter.Network, ex.Message);
}
}
@@ -73,16 +73,17 @@ namespace Framework.Networking
{
await _stream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls12, false);
}
catch(Exception ex)
catch (Exception ex)
{
Log.outException(ex);
CloseSocket();
await HandshakeHandler(ex);
return;
}
await AsyncRead();
await HandshakeHandler();
}
public abstract Task HandshakeHandler(Exception ex = null);
public abstract void ReadHandler(byte[] data, int receivedLength);
public async Task AsyncWrite(byte[] data)
@@ -109,7 +110,7 @@ namespace Framework.Networking
}
catch (Exception ex)
{
Log.outDebug(LogFilter.Network, $"WorldSocket.CloseSocket: {GetRemoteIpEndPoint()} errored when shutting down socket: {ex.Message}");
Log.outDebug(LogFilter.Network, $"WorldSocket.CloseSocket: {GetRemoteIpAddress()} errored when shutting down socket: {ex.Message}");
}
}
+2 -2
View File
@@ -9,7 +9,7 @@ namespace Framework.Networking
{
public interface ISocket
{
void Accept();
void Start();
bool Update();
bool IsOpen();
void CloseSocket();
@@ -43,7 +43,7 @@ namespace Framework.Networking
_socket.Dispose();
}
public abstract void Accept();
public virtual void Start() { }
public virtual bool Update()
{
+2 -4
View File
@@ -32,8 +32,6 @@ namespace Framework.Networking
_threads[i].Start();
}
Acceptor.AsyncAcceptSocket(OnSocketOpen);
return true;
}
@@ -64,7 +62,7 @@ namespace Framework.Networking
try
{
TSocketType newSocket = (TSocketType)Activator.CreateInstance(typeof(TSocketType), sock);
newSocket.Accept();
newSocket.Start();
_threads[SelectThreadWithMinConnections()].AddSocket(newSocket);
}
@@ -87,4 +85,4 @@ namespace Framework.Networking
return min;
}
}
}
}
+24 -11
View File
@@ -3,15 +3,18 @@
using Framework.Constants;
using Framework.Database;
using Framework.IO;
using Framework.Realm;
using Framework.Web;
using Framework.Serialization;
using Framework.Web.Rest.Realmlist;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Timers;
using System.Collections.Concurrent;
using Framework.Realm;
public class RealmManager : Singleton<RealmManager>
{
@@ -71,7 +74,7 @@ public class RealmManager : Singleton<RealmManager>
{
var oldRealm = _realms.LookupByKey(realm.Id);
if (oldRealm != null && oldRealm == realm)
return;
return;
_realms[realm.Id] = realm;
}
@@ -154,7 +157,7 @@ public class RealmManager : Singleton<RealmManager>
return true;
}
public RealmBuildInfo GetBuildInfo(uint build)
public RealmBuildInfo GetBuildInfo(uint build)
{
foreach (var clientBuild in _builds)
if (clientBuild.Build == build)
@@ -217,7 +220,10 @@ public RealmBuildInfo GetBuildInfo(uint build)
realmEntry.CfgConfigsID = (int)realm.GetConfigId();
realmEntry.CfgLanguagesID = 1;
compressed = Json.Deflate("JamJSONRealmEntry", realmEntry);
var jsonData = Encoding.UTF8.GetBytes("JamJSONRealmEntry:" + JsonSerializer.Serialize(realmEntry) + "\0");
var compressedData = ZLib.Compress(jsonData);
compressed = BitConverter.GetBytes(jsonData.Length).Combine(compressedData);
}
}
@@ -269,10 +275,13 @@ public RealmBuildInfo GetBuildInfo(uint build)
realmList.Updates.Add(realmListUpdate);
}
return Json.Deflate("JSONRealmListUpdates", realmList);
var jsonData = Encoding.UTF8.GetBytes("JSONRealmListUpdates:" + JsonSerializer.Serialize(realmList) + "\0");
var compressedData = ZLib.Compress(jsonData);
return BitConverter.GetBytes(jsonData.Length).Combine(compressedData);
}
public BattlenetRpcErrorCode JoinRealm(uint realmAddress, uint build, IPAddress clientAddress, byte[] clientSecret, Locale locale, string os, string accountName, Bgs.Protocol.GameUtilities.V1.ClientResponse response)
public BattlenetRpcErrorCode JoinRealm(uint realmAddress, uint build, IPAddress clientAddress, byte[] clientSecret, Locale locale, string os, TimeSpan timezoneOffset, string accountName, Bgs.Protocol.GameUtilities.V1.ClientResponse response)
{
Realm realm = GetRealm(new RealmId(realmAddress));
if (realm != null)
@@ -290,17 +299,21 @@ public RealmBuildInfo GetBuildInfo(uint build)
addressFamily.Addresses.Add(address);
serverAddresses.Families.Add(addressFamily);
byte[] compressed = Json.Deflate("JSONRealmListServerIPAddresses", serverAddresses);
var jsonData = Encoding.UTF8.GetBytes("JSONRealmListServerIPAddresses:" + JsonSerializer.Serialize(serverAddresses) + "\0");
var compressedData = ZLib.Compress(jsonData);
byte[] compressed = BitConverter.GetBytes(jsonData.Length).Combine(compressedData);
byte[] serverSecret = new byte[0].GenerateRandomKey(32);
byte[] keyData = clientSecret.ToArray().Combine(serverSecret);
byte[] keyData = clientSecret.Combine(serverSecret);
PreparedStatement stmt = LoginDatabase.GetPreparedStatement(LoginStatements.UPD_BNET_GAME_ACCOUNT_LOGIN_INFO);
stmt.AddValue(0, keyData);
stmt.AddValue(1, clientAddress.ToString());
stmt.AddValue(2, (byte)locale);
stmt.AddValue(3, os);
stmt.AddValue(4, accountName);
stmt.AddValue(4, (short)timezoneOffset.TotalMinutes);
stmt.AddValue(5, accountName);
DB.Login.DirectExecute(stmt);
Bgs.Protocol.Attribute attribute = new();
-65
View File
@@ -1,65 +0,0 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
namespace Framework.Serialization
{
public class Json
{
public static string CreateString<T>(T dataObject)
{
return Encoding.UTF8.GetString(CreateArray(dataObject));
}
public static byte[] CreateArray<T>(T dataObject)
{
var serializer = new DataContractJsonSerializer(typeof(T));
var stream = new MemoryStream();
serializer.WriteObject(stream, dataObject);
return stream.ToArray();
}
public static T CreateObject<T>(Stream jsonData)
{
var serializer = new DataContractJsonSerializer(typeof(T));
return (T)serializer.ReadObject(jsonData);
}
public static T CreateObject<T>(string jsonData, bool split = false)
{
return CreateObject<T>(Encoding.UTF8.GetBytes(split ? jsonData.Split(new[] { ':' }, 2)[1] : jsonData));
}
public static T CreateObject<T>(byte[] jsonData) => CreateObject<T>(new MemoryStream(jsonData));
public static object CreateObject(Stream jsonData, Type type)
{
var serializer = new DataContractJsonSerializer(type);
return serializer.ReadObject(jsonData);
}
public static object CreateObject(string jsonData, Type type, bool split = false)
{
return CreateObject(Encoding.UTF8.GetBytes(split ? jsonData.Split(new[] { ':' }, 2)[1] : jsonData), type);
}
public static object CreateObject(byte[] jsonData, Type type) => CreateObject(new MemoryStream(jsonData), type);
// Used for protobuf json strings.
public static byte[] Deflate<T>(string name, T data)
{
var jsonData = Encoding.UTF8.GetBytes(name + ":" + CreateString(data) + "\0");
var compressedData = IO.ZLib.Compress(jsonData);
return BitConverter.GetBytes(jsonData.Length).Combine(compressedData);
}
}
}
+8 -8
View File
@@ -6,10 +6,10 @@ using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Runtime.CompilerServices;
namespace System
{
@@ -29,8 +29,8 @@ namespace System
else
return byteArray.Aggregate("", (current, b) => current + b.ToString("X2"));
}
public static byte[] ToByteArray(this string str)
public static byte[] ToByteArray(this string str, bool reverse = false)
{
str = str.Replace(" ", String.Empty);
@@ -40,7 +40,7 @@ namespace System
string temp = String.Concat(str[i * 2], str[i * 2 + 1]);
res[i] = Convert.ToByte(temp, 16);
}
return res;
return reverse ? res.Reverse().ToArray() : res;
}
public static byte[] ToByteArray(this string value, char separator)
@@ -124,7 +124,7 @@ namespace System
public static uint[] SerializeObject<T>(this T obj)
{
//if (obj.GetType()<StructLayoutAttribute>() == null)
//return null;
//return null;
var size = Marshal.SizeOf(typeof(T));
var ptr = Marshal.AllocHGlobal(size);
@@ -233,7 +233,7 @@ namespace System
float invSqrt = 1.0f / MathF.Sqrt(lenSquared);
return new Vector3(vector.X * invSqrt, vector.Y * invSqrt, vector.Z * invSqrt);
}
public static Vector3 directionOrZero(this Vector3 vector)
{
float mag = vector.LengthSquared();
@@ -282,7 +282,7 @@ namespace System
x = 0.0f;
}
}
public static Matrix4x4 fromEulerAnglesZYX(float fYAngle, float fPAngle, float fRAngle)
{
float fCos = MathF.Cos(fYAngle);
@@ -318,7 +318,7 @@ namespace System
public static string ConvertFormatSyntax(this string str)
{
string pattern = @"(%\W*\d*[a-zA-Z]*)";
int count = 0;
string result = Regex.Replace(str, pattern, m => string.Concat("{", count++, "}"));
+7
View File
@@ -84,6 +84,13 @@ public class RandomHelper
rand.NextBytes(buffer);
}
public static byte[] GetRandomBytes(int length)
{
byte[] buffer = new byte[length];
rand.NextBytes(buffer);
return buffer;
}
public static T RAND<T>(params T[] args)
{
int randIndex = IRand(0, args.Length - 1);
-10
View File
@@ -1,10 +0,0 @@
using System;
namespace Framework.Web.API
{
public class ApiRequest<T>
{
public uint? SearchId { get; set; }
public Func<T, bool> SearchFunc { get; set; }
}
}
+23 -23
View File
@@ -1,10 +1,12 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Framework.Networking.Http;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
@@ -15,14 +17,14 @@ namespace Framework.Web
public string Method { get; set; }
public string Path { get; set; }
public string Type { get; set; }
public string Host { get; set; }
public string DeviceId { get; set; }
public string Authorization { get; set; }
public string ContentType { get; set; }
public int ContentLength { get; set; }
public string AcceptLanguage { get; set; }
public string Accept { get; set; }
public string UserAgent { get; set; }
public string Content { get; set; }
public string Content { get; set; } = "";
public HttpStatusCode Status { get; set; } = HttpStatusCode.OK;
public string Cookie { get; set; }
public bool KeepAlive { get; set; }
public string Host { get; set; }
}
public enum HttpCode
@@ -35,43 +37,41 @@ namespace Framework.Web
public class HttpHelper
{
public static byte[] CreateResponse(HttpCode httpCode, string content, bool closeConnection = false)
public static byte[] CreateResponse(RequestContext requestContext)
{
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
sw.WriteLine($"HTTP/1.1 {(int)httpCode} {httpCode}");
sw.WriteLine($"HTTP/1.1 {(int)requestContext.response.Status} {requestContext.response.Status}");
sw.WriteLine($"Content-Length: {requestContext.response.Content.Length}");
//sw.WriteLine($"Date: {DateTime.Now.ToUniversalTime():r}");
//sw.WriteLine("Server: Arctium-Emulation");
//sw.WriteLine("Retry-After: 600");
sw.WriteLine($"Content-Length: {content.Length}");
//sw.WriteLine("Vary: Accept-Encoding");
if (closeConnection)
if (!requestContext.response.KeepAlive)
sw.WriteLine("Connection: close");
sw.WriteLine("Content-Type: application/json;charset=UTF-8");
if (!requestContext.response.Cookie.IsEmpty())
sw.WriteLine($"Set-Cookie: {requestContext.response.Cookie}");
sw.WriteLine($"Content-Type: {requestContext.response.ContentType}");
sw.WriteLine();
sw.WriteLine(content);
sw.WriteLine(requestContext.response.Content);
}
return Encoding.UTF8.GetBytes(sb.ToString());
}
public static HttpHeader ParseRequest(byte[] data, int length)
public static bool ParseRequest(byte[] data, int length, out HttpHeader httpHeader)
{
var headerValues = new Dictionary<string, object>();
var header = new HttpHeader();
httpHeader = new HttpHeader();
using (var sr = new StreamReader(new MemoryStream(data, 0, length)))
{
var info = sr.ReadLine().Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
if (info.Length != 3)
return null;
return false;
headerValues.Add("method", info[0]);
headerValues.Add("path", info[1]);
@@ -113,13 +113,13 @@ namespace Framework.Web
if (headerValues.TryGetValue(f.Name.ToLower(), out val))
{
if (f.PropertyType == typeof(int))
f.SetValue(header, Convert.ChangeType(Convert.ToInt32(val), f.PropertyType));
f.SetValue(httpHeader, Convert.ChangeType(Convert.ToInt32(val), f.PropertyType));
else
f.SetValue(header, Convert.ChangeType(val, f.PropertyType));
f.SetValue(httpHeader, Convert.ChangeType(val, f.PropertyType));
}
}
return header;
return true;
}
}
}
@@ -1,27 +0,0 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
namespace Framework.Web
{
[DataContract]
public class LogonData
{
public string this[string inputId] => Inputs.SingleOrDefault(i => i.Id == inputId)?.Value;
[DataMember(Name = "version")]
public string Version { get; set; }
[DataMember(Name = "program_id")]
public string Program { get; set; }
[DataMember(Name = "platform_id")]
public string Platform { get; set; }
[DataMember(Name = "inputs")]
public List<FormInputValue> Inputs { get; set; } = new List<FormInputValue>();
}
}
@@ -1,23 +1,22 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
namespace Framework.Web.Rest.Login
{
[DataContract]
public class FormInput
{
[DataMember(Name = "input_id")]
[JsonPropertyName("input_id")]
public string Id { get; set; }
[DataMember(Name = "type")]
[JsonPropertyName("type")]
public string Type { get; set; }
[DataMember(Name = "label")]
[JsonPropertyName("label")]
public string Label { get; set; }
[DataMember(Name = "max_length")]
[JsonPropertyName("max_length")]
public int MaxLength { get; set; }
}
}
@@ -1,17 +1,16 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
namespace Framework.Web.Rest.Login
{
[DataContract]
public class FormInputValue
{
[DataMember(Name = "input_id")]
[JsonPropertyName("input_id")]
public string Id { get; set; }
[DataMember(Name = "value")]
[JsonPropertyName("value")]
public string Value { get; set; }
}
}
@@ -2,20 +2,22 @@
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
namespace Framework.Web.Rest.Login
{
[DataContract]
public class FormInputs
{
[DataMember(Name = "type")]
[JsonPropertyName("type")]
public string Type { get; set; }
[DataMember(Name = "prompt")]
public string Prompt { get; set; }
[DataMember(Name = "inputs")]
[JsonPropertyName("inputs")]
public List<FormInput> Inputs { get; set; } = new List<FormInput>();
[JsonPropertyName("srp_url")]
public string SrpUrl { get; set; }
[JsonPropertyName("srp_js")]
public string SrpJs { get; set; }
}
}
@@ -0,0 +1,28 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Text.Json.Serialization;
namespace Framework.Web.Rest.Login
{
public class GameAccountInfo
{
[JsonPropertyName("display_name")]
public string DisplayName { get; set; }
[JsonPropertyName("expansion")]
public int Expansion { get; set; }
[JsonPropertyName("is_suspended")]
public bool IsSuspended { get; set; }
[JsonPropertyName("is_banned")]
public bool IsBanned { get; set; }
[JsonPropertyName("suspension_expires")]
public long SuspensionExpires { get; set; }
[JsonPropertyName("suspension_reason")]
public string SuspensionReason { get; set; }
}
}
@@ -0,0 +1,14 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Framework.Web.Rest.Login
{
public class GameAccountList
{
[JsonPropertyName("game_accounts")]
public List<GameAccountInfo> GameAccounts { get; set; }
}
}
@@ -0,0 +1,23 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Framework.Web.Rest.Login
{
public class LoginForm
{
[JsonPropertyName("platform_id")]
public string PlatformId { get; set; }
[JsonPropertyName("program_id")]
public string ProgramId { get; set; }
[JsonPropertyName("version")]
public string Version { get; set; }
[JsonPropertyName("inputs")]
public List<FormInputValue> Inputs { get; set; } = new List<FormInputValue>();
}
}
@@ -0,0 +1,16 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Text.Json.Serialization;
namespace Framework.Web.Rest.Login
{
public class LoginRefreshResult
{
[JsonPropertyName("login_ticket_expiry")]
public long LoginTicketExpiry { get; set; }
[JsonPropertyName("is_expired")]
public bool IsExpired { get; set; }
}
}
@@ -1,30 +1,29 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
namespace Framework.Web.Rest.Login
{
[DataContract]
public class LogonResult
public class LoginResult
{
[DataMember(Name = "authentication_state")]
[JsonPropertyName("authentication_state")]
public string AuthenticationState { get; set; }
[DataMember(Name = "login_ticket")]
public string LoginTicket { get; set; }
[DataMember(Name = "error_code")]
[JsonPropertyName("error_code")]
public string ErrorCode { get; set; }
[DataMember(Name = "error_message")]
[JsonPropertyName("error_message")]
public string ErrorMessage { get; set; }
[DataMember(Name = "support_error_code")]
public string SupportErrorCode { get; set; }
[JsonPropertyName("url")]
public string Url { get; set; }
[DataMember(Name = "authenticator_form")]
public FormInputs AuthenticatorForm { get; set; } = new FormInputs();
[JsonPropertyName("login_ticket")]
public string LoginTicket { get; set; }
[JsonPropertyName("server_evidence_M2")]
public string ServerEvidenceM2 { get; set; }
}
public enum AuthenticationState
@@ -0,0 +1,37 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Text.Json.Serialization;
namespace Framework.Web.Rest.Login
{
public class SrpLoginChallenge
{
[JsonPropertyName("version")]
public int Version { get; set; }
[JsonPropertyName("iterations")]
public int Iterations { get; set; }
[JsonPropertyName("modulus")]
public string Modulus { get; set; }
[JsonPropertyName("generator")]
public string Generator { get; set; }
[JsonPropertyName("hash_function")]
public string HashFunction { get; set; }
[JsonPropertyName("username")]
public string Username { get; set; }
[JsonPropertyName("salt")]
public string Salt { get; set; }
[JsonPropertyName("public_B")]
public string PublicB { get; set; }
[JsonPropertyName("eligible_credential_upgrade")]
public bool EligibleCredentialUpgrade { get; set; }
}
}
@@ -1,17 +1,16 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
namespace Framework.Web.Rest.Realmlist
{
[DataContract]
public class Address
{
[DataMember(Name = "ip")]
[JsonPropertyName("ip")]
public string Ip { get; set; }
[DataMember(Name = "port")]
[JsonPropertyName("port")]
public int Port { get; set; }
}
}
@@ -2,17 +2,16 @@
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
namespace Framework.Web.Rest.Realmlist
{
[DataContract]
public class AddressFamily
{
[DataMember(Name = "family")]
[JsonPropertyName("family")]
public int Id { get; set; }
[DataMember(Name = "addresses")]
[JsonPropertyName("addresses")]
public IList<Address> Addresses { get; set; } = new List<Address>();
}
}
@@ -2,53 +2,53 @@
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Framework.Web.Rest.Realmlist;
namespace Framework.Web
{
[DataContract]
public class RealmListTicketInformation
public class ClientInformation
{
[DataMember(Name = "platform")]
[JsonPropertyName("platform")]
public int Platform { get; set; }
[DataMember(Name = "buildVariant")]
[JsonPropertyName("buildVariant")]
public string BuildVariant { get; set; }
[DataMember(Name = "type")]
[JsonPropertyName("type")]
public int Type { get; set; }
[DataMember(Name = "timeZone")]
[JsonPropertyName("timeZone")]
public string Timezone { get; set; }
[DataMember(Name = "currentTime")]
[JsonPropertyName("currentTime")]
public int CurrentTime { get; set; }
[DataMember(Name = "textLocale")]
[JsonPropertyName("textLocale")]
public int TextLocale { get; set; }
[DataMember(Name = "audioLocale")]
[JsonPropertyName("audioLocale")]
public int AudioLocale { get; set; }
[DataMember(Name = "versionDataBuild")]
[JsonPropertyName("versionDataBuild")]
public int VersionDataBuild { get; set; }
[DataMember(Name = "version")]
[JsonPropertyName("version")]
public ClientVersion ClientVersion { get; set; } = new ClientVersion();
[DataMember(Name = "secret")]
[JsonPropertyName("secret")]
public List<int> Secret { get; set; }
[DataMember(Name = "clientArch")]
[JsonPropertyName("clientArch")]
public int ClientArch { get; set; }
[DataMember(Name = "systemVersion")]
[JsonPropertyName("systemVersion")]
public string SystemVersion { get; set; }
[DataMember(Name = "platformType")]
[JsonPropertyName("platformType")]
public int PlatformType { get; set; }
[DataMember(Name = "systemArch")]
[JsonPropertyName("systemArch")]
public int SystemArch { get; set; }
}
}
@@ -1,23 +1,22 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
namespace Framework.Web.Rest.Realmlist
{
[DataContract]
public class ClientVersion
{
[DataMember(Name = "versionMajor")]
[JsonPropertyName("versionMajor")]
public int Major { get; set; }
[DataMember(Name = "versionBuild")]
[JsonPropertyName("versionBuild")]
public int Build { get; set; }
[DataMember(Name = "versionMinor")]
[JsonPropertyName("versionMinor")]
public int Minor { get; set; }
[DataMember(Name = "versionRevision")]
[JsonPropertyName("versionRevision")]
public int Revision { get; set; }
}
}
@@ -1,17 +1,16 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
namespace Framework.Web.Rest.Realmlist
{
[DataContract]
public class RealmCharacterCountEntry
{
[DataMember(Name = "wowRealmAddress")]
[JsonPropertyName("wowRealmAddress")]
public int WowRealmAddress { get; set; }
[DataMember(Name = "count")]
[JsonPropertyName("count")]
public int Count { get; set; }
}
}
@@ -2,14 +2,14 @@
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Framework.Web.Rest.Realmlist;
namespace Framework.Web
{
[DataContract]
public class RealmCharacterCountList
{
[DataMember(Name = "counts")]
[JsonPropertyName("counts")]
public IList<RealmCharacterCountEntry> Counts { get; set; } = new List<RealmCharacterCountEntry>();
}
}
@@ -1,42 +1,41 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Framework.Web.Rest.Realmlist;
namespace Framework.Web
{
[DataContract]
public class RealmEntry
{
[DataMember(Name = "wowRealmAddress")]
[JsonPropertyName("wowRealmAddress")]
public int WowRealmAddress { get; set; }
[DataMember(Name = "cfgTimezonesID")]
[JsonPropertyName("cfgTimezonesID")]
public int CfgTimezonesID { get; set; }
[DataMember(Name = "populationState")]
[JsonPropertyName("populationState")]
public int PopulationState { get; set; }
[DataMember(Name = "cfgCategoriesID")]
[JsonPropertyName("cfgCategoriesID")]
public int CfgCategoriesID { get; set; }
[DataMember(Name = "version")]
[JsonPropertyName("version")]
public ClientVersion Version { get; set; } = new ClientVersion();
[DataMember(Name = "cfgRealmsID")]
[JsonPropertyName("cfgRealmsID")]
public int CfgRealmsID { get; set; }
[DataMember(Name = "flags")]
[JsonPropertyName("flags")]
public int Flags { get; set; }
[DataMember(Name = "name")]
[JsonPropertyName("name")]
public string Name { get; set; }
[DataMember(Name = "cfgConfigsID")]
[JsonPropertyName("cfgConfigsID")]
public int CfgConfigsID { get; set; }
[DataMember(Name = "cfgLanguagesID")]
[JsonPropertyName("cfgLanguagesID")]
public int CfgLanguagesID { get; set; }
}
}
@@ -2,14 +2,14 @@
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Framework.Web.Rest.Realmlist;
namespace Framework.Web
{
[DataContract]
public class RealmListServerIPAddresses
{
[DataMember(Name = "families")]
[JsonPropertyName("families")]
public IList<AddressFamily> Families { get; set; } = new List<AddressFamily>();
}
}
@@ -1,14 +1,13 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
{
[DataContract]
public class RealmListTicketClientInformation
{
[DataMember(Name = "info")]
public RealmListTicketInformation Info { get; set; } = new RealmListTicketInformation();
[JsonPropertyName("info")]
public ClientInformation Info { get; set; } = new ClientInformation();
}
}
@@ -1,17 +1,16 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
{
[DataContract]
public class RealmListTicketIdentity
{
[DataMember(Name = "gameAccountID")]
[JsonPropertyName("gameAccountID")]
public int GameAccountId { get; set; }
[DataMember(Name = "gameAccountRegion")]
[JsonPropertyName("gameAccountRegion")]
public int GameAccountRegion { get; set; }
}
}
@@ -1,17 +1,16 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
{
[DataContract]
public class RealmListUpdate
{
[DataMember(Name = "update")]
[JsonPropertyName("update")]
public RealmEntry Update { get; set; } = new RealmEntry();
[DataMember(Name = "deleting")]
[JsonPropertyName("deleting")]
public bool Deleting { get; set; }
}
}
@@ -2,14 +2,13 @@
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Framework.Web
{
[DataContract]
public class RealmListUpdates
{
[DataMember(Name = "updates")]
[JsonPropertyName("updates")]
public IList<RealmListUpdate> Updates { get; set; } = new List<RealmListUpdate>();
}
}