From 2bbd624c9a0f6b714ca32bd6e40480861a7463fa Mon Sep 17 00:00:00 2001 From: server Date: Mon, 13 Apr 2026 21:31:53 +0200 Subject: [PATCH] game: prepare auth login query --- src/game/db.cpp | 440 ++++++++++++++++++++++++++-------------- src/game/db.h | 2 + src/game/input_auth.cpp | 45 +--- 3 files changed, 289 insertions(+), 198 deletions(-) diff --git a/src/game/db.cpp b/src/game/db.cpp index 75757d6..f452597 100644 --- a/src/game/db.cpp +++ b/src/game/db.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include #include #include "common/length.h" @@ -17,9 +18,267 @@ #include "login_data.h" #include "locale_service.h" #include "spam.h" +#include "libsql/Statement.h" extern std::string g_stBlockDate; +namespace +{ +struct AuthLoginData +{ + char encryptedPassword[45 + 1] = {}; + char password[45 + 1] = {}; + char socialId[SOCIAL_ID_MAX_LEN + 1] = {}; + char status[ACCOUNT_STATUS_MAX_LEN + 1] = {}; + uint32_t accountId = 0; + uint8_t notAvailable = 0; + std::array premiumTimes = {}; + long long createTime = 0; +}; + +bool IsChannelServiceLogin(const char* login) +{ + return login && login[0] == '['; +} + +bool PrepareGameStmt(CStmt& stmt, const std::string& query) +{ + CAsyncSQL* sql = DBManager::instance().GetDirectSQL(); + + if (!sql) + { + sys_err("game direct SQL handle is not initialized"); + return false; + } + + return stmt.Prepare(sql, query.c_str()); +} + +bool CopyRequiredAuthString(const MYSQL_ROW row, int column, char* dst, size_t dstSize) +{ + if (!row[column]) + { + sys_err("error column %d", column); + return false; + } + + strlcpy(dst, row[column], dstSize); + return true; +} + +template +void CopyOptionalAuthNumber(const char* src, T& value) +{ + if (!src) + return; + + str_to_number(value, src); +} + +bool ParseAuthLoginRow(MYSQL_ROW row, AuthLoginData& auth) +{ + int col = 0; + + if (!CopyRequiredAuthString(row, col++, auth.encryptedPassword, sizeof(auth.encryptedPassword)) + || !CopyRequiredAuthString(row, col++, auth.password, sizeof(auth.password)) + || !CopyRequiredAuthString(row, col++, auth.socialId, sizeof(auth.socialId))) + { + return false; + } + + if (!row[col]) + { + sys_err("error column %d", col); + return false; + } + str_to_number(auth.accountId, row[col++]); + + if (!CopyRequiredAuthString(row, col++, auth.status, sizeof(auth.status))) + return false; + + CopyOptionalAuthNumber(row[col++], auth.notAvailable); + CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_EXP]); + CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_ITEM]); + CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_SAFEBOX]); + CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_AUTOLOOT]); + CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_FISH_MIND]); + CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_MARRIAGE_FAST]); + CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_GOLD]); + CopyOptionalAuthNumber(row[col], auth.createTime); + return true; +} + +bool LoadAuthLoginData(const char* login, const char* passwd, AuthLoginData& auth, bool& found) +{ + CStmt stmt; + const bool channelServiceLogin = IsChannelServiceLogin(login); + const std::string query = channelServiceLogin + ? "SELECT ?, password, social_id, id, status, availDt - NOW() > 0," + "UNIX_TIMESTAMP(silver_expire)," + "UNIX_TIMESTAMP(gold_expire)," + "UNIX_TIMESTAMP(safebox_expire)," + "UNIX_TIMESTAMP(autoloot_expire)," + "UNIX_TIMESTAMP(fish_mind_expire)," + "UNIX_TIMESTAMP(marriage_fast_expire)," + "UNIX_TIMESTAMP(money_drop_rate_expire)," + "UNIX_TIMESTAMP(create_time)" + " FROM account WHERE login=?" + : "SELECT PASSWORD(?), password, social_id, id, status, availDt - NOW() > 0," + "UNIX_TIMESTAMP(silver_expire)," + "UNIX_TIMESTAMP(gold_expire)," + "UNIX_TIMESTAMP(safebox_expire)," + "UNIX_TIMESTAMP(autoloot_expire)," + "UNIX_TIMESTAMP(fish_mind_expire)," + "UNIX_TIMESTAMP(marriage_fast_expire)," + "UNIX_TIMESTAMP(money_drop_rate_expire)," + "UNIX_TIMESTAMP(create_time)" + " FROM account WHERE login=?"; + + found = false; + + if (!PrepareGameStmt(stmt, query)) + return false; + + if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast(passwd)) + || !stmt.BindParam(MYSQL_TYPE_STRING, const_cast(login))) + { + return false; + } + + if (!stmt.BindResult(MYSQL_TYPE_STRING, auth.encryptedPassword, sizeof(auth.encryptedPassword)) + || !stmt.BindResult(MYSQL_TYPE_STRING, auth.password, sizeof(auth.password)) + || !stmt.BindResult(MYSQL_TYPE_STRING, auth.socialId, sizeof(auth.socialId)) + || !stmt.BindResult(MYSQL_TYPE_LONG, &auth.accountId) + || !stmt.BindResult(MYSQL_TYPE_STRING, auth.status, sizeof(auth.status)) + || !stmt.BindResult(MYSQL_TYPE_TINY, &auth.notAvailable) + || !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_EXP]) + || !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_ITEM]) + || !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_SAFEBOX]) + || !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_AUTOLOOT]) + || !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_FISH_MIND]) + || !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_MARRIAGE_FAST]) + || !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_GOLD]) + || !stmt.BindResult(MYSQL_TYPE_LONGLONG, &auth.createTime)) + { + return false; + } + + if (!stmt.Execute()) + return false; + + if (stmt.iRows == 0) + return true; + + if (!stmt.Fetch()) + return false; + + found = true; + return true; +} + +void FormatCreateDate(const AuthLoginData& auth, char* dst, size_t dstSize) +{ + strlcpy(dst, "00000000", dstSize); + + if (auth.createTime <= 0) + return; + + time_t createTime = static_cast(auth.createTime); + struct tm tmBuf; + + if (!localtime_r(&createTime, &tmBuf)) + return; + + strftime(dst, dstSize, "%Y%m%d", &tmBuf); +} + +void UpdateAccountLastPlay(uint32_t accountId) +{ + CStmt stmt; + const std::string query = "UPDATE account SET last_play=NOW() WHERE id=?"; + + if (!PrepareGameStmt(stmt, query)) + return; + + if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId)) + return; + + if (!stmt.Execute()) + { + sys_err("failed to update last_play for account %u", accountId); + } +} + +void FinalizeAuthLogin(LPDESC d, const char* login, const char* passwd, const AuthLoginData& auth, const char* logPrefix) +{ + char createDate[256] = "00000000"; + + if (LC_IsEurope() || test_server) + { + FormatCreateDate(auth, createDate, sizeof(createDate)); + sys_log(0, "Create_Time %lld %s", auth.createTime, createDate); + sys_log(0, "Block Time %d ", strncmp(createDate, g_stBlockDate.c_str(), 8)); + } + + const int passwordDiff = strcmp(auth.encryptedPassword, auth.password); + + if (passwordDiff) + { + RecordLoginFailure(d->GetHostName()); + LoginFailure(d, "WRONGPWD"); + sys_log(0, " WRONGPWD"); + return; + } + + if (auth.notAvailable) + { + LoginFailure(d, "NOTAVAIL"); + sys_log(0, " NOTAVAIL"); + return; + } + + if (DESC_MANAGER::instance().FindByLoginName(login)) + { + LoginFailure(d, "ALREADY"); + sys_log(0, " ALREADY"); + return; + } + + if (strcmp(auth.status, "OK")) + { + LoginFailure(d, auth.status); + sys_log(0, " STATUS: %s", auth.status); + return; + } + + if (LC_IsEurope()) + { + if (strncmp(createDate, g_stBlockDate.c_str(), 8) >= 0) + { + LoginFailure(d, "BLKLOGIN"); + sys_log(0, " BLKLOGIN"); + return; + } + + UpdateAccountLastPlay(auth.accountId); + } + + TAccountTable& r = d->GetAccountTable(); + int premiumTimes[PREMIUM_MAX_NUM]; + + r.id = auth.accountId; + trim_and_lower(login, r.login, sizeof(r.login)); + strlcpy(r.passwd, passwd, sizeof(r.passwd)); + strlcpy(r.social_id, auth.socialId, sizeof(r.social_id)); + DESC_MANAGER::instance().ConnectAccount(r.login, d); + ClearLoginFailure(d->GetHostName()); + + thecore_memcpy(premiumTimes, auth.premiumTimes.data(), sizeof(premiumTimes)); + DBManager::instance().LoginPrepare(d, premiumTimes); + sys_log(0, "%s: SUCCESS %s", logPrefix, login); +} +} + DBManager::DBManager() : m_bIsConnect(false) { } @@ -232,6 +491,29 @@ void DBManager::LoginPrepare(LPDESC d, int * paiPremiumTimes) SendAuthLogin(d); } +void DBManager::AuthenticateLogin(LPDESC d, const char* login, const char* passwd) +{ + AuthLoginData auth; + bool found = false; + const bool channelServiceLogin = IsChannelServiceLogin(login); + + d->SetLogin(login); + sys_log(0, "AUTH_LOGIN_DIRECT: START %u %p", d->GetLoginKey(), get_pointer(d)); + + if (channelServiceLogin) + sys_log(0, "ChannelServiceLogin [%s]", login); + + if (!LoadAuthLoginData(login, passwd, auth, found) || !found) + { + sys_log(0, " NOID"); + RecordLoginFailure(d->GetHostName()); + LoginFailure(d, "NOID"); + return; + } + + FinalizeAuthLogin(d, login, passwd, auth, "AUTH_LOGIN_DIRECT"); +} + void DBManager::AnalyzeReturnQuery(SQLMsg * pMsg) { CReturnQueryInfo * qi = (CReturnQueryInfo *) pMsg->pvUserData; @@ -263,162 +545,12 @@ void DBManager::AnalyzeReturnQuery(SQLMsg * pMsg) else { MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); - int col = 0; + AuthLoginData auth; - // PASSWORD('%s'), password, securitycode, social_id, id, status - char szEncrytPassword[45 + 1]; - char szPassword[45 + 1]; - char szSocialID[SOCIAL_ID_MAX_LEN + 1]; - char szStatus[ACCOUNT_STATUS_MAX_LEN + 1]; - DWORD dwID = 0; + if (ParseAuthLoginRow(row, auth)) + FinalizeAuthLogin(d, pinfo->login, pinfo->passwd, auth, "QID_AUTH_LOGIN"); - if (!row[col]) - { - sys_err("error column %d", col); - M2_DELETE(pinfo); - break; - } - - strlcpy(szEncrytPassword, row[col++], sizeof(szEncrytPassword)); - - if (!row[col]) - { - sys_err("error column %d", col); - M2_DELETE(pinfo); - break; - } - - strlcpy(szPassword, row[col++], sizeof(szPassword)); - - if (!row[col]) - { - sys_err("error column %d", col); - M2_DELETE(pinfo); - break; - } - - strlcpy(szSocialID, row[col++], sizeof(szSocialID)); - - if (!row[col]) - { - sys_err("error column %d", col); - M2_DELETE(pinfo); - break; - } - - str_to_number(dwID, row[col++]); - - if (!row[col]) - { - sys_err("error column %d", col); - M2_DELETE(pinfo); - break; - } - - strlcpy(szStatus, row[col++], sizeof(szStatus)); - - BYTE bNotAvail = 0; - str_to_number(bNotAvail, row[col++]); - - int aiPremiumTimes[PREMIUM_MAX_NUM]; - memset(&aiPremiumTimes, 0, sizeof(aiPremiumTimes)); - - char szCreateDate[256] = "00000000"; - - if (!g_iUseLocale) - { - str_to_number(aiPremiumTimes[PREMIUM_EXP], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_ITEM], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_SAFEBOX], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_AUTOLOOT], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_FISH_MIND], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_MARRIAGE_FAST], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_GOLD], row[col++]); - } - else - { - str_to_number(aiPremiumTimes[PREMIUM_EXP], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_ITEM], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_SAFEBOX], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_AUTOLOOT], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_FISH_MIND], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_MARRIAGE_FAST], row[col++]); - str_to_number(aiPremiumTimes[PREMIUM_GOLD], row[col++]); - - if (LC_IsEurope() || test_server) - { - long retValue = 0; - str_to_number(retValue, row[col]); - - time_t create_time = retValue; - struct tm * tm1; - tm1 = localtime(&create_time); - strftime(szCreateDate, 255, "%Y%m%d", tm1); - - sys_log(0, "Create_Time %d %s", retValue, szCreateDate); - sys_log(0, "Block Time %d ", strncmp(szCreateDate, g_stBlockDate.c_str(), 8)); - } - } - - int nPasswordDiff = strcmp(szEncrytPassword, szPassword); - - if (nPasswordDiff) - { - RecordLoginFailure(d->GetHostName()); - LoginFailure(d, "WRONGPWD"); - sys_log(0, " WRONGPWD"); - M2_DELETE(pinfo); - } - else if (bNotAvail) - { - LoginFailure(d, "NOTAVAIL"); - sys_log(0, " NOTAVAIL"); - M2_DELETE(pinfo); - } - else if (DESC_MANAGER::instance().FindByLoginName(pinfo->login)) - { - LoginFailure(d, "ALREADY"); - sys_log(0, " ALREADY"); - M2_DELETE(pinfo); - } - else if (strcmp(szStatus, "OK")) - { - LoginFailure(d, szStatus); - sys_log(0, " STATUS: %s", szStatus); - M2_DELETE(pinfo); - } - else - { - if (LC_IsEurope()) - { - //stBlockData >= 0 == 날짜가 BlockDate 보다 미래 - if (strncmp(szCreateDate, g_stBlockDate.c_str(), 8) >= 0) - { - LoginFailure(d, "BLKLOGIN"); - sys_log(0, " BLKLOGIN"); - M2_DELETE(pinfo); - break; - } - - char szQuery[1024]; - snprintf(szQuery, sizeof(szQuery), "UPDATE account SET last_play=NOW() WHERE id=%u", dwID); - DBManager::instance().DirectQuery(szQuery); - } - - TAccountTable & r = d->GetAccountTable(); - - r.id = dwID; - trim_and_lower(pinfo->login, r.login, sizeof(r.login)); - strlcpy(r.passwd, pinfo->passwd, sizeof(r.passwd)); - strlcpy(r.social_id, szSocialID, sizeof(r.social_id)); - DESC_MANAGER::instance().ConnectAccount(r.login, d); - ClearLoginFailure(d->GetHostName()); - - LoginPrepare(d, aiPremiumTimes); - M2_DELETE(pinfo); - - sys_log(0, "QID_AUTH_LOGIN: SUCCESS %s", pinfo->login); - } + M2_DELETE(pinfo); } } break; diff --git a/src/game/db.h b/src/game/db.h index 731a7b1..6bd9ffc 100644 --- a/src/game/db.h +++ b/src/game/db.h @@ -81,8 +81,10 @@ class DBManager : public singleton void SendMoneyLog(BYTE type, DWORD vnum, int gold); void LoginPrepare(LPDESC d, int * paiPremiumTimes = NULL); + void AuthenticateLogin(LPDESC d, const char* login, const char* passwd); void SendAuthLogin(LPDESC d); void SendLoginPing(const char * c_pszLogin); + CAsyncSQL* GetDirectSQL() { return &m_sql_direct; } void InsertLoginData(CLoginData * pkLD); void DeleteLoginData(CLoginData * pkLD); diff --git a/src/game/input_auth.cpp b/src/game/input_auth.cpp index 6a2bb98..e409db3 100644 --- a/src/game/input_auth.cpp +++ b/src/game/input_auth.cpp @@ -233,50 +233,7 @@ void CInputAuth::Login(LPDESC d, const char * c_pData) sys_log(0, "InputAuth::Login : key %u login %s", dwKey, login); - TPacketCGLogin3 * p = M2_NEW TPacketCGLogin3; - thecore_memcpy(p, pinfo, sizeof(TPacketCGLogin3)); - - char szPasswd[PASSWD_MAX_LEN * 2 + 1]; - DBManager::instance().EscapeString(szPasswd, sizeof(szPasswd), passwd, strlen(passwd)); - - char szLogin[LOGIN_MAX_LEN * 2 + 1]; - DBManager::instance().EscapeString(szLogin, sizeof(szLogin), login, strlen(login)); - - // CHANNEL_SERVICE_LOGIN - if (Login_IsInChannelService(szLogin)) - { - sys_log(0, "ChannelServiceLogin [%s]", szLogin); - - DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p, - "SELECT '%s',password,social_id,id,status,availDt - NOW() > 0," - "UNIX_TIMESTAMP(silver_expire)," - "UNIX_TIMESTAMP(gold_expire)," - "UNIX_TIMESTAMP(safebox_expire)," - "UNIX_TIMESTAMP(autoloot_expire)," - "UNIX_TIMESTAMP(fish_mind_expire)," - "UNIX_TIMESTAMP(marriage_fast_expire)," - "UNIX_TIMESTAMP(money_drop_rate_expire)," - "UNIX_TIMESTAMP(create_time)" - " FROM account WHERE login='%s'", - - szPasswd, szLogin); - } - // END_OF_CHANNEL_SERVICE_LOGIN - else - { - DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p, - "SELECT PASSWORD('%s'),password,social_id,id,status,availDt - NOW() > 0," - "UNIX_TIMESTAMP(silver_expire)," - "UNIX_TIMESTAMP(gold_expire)," - "UNIX_TIMESTAMP(safebox_expire)," - "UNIX_TIMESTAMP(autoloot_expire)," - "UNIX_TIMESTAMP(fish_mind_expire)," - "UNIX_TIMESTAMP(marriage_fast_expire)," - "UNIX_TIMESTAMP(money_drop_rate_expire)," - "UNIX_TIMESTAMP(create_time)" - " FROM account WHERE login='%s'", - szPasswd, szLogin); - } + DBManager::instance().AuthenticateLogin(d, login, passwd); } int CInputAuth::Analyze(LPDESC d, uint16_t wHeader, const char * c_pData)