/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "StdAfx.h"
#pragma pack(push, 1)
typedef struct
{
uint16 opcode;
uint32 size;
}logonpacket;
#pragma pack(pop)
#ifndef CLUSTERING
LogonCommClientSocket::LogonCommClientSocket(SOCKET fd) : Socket(fd, 524288, 65536)
{
// do nothing
last_ping = last_pong = time(NULL);
remaining = opcode = 0;
_id=0;
latency = 0;
use_crypto = false;
authenticated = 0;
}
void LogonCommClientSocket::OnRead()
{
while(true)
{
if(!remaining)
{
if(GetReadBufferSize() < 4)
return; // no header
// read header
Read(2, (uint8*)&opcode);
Read(4, (uint8*)&remaining);
// decrypt the first two bytes
if(use_crypto)
{
_recvCrypto.Process((uint8*)&opcode, (uint8*)&opcode, 2);
_recvCrypto.Process((uint8*)&remaining, (uint8*)&remaining, 4);
}
#ifdef USING_BIG_ENDIAN
opcode = swap16(opcode);
#else
// convert network byte order
remaining = ntohl(remaining);
#endif
}
// do we have a full packet?
if(GetReadBufferSize() < remaining)
return;
// create the buffer
WorldPacket buff(opcode, remaining);
if(remaining)
{
buff.resize(remaining);
Read(remaining, (uint8*)buff.contents());
}
// decrypt the rest of the packet
if(use_crypto && remaining)
_recvCrypto.Process((unsigned char*)buff.contents(), (unsigned char*)buff.contents(), remaining);
// handle the packet
HandlePacket(buff);
remaining = 0;
opcode = 0;
}
}
void LogonCommClientSocket::HandlePacket(WorldPacket & recvData)
{
static logonpacket_handler Handlers[RMSG_COUNT] = {
NULL, // RMSG_NULL
NULL, // RCMSG_REGISTER_REALM
&LogonCommClientSocket::HandleRegister, // RSMSG_REALM_REGISTERED
NULL, // RCMSG_REQUEST_SESSION
&LogonCommClientSocket::HandleSessionInfo, // RSMSG_SESSION_RESULT
NULL, // RCMSG_PING
&LogonCommClientSocket::HandlePong, // RSMSG_PONG
NULL, // RCMSG_SQL_EXECUTE
NULL, // RCMSG_RELOAD_ACCOUNTS
NULL, // RCMSG_AUTH_CHALLENGE
&LogonCommClientSocket::HandleAuthResponse, // RSMSG_AUTH_RESPONSE
&LogonCommClientSocket::HandleRequestAccountMapping,// RSMSG_REQUEST_ACCOUNT_CHARACTER_MAPPING
NULL, // RCMSG_ACCOUNT_CHARACTER_MAPPING_REPLY
NULL, // RCMSG_UPDATE_CHARACTER_MAPPING_COUNT
};
if(recvData.GetOpcode() >= RMSG_COUNT || Handlers[recvData.GetOpcode()] == 0)
{
printf("Got unknwon packet from logoncomm: %u\n", recvData.GetOpcode());
return;
}
(this->*(Handlers[recvData.GetOpcode()]))(recvData);
}
void LogonCommClientSocket::HandleRegister(WorldPacket & recvData)
{
uint32 realmlid;
uint32 error;
string realmname;
recvData >> error >> realmlid >> realmname;
sLog.outColor(TNORMAL, "\n >> realm `%s` registered under id ", realmname.c_str());
sLog.outColor(TGREEN, "%u", realmlid);
LogonCommHandler::getSingleton().AdditionAck(_id, realmlid);
realm_ids.insert(realmlid);
}
void LogonCommClientSocket::HandleSessionInfo(WorldPacket & recvData)
{
uint32 request_id;
recvData >> request_id;
Mutex & m = sLogonCommHandler.GetPendingLock();
m.Acquire();
// find the socket with this request
WorldSocket * sock = sLogonCommHandler.GetSocketByRequest(request_id);
if(sock == 0 || sock->Authed) // Expired/Client disconnected
{
m.Release();
return;
}
// extract sessionkey / account information (done by WS)
sock->Authed = true;
sLogonCommHandler.RemoveUnauthedSocket(request_id);
sock->InformationRetreiveCallback(recvData, request_id);
m.Release();
}
void LogonCommClientSocket::HandlePong(WorldPacket & recvData)
{
if(latency)
sLog.outDebug(">> logonserver latency: %ums", getMSTime() - pingtime);
latency = getMSTime() - pingtime;
last_pong = time(NULL);
}
void LogonCommClientSocket::SendPing()
{
pingtime = getMSTime();
WorldPacket data(RCMSG_PING, 4);
SendPacket(&data);
last_ping = time(NULL);
}
void LogonCommClientSocket::SendPacket(WorldPacket * data)
{
logonpacket header;
bool rv;
BurstBegin();
#ifndef USING_BIG_ENDIAN
header.opcode = data->GetOpcode();
header.size = ntohl(data->size());
#else
header.opcode = swap16(uint16(data->GetOpcode()));
header.size = data->size();
#endif
if(use_crypto)
_sendCrypto.Process((unsigned char*)&header, (unsigned char*)&header, 6);
rv = BurstSend((const uint8*)&header, 6);
if(data->size() > 0 && rv)
{
if(use_crypto)
_sendCrypto.Process((unsigned char*)data->contents(), (unsigned char*)data->contents(), data->size());
rv = BurstSend((const uint8*)data->contents(), data->size());
}
if(rv) BurstPush();
BurstEnd();
}
void LogonCommClientSocket::OnDisconnect()
{
if(_id != 0)
{
printf("Calling ConnectionDropped() due to OnDisconnect().\n");
sLogonCommHandler.ConnectionDropped(_id);
}
}
LogonCommClientSocket::~LogonCommClientSocket()
{
}
void LogonCommClientSocket::SendChallenge()
{
uint8 * key = sLogonCommHandler.sql_passhash;
WorldPacket data(RCMSG_AUTH_CHALLENGE, 20);
data.append(key, 20);
SendPacket(&data);
/* initialize rc4 keys */
printf("Key:");
sLog.outColor(TGREEN, " ");
for(int i = 0; i < 20; ++i)
printf("%.2X ", key[i]);
sLog.outColor(TNORMAL, "\n");
_recvCrypto.Setup(key, 20);
_sendCrypto.Setup(key, 20);
/* packets are encrypted from now on */
use_crypto = true;
}
void LogonCommClientSocket::HandleAuthResponse(WorldPacket & recvData)
{
uint8 result;
recvData >> result;
if(result != 1)
{
authenticated = 0xFFFFFFFF;
}
else
{
authenticated = 1;
}
}
void LogonCommClientSocket::UpdateAccountCount(uint32 account_id, uint8 add)
{
WorldPacket data(RCMSG_UPDATE_CHARACTER_MAPPING_COUNT, 9);
set::iterator itr = realm_ids.begin();
for(; itr != realm_ids.end(); ++itr)
{
data.clear();
data << (*itr) << account_id << add;
SendPacket(&data);
}
}
void LogonCommClientSocket::HandleRequestAccountMapping(WorldPacket & recvData)
{
uint32 t= getMSTime();
uint32 realm_id;
uint32 account_id;
QueryResult * result;
map mapping_to_send;
map::iterator itr;
// grab the realm id
recvData >> realm_id;
// fetch the character mapping
result = CharacterDatabase.Query("SELECT acct FROM characters");
if(result)
{
do
{
account_id = result->Fetch()[0].GetUInt32();
itr = mapping_to_send.find(account_id);
if(itr != mapping_to_send.end())
itr->second++;
else
mapping_to_send.insert( make_pair( account_id, 1 ) );
} while(result->NextRow());
delete result;
}
if(!mapping_to_send.size())
{
// no point sending empty shit
return;
}
ByteBuffer uncompressed(40000 * 5 + 8);
//uint32 Count = 0;
uint32 Remaining = mapping_to_send.size();
itr = mapping_to_send.begin();
for(;;)
{
// Send no more than 40000 characters at once.
uncompressed << realm_id;
if(Remaining > 40000)
uncompressed << uint32(40000);
else
uncompressed << Remaining;
for(uint32 i = 0; i < 40000; ++i, ++itr)
{
uncompressed << uint32(itr->first) << uint8(itr->second);
if(!--Remaining)
break;
}
CompressAndSend(uncompressed);
if(!Remaining)
break;
uncompressed.clear();
}
sLog.outString("Took %u msec to build character mapping list for realm %u", getMSTime() - t, realm_id);
}
void LogonCommClientSocket::CompressAndSend(ByteBuffer & uncompressed)
{
// I still got no idea where this came from :p
uint32 destsize = uncompressed.size() + uncompressed.size()/10 + 16;
// w000t w000t kat000t for gzipped packets
WorldPacket data(RCMSG_ACCOUNT_CHARACTER_MAPPING_REPLY, destsize + 4);
data.resize(destsize + 4);
z_stream stream;
stream.zalloc = 0;
stream.zfree = 0;
stream.opaque = 0;
if(deflateInit(&stream, 1) != Z_OK)
{
sLog.outError("deflateInit failed.");
return;
}
// set up stream pointers
stream.next_out = (Bytef*)((uint8*)data.contents())+4;
stream.avail_out = destsize;
stream.next_in = (Bytef*)uncompressed.contents();
stream.avail_in = uncompressed.size();
// call the actual process
if(deflate(&stream, Z_NO_FLUSH) != Z_OK ||
stream.avail_in != 0)
{
sLog.outError("deflate failed.");
return;
}
// finish the deflate
if(deflate(&stream, Z_FINISH) != Z_STREAM_END)
{
sLog.outError("deflate failed: did not end stream");
return;
}
// finish up
if(deflateEnd(&stream) != Z_OK)
{
sLog.outError("deflateEnd failed.");
return;
}
#ifdef USING_BIG_ENDIAN
*(uint32*)data.contents() = swap32(uint32(uncompressed.size()));
#else
*(uint32*)data.contents() = uncompressed.size();
#endif
data.resize(stream.total_out + 4);
SendPacket(&data);
}
#else
void LogonCommHandler::LogonDatabaseReloadAccounts()
{
}
void LogonCommHandler::LogonDatabaseSQLExecute(const char* str, ...)
{
}
void LogonCommHandler::Startup()
{
}
void LogonCommHandler::UpdateAccountCount(uint32 account_id, uint8 add)
{
}
void LogonCommHandler::UpdateSockets()
{
}
LogonCommHandler::LogonCommHandler()
{
}
LogonCommHandler::~LogonCommHandler()
{
}
#endif