byte CClient::Login_ServerList( const char * pszAccount, const char * pszPassword ) { ADDTOCALLSTACK("CClient::Login_ServerList"); // XCMD_ServersReq // Initial login (Login on "loginserver", new format) // If the messages are garbled make sure they are terminated to correct length. tchar szAccount[MAX_ACCOUNT_NAME_SIZE+3]; size_t iLenAccount = Str_GetBare( szAccount, pszAccount, sizeof(szAccount)-1 ); if ( iLenAccount > MAX_ACCOUNT_NAME_SIZE ) return( PacketLoginError::BadAccount ); if ( iLenAccount != strlen(pszAccount)) return( PacketLoginError::BadAccount ); tchar szPassword[MAX_NAME_SIZE+3]; size_t iLenPassword = Str_GetBare( szPassword, pszPassword, sizeof( szPassword )-1 ); if ( iLenPassword > MAX_NAME_SIZE ) return( PacketLoginError::BadPassword ); if ( iLenPassword != strlen(pszPassword)) return( PacketLoginError::BadPassword ); // don't bother logging in yet. // Give the server list to everyone. // if ( LogIn( pszAccount, pszPassword ) ) // return( PacketLoginError::BadPass ); CSString sMsg; byte lErr = LogIn( pszAccount, pszPassword, sMsg ); if ( lErr != PacketLoginError::Success ) { return( lErr ); } new PacketServerList(this); m_Targ_Mode = CLIMODE_SETUP_SERVERS; return( PacketLoginError::Success ); }
void CServerDef::SetName( lpctstr pszName ) { ADDTOCALLSTACK("CServerDef::SetName"); if ( ! pszName ) return; // No HTML tags using <> either. tchar szName[ 2*MAX_SERVER_NAME_SIZE ]; size_t len = Str_GetBare( szName, pszName, sizeof(szName), "<>/\"\\" ); if ( len <= 0 ) return; // allow just basic chars. No spaces, only numbers, letters and underbar. if ( g_Cfg.IsObscene( szName ) ) { DEBUG_ERR(( "Obscene server '%s' ignored.\n", szName )); return; } m_sName = szName; }
bool CServerDef::r_LoadVal( CScript & s ) { ADDTOCALLSTACK("CServerDef::r_LoadVal"); EXC_TRY("LoadVal"); switch ( FindTableSorted( s.GetKey(), sm_szLoadKeys, CountOf( sm_szLoadKeys )-1 ) ) { case SC_ACCAPP: case SC_ACCAPPS: // Treat it as a value or a string. if ( IsDigit( s.GetArgStr()[0] )) m_eAccApp = static_cast<ACCAPP_TYPE>(s.GetArgVal() ); else { // Treat it as a string. "Manual","Automatic","Guest" m_eAccApp = static_cast<ACCAPP_TYPE>(FindTable(s.GetArgStr(), sm_AccAppTable, CountOf(sm_AccAppTable))); } if ( m_eAccApp < 0 || m_eAccApp >= ACCAPP_QTY ) m_eAccApp = ACCAPP_Unspecified; break; case SC_AGE: break; case SC_CLIENTVERSION: m_sClientVersion = s.GetArgRaw(); // m_ClientVersion.SetClientVer( s.GetArgRaw()); break; case SC_CREATE: m_timeCreate = CServerTime::GetCurrentTime() - ( s.GetArgLLVal() * TICK_PER_SEC ); break; case SC_ADMINEMAIL: if ( this != &g_Serv && !g_Serv.m_sEMail.IsEmpty() && strstr(s.GetArgStr(), g_Serv.m_sEMail) ) return false; if ( !g_Cfg.IsValidEmailAddressFormat(s.GetArgStr()) ) return false; if ( g_Cfg.IsObscene(s.GetArgStr()) ) return false; m_sEMail = s.GetArgStr(); break; case SC_LANG: { tchar szLang[ 32 ]; Str_GetBare( szLang, s.GetArgStr(), sizeof(szLang), "<>/\"\\" ); if ( g_Cfg.IsObscene(szLang) ) // Is the name unacceptable? return false; m_sLang = szLang; } break; case SC_LASTVALIDDATE: m_dateLastValid.Read( s.GetArgStr() ); break; case SC_LASTVALIDTIME: { int iVal = s.GetArgVal() * TICK_PER_SEC; if ( iVal < 0 ) m_timeLastValid = CServerTime::GetCurrentTime() + iVal; else m_timeLastValid = CServerTime::GetCurrentTime() - iVal; } break; case SC_SERVIP: m_ip.SetHostPortStr( s.GetArgStr() ); break; case SC_NAME: case SC_SERVNAME: SetName( s.GetArgStr() ); break; case SC_SERVPORT: m_ip.SetPort( (word)s.GetArgVal() ); break; case SC_ACCOUNTS: SetStat( SERV_STAT_ACCOUNTS, s.GetArgVal() ); break; case SC_CLIENTS: { int iClients = s.GetArgVal(); if ( iClients < 0 ) return false; // invalid if ( iClients > FD_SETSIZE ) // Number is bugged ! return false; SetStat( SERV_STAT_CLIENTS, iClients ); } break; case SC_ITEMS: SetStat( SERV_STAT_ITEMS, s.GetArgVal() ); break; case SC_CHARS: SetStat( SERV_STAT_CHARS, s.GetArgVal() ); break; case SC_TIMEZONE: m_TimeZone = (char)s.GetArgVal(); break; case SC_URL: case SC_URLLINK: // It is a basically valid URL ? if ( this != &g_Serv ) { if ( !g_Serv.m_sURL.IsEmpty() && strstr(s.GetArgStr(), g_Serv.m_sURL) ) return false; } if ( !strchr(s.GetArgStr(), '.' ) ) return false; if ( g_Cfg.IsObscene( s.GetArgStr()) ) // Is the name unacceptable? return false; m_sURL = s.GetArgStr(); break; default: return CScriptObj::r_LoadVal(s); } return true; EXC_CATCH; EXC_DEBUG_START; EXC_ADD_SCRIPT; EXC_DEBUG_END; return false; }
bool CWebPageDef::ServPage( CClient * pClient, TCHAR * pszPage, CGTime * pdateIfModifiedSince ) // static { ADDTOCALLSTACK("CWebPageDef::ServPage"); // make sure this is a valid format for the request. TCHAR szPageName[_MAX_PATH]; Str_GetBare( szPageName, pszPage, sizeof(szPageName), "!\"#$%&()*,:;<=>?[]^{|}-+'`" ); int iError = 404; CWebPageDef * pWebPage = g_Cfg.FindWebPage(szPageName); if ( pWebPage ) { iError = pWebPage->ServPageRequest(pClient, szPageName, pdateIfModifiedSince); if ( ! iError ) return true; } // Is it a file in the Script directory ? if ( iError == 404 ) { const RESOURCE_ID ridjunk( RES_UNKNOWN, 1 ); CWebPageDef tmppage( ridjunk ); if ( tmppage.SetSourceFile( szPageName, pClient )) { if ( !tmppage.ServPageRequest(pClient, szPageName, pdateIfModifiedSince) ) return true; } } // Can't find it !? // just take the default page. or have a custom 404 page ? pClient->m_Targ_Text = pszPage; TCHAR *pszTemp = Str_GetTemp(); sprintf(pszTemp, GRAY_FILE "%d.htm", iError); pWebPage = g_Cfg.FindWebPage(pszTemp); if ( pWebPage ) { if ( ! pWebPage->ServPageRequest( pClient, pszPage, NULL )) return true; } // Hmm we should do something !!!? // Try to give a reasonable default error msg. LPCTSTR pszErrText; switch (iError) { case 401: pszErrText = "Authorization Required"; break; case 403: pszErrText = "Forbidden"; break; case 404: pszErrText = "Object Not Found"; break; case 500: pszErrText = "Internal Server Error"; break; default: pszErrText = "Unknown Error"; break; } CGTime datetime = CGTime::GetCurrentTime(); const char *sDate = datetime.FormatGmt(NULL); CGString sMsgHead; CGString sText; sText.Format( "<html><head><title>Error %d</title>" "<meta name=robots content=noindex>" "</head><body>" "<h2>HTTP Error %d</h2><p><strong>%d %s</strong></p>" "<p>The " GRAY_TITLE " server cannot deliver the file or script you asked for.</p>" "<p>Please contact the server's administrator if this problem persists.</p>" "</body></html>", iError, iError, iError, static_cast<LPCTSTR>(pszErrText)); sMsgHead.Format( "HTTP/1.1 %d %s\r\n" "Date: %s\r\n" "Server: " GRAY_TITLE " V " GRAY_VERSION "\r\n" "Content-Type: text/html\r\n" "Content-Length: %d\r\n" "Connection: close\r\n" "\r\n%s", iError, static_cast<LPCTSTR>(pszErrText), static_cast<LPCTSTR>(sDate), sText.GetLength(), static_cast<LPCTSTR>(sText)); new PacketWeb(pClient, reinterpret_cast<const BYTE *>(sMsgHead.GetPtr()), sMsgHead.GetLength()); 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 != 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::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); }