CClient::CClient(NetState* state) { // This may be a web connection or Telnet ? m_net = state; SetConnectType( CONNECT_UNK ); // don't know what sort of connect this is yet. // update ip history #ifndef _MTNETWORK HistoryIP& history = g_NetworkIn.getIPHistoryManager().getHistoryForIP(GetPeer()); #else HistoryIP& history = g_NetworkManager.getIPHistoryManager().getHistoryForIP(GetPeer()); #endif history.m_connecting++; history.m_connected++; m_Crypt.SetClientVer( g_Serv.m_ClientVersion ); m_pAccount = NULL; m_pChar = NULL; m_pGMPage = NULL; m_timeLogin.Init(); m_timeLastSend = m_timeLastEvent = CServTime::GetCurrentTime(); m_timeLastEventWalk = CServTime::GetCurrentTime(); m_iWalkStepCount = 0; m_iWalkTimeAvg = 100; m_timeWalkStep = GetTickCount(); m_Targ_Timeout.Init(); m_Targ_Mode = CLIMODE_SETUP_CONNECTING; m_Prompt_Mode = CLIMODE_NORMAL; m_tmSetup.m_dwIP = 0; m_tmSetup.m_iConnect = 0; m_tmSetup.m_bNewSeed = false; m_Env.SetInvalid(); g_Log.Event(LOGM_CLIENTS_LOG, "%lx:Client connected [Total:%lu] ('%s' %ld/%ld)\n", GetSocketID(), g_Serv.StatGet(SERV_STAT_CLIENTS), GetPeerStr(), history.m_connecting, history.m_connected); m_zLastMessage[0] = 0; m_zLastObjMessage[0] = 0; m_tNextPickup.Init(); m_BfAntiCheat.lastvalue = m_BfAntiCheat.count = 0x0; m_ScreenSize.x = m_ScreenSize.y = 0x0; m_pPopupPacket = NULL; m_pHouseDesign = NULL; m_fUpdateStats = 0; }
bool CClient::xProcessClientSetup( CEvent * pEvent, size_t iLen ) { ADDTOCALLSTACK("CClient::xProcessClientSetup"); // If this is a login then try to process the data and figure out what client it is. // try to figure out which client version we are talking to. // (CEvent::ServersReq) or (CEvent::CharListReq) // NOTE: Anything else we get at this point is tossed ! ASSERT( GetConnectType() == CONNECT_CRYPT ); ASSERT( !m_Crypt.IsInit()); ASSERT( pEvent != NULL ); ASSERT( iLen > 0 ); // Try all client versions on the msg. CEvent bincopy; // in buffer. (from client) ASSERT( iLen <= sizeof(bincopy)); memcpy( bincopy.m_Raw, pEvent->m_Raw, iLen ); if ( !m_Crypt.Init( m_net->m_seed, bincopy.m_Raw, iLen, GetNetState()->isClientKR() ) ) { DEBUG_MSG(( "%x:Odd login message length %" PRIuSIZE_T "?\n", GetSocketID(), iLen )); #ifdef _DEBUG xRecordPacketData(this, (const byte *)pEvent, iLen, "client->server"); #endif addLoginErr( PacketLoginError::BadEncLength ); return false; } GetNetState()->detectAsyncMode(); SetConnectType( m_Crypt.GetConnectType() ); if ( !xCanEncLogin() ) { addLoginErr((uchar)((m_Crypt.GetEncryptionType() == ENC_NONE? PacketLoginError::EncNoCrypt : PacketLoginError::EncCrypt) )); return false; } else if ( m_Crypt.GetConnectType() == CONNECT_LOGIN && !xCanEncLogin(true) ) { addLoginErr( PacketLoginError::BadVersion ); return false; } byte lErr = PacketLoginError::EncUnknown; m_Crypt.Decrypt( pEvent->m_Raw, bincopy.m_Raw, iLen ); tchar szAccount[MAX_ACCOUNT_NAME_SIZE+3]; switch ( pEvent->Default.m_Cmd ) { case XCMD_ServersReq: { if ( iLen < sizeof( pEvent->ServersReq )) return false; lErr = Login_ServerList( pEvent->ServersReq.m_acctname, pEvent->ServersReq.m_acctpass ); if ( lErr == PacketLoginError::Success ) { Str_GetBare( szAccount, pEvent->ServersReq.m_acctname, sizeof(szAccount)-1 ); CAccountRef pAcc = g_Accounts.Account_Find( szAccount ); if (pAcc) { pAcc->m_TagDefs.SetNum("clientversion", m_Crypt.GetClientVer()); pAcc->m_TagDefs.SetNum("reportedcliver", GetNetState()->getReportedVersion()); } else { // If i can't set the tag is better to stop login now lErr = PacketLoginError::Invalid; } } break; } case XCMD_CharListReq: { if ( iLen < sizeof( pEvent->CharListReq )) return false; lErr = Setup_ListReq( pEvent->CharListReq.m_acctname, pEvent->CharListReq.m_acctpass, true ); if ( lErr == PacketLoginError::Success ) { // pass detected client version to the game server to make valid cliver used Str_GetBare( szAccount, pEvent->CharListReq.m_acctname, sizeof(szAccount)-1 ); CAccountRef pAcc = g_Accounts.Account_Find( szAccount ); if (pAcc) { dword tmSid = 0x7f000001; dword tmVer = (dword)(pAcc->m_TagDefs.GetKeyNum("clientversion")); dword tmVerReported = (dword)(pAcc->m_TagDefs.GetKeyNum("reportedcliver")); pAcc->m_TagDefs.DeleteKey("clientversion"); pAcc->m_TagDefs.DeleteKey("reportedcliver"); if ( g_Cfg.m_fUseAuthID ) { tmSid = (dword)(pAcc->m_TagDefs.GetKeyNum("customerid")); pAcc->m_TagDefs.DeleteKey("customerid"); } DEBUG_MSG(("%x:xProcessClientSetup for %s, with AuthId %u and CliVersion %u / CliVersionReported %u\n", GetSocketID(), pAcc->GetName(), tmSid, tmVer, tmVerReported)); if ( tmSid != 0 && tmSid == pEvent->CharListReq.m_Account ) { // request client version if the client has not reported it to server yet if ( tmVerReported == 0 ) new PacketClientVersionReq(this); if ( tmVerReported != 0 ) { GetNetState()->m_reportedVersion = tmVerReported; } else if ( tmVer != 0 ) { m_Crypt.SetClientVerEnum(tmVer, false); GetNetState()->m_clientVersion = tmVer; } // client version change may toggle async mode, it's important to flush pending data to the client before this happens GetNetState()->detectAsyncMode(); if ( !xCanEncLogin(true) ) lErr = PacketLoginError::BadVersion; } else { lErr = PacketLoginError::BadAuthID; } } else { lErr = PacketLoginError::Invalid; } } break; } #ifdef _DEBUG default: { DEBUG_ERR(("Unknown/bad packet to receive at this time: 0x%X\n", pEvent->Default.m_Cmd)); } #endif } xRecordPacketData(this, (const byte *)pEvent, iLen, "client->server"); if ( lErr != PacketLoginError::Success ) // it never matched any crypt format. { addLoginErr( lErr ); } return( lErr == PacketLoginError::Success ); }
CClient::CClient(NetState *state) { m_pChar = NULL; m_Env.SetInvalid(); m_fUpdateStats = 0; m_iWalkTimeAvg = 100; m_iWalkStepCount = 0; m_timeWalkStep = GetTickCount64(); m_ScreenSize.x = m_ScreenSize.y = 0; m_Prompt_Mode = CLIMODE_NORMAL; SetConnectType(CONNECT_UNK); // don't know what sort of connect this is yet m_NetState = state; m_pAccount = NULL; m_FeatureFlags = 0; m_CharacterListFlags = 0; m_TooltipEnabled = false; m_ContainerGridEnabled = false; m_UseNewChatSystem = false; m_timeLogin.Init(); m_timeLastEvent = CServTime::GetCurrentTime(); m_timeLastEventItemPickup = CServTime::GetCurrentTime(); m_timeLastEventWalk = CServTime::GetCurrentTime(); m_timeNextEventWalk = 0; m_pGMPage = NULL; m_timeLastSkillThrowing.Init(); m_pSkillThrowingTarg = NULL; m_SkillThrowingAnimID = ITEMID_NOTHING; m_SkillThrowingAnimHue = 0; m_SkillThrowingAnimRender = 0; m_Targ_Mode = CLIMODE_SETUP_CONNECTING; m_Targ_Timeout.Init(); m_Crypt.SetClientVer(g_Serv.m_ClientVersion); m_pPopupPacket = NULL; m_zLastMessage[0] = 0; m_zLastObjMessage[0] = 0; m_pHouseDesign = NULL; // Update IP history #ifdef _MTNETWORK HistoryIP &history = g_NetworkManager.getIPHistoryManager().getHistoryForIP(GetPeer()); #else HistoryIP &history = g_NetworkIn.getIPHistoryManager().getHistoryForIP(GetPeer()); #endif ++history.m_connecting; ++history.m_connected; g_Log.Event(LOGM_CLIENTS_LOG, "%lx:Client connected [Total:%lu] ('%s' %ld/%ld)\n", GetSocketID(), g_Serv.StatGet(SERV_STAT_CLIENTS), GetPeerStr(), history.m_connecting, history.m_connected); }
bool CClient::OnRxPing( const byte * pData, size_t iLen ) { ADDTOCALLSTACK("CClient::OnRxPing"); // packet iLen < 5 // UOMon should work like this. // RETURN: true = keep the connection open. if ( GetConnectType() != CONNECT_UNK ) return false; if ( !iLen || iLen > 4 ) return false; switch ( pData[0] ) { // Remote Admin Console case '\x1': case ' ': { if ( (iLen > 1) && (iLen != 2 || pData[1] != '\n') && (iLen != 3 || pData[1] != '\r' || pData[2] != '\n') && (iLen != 3 || pData[1] != '\n' || pData[2] != '\0') ) break; // enter into remote admin mode. (look for password). SetConnectType( CONNECT_TELNET ); m_zLogin[0] = 0; SysMessagef("%s %s Admin Telnet\n", g_Cfg.GetDefaultMsg(DEFMSG_CONSOLE_WELCOME_1), g_Serv.GetName()); if ( g_Cfg.m_fLocalIPAdmin ) { // don't bother logging in if local. if ( GetPeer().IsLocalAddr() ) { CAccountRef pAccount = g_Accounts.Account_Find("Administrator"); if ( !pAccount ) pAccount = g_Accounts.Account_Find("RemoteAdmin"); if ( pAccount ) { CSString sMsg; byte lErr = LogIn( pAccount, sMsg ); if ( lErr != PacketLoginError::Success ) { if ( lErr != PacketLoginError::Invalid ) SysMessage( sMsg ); return false; } return OnRxConsoleLoginComplete(); } } } SysMessage("Login:\n"); return true; } //Axis Connection case '@': { if ( (iLen > 1) && (iLen != 2 || pData[1] != '\n') && (iLen != 3 || pData[1] != '\r' || pData[2] != '\n') && (iLen != 3 || pData[1] != '\n' || pData[2] != '\0') ) break; // enter into Axis mode. (look for password). SetConnectType( CONNECT_AXIS ); m_zLogin[0] = 0; time_t dateChange; dword dwSize = 0; CSFileList::ReadFileInfo( "Axis.db", dateChange, dwSize ); SysMessagef("%u",dwSize); return true; } // ConnectUO Status case 0xF1: { // ConnectUO sends a 4-byte packet when requesting status info // byte Cmd (0xF1) // word Unk (0x04) // byte SubCmd (0xFF) if ( iLen != MAKEWORD( pData[2], pData[1] ) ) break; if ( pData[3] != 0xFF ) break; if ( g_Cfg.m_fCUOStatus == false ) { g_Log.Event( LOGM_CLIENTS_LOG|LOGL_EVENT, "%x:CUO Status request from %s has been rejected.\n", GetSocketID(), GetPeerStr()); return false; } // enter 'remote admin mode' SetConnectType( CONNECT_TELNET ); g_Log.Event( LOGM_CLIENTS_LOG|LOGL_EVENT, "%x:CUO Status request from %s\n", GetSocketID(), GetPeerStr()); SysMessage( g_Serv.GetStatusString( 0x25 ) ); // exit 'remote admin mode' SetConnectType( CONNECT_UNK ); return false; } // UOGateway Status case 0xFF: case 0x7F: case 0x22: { if ( iLen > 1 ) break; if ( g_Cfg.m_fUOGStatus == false ) { g_Log.Event( LOGM_CLIENTS_LOG|LOGL_EVENT, "%x:UOG Status request from %s has been rejected.\n", GetSocketID(), GetPeerStr()); return false; } // enter 'remote admin mode' SetConnectType( CONNECT_TELNET ); g_Log.Event( LOGM_CLIENTS_LOG|LOGL_EVENT, "%x:UOG Status request from %s\n", GetSocketID(), GetPeerStr()); if (pData[0] == 0x7F) SetConnectType( CONNECT_UOG ); SysMessage( g_Serv.GetStatusString( 0x22 ) ); // exit 'remote admin mode' SetConnectType( CONNECT_UNK ); return false; } } g_Log.Event( LOGM_CLIENTS_LOG|LOGL_EVENT, "%x:Unknown/invalid ping data '0x%x' from %s (Len: %" PRIuSIZE_T ")\n", GetSocketID(), pData[0], GetPeerStr(), iLen); return false; }
bool CClient::xProcessClientSetup( CEvent * pEvent, size_t iLen ) { ADDTOCALLSTACK("CClient::xProcessClientSetup"); // If this is a login then try to process the data and figure out what client it is. // try to figure out which client version we are talking to. // (CEvent::ServersReq) or (CEvent::CharListReq) // NOTE: Anything else we get at this point is tossed ! ASSERT(GetConnectType() == CONNECT_CRYPT); ASSERT(!m_Crypt.IsInit()); ASSERT(pEvent); ASSERT(iLen > 0); // Try all client versions on the msg. CEvent bincopy; // in buffer (from client) ASSERT(iLen <= sizeof(bincopy)); memcpy(bincopy.m_Raw, pEvent->m_Raw, iLen); if ( !m_Crypt.Init(m_NetState->m_seed, bincopy.m_Raw, iLen, m_NetState->isClientKR()) ) { DEBUG_MSG(("%lx:Odd login message length %" FMTSIZE_T "?\n", GetSocketID(), iLen)); #ifdef _DEBUG xRecordPacketData(this, (const BYTE *)pEvent, iLen, "client->server"); #endif addLoginErr(PacketLoginError::BadEncLength); return false; } SetConnectType(m_Crypt.GetConnectType()); if ( !xCanEncLogin() ) { addLoginErr(m_Crypt.GetEncryptionType() == ENC_NONE ? PacketLoginError::EncNoCrypt : PacketLoginError::EncCrypt); return false; } else if ( (m_Crypt.GetConnectType() == CONNECT_LOGIN) && !xCanEncLogin(true) ) { addLoginErr(PacketLoginError::BadVersion); return false; } BYTE lErr = PacketLoginError::EncUnknown; m_Crypt.Decrypt(pEvent->m_Raw, bincopy.m_Raw, iLen); TCHAR szAccount[MAX_ACCOUNT_NAME_SIZE + 3]; switch ( pEvent->Default.m_Cmd ) { case XCMD_ServersReq: { if ( iLen < sizeof(pEvent->ServersReq) ) return false; lErr = Login_ServerList(pEvent->ServersReq.m_acctname, pEvent->ServersReq.m_acctpass); if ( lErr == PacketLoginError::Success ) { Str_GetBare(szAccount, pEvent->ServersReq.m_acctname, sizeof(szAccount) - 1); CAccountRef pAcc = g_Accounts.Account_Find(szAccount); if ( pAcc ) { // On login proccess, the client will connect on IP only to request the servers list, and after select an server, // it will disconnect from this IP and connect again now on server IP to request the account character list. But // on the 2nd connection the client doesn't report its version to server again, so we must use tags to temporarily // store the client version on XCMD_ServersReq and restore it on XCMD_CharListReq. if ( m_Crypt.GetClientVer() ) pAcc->m_TagDefs.SetNum("ClientVersion", m_Crypt.GetClientVer()); if ( m_NetState->getReportedVersion() ) pAcc->m_TagDefs.SetNum("ReportedCliVer", m_NetState->getReportedVersion()); else new PacketClientVersionReq(this); } else { lErr = PacketLoginError::Invalid; } } break; } case XCMD_CharListReq: { if ( iLen < sizeof(pEvent->CharListReq) ) return false; Str_GetBare(szAccount, pEvent->CharListReq.m_acctname, sizeof(szAccount) - 1); CAccountRef pAcc = g_Accounts.Account_Find(szAccount); DWORD dwCustomerID = 0x7f000001; if ( pAcc ) { if ( g_Cfg.m_fUseAuthID ) { dwCustomerID = static_cast<DWORD>(pAcc->m_TagDefs.GetKeyNum("CustomerID")); pAcc->m_TagDefs.DeleteKey("CustomerID"); } DWORD tmVer = static_cast<DWORD>(pAcc->m_TagDefs.GetKeyNum("ClientVersion")); if ( tmVer ) { m_Crypt.SetClientVerEnum(tmVer, false); m_NetState->m_clientVersion = tmVer; pAcc->m_TagDefs.DeleteKey("ClientVersion"); } DWORD tmVerReported = static_cast<DWORD>(pAcc->m_TagDefs.GetKeyNum("ReportedCliVer")); if ( tmVerReported ) { m_NetState->m_reportedVersion = tmVerReported; pAcc->m_TagDefs.DeleteKey("ReportedCliVer"); } // Enhanced clients must enable some specific features on UpdateFeatureFlags() but they only report the // client type to server after this function, so we must estimate the client type based on client version // to enable these features properly even when the client don't reported its client type to server yet. if ( m_NetState->isClientVersion(MASK_CLIENTTYPE_EC) ) m_NetState->m_clientType = CLIENTTYPE_EC; } lErr = Setup_ListReq(pEvent->CharListReq.m_acctname, pEvent->CharListReq.m_acctpass, true); if ( lErr == PacketLoginError::Success ) { if ( pAcc ) { DEBUG_MSG(("%lx:xProcessClientSetup for %s, with AuthId %lu and CliVersion %lu / CliVersionReported %lu\n", GetSocketID(), pAcc->GetName(), CustomerID, m_Crypt.GetClientVer(), m_NetState->getReportedVersion())); if ( (dwCustomerID != 0) && (dwCustomerID == pEvent->CharListReq.m_Account) ) { if ( !xCanEncLogin(true) ) lErr = PacketLoginError::BadVersion; } else { lErr = PacketLoginError::BadAuthID; } } else { lErr = PacketLoginError::Invalid; } } break; } #ifdef _DEBUG default: DEBUG_ERR(("Unknown/bad packet to receive at this time: 0x%X\n", pEvent->Default.m_Cmd)); #endif } xRecordPacketData(this, (const BYTE *)pEvent, iLen, "client->server"); if ( lErr != PacketLoginError::Success ) // it never matched any crypt format. addLoginErr(lErr); return (lErr == PacketLoginError::Success); }