CClient::~CClient() { // Update IP history #ifdef _MTNETWORK HistoryIP &history = g_NetworkManager.getIPHistoryManager().getHistoryForIP(GetPeer()); #else HistoryIP &history = g_NetworkIn.getIPHistoryManager().getHistoryForIP(GetPeer()); #endif if ( GetConnectType() != CONNECT_GAME ) --history.m_connecting; --history.m_connected; bool fWasChar = (m_pChar != NULL); CharDisconnect(); // am i a char in game ? Cmd_GM_PageClear(); // Clear containers (CTAG and TOOLTIP) m_TagDefs.Empty(); m_TooltipData.Clean(true); if ( m_pAccount ) { m_pAccount->OnLogout(this, fWasChar); m_pAccount = NULL; } if ( m_pPopupPacket ) { delete m_pPopupPacket; m_pPopupPacket = NULL; } if ( !m_NetState->isClosed() ) g_Log.EventError("Client being deleted without being safely removed from the network system\n"); }
bool CClient::IsConnecting() const { ADDTOCALLSTACK("CClient::IsConnecting"); switch ( GetConnectType() ) { case CONNECT_TELNET: case CONNECT_AXIS: case CONNECT_HTTP: case CONNECT_GAME: return false; default: return true; } }
bool CClient::OnRxConsoleLoginComplete() { ADDTOCALLSTACK("CClient::OnRxConsoleLoginComplete"); if ( GetConnectType() != CONNECT_TELNET ) return false; if ( !GetPeer().IsValidAddr() ) return false; if ( GetPrivLevel() < PLEVEL_Admin ) // this really should not happen. { SysMessagef("%s\n", g_Cfg.GetDefaultMsg(DEFMSG_CONSOLE_NO_ADMIN)); return false; } SysMessagef("%s '%s' ('%s')\n", g_Cfg.GetDefaultMsg(DEFMSG_CONSOLE_WELCOME_2), GetName(), GetPeerStr()); return true; }
void CClient::SysMessage(LPCTSTR pszMsg) const // system message (in lower left corner) { ADDTOCALLSTACK("CClient::SysMessage"); // Diff sorts of clients. if ( !pszMsg || !*pszMsg ) return; switch ( GetConnectType() ) { case CONNECT_CRYPT: case CONNECT_LOGIN: case CONNECT_GAME: { const_cast<CClient *>(this)->addSysMessage(pszMsg); return; } case CONNECT_HTTP: { const_cast<CClient *>(this)->m_Targ_Text = pszMsg; return; } case CONNECT_TELNET: case CONNECT_AXIS: { if ( ISINTRESOURCE(pszMsg) || (*pszMsg == '\0') ) return; new PacketTelnet(this, pszMsg); return; } case CONNECT_UOG: { if ( ISINTRESOURCE(pszMsg) || (*pszMsg == '\0') ) return; new PacketTelnet(this, pszMsg, true); return; } } }
wxString pgHbaConfigLine::GetText() { if (!changed) return text; wxString str; wxString tabspace = wxT("\t "); if (isComment) str = wxT("# "); str += GetConnectType() + tabspace + database + tabspace + user; if (connectType != PGC_LOCAL) str += tabspace + ipaddress; str += tabspace + GetMethod(); if (method >= PGC_IDENT && !option.IsEmpty()) str += tabspace + option; return str; }
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 ); }
bool CClient::OnRxWebPageRequest( byte * pRequest, size_t iLen ) { ADDTOCALLSTACK("CClient::OnRxWebPageRequest"); // Seems to be a web browser pointing at us ? typical stuff : if ( GetConnectType() != CONNECT_HTTP ) return false; // ensure request is null-terminated (if the request is well-formed, we are overwriting a trailing \n here) pRequest[iLen - 1] = '\0'; if ( strlen(reinterpret_cast<char *>(pRequest)) > 1024 ) // too long request return false; if ( !strpbrk( reinterpret_cast<char *>(pRequest), " \t\012\015" ) ) // malformed request return false; tchar * ppLines[16]; size_t iQtyLines = Str_ParseCmds(reinterpret_cast<char *>(pRequest), ppLines, CountOf(ppLines), "\r\n"); if (( iQtyLines < 1 ) || ( iQtyLines >= 15 )) // too long request return false; // Look for what they want to do with the connection. bool fKeepAlive = false; CSTime dateIfModifiedSince; tchar * pszReferer = NULL; size_t stContentLength = 0; for ( size_t j = 1; j < iQtyLines; j++ ) { tchar *pszArgs = Str_TrimWhitespace(ppLines[j]); if ( !strnicmp(pszArgs, "Connection:", 11 ) ) { pszArgs += 11; GETNONWHITESPACE(pszArgs); if ( !strnicmp(pszArgs, "Keep-Alive", 10) ) fKeepAlive = true; } else if ( !strnicmp(pszArgs, "Referer:", 8) ) { pszReferer = pszArgs+8; } else if ( !strnicmp(pszArgs, "Content-Length:", 15) ) { pszArgs += 15; GETNONWHITESPACE(pszArgs); stContentLength = strtoul(pszArgs, NULL, 10); } else if ( ! strnicmp( pszArgs, "If-Modified-Since:", 18 )) { // If-Modified-Since: Fri, 17 Dec 1999 14:59:20 GMT\r\n pszArgs += 18; dateIfModifiedSince.Read(pszArgs); } } tchar * ppRequest[4]; size_t iQtyArgs = Str_ParseCmds(ppLines[0], ppRequest, CountOf(ppRequest), " "); if (( iQtyArgs < 2 ) || ( strlen(ppRequest[1]) >= _MAX_PATH )) return false; if ( strchr(ppRequest[1], '\r') || strchr(ppRequest[1], 0x0c) ) return false; // if the client hasn't requested a keep alive, we must act as if they had // when async networking is used, otherwise data may not be completely sent if ( fKeepAlive == false ) { fKeepAlive = m_net->isAsyncMode(); // must switch to a blocking socket when the connection is not being kept // alive, or else pending data will be lost when the socket shuts down if ( fKeepAlive == false ) m_net->m_socket.SetNonBlocking(false); } linger llinger; llinger.l_onoff = 1; llinger.l_linger = 500; // in mSec m_net->m_socket.SetSockOpt(SO_LINGER, reinterpret_cast<char *>(&llinger), sizeof(linger)); char nbool = true; m_net->m_socket.SetSockOpt(SO_KEEPALIVE, &nbool, sizeof(char)); // disable NAGLE algorythm for data compression nbool = true; m_net->m_socket.SetSockOpt( TCP_NODELAY, &nbool, sizeof(char), IPPROTO_TCP); if ( memcmp(ppLines[0], "POST", 4) == 0 ) { if ( stContentLength > strlen(ppLines[iQtyLines-1]) ) return false; // POST /--WEBBOT-SELF-- HTTP/1.1 // Referer: http://127.0.0.1:2593/spherestatus.htm // Content-Type: application/x-www-form-urlencoded // Host: 127.0.0.1:2593 // Content-Length: 29 // T1=stuff1&B1=Submit&T2=stuff2 g_Log.Event(LOGM_HTTP|LOGL_EVENT, "%x:HTTP Page Post '%s'\n", GetSocketID(), static_cast<lpctstr>(ppRequest[1])); CWebPageDef *pWebPage = g_Cfg.FindWebPage(ppRequest[1]); if ( !pWebPage ) pWebPage = g_Cfg.FindWebPage(pszReferer); if ( pWebPage ) { if ( pWebPage->ServPagePost(this, ppRequest[1], ppLines[iQtyLines-1], stContentLength) ) { if ( fKeepAlive ) return true; return false; } return false; } } else if ( !memcmp(ppLines[0], "GET", 3) ) { // GET /pagename.htm HTTP/1.1\r\n // If-Modified-Since: Fri, 17 Dec 1999 14:59:20 GMT\r\n // Host: localhost:2593\r\n // \r\n tchar szPageName[_MAX_PATH]; if ( !Str_GetBare( szPageName, Str_TrimWhitespace(ppRequest[1]), sizeof(szPageName), "!\"#$%&()*,:;<=>?[]^{|}-+'`" ) ) return false; g_Log.Event(LOGM_HTTP|LOGL_EVENT, "%x:HTTP Page Request '%s', alive=%d\n", GetSocketID(), static_cast<lpctstr>(szPageName), fKeepAlive); if ( CWebPageDef::ServPage(this, szPageName, &dateIfModifiedSince) ) { if ( fKeepAlive ) return true; return false; } } return false; }
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::OnRxAxis( const byte * pData, size_t iLen ) { ADDTOCALLSTACK("CClient::OnRxAxis"); if ( !iLen || ( GetConnectType() != CONNECT_AXIS )) return false; while ( iLen -- ) { int iRet = OnConsoleKey( m_Targ_Text, *pData++, GetAccount() != NULL ); if ( ! iRet ) return false; if ( iRet == 2 ) { if ( GetAccount() == NULL ) { if ( !m_zLogin[0] ) { if ( (uint)(m_Targ_Text.GetLength()) <= (CountOf(m_zLogin) - 1) ) strcpy(m_zLogin, m_Targ_Text); m_Targ_Text.Empty(); } else { CSString sMsg; CAccountRef pAccount = g_Accounts.Account_Find(m_zLogin); if (( pAccount == NULL ) || ( pAccount->GetPrivLevel() < PLEVEL_Counsel )) { SysMessagef("\"MSG:%s\"", g_Cfg.GetDefaultMsg(DEFMSG_AXIS_NOT_PRIV)); m_Targ_Text.Empty(); return false; } if ( LogIn(m_zLogin, m_Targ_Text, sMsg ) == PacketLoginError::Success ) { m_Targ_Text.Empty(); if ( GetPrivLevel() < PLEVEL_Counsel ) { SysMessagef("\"MSG:%s\"", g_Cfg.GetDefaultMsg(DEFMSG_AXIS_NOT_PRIV)); return false; } if (GetPeer().IsValidAddr()) { CScriptTriggerArgs Args; Args.m_VarsLocal.SetStrNew("Account",GetName()); Args.m_VarsLocal.SetStrNew("IP",GetPeer().GetAddrStr()); TRIGRET_TYPE tRet = TRIGRET_RET_DEFAULT; r_Call("f_axis_preload", this, &Args, NULL, &tRet); if ( tRet == TRIGRET_RET_FALSE ) return false; if ( tRet == TRIGRET_RET_TRUE ) { SysMessagef("\"MSG:%s\"", g_Cfg.GetDefaultMsg(DEFMSG_AXIS_DENIED)); return false; } time_t dateChange; dword dwSize; if ( ! CSFileList::ReadFileInfo( "Axis.db", dateChange, dwSize )) { SysMessagef("\"MSG:%s\"", g_Cfg.GetDefaultMsg(DEFMSG_AXIS_INFO_ERROR)); return false; } CSFile FileRead; if ( ! FileRead.Open( "Axis.db", OF_READ|OF_BINARY )) { SysMessagef("\"MSG:%s\"", g_Cfg.GetDefaultMsg(DEFMSG_AXIS_FILE_ERROR)); return false; } tchar szTmp[8*1024]; PacketWeb packet; for (;;) { size_t iLength = FileRead.Read( szTmp, sizeof( szTmp ) ); if ( iLength <= 0 ) break; packet.setData((byte*)szTmp, iLength); packet.send(this); dwSize -= (dword)iLength; if ( dwSize <= 0 ) break; } return true; } return false; } else if ( ! sMsg.IsEmpty()) { SysMessagef("\"MSG:%s\"", (lpctstr)sMsg); return false; } m_Targ_Text.Empty(); } return true; } } } return true; }
bool CClient::OnRxConsole( const byte * pData, size_t iLen ) { ADDTOCALLSTACK("CClient::OnRxConsole"); // A special console version of the client. (Not game protocol) if ( !iLen || ( GetConnectType() != CONNECT_TELNET )) return false; if ( IsSetEF( EF_AllowTelnetPacketFilter ) ) { bool fFiltered = xPacketFilter(pData, iLen); if ( fFiltered ) return fFiltered; } while ( iLen -- ) { int iRet = OnConsoleKey( m_Targ_Text, *pData++, GetAccount() != NULL ); if ( ! iRet ) return false; if ( iRet == 2 ) { if ( GetAccount() == NULL ) { if ( !m_zLogin[0] ) { if ( (uint)(m_Targ_Text.GetLength()) > (CountOf(m_zLogin) - 1) ) { SysMessage("Login:\n"); } else { strcpy(m_zLogin, m_Targ_Text); SysMessage("Password:\n"); } m_Targ_Text.Empty(); } else { CSString sMsg; CAccountRef pAccount = g_Accounts.Account_Find(m_zLogin); if (( pAccount == NULL ) || ( pAccount->GetPrivLevel() < PLEVEL_Admin )) { SysMessagef("%s\n", g_Cfg.GetDefaultMsg(DEFMSG_CONSOLE_NOT_PRIV)); m_Targ_Text.Empty(); return false; } if ( LogIn(m_zLogin, m_Targ_Text, sMsg ) == PacketLoginError::Success ) { m_Targ_Text.Empty(); return OnRxConsoleLoginComplete(); } else if ( ! sMsg.IsEmpty()) { SysMessage( sMsg ); return false; } m_Targ_Text.Empty(); } return true; } else { iRet = g_Serv.OnConsoleCmd( m_Targ_Text, this ); if (g_Cfg.m_fTelnetLog && GetPrivLevel() >= g_Cfg.m_iCommandLog) g_Log.Event(LOGM_GM_CMDS, "%x:'%s' commands '%s'=%d\n", GetSocketID(), GetName(), static_cast<lpctstr>(m_Targ_Text), iRet); } } } return true; }
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); }