#include "stdafx.h" #include #include "desc.h" #include "desc_manager.h" #include "char.h" #include "buffer_manager.h" #include "config.h" #include "profiler.h" #include "p2p.h" #include "log.h" #include "db.h" #include "questmanager.h" #include "login_sim.h" #include "fishing.h" #include "TrafficProfiler.h" #include "priv_manager.h" #include "castle.h" #include "dev_log.h" #include "HackShield_Impl.h" #ifndef __WIN32__ #include "limit_time.h" #endif #include "../../common/CommonDefines.h" #include "utils.h" bool IsEmptyAdminPage() { return g_stAdminPageIP.empty(); } bool IsAdminPage(const char * ip) { for (size_t n = 0; n < g_stAdminPageIP.size(); ++n) { if (g_stAdminPageIP[n] == ip) return 1; } return 0; } void ClearAdminPages() { for (size_t n = 0; n < g_stAdminPageIP.size(); ++n) g_stAdminPageIP[n].clear(); g_stAdminPageIP.clear(); } CInputProcessor::CInputProcessor() : m_pPacketInfo(NULL), m_iBufferLeft(0) { if (!m_pPacketInfo) BindPacketInfo(&m_packetInfoCG); } void CInputProcessor::BindPacketInfo(CPacketInfo * pPacketInfo) { m_pPacketInfo = pPacketInfo; } bool CInputProcessor::Process(LPDESC lpDesc, const void * c_pvOrig, int iBytes, int & r_iBytesProceed) { const char * c_pData = (const char *) c_pvOrig; BYTE bLastHeader = 0; int iLastPacketLen = 0; int iPacketLen; #if defined(__IMPROVED_HANDSHAKE_PROCESS__) // Ignore input process if the host is an intruder if (lpDesc && DESC_MANAGER::instance().IsIntruder(lpDesc->GetHostName())) { // Set host close phase lpDesc->SetPhase(PHASE_CLOSE); return true; } #endif if (!m_pPacketInfo) { sys_err("No packet info has been binded to"); return true; } for (m_iBufferLeft = iBytes; m_iBufferLeft > 0;) { BYTE bHeader = (BYTE) *(c_pData); const char * c_pszName; if (bHeader == 0) iPacketLen = 1; else if (!m_pPacketInfo->Get(bHeader, &iPacketLen, &c_pszName)) { sys_err("UNKNOWN HEADER: %d, LAST HEADER: %d(%d), REMAIN BYTES: %d, fd: %d", bHeader, bLastHeader, iLastPacketLen, m_iBufferLeft, lpDesc->GetSocket()); //printdata((BYTE *) c_pvOrig, m_iBufferLeft); lpDesc->SetPhase(PHASE_CLOSE); return true; } if (m_iBufferLeft < iPacketLen) return true; if (bHeader) { if (test_server && bHeader != HEADER_CG_MOVE) sys_log(0, "Packet Analyze [Header %d][bufferLeft %d] ", bHeader, m_iBufferLeft); m_pPacketInfo->Start(); int iExtraPacketSize = Analyze(lpDesc, bHeader, c_pData); if (iExtraPacketSize < 0) return true; iPacketLen += iExtraPacketSize; lpDesc->Log("%s %d", c_pszName, iPacketLen); m_pPacketInfo->End(); } // TRAFFIC_PROFILER if (g_bTrafficProfileOn) TrafficProfiler::instance().Report(TrafficProfiler::IODIR_INPUT, bHeader, iPacketLen); // END_OF_TRAFFIC_PROFILER #ifdef ENABLE_SEQUENCE_SYSTEM if (bHeader == HEADER_CG_PONG) sys_log(0, "PONG! %u %u", m_pPacketInfo->IsSequence(bHeader), *(BYTE *) (c_pData + iPacketLen - sizeof(BYTE))); if (m_pPacketInfo->IsSequence(bHeader)) { BYTE bSeq = lpDesc->GetSequence(); BYTE bSeqReceived = *(BYTE *) (c_pData + iPacketLen - sizeof(BYTE)); if (bSeq != bSeqReceived) { sys_err("SEQUENCE %x mismatch 0x%x != 0x%x header %u", get_pointer(lpDesc), bSeq, bSeqReceived, bHeader); LPCHARACTER ch = lpDesc->GetCharacter(); char buf[1024]; int offset, len; offset = snprintf(buf, sizeof(buf), "SEQUENCE_LOG [%s]-------------\n", ch ? ch->GetName() : "UNKNOWN"); if (offset < 0 || offset >= (int) sizeof(buf)) offset = sizeof(buf) - 1; for (size_t i = 0; i < lpDesc->m_seq_vector.size(); ++i) { len = snprintf(buf + offset, sizeof(buf) - offset, "\t[%03d : 0x%x]\n", lpDesc->m_seq_vector[i].hdr, lpDesc->m_seq_vector[i].seq); if (len < 0 || len >= (int) sizeof(buf) - offset) offset += (sizeof(buf) - offset) - 1; else offset += len; } snprintf(buf + offset, sizeof(buf) - offset, "\t[%03d : 0x%x]\n", bHeader, bSeq); sys_err("%s", buf); lpDesc->SetPhase(PHASE_CLOSE); return true; } else { lpDesc->push_seq(bHeader, bSeq); lpDesc->SetNextSequence(); //sys_err("SEQUENCE %x match %u next %u header %u", lpDesc, bSeq, lpDesc->GetSequence(), bHeader); } } #endif c_pData += iPacketLen; m_iBufferLeft -= iPacketLen; r_iBytesProceed += iPacketLen; iLastPacketLen = iPacketLen; bLastHeader = bHeader; if (GetType() != lpDesc->GetInputProcessor()->GetType()) return false; } return true; } void CInputProcessor::Pong(LPDESC d) { d->SetPong(true); } void CInputProcessor::Handshake(LPDESC d, const char * c_pData) { TPacketCGHandshake * p = (TPacketCGHandshake *) c_pData; if (d->GetHandshake() != p->dwHandshake) { sys_err("Invalid Handshake on %d", d->GetSocket()); d->SetPhase(PHASE_CLOSE); } else { if (d->IsPhase(PHASE_HANDSHAKE)) { if (d->HandshakeProcess(p->dwTime, p->lDelta, false)) { #ifdef _IMPROVED_PACKET_ENCRYPTION_ d->SendKeyAgreement(); #else if (g_bAuthServer) d->SetPhase(PHASE_AUTH); else d->SetPhase(PHASE_LOGIN); #endif // #ifdef _IMPROVED_PACKET_ENCRYPTION_ } } else d->HandshakeProcess(p->dwTime, p->lDelta, true); } } void CInputProcessor::Version(LPCHARACTER ch, const char* c_pData) { if (!ch) return; TPacketCGClientVersion * p = (TPacketCGClientVersion *) c_pData; sys_log(0, "VERSION: %s %s %s", ch->GetName(), p->timestamp, p->filename); ch->GetDesc()->SetClientVersion(p->timestamp); } struct SWhisperPacketFunc { const char * c_pszText; SWhisperPacketFunc(const char * text) : c_pszText(text) { } void operator () (LPDESC d) { if (!d || !d->GetCharacter()) return; struct packet_bulk_whisper bulk_whisper_pack; bulk_whisper_pack.header = HEADER_GC_BULK_WHISPER; bulk_whisper_pack.size = sizeof(struct packet_bulk_whisper) + strlen(c_pszText); TEMP_BUFFER buf; buf.write(&bulk_whisper_pack, sizeof(struct packet_bulk_whisper)); buf.write(c_pszText, strlen(c_pszText)); d->Packet(buf.read_peek(), buf.size()); } }; void CInputProcessor::SendBulkWhisper(const char * c_pszText) { const DESC_MANAGER::DESC_SET & f = DESC_MANAGER::instance().GetClientSet(); std::for_each(f.begin(), f.end(), SWhisperPacketFunc(c_pszText)); } void LoginFailure(LPDESC d, const char * c_pszStatus) { if (!d) return; TPacketGCLoginFailure failurePacket; failurePacket.header = HEADER_GC_LOGIN_FAILURE; strlcpy(failurePacket.szStatus, c_pszStatus, sizeof(failurePacket.szStatus)); d->Packet(&failurePacket, sizeof(failurePacket)); } CInputHandshake::CInputHandshake() { CPacketInfoCG * pkPacketInfo = M2_NEW CPacketInfoCG; #ifdef ENABLE_SEQUENCE_SYSTEM pkPacketInfo->SetSequence(HEADER_CG_PONG, false); #endif m_pMainPacketInfo = m_pPacketInfo; BindPacketInfo(pkPacketInfo); } CInputHandshake::~CInputHandshake() { if( NULL != m_pPacketInfo ) { M2_DELETE(m_pPacketInfo); m_pPacketInfo = NULL; } } std::map g_sim; std::map g_simByPID; std::vector g_vec_save; // BLOCK_CHAT ACMD(do_block_chat); // END_OF_BLOCK_CHAT int CInputHandshake::Analyze(LPDESC d, BYTE bHeader, const char * c_pData) { if (bHeader == 10) return 0; if (bHeader == HEADER_CG_TEXT) { #ifdef ENABLE_PORT_SECURITY if (IsEmptyAdminPage() || !IsAdminPage(inet_ntoa(d->GetAddr().sin_addr))) // block if adminpage is not set or if not admin { sys_log(0, "SOCKET_CMD: BLOCK FROM(%s)", d->GetHostName()); return -1; } #endif ++c_pData; const char * c_pSep; if (!(c_pSep = strchr(c_pData, '\n'))) return -1; if (*(c_pSep - 1) == '\r') --c_pSep; std::string stResult; std::string stBuf; stBuf.assign(c_pData, 0, c_pSep - c_pData); sys_log(0, "SOCKET_CMD: FROM(%s) CMD(%s)", d->GetHostName(), stBuf.c_str()); if (!stBuf.compare("IS_SERVER_UP")) { if (g_bNoMoreClient) stResult = "NO"; else stResult = "YES"; } else if (!stBuf.compare("IS_PASSPOD_UP")) { if (g_bNoPasspod) stResult = "NO"; else stResult = "YES"; } //else if (!stBuf.compare("SHOWMETHEMONEY")) else if (stBuf == g_stAdminPagePassword) { if (!IsEmptyAdminPage()) { if (!IsAdminPage(inet_ntoa(d->GetAddr().sin_addr))) { char szTmp[64]; snprintf(szTmp, sizeof(szTmp), "WEBADMIN : Wrong Connector : %s", inet_ntoa(d->GetAddr().sin_addr)); stResult += szTmp; } else { d->SetAdminMode(); stResult = "UNKNOWN"; } } else { d->SetAdminMode(); stResult = "UNKNOWN"; } } else if (!stBuf.compare("USER_COUNT")) { char szTmp[64]; if (!IsEmptyAdminPage()) { if (!IsAdminPage(inet_ntoa(d->GetAddr().sin_addr))) { snprintf(szTmp, sizeof(szTmp), "WEBADMIN : Wrong Connector : %s", inet_ntoa(d->GetAddr().sin_addr)); } else { int iTotal; int * paiEmpireUserCount; int iLocal; DESC_MANAGER::instance().GetUserCount(iTotal, &paiEmpireUserCount, iLocal); snprintf(szTmp, sizeof(szTmp), "%d %d %d %d %d", iTotal, paiEmpireUserCount[1], paiEmpireUserCount[2], paiEmpireUserCount[3], iLocal); } } else { int iTotal; int * paiEmpireUserCount; int iLocal; DESC_MANAGER::instance().GetUserCount(iTotal, &paiEmpireUserCount, iLocal); snprintf(szTmp, sizeof(szTmp), "%d %d %d %d %d", iTotal, paiEmpireUserCount[1], paiEmpireUserCount[2], paiEmpireUserCount[3], iLocal); } stResult += szTmp; } else if (!stBuf.compare("CHECK_P2P_CONNECTIONS")) { std::ostringstream oss(std::ostringstream::out); oss << "P2P CONNECTION NUMBER : " << P2P_MANAGER::instance().GetDescCount() << "\n"; std::string hostNames; P2P_MANAGER::Instance().GetP2PHostNames(hostNames); oss << hostNames; stResult = oss.str(); TPacketGGCheckAwakeness packet; packet.bHeader = HEADER_GG_CHECK_AWAKENESS; P2P_MANAGER::instance().Send(&packet, sizeof(packet)); } else if (!stBuf.compare("PACKET_INFO")) { m_pMainPacketInfo->Log("packet_info.txt"); stResult = "OK"; } else if (!stBuf.compare("PROFILE")) { CProfiler::instance().Log("profile.txt"); stResult = "OK"; } //gift notify delete command else if (!stBuf.compare(0,15,"DELETE_AWARDID ")) { char szTmp[64]; std::string msg = stBuf.substr(15,26); TPacketDeleteAwardID p; p.dwID = (DWORD)(atoi(msg.c_str())); snprintf(szTmp,sizeof(szTmp),"Sent to DB cache to delete ItemAward, id: %d",p.dwID); //sys_log(0,"%d",p.dwID); // strlcpy(p.login, msg.c_str(), sizeof(p.login)); db_clientdesc->DBPacket(HEADER_GD_DELETE_AWARDID, 0, &p, sizeof(p)); stResult += szTmp; } else { stResult = "UNKNOWN"; if (d->IsAdminMode()) { if (!stBuf.compare(0, 7, "NOTICE ")) { std::string msg = stBuf.substr(7, 50); LogManager::instance().CharLog(0, 0, 0, 1, "NOTICE", msg.c_str(), d->GetHostName()); BroadcastNotice(msg.c_str()); } #ifdef ENABLE_FULL_NOTICE else if (!stBuf.compare(0, 11, "BIG_NOTICE ")) { std::string msg = stBuf.substr(11, 50); LogManager::instance().CharLog(0, 0, 0, 1, "BIG_NOTICE", msg.c_str(), d->GetHostName()); BroadcastNotice(msg.c_str(), true); } #endif else if (!stBuf.compare("CLOSE_PASSPOD")) { g_bNoPasspod = true; stResult += "CLOSE_PASSPOD"; } else if (!stBuf.compare("OPEN_PASSPOD")) { g_bNoPasspod = false; stResult += "OPEN_PASSPOD"; } else if (!stBuf.compare("SHUTDOWN")) { LogManager::instance().CharLog(0, 0, 0, 2, "SHUTDOWN", "", d->GetHostName()); TPacketGGShutdown p; p.bHeader = HEADER_GG_SHUTDOWN; P2P_MANAGER::instance().Send(&p, sizeof(TPacketGGShutdown)); sys_err("Accept shutdown command from %s.", d->GetHostName()); Shutdown(10); } else if (!stBuf.compare("SHUTDOWN_ONLY")) { LogManager::instance().CharLog(0, 0, 0, 2, "SHUTDOWN", "", d->GetHostName()); sys_err("Accept shutdown only command from %s.", d->GetHostName()); Shutdown(10); } else if (!stBuf.compare(0, 3, "DC ")) { std::string msg = stBuf.substr(3, LOGIN_MAX_LEN); dev_log(LOG_DEB0, "DC : '%s'", msg.c_str()); TPacketGGDisconnect pgg; pgg.bHeader = HEADER_GG_DISCONNECT; strlcpy(pgg.szLogin, msg.c_str(), sizeof(pgg.szLogin)); P2P_MANAGER::instance().Send(&pgg, sizeof(TPacketGGDisconnect)); // delete login key { TPacketDC p; strlcpy(p.login, msg.c_str(), sizeof(p.login)); db_clientdesc->DBPacket(HEADER_GD_DC, 0, &p, sizeof(p)); } } else if (!stBuf.compare(0, 10, "RELOAD_CRC")) { LoadValidCRCList(); BYTE bHeader = HEADER_GG_RELOAD_CRC_LIST; P2P_MANAGER::instance().Send(&bHeader, sizeof(BYTE)); stResult = "OK"; } else if (!stBuf.compare(0, 20, "CHECK_CLIENT_VERSION")) { CheckClientVersion(); BYTE bHeader = HEADER_GG_CHECK_CLIENT_VERSION; P2P_MANAGER::instance().Send(&bHeader, sizeof(BYTE)); stResult = "OK"; } else if (!stBuf.compare(0, 6, "RELOAD")) { if (stBuf.size() == 6) { LoadStateUserCount(); db_clientdesc->DBPacket(HEADER_GD_RELOAD_PROTO, 0, NULL, 0); DBManager::instance().LoadDBString(); } else { char c = stBuf[7]; switch (LOWER(c)) { case 'u': LoadStateUserCount(); break; case 'p': db_clientdesc->DBPacket(HEADER_GD_RELOAD_PROTO, 0, NULL, 0); break; case 's': DBManager::instance().LoadDBString(); break; case 'q': quest::CQuestManager::instance().Reload(); break; case 'f': fishing::Initialize(); break; case 'a': db_clientdesc->DBPacket(HEADER_GD_RELOAD_ADMIN, 0, NULL, 0); sys_log(0, "Reloading admin infomation."); break; } } } else if (!stBuf.compare(0, 6, "EVENT ")) { std::istringstream is(stBuf); std::string strEvent, strFlagName; long lValue; is >> strEvent >> strFlagName >> lValue; if (!is.fail()) { sys_log(0, "EXTERNAL EVENT FLAG name %s value %d", strFlagName.c_str(), lValue); quest::CQuestManager::instance().RequestSetEventFlag(strFlagName, lValue); stResult = "EVENT FLAG CHANGE "; stResult += strFlagName; } else { stResult = "EVENT FLAG FAIL"; } } // BLOCK_CHAT else if (!stBuf.compare(0, 11, "BLOCK_CHAT ")) { std::istringstream is(stBuf); std::string strBlockChat, strCharName; long lDuration; is >> strBlockChat >> strCharName >> lDuration; if (!is.fail()) { sys_log(0, "EXTERNAL BLOCK_CHAT name %s duration %d", strCharName.c_str(), lDuration); do_block_chat(NULL, const_cast(stBuf.c_str() + 11), 0, 0); stResult = "BLOCK_CHAT "; stResult += strCharName; } else { stResult = "BLOCK_CHAT FAIL"; } } // END_OF_BLOCK_CHAT else if (!stBuf.compare(0, 12, "PRIV_EMPIRE ")) { int empire, type, value, duration; std::istringstream is(stBuf); std::string strPrivEmpire; is >> strPrivEmpire >> empire >> type >> value >> duration; value = MINMAX(0, value, 1000); stResult = "PRIV_EMPIRE FAIL"; if (!is.fail()) { // check parameter if (empire < 0 || 3 < empire); else if (type < 1 || 4 < type); else if (value < 0); else if (duration < 0); else { stResult = "PRIV_EMPIRE SUCCEED"; duration = duration * (60 * 60); sys_log(0, "_give_empire_privileage(empire=%d, type=%d, value=%d, duration=%d) by web", empire, type, value, duration); CPrivManager::instance().RequestGiveEmpirePriv(empire, type, value, duration); } } } else if (!stBuf.compare(0, 15, "BLOCK_EXCEPTION")) { // BLOCK_EXCEPTION cmd(add=1, del=2) login std::istringstream is(stBuf); std::string dummy_string; std::string login_string; int cmd; is >> dummy_string >> cmd >> login_string; sys_log(0, "block_exception %s:%d", login_string.c_str(), cmd); DBManager::instance().RequestBlockException(login_string.c_str(), cmd); stResult = "BLOCK_EXCEPTION_YES"; } } } sys_log(1, "TEXT %s RESULT %s", stBuf.c_str(), stResult.c_str()); stResult += "\n"; d->Packet(stResult.c_str(), stResult.length()); return (c_pSep - c_pData) + 1; } else if (bHeader == HEADER_CG_MARK_LOGIN) { if (!guild_mark_server) { sys_err("Guild Mark login requested but i'm not a mark server!"); d->SetPhase(PHASE_CLOSE); return 0; } sys_log(0, "MARK_SERVER: Login"); d->SetPhase(PHASE_LOGIN); return 0; } else if (bHeader == HEADER_CG_STATE_CHECKER) { if (d->isChannelStatusRequested()) { return 0; } d->SetChannelStatusRequested(true); db_clientdesc->DBPacket(HEADER_GD_REQUEST_CHANNELSTATUS, d->GetHandle(), NULL, 0); } else if (bHeader == HEADER_CG_PONG) Pong(d); else if (bHeader == HEADER_CG_HANDSHAKE) Handshake(d, c_pData); #ifdef _IMPROVED_PACKET_ENCRYPTION_ else if (bHeader == HEADER_CG_KEY_AGREEMENT) { // Send out the key agreement completion packet first // to help client to enter encryption mode d->SendKeyAgreementCompleted(); // Flush socket output before going encrypted d->ProcessOutput(); TPacketKeyAgreement* p = (TPacketKeyAgreement*)c_pData; if (!d->IsCipherPrepared()) { sys_err ("Cipher isn't prepared. %s maybe a Hacker.", inet_ntoa(d->GetAddr().sin_addr)); d->DelayedDisconnect(5); return 0; } if (d->FinishHandshake(p->wAgreedLength, p->data, p->wDataLength)) { // Handshaking succeeded if (g_bAuthServer) { d->SetPhase(PHASE_AUTH); } else { d->SetPhase(PHASE_LOGIN); } } else { sys_log(0, "[CInputHandshake] Key agreement failed: al=%u dl=%u", p->wAgreedLength, p->wDataLength); d->SetPhase(PHASE_CLOSE); } } #endif // _IMPROVED_PACKET_ENCRYPTION_ else sys_err("Handshake phase does not handle packet %d (fd %d)", bHeader, d->GetSocket()); return 0; }