/*
* 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 "LogonStdAfx.h"
#pragma pack(push, 1)
typedef struct
{
uint16 opcode;
uint32 size;
}logonpacket;
#pragma pack(pop)
LogonCommServerSocket::LogonCommServerSocket(SOCKET fd) : Socket(fd, 65536, 524288)
{
// do nothing
last_ping = time(NULL);
remaining = opcode = 0;
sInfoCore.AddServerSocket(this);
removed = false;
use_crypto = false;
authenticated = 0;
}
LogonCommServerSocket::~LogonCommServerSocket()
{
}
void LogonCommServerSocket::OnDisconnect()
{
// if we're registered -> de-register
if(!removed)
{
set::iterator itr = server_ids.begin();
for(; itr != server_ids.end(); ++itr)
sInfoCore.RemoveRealm((*itr));
sInfoCore.RemoveServerSocket(this);
}
}
void LogonCommServerSocket::OnRead()
{
while(true)
{
if(!remaining)
{
if(GetReadBufferSize() < 4)
return; // no header
// read header
Read(2, (uint8*)&opcode);
Read(4, (uint8*)&remaining);
if(use_crypto)
{
// decrypt the packet
recvCrypto.Process((unsigned char*)&opcode, (unsigned char*)&opcode, 2);
recvCrypto.Process((unsigned char*)&remaining, (unsigned char*)&remaining, 4);
}
#ifdef USING_BIG_ENDIAN
opcode = swap16(opcode);
#else
/* reverse 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());
}
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 LogonCommServerSocket::HandlePacket(WorldPacket & recvData)
{
if(authenticated == 0 && recvData.GetOpcode() != RCMSG_AUTH_CHALLENGE)
{
// invalid
Disconnect();
return;
}
static logonpacket_handler Handlers[RMSG_COUNT] = {
NULL, // RMSG_NULL
&LogonCommServerSocket::HandleRegister, // RCMSG_REGISTER_REALM
NULL, // RSMSG_REALM_REGISTERED
&LogonCommServerSocket::HandleSessionRequest, // RCMSG_REQUEST_SESSION
NULL, // RSMSG_SESSION_RESULT
&LogonCommServerSocket::HandlePing, // RCMSG_PING
NULL, // RSMSG_PONG
&LogonCommServerSocket::HandleSQLExecute, // RCMSG_SQL_EXECUTE
&LogonCommServerSocket::HandleReloadAccounts, // RCMSG_RELOAD_ACCOUNTS
&LogonCommServerSocket::HandleAuthChallenge, // RCMSG_AUTH_CHALLENGE
NULL, // RSMSG_AUTH_RESPONSE
NULL, // RSMSG_REQUEST_ACCOUNT_CHARACTER_MAPPING
&LogonCommServerSocket::HandleMappingReply, // RCMSG_ACCOUNT_CHARACTER_MAPPING_REPLY
&LogonCommServerSocket::HandleUpdateMapping, // 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 LogonCommServerSocket::HandleRegister(WorldPacket & recvData)
{
Realm * realm = new Realm;
recvData >> realm->Name >> realm->Address;
recvData >> realm->Colour >> realm->Icon >> realm->TimeZone >> realm->Population;
uint32 my_id = sInfoCore.GenerateRealmID();
sLog.outString("Registering realm `%s` under ID %u.", realm->Name.c_str(), my_id);
// Add to the main realm list
sInfoCore.AddRealm(my_id, realm);
// Send back response packet.
WorldPacket data(RSMSG_REALM_REGISTERED, 4);
data << uint32(0); // Error
data << my_id; // Realm ID
data << realm->Name;
SendPacket(&data);
server_ids.insert(my_id);
/* request character mapping for this realm */
data.Initialize(RSMSG_REQUEST_ACCOUNT_CHARACTER_MAPPING);
data << my_id;
SendPacket(&data);
}
void LogonCommServerSocket::HandleSessionRequest(WorldPacket & recvData)
{
uint32 request_id;
string account_name;
recvData >> request_id;
recvData >> account_name;
// get sessionkey!
uint32 error = 0;
Account * acct = sAccountMgr.GetAccount(account_name);
BigNumber * sessionkey = acct ? sInfoCore.GetSessionKey(acct->AccountId) : 0;
if(sessionkey == 0 || acct == 0)
error = 1; // Unauthorized user.
// build response packet
WorldPacket data(RSMSG_SESSION_RESULT, 150);
data << request_id;
data << error;
if(!error)
{
// Append account information.
data << acct->AccountId;
data << acct->Username;
data << acct->GMFlags;
data << acct->AccountFlags;
data.append(sessionkey->AsByteArray(), 40);
}
SendPacket(&data);
}
void LogonCommServerSocket::HandlePing(WorldPacket & recvData)
{
WorldPacket data(RSMSG_PONG, 4);
SendPacket(&data);
last_ping = time(NULL);
}
void LogonCommServerSocket::SendPacket(WorldPacket * data)
{
bool rv;
BurstBegin();
logonpacket header;
#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((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(data->contents(), data->size());
}
if(rv) BurstPush();
BurstEnd();
}
void LogonCommServerSocket::HandleSQLExecute(WorldPacket & recvData)
{
string Query;
recvData >> Query;
sLogonSQL->Execute(Query.c_str());
}
void LogonCommServerSocket::HandleReloadAccounts(WorldPacket & recvData)
{
sAccountMgr.ReloadAccounts(true);
}
void LogonCommServerSocket::HandleAuthChallenge(WorldPacket & recvData)
{
unsigned char key[20];
uint32 result = 1;
recvData.read(key, 20);
// check if we have the correct password
if(memcmp(key, LogonServer::getSingleton().sql_hash, 20))
result = 0;
sLog.outString("Authentication request from %s, result %s.", GetRemoteIP().c_str(), result ? "OK" : "FAIL");
printf("Key: ");
for(int i = 0; i < 20; ++i)
printf("%.2X", key[i]);
printf("\n");
recvCrypto.Setup(key, 20);
sendCrypto.Setup(key, 20);
/* packets are encrypted from now on */
use_crypto = true;
/* send the response packet */
WorldPacket data(RSMSG_AUTH_RESPONSE, 1);
data << result;
SendPacket(&data);
/* set our general var */
authenticated = result;
}
void LogonCommServerSocket::HandleMappingReply(WorldPacket & recvData)
{
/* this packet is gzipped, whee! :D */
uint32 real_size;
recvData >> real_size;
uLongf rsize = real_size;
ByteBuffer buf(real_size);
buf.resize(real_size);
if(uncompress((uint8*)buf.contents(), &rsize, recvData.contents() + 4, recvData.size() - 4) != Z_OK)
{
printf("Uncompress of mapping failed.\n");
return;
}
uint32 account_id;
uint8 number_of_characters;
uint32 count;
uint32 realm_id;
buf >> realm_id;
Realm * realm = sInfoCore.GetRealm(realm_id);
if(!realm)
return;
sInfoCore.getRealmLock().Acquire();
HM_NAMESPACE::hash_map::iterator itr;
buf >> count;
printf("Got mapping packet for realm %u, total of %u entries.\n", (unsigned int)realm_id, (unsigned int)count);
for(uint32 i = 0; i < count; ++i)
{
buf >> account_id >> number_of_characters;
itr = realm->CharacterMap.find(account_id);
if(itr != realm->CharacterMap.end())
itr->second = number_of_characters;
else
realm->CharacterMap.insert( make_pair( account_id, number_of_characters ) );
}
sInfoCore.getRealmLock().Release();
}
void LogonCommServerSocket::HandleUpdateMapping(WorldPacket & recvData)
{
uint32 realm_id;
uint32 account_id;
uint8 chars_to_add;
recvData >> realm_id;
Realm * realm = sInfoCore.GetRealm(realm_id);
if(!realm)
return;
sInfoCore.getRealmLock().Acquire();
recvData >> account_id >> chars_to_add;
HM_NAMESPACE::hash_map::iterator itr = realm->CharacterMap.find(account_id);
if(itr != realm->CharacterMap.end())
itr->second += chars_to_add;
else
realm->CharacterMap.insert( make_pair( account_id, chars_to_add ) );
sInfoCore.getRealmLock().Release();
}