init protocol login

This commit is contained in:
Henrique 2011-05-30 00:11:12 -03:00
parent a54f5dd3f9
commit ad10754779
11 changed files with 379 additions and 29 deletions

View File

@ -40,16 +40,26 @@ void Connection::poll()
ioService.reset(); ioService.reset();
} }
void Connection::connect(const std::string& host, uint16 port, const SimpleCallback& callback) void Connection::connect(const std::string& host, uint16 port, const SimpleCallback& connectCallback)
{ {
m_connectCallback = callback; m_connectCallback = connectCallback;
m_connectionState = CONNECTION_STATE_RESOLVING; m_connectionState = CONNECTION_STATE_RESOLVING;
boost::asio::ip::tcp::resolver::query query(host, convert_cast<std::string>(port)); boost::asio::ip::tcp::resolver::query query(host, convert_cast<std::string>(port));
m_resolver.async_resolve(query, boost::bind(&Connection::onResolve, this, boost::asio::placeholders::error, boost::asio::placeholders::iterator)); m_resolver.async_resolve(query, boost::bind(&Connection::onResolve, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::iterator));
m_timer.expires_from_now(boost::posix_time::seconds(2)); m_timer.expires_from_now(boost::posix_time::seconds(2));
m_timer.async_wait(boost::bind(&Connection::onTimeout, this, boost::asio::placeholders::error)); m_timer.async_wait(boost::bind(&Connection::onTimeout, shared_from_this(), boost::asio::placeholders::error));
}
void Connection::send(OutputMessage *outputMessage)
{
boost::asio::async_write(m_socket,
boost::asio::buffer(outputMessage->getBuffer(), outputMessage->getMessageSize()),
boost::bind(&Connection::onSend, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
m_timer.expires_from_now(boost::posix_time::seconds(2));
m_timer.async_wait(boost::bind(&Connection::onTimeout, shared_from_this(), boost::asio::placeholders::error));
} }
void Connection::onTimeout(const boost::system::error_code& error) void Connection::onTimeout(const boost::system::error_code& error)
@ -65,14 +75,15 @@ void Connection::onResolve(const boost::system::error_code& error, boost::asio::
m_timer.cancel(); m_timer.cancel();
if(error) { if(error) {
if(m_errorCallback)
g_dispatcher.addTask(boost::bind(m_errorCallback, error)); g_dispatcher.addTask(boost::bind(m_errorCallback, error));
return; return;
} }
m_socket.async_connect(*endpointIterator, boost::bind(&Connection::onConnect, this, boost::asio::placeholders::error)); m_socket.async_connect(*endpointIterator, boost::bind(&Connection::onConnect, shared_from_this(), boost::asio::placeholders::error));
m_timer.expires_from_now(boost::posix_time::seconds(2)); m_timer.expires_from_now(boost::posix_time::seconds(2));
m_timer.async_wait(boost::bind(&Connection::onTimeout, this, boost::asio::placeholders::error)); m_timer.async_wait(boost::bind(&Connection::onTimeout, shared_from_this(), boost::asio::placeholders::error));
} }
void Connection::onConnect(const boost::system::error_code& error) void Connection::onConnect(const boost::system::error_code& error)
@ -82,9 +93,73 @@ void Connection::onConnect(const boost::system::error_code& error)
m_timer.cancel(); m_timer.cancel();
if(error) { if(error) {
if(m_errorCallback)
g_dispatcher.addTask(boost::bind(m_errorCallback, error)); g_dispatcher.addTask(boost::bind(m_errorCallback, error));
return; return;
} }
g_dispatcher.addTask(m_connectCallback); g_dispatcher.addTask(m_connectCallback);
// Start listening.
InputMessage *inputMessage = new InputMessage;
boost::asio::async_read(m_socket,
boost::asio::buffer(inputMessage->getBuffer(), InputMessage::HEADER_LENGTH),
boost::bind(&Connection::onRecvHeader, shared_from_this(), boost::asio::placeholders::error, inputMessage));
}
void Connection::onSend(const boost::system::error_code& error, size_t)
{
logTrace();
m_timer.cancel();
if(error) {
if(m_errorCallback)
g_dispatcher.addTask(boost::bind(m_errorCallback, error));
return;
}
}
void Connection::onRecvHeader(const boost::system::error_code& error, InputMessage *inputMessage)
{
logTrace();
if(error) {
if(m_errorCallback)
g_dispatcher.addTask(boost::bind(m_errorCallback, error));
return;
}
uint16 messageSize = inputMessage->getU16();
inputMessage->setMessageSize(messageSize);
boost::asio::async_read(m_socket,
boost::asio::buffer(inputMessage->getBuffer() + InputMessage::CHECKSUM_POS, messageSize),
boost::bind(&Connection::onRecvData, shared_from_this(), boost::asio::placeholders::error, inputMessage));
}
void Connection::onRecvData(const boost::system::error_code& error, InputMessage *inputMessage)
{
logTrace();
if(error) {
if(m_errorCallback)
g_dispatcher.addTask(boost::bind(m_errorCallback, error));
return;
}
// call callback
if(m_recvCallback)
g_dispatcher.addTask(boost::bind(m_recvCallback, inputMessage));
// FIXME:
// TODO declare inside class? call onRecvHeader.
// this needs a remake
/*delete inputMessage;
inputMessage = new InputMessage;
boost::asio::async_read(m_socket,
boost::asio::buffer(inputMessage->getBuffer(), InputMessage::HEADER_LENGTH),
boost::bind(&Connection::onRecvHeader, shared_from_this(), boost::asio::placeholders::error, inputMessage));*/
} }

View File

@ -25,24 +25,33 @@
#ifndef CONNECTION_H #ifndef CONNECTION_H
#define CONNECTION_H #define CONNECTION_H
#include <net/inputmessage.h>
#include <net/outputmessage.h>
#include <prerequisites.h> #include <prerequisites.h>
#include <boost/asio.hpp> #include <boost/asio.hpp>
typedef boost::function<void(boost::system::error_code&)> ErrorCallback; typedef boost::function<void(boost::system::error_code&)> ErrorCallback;
typedef boost::function<void(InputMessage*)> RecvCallback;
class Connection class Connection : public boost::enable_shared_from_this<Connection>, boost::noncopyable
{ {
public: public:
Connection(); Connection();
static void poll(); static void poll();
void connect(const std::string& host, uint16 port, const SimpleCallback& callback); void connect(const std::string& host, uint16 port, const SimpleCallback& connectCallback);
void send(OutputMessage *outputMessage);
void setErrorCallback(const ErrorCallback& errorCallback) { m_errorCallback = errorCallback; } void setErrorCallback(const ErrorCallback& errorCallback) { m_errorCallback = errorCallback; }
void setRecvCallback(const RecvCallback& recvCallback) { m_recvCallback = recvCallback; }
void onTimeout(const boost::system::error_code& error); void onTimeout(const boost::system::error_code& error);
void onResolve(const boost::system::error_code& error, boost::asio::ip::tcp::resolver::iterator endpointIterator); void onResolve(const boost::system::error_code& error, boost::asio::ip::tcp::resolver::iterator endpointIterator);
void onConnect(const boost::system::error_code& error); void onConnect(const boost::system::error_code& error);
void onSend(const boost::system::error_code& error, size_t);
void onRecvHeader(const boost::system::error_code& error, InputMessage *inputMessage);
void onRecvData(const boost::system::error_code& error, InputMessage *inputMessage);
enum ConnectionState_t { enum ConnectionState_t {
CONNECTION_STATE_IDLE = 0, CONNECTION_STATE_IDLE = 0,
@ -53,6 +62,7 @@ public:
private: private:
ErrorCallback m_errorCallback; ErrorCallback m_errorCallback;
RecvCallback m_recvCallback;
SimpleCallback m_connectCallback; SimpleCallback m_connectCallback;
ConnectionState_t m_connectionState; ConnectionState_t m_connectionState;

View File

@ -32,7 +32,7 @@ InputMessage::InputMessage()
void InputMessage::reset() void InputMessage::reset()
{ {
m_readPos = 0; m_readPos = 0;
m_messageSize = 0; m_messageSize = 2;
} }
uint8 InputMessage::getU8() uint8 InputMessage::getU8()

View File

@ -32,8 +32,12 @@ class InputMessage
public: public:
enum { enum {
BUFFER_MAXSIZE = 256, BUFFER_MAXSIZE = 256,
HEADER_POS = 0,
HEADER_LENGTH = 2, HEADER_LENGTH = 2,
CHECKSUM_LENGTH = 4 CHECKSUM_POS = 2,
CHECKSUM_LENGTH = 4,
DATA_POS = 6,
UNENCRYPTED_DATA_POS = 8
}; };
InputMessage(); InputMessage();
@ -46,13 +50,17 @@ public:
uint64 getU64(); uint64 getU64();
std::string getString(); std::string getString();
uint8 *getBuffer() { return m_buffer; }
uint16 getMessageSize() { return m_messageSize; }
void setMessageSize(uint16 messageSize) { m_messageSize = messageSize; }
bool end() { return m_readPos == m_messageSize; }
private: private:
bool canRead(int bytes); bool canRead(int bytes);
uint16_t m_readPos; uint16 m_readPos;
uint16_t m_messageSize; uint16 m_messageSize;
uint8_t m_buffer[BUFFER_MAXSIZE]; uint8 m_buffer[BUFFER_MAXSIZE];
}; };
#endif #endif

View File

@ -31,7 +31,7 @@ OutputMessage::OutputMessage()
void OutputMessage::reset() void OutputMessage::reset()
{ {
m_writePos = 0; m_writePos = DATA_POS;
m_messageSize = 0; m_messageSize = 0;
} }
@ -92,6 +92,16 @@ void OutputMessage::addString(const std::string &value)
addString(value.c_str()); addString(value.c_str());
} }
void OutputMessage::addPaddingBytes(int bytes, uint8 byte)
{
if(!canWrite(bytes))
return;
memset((void*)&m_buffer[m_writePos], byte, bytes);
m_writePos += bytes;
m_messageSize += bytes;
}
bool OutputMessage::canWrite(int bytes) bool OutputMessage::canWrite(int bytes)
{ {
return (m_writePos + bytes <= BUFFER_MAXSIZE); return (m_writePos + bytes <= BUFFER_MAXSIZE);

View File

@ -31,7 +31,12 @@ class OutputMessage
{ {
public: public:
enum { enum {
BUFFER_MAXSIZE = 1024 BUFFER_MAXSIZE = 1024,
HEADER_POS = 0,
HEADER_LENGTH = 2,
CHECKSUM_POS = 2,
CHECKSUM_LENGTH = 4,
DATA_POS = 6
}; };
OutputMessage(); OutputMessage();
@ -44,13 +49,18 @@ public:
void addU64(uint64 value); void addU64(uint64 value);
void addString(const char* value); void addString(const char* value);
void addString(const std::string &value); void addString(const std::string &value);
void addPaddingBytes(int bytes, uint8 byte = 0);
uint8 *getBuffer() { return m_buffer; }
uint16 getMessageSize() { return m_messageSize; }
void setWritePos(uint16 writePos) { m_writePos = writePos; }
private: private:
bool canWrite(int bytes); bool canWrite(int bytes);
uint16_t m_writePos; uint16 m_writePos;
uint16_t m_messageSize; uint16 m_messageSize;
uint8_t m_buffer[BUFFER_MAXSIZE]; uint8 m_buffer[BUFFER_MAXSIZE];
}; };
#endif #endif

View File

@ -28,6 +28,8 @@ Protocol::Protocol() :
m_connection(new Connection) m_connection(new Connection)
{ {
m_connection->setErrorCallback(boost::bind(&Protocol::onError, this, _1)); m_connection->setErrorCallback(boost::bind(&Protocol::onError, this, _1));
m_connection->setRecvCallback(boost::bind(&Protocol::onRecv, this, _1));
m_xteaEncryptionEnabled = false;
} }
void Protocol::connect(const std::string& host, uint16 port, const SimpleCallback& callback) void Protocol::connect(const std::string& host, uint16 port, const SimpleCallback& callback)
@ -35,12 +37,128 @@ void Protocol::connect(const std::string& host, uint16 port, const SimpleCallbac
m_connection->connect(host, port, callback); m_connection->connect(host, port, callback);
} }
void Protocol::send(OutputMessage *outputMessage)
{
// Encrypt
if(m_xteaEncryptionEnabled)
xteaEncrypt(outputMessage);
// Set checksum
uint32 checksum = getAdlerChecksum(outputMessage->getBuffer() + OutputMessage::DATA_POS, outputMessage->getMessageSize());
outputMessage->setWritePos(OutputMessage::CHECKSUM_POS);
outputMessage->addU32(checksum);
// Set size
uint16 messageSize = outputMessage->getMessageSize();
outputMessage->setWritePos(OutputMessage::HEADER_POS);
outputMessage->addU16(messageSize);
// Send
m_connection->send(outputMessage);
}
void Protocol::onRecv(InputMessage *inputMessage)
{
uint32 checksum = getAdlerChecksum(inputMessage->getBuffer() + InputMessage::DATA_POS, inputMessage->getMessageSize() - InputMessage::CHECKSUM_LENGTH);
if(inputMessage->getU32() != checksum) {
// error
logError("Checksum is invalid.");
return;
}
if(m_xteaEncryptionEnabled)
xteaDecrypt(inputMessage);
}
void Protocol::onError(const boost::system::error_code& error) void Protocol::onError(const boost::system::error_code& error)
{ {
flogError("PROTOCOL ERROR: ", error.message()); flogError("PROTOCOL ERROR: %s", error.message());
// invalid hostname // invalid hostname
// connection timeouted // connection timeouted
// displays a dialog, finish protocol // displays a dialog, finish protocol
} }
bool Protocol::xteaDecrypt(InputMessage *inputMessage)
{
// FIXME: this function has not been tested yet
uint16 messageSize = inputMessage->getMessageSize() - InputMessage::CHECKSUM_LENGTH;
if(messageSize % 8 != 0) {
//LOG_TRACE_DEBUG("not valid encrypted message size")
return false;
}
uint32 *buffer = (uint32*)(inputMessage->getBuffer() + InputMessage::DATA_POS);
int readPos = 0;
while(readPos < messageSize/4) {
uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1];
uint32 delta = 0x61C88647;
uint32 sum = 0xC6EF3720;
for(int32 i = 0; i < 32; i++) {
v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + m_xteaKey[sum>>11 & 3]);
sum += delta;
v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + m_xteaKey[sum & 3]);
}
buffer[readPos] = v0; buffer[readPos + 1] = v1;
readPos = readPos + 2;
}
int tmp = inputMessage->getU16();
if(tmp > inputMessage->getMessageSize() - 4) {
//LOG_TRACE_DEBUG("not valid unencrypted message size")
return false;
}
inputMessage->setMessageSize(tmp + InputMessage::UNENCRYPTED_DATA_POS);
return true;
}
void Protocol::xteaEncrypt(OutputMessage *outputMessage)
{
uint16 messageLength = outputMessage->getMessageSize();
//add bytes until reach 8 multiple
if((messageLength % 8) != 0) {
uint16 n = 8 - (messageLength % 8);
outputMessage->addPaddingBytes(n);
messageLength += n;
}
int readPos = 0;
uint32 *buffer = (uint32*)outputMessage->getBuffer() + OutputMessage::DATA_POS;
while(readPos < messageLength / 4) {
uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1];
uint32 delta = 0x61C88647;
uint32 sum = 0;
for(int32 i = 0; i < 32; i++) {
v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + m_xteaKey[sum & 3]);
sum -= delta;
v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + m_xteaKey[sum>>11 & 3]);
}
buffer[readPos] = v0; buffer[readPos + 1] = v1;
readPos = readPos + 2;
}
}
uint32 Protocol::getAdlerChecksum(uint8 *buffer, uint16 size)
{
uint32 a = 1, b = 0;
while (size > 0) {
size_t tlen = size > 5552 ? 5552 : size;
size -= tlen;
do {
a += *buffer++;
b += a;
} while (--tlen);
a %= 65521;
b %= 65521;
}
return (b << 16) | a;
}

View File

@ -26,6 +26,16 @@
#define PROTOCOL_H #define PROTOCOL_H
#include <net/connection.h> #include <net/connection.h>
#include <net/inputmessage.h>
#include <net/outputmessage.h>
#define CIPSOFT_PUBLIC_RSA "1321277432058722840622950990822933849527763264961655079678763618" \
"4334395343554449668205332383339435179772895415509701210392836078" \
"6959821132214473291575712138800495033169914814069637740318278150" \
"2907336840325241747827401343576296990629870233111328210165697754" \
"88792221429527047321331896351555606801473202394175817"
//#define RSA "109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413"
class Protocol class Protocol
{ {
@ -33,10 +43,20 @@ public:
Protocol(); Protocol();
void connect(const std::string& host, uint16 port, const SimpleCallback& callback); void connect(const std::string& host, uint16 port, const SimpleCallback& callback);
void send(OutputMessage *outputMessage);
virtual void onRecv(InputMessage *inputMessage);
virtual void onError(const boost::system::error_code& error); virtual void onError(const boost::system::error_code& error);
protected:
uint32 m_xteaKey[4];
bool m_xteaEncryptionEnabled;
private: private:
bool xteaDecrypt(InputMessage *inputMessage);
void xteaEncrypt(OutputMessage *outputMessage);
uint32 getAdlerChecksum(uint8 *buffer, uint16 size);
ConnectionPtr m_connection; ConnectionPtr m_connection;
}; };

View File

@ -32,6 +32,8 @@
#include <script/luascript.h> #include <script/luascript.h>
#include <ui/uicontainer.h> #include <ui/uicontainer.h>
#include "protocollogin.h"
void signal_handler(int sig) void signal_handler(int sig)
{ {
static bool stopping = false; static bool stopping = false;
@ -83,6 +85,9 @@ int main(int argc, const char *argv[])
args.push_back(argv[i]); args.push_back(argv[i]);
#endif #endif
ProtocolLogin login;
login.login("tibialua0", "lua123456");
logInfo("OTClient 0.2.0"); logInfo("OTClient 0.2.0");
// install exit signal handler // install exit signal handler

View File

@ -21,20 +21,24 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "protocollogin.h" #include "protocollogin.h"
#include <net/outputmessage.h>
#include <util/rsa.h>
ProtocolLogin::ProtocolLogin() ProtocolLogin::ProtocolLogin()
{ {
} }
void ProtocolLogin::login(const std::string& accountName, const std::string& password) void ProtocolLogin::login(const std::string& accountName, const std::string& accountPassword)
{ {
// return error if acc or password is empty or any other condition if(accountName.empty() || accountPassword.empty()) {
// shows error dialog
return;
}
m_accountName = accountName; m_accountName = accountName;
m_password = password; m_accountPassword = accountPassword;
static const char hosts[][32] = { static const char hosts[][32] = {
"login01.tibia.com", "login01.tibia.com",
@ -49,8 +53,9 @@ void ProtocolLogin::login(const std::string& accountName, const std::string& pas
"tibia05.cipsoft.com" "tibia05.cipsoft.com"
}; };
const std::string host = hosts[rand() % 10]; std::string host = hosts[rand() % 10];
const uint16 port = 7171; //std::string host = "tecserver.zapto.org";
uint16 port = 7171;
connect(host, port, boost::bind(&ProtocolLogin::onConnect, this)); connect(host, port, boost::bind(&ProtocolLogin::onConnect, this));
} }
@ -63,5 +68,89 @@ void ProtocolLogin::onConnect()
void ProtocolLogin::sendPacket() void ProtocolLogin::sendPacket()
{ {
OutputMessage oMsg;
oMsg.addU8(0x01); // Protocol id
oMsg.addU16(0x02); // OS
oMsg.addU16(874); // Client version
oMsg.addU32(0x4DBAA20B); // Data Signature
oMsg.addU32(0x4DAD1A32); // Sprite Signature
oMsg.addU32(0x4DA2D2B5); // Picture Signature
oMsg.addU8(0); // First RSA byte must be 0x00 // 1
// Generete xtea key.
m_xteaKey[0] = 432324;
m_xteaKey[1] = 24324;
m_xteaKey[2] = 423432;
m_xteaKey[3] = 234324;
// Add xtea key
oMsg.addU32(m_xteaKey[0]); // 5
oMsg.addU32(m_xteaKey[1]); // 9
oMsg.addU32(m_xteaKey[2]); // 13
oMsg.addU32(m_xteaKey[3]); // 17
oMsg.addString(m_accountName); // Account Name // 19
oMsg.addString(m_accountPassword); // Password // 21
// Packet data must have since byte 0, start, 128 bytes
oMsg.addPaddingBytes(128 - (21 + m_accountName.length() + m_accountPassword.length()));
// Encrypt msg with RSA
if(!Rsa::encrypt((char*)oMsg.getBuffer() + 6 + oMsg.getMessageSize() - 128, 128, CIPSOFT_PUBLIC_RSA))
return;
send(&oMsg);
m_xteaEncryptionEnabled = true;
}
void ProtocolLogin::onRecv(InputMessage *inputMessage)
{
Protocol::onRecv(inputMessage);
while(!inputMessage->end()) {
uint8 opt = inputMessage->getU8();
flogError("%d", (int)opt);
switch(opt) {
case 0x0A:
parseError(inputMessage);
break;
case 0x14:
parseMOTD(inputMessage);
break;
case 0x64:
parseCharacterList(inputMessage);
break;
}
}
}
void ProtocolLogin::parseError(InputMessage *inputMessage)
{
std::string error = inputMessage->getString();
logError(error.c_str());
}
void ProtocolLogin::parseMOTD(InputMessage *inputMessage)
{
std::string motd = inputMessage->getString();
logError(motd.c_str());
}
void ProtocolLogin::parseCharacterList(InputMessage *inputMessage)
{
uint8 characters = inputMessage->getU8();
for(int i = 0; i < characters; ++i) {
std::string name = inputMessage->getString();
std::string world = inputMessage->getString();
uint32 ip = inputMessage->getU32();
uint16 port = inputMessage->getU16();
flogError("%s %s %d %d", name.c_str() % world.c_str() % ip % port);
}
uint16 premiumDays = inputMessage->getU16();
flogError("%d", premiumDays);
} }

View File

@ -32,14 +32,19 @@ class ProtocolLogin : public Protocol
public: public:
ProtocolLogin(); ProtocolLogin();
void login(const std::string& accountName, const std::string& password); void login(const std::string& accountName, const std::string& accountPassword);
void onConnect(); void onConnect();
void sendPacket(); void sendPacket();
void onRecv(InputMessage *inputMessage);
private: private:
std::string m_accountName, m_password; void parseError(InputMessage *inputMessage);
void parseMOTD(InputMessage *inputMessage);
void parseCharacterList(InputMessage *inputMessage);
std::string m_accountName, m_accountPassword;
}; };