Otclient  14/8/2020
protocol.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010-2020 OTClient <https://github.com/edubart/otclient>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 
23 #include "protocol.h"
24 #include "connection.h"
26 #include <random>
27 
29 {
30  m_xteaEncryptionEnabled = false;
31  m_checksumEnabled = false;
32  m_inputMessage = InputMessagePtr(new InputMessage);
33 }
34 
36 {
37 #ifndef NDEBUG
38  assert(!g_app.isTerminated());
39 #endif
40  disconnect();
41 }
42 
43 void Protocol::connect(const std::string& host, uint16 port)
44 {
45  m_connection = ConnectionPtr(new Connection);
46  m_connection->setErrorCallback(std::bind(&Protocol::onError, asProtocol(), std::placeholders::_1));
47  m_connection->connect(host, port, std::bind(&Protocol::onConnect, asProtocol()));
48 }
49 
51 {
52  if(m_connection) {
53  m_connection->close();
54  m_connection.reset();
55  }
56 }
57 
59 {
60  if(m_connection && m_connection->isConnected())
61  return true;
62  return false;
63 }
64 
66 {
67  if(m_connection && m_connection->isConnecting())
68  return true;
69  return false;
70 }
71 
72 void Protocol::send(const OutputMessagePtr& outputMessage)
73 {
74  // encrypt
75  if(m_xteaEncryptionEnabled)
76  xteaEncrypt(outputMessage);
77 
78  // write checksum
79  if(m_checksumEnabled)
80  outputMessage->writeChecksum();
81 
82  // write message size
83  outputMessage->writeMessageSize();
84 
85  // send
86  if(m_connection)
87  m_connection->write(outputMessage->getHeaderBuffer(), outputMessage->getMessageSize());
88 
89  // reset message to allow reuse
90  outputMessage->reset();
91 }
92 
94 {
95  m_inputMessage->reset();
96 
97  // first update message header size
98  int headerSize = 2; // 2 bytes for message size
99  if(m_checksumEnabled)
100  headerSize += 4; // 4 bytes for checksum
101  if(m_xteaEncryptionEnabled)
102  headerSize += 2; // 2 bytes for XTEA encrypted message size
103  m_inputMessage->setHeaderSize(headerSize);
104 
105  // read the first 2 bytes which contain the message size
106  if(m_connection)
107  m_connection->read(2, std::bind(&Protocol::internalRecvHeader, asProtocol(), std::placeholders::_1, std::placeholders::_2));
108 }
109 
110 void Protocol::internalRecvHeader(uint8* buffer, uint16 size)
111 {
112  // read message size
113  m_inputMessage->fillBuffer(buffer, size);
114  uint16 remainingSize = m_inputMessage->readSize();
115 
116  // read remaining message data
117  if(m_connection)
118  m_connection->read(remainingSize, std::bind(&Protocol::internalRecvData, asProtocol(), std::placeholders::_1, std::placeholders::_2));
119 }
120 
121 void Protocol::internalRecvData(uint8* buffer, uint16 size)
122 {
123  // process data only if really connected
124  if(!isConnected()) {
125  g_logger.traceError("received data while disconnected");
126  return;
127  }
128 
129  m_inputMessage->fillBuffer(buffer, size);
130 
131  if(m_checksumEnabled && !m_inputMessage->readChecksum()) {
132  g_logger.traceError("got a network message with invalid checksum");
133  return;
134  }
135 
136  if(m_xteaEncryptionEnabled) {
137  if(!xteaDecrypt(m_inputMessage)) {
138  g_logger.traceError("failed to decrypt message");
139  return;
140  }
141  }
142  onRecv(m_inputMessage);
143 }
144 
146 {
147  std::mt19937 eng(std::time(nullptr));
148  std::uniform_int_distribution<uint32> unif(0, 0xFFFFFFFF);
149  m_xteaKey[0] = unif(eng);
150  m_xteaKey[1] = unif(eng);
151  m_xteaKey[2] = unif(eng);
152  m_xteaKey[3] = unif(eng);
153 }
154 
156 {
157  m_xteaKey[0] = a;
158  m_xteaKey[1] = b;
159  m_xteaKey[2] = c;
160  m_xteaKey[3] = d;
161 }
162 
163 std::vector<uint32> Protocol::getXteaKey()
164 {
165  std::vector<uint32> xteaKey;
166  xteaKey.resize(4);
167  for(int i = 0; i < 4; ++i)
168  xteaKey[i] = m_xteaKey[i];
169  return xteaKey;
170 }
171 
172 bool Protocol::xteaDecrypt(const InputMessagePtr& inputMessage)
173 {
174  uint16 encryptedSize = inputMessage->getUnreadSize();
175  if(encryptedSize % 8 != 0) {
176  g_logger.traceError("invalid encrypted network message");
177  return false;
178  }
179 
180  uint32 *buffer = (uint32*)(inputMessage->getReadBuffer());
181  int readPos = 0;
182 
183  while(readPos < encryptedSize/4) {
184  uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1];
185  uint32 delta = 0x61C88647;
186  uint32 sum = 0xC6EF3720;
187 
188  for(int32 i = 0; i < 32; i++) {
189  v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + m_xteaKey[sum>>11 & 3]);
190  sum += delta;
191  v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + m_xteaKey[sum & 3]);
192  }
193  buffer[readPos] = v0; buffer[readPos + 1] = v1;
194  readPos = readPos + 2;
195  }
196 
197  uint16 decryptedSize = inputMessage->getU16() + 2;
198  int sizeDelta = decryptedSize - encryptedSize;
199  if(sizeDelta > 0 || -sizeDelta > encryptedSize) {
200  g_logger.traceError("invalid decrypted network message");
201  return false;
202  }
203 
204  inputMessage->setMessageSize(inputMessage->getMessageSize() + sizeDelta);
205  return true;
206 }
207 
208 void Protocol::xteaEncrypt(const OutputMessagePtr& outputMessage)
209 {
210  outputMessage->writeMessageSize();
211  uint16 encryptedSize = outputMessage->getMessageSize();
212 
213  //add bytes until reach 8 multiple
214  if((encryptedSize % 8) != 0) {
215  uint16 n = 8 - (encryptedSize % 8);
216  outputMessage->addPaddingBytes(n);
217  encryptedSize += n;
218  }
219 
220  int readPos = 0;
221  uint32 *buffer = (uint32*)(outputMessage->getDataBuffer() - 2);
222  while(readPos < encryptedSize / 4) {
223  uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1];
224  uint32 delta = 0x61C88647;
225  uint32 sum = 0;
226 
227  for(int32 i = 0; i < 32; i++) {
228  v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + m_xteaKey[sum & 3]);
229  sum -= delta;
230  v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + m_xteaKey[sum>>11 & 3]);
231  }
232  buffer[readPos] = v0; buffer[readPos + 1] = v1;
233  readPos = readPos + 2;
234  }
235 }
236 
238 {
239  callLuaField("onConnect");
240 }
241 
242 void Protocol::onRecv(const InputMessagePtr& inputMessage)
243 {
244  callLuaField("onRecv", inputMessage);
245 }
246 
247 void Protocol::onError(const boost::system::error_code& err)
248 {
249  callLuaField("onError", err.message(), err.value());
250  disconnect();
251 }
ConnectionPtr
stdext::shared_object_ptr< Connection > ConnectionPtr
Definition: declarations.h:41
Protocol::disconnect
void disconnect()
Definition: protocol.cpp:50
Protocol::asProtocol
ProtocolPtr asProtocol()
Definition: protocol.h:60
Protocol::Protocol
Protocol()
Definition: protocol.cpp:28
InputMessage::setHeaderSize
void setHeaderSize(uint16 size)
Definition: inputmessage.cpp:110
Connection::read
void read(uint16 bytes, const RecvCallback &callback)
Definition: connection.cpp:157
InputMessage::getU16
uint16 getU16()
Definition: inputmessage.cpp:56
uint32
uint32_t uint32
Definition: types.h:35
Protocol::onError
virtual void onError(const boost::system::error_code &err)
Definition: protocol.cpp:247
InputMessage
Definition: inputmessage.h:30
Protocol::onConnect
virtual void onConnect()
Definition: protocol.cpp:237
Protocol::connect
void connect(const std::string &host, uint16 port)
Definition: protocol.cpp:43
stdext::time
ticks_t time()
Definition: time.cpp:33
InputMessage::getUnreadSize
int getUnreadSize()
Definition: inputmessage.h:61
protocol.h
uint16
uint16_t uint16
Definition: types.h:36
Protocol::m_xteaKey
uint32 m_xteaKey[4]
Definition: protocol.h:67
LuaObject::callLuaField
R callLuaField(const std::string &field, const T &... args)
Definition: luaobject.h:172
Connection::isConnected
bool isConnected()
Definition: connection.h:63
Connection
Definition: connection.h:31
g_logger
Logger g_logger
Definition: logger.cpp:35
int32
int32_t int32
Definition: types.h:39
InputMessage::setMessageSize
void setMessageSize(uint16 size)
Definition: inputmessage.h:71
stdext::shared_object_ptr::reset
void reset()
Definition: shared_object.h:79
Connection::close
void close()
Definition: connection.cpp:66
Protocol::getXteaKey
std::vector< uint32 > getXteaKey()
Definition: protocol.cpp:163
Protocol::recv
virtual void recv()
Definition: protocol.cpp:93
Protocol::isConnected
bool isConnected()
Definition: protocol.cpp:58
Protocol::send
virtual void send(const OutputMessagePtr &outputMessage)
Definition: protocol.cpp:72
InputMessagePtr
stdext::shared_object_ptr< InputMessage > InputMessagePtr
Definition: declarations.h:37
g_app
ConsoleApplication g_app
Definition: consoleapplication.cpp:32
Connection::write
void write(uint8 *buffer, size_t size)
Definition: connection.cpp:117
Connection::connect
void connect(const std::string &host, uint16 port, const std::function< void()> &connectCallback)
Definition: connection.cpp:93
Protocol::onRecv
virtual void onRecv(const InputMessagePtr &inputMessage)
Definition: protocol.cpp:242
connection.h
Protocol::generateXteaKey
void generateXteaKey()
Definition: protocol.cpp:145
InputMessage::getReadBuffer
uint8 * getReadBuffer()
Definition: inputmessage.h:73
InputMessage::reset
void reset()
Definition: inputmessage.cpp:31
stdext::shared_object_ptr
Definition: shared_object.h:39
Protocol::isConnecting
bool isConnecting()
Definition: protocol.cpp:65
Application::isTerminated
bool isTerminated()
Definition: application.h:50
InputMessage::fillBuffer
void fillBuffer(uint8 *buffer, uint16 size)
Definition: inputmessage.cpp:103
Protocol::setXteaKey
void setXteaKey(uint32 a, uint32 b, uint32 c, uint32 d)
Definition: protocol.cpp:155
Connection::isConnecting
bool isConnecting()
Definition: connection.h:62
InputMessage::readSize
uint16 readSize()
Definition: inputmessage.h:78
Connection::setErrorCallback
void setErrorCallback(const ErrorCallback &errorCallback)
Definition: connection.h:58
InputMessage::readChecksum
bool readChecksum()
Definition: inputmessage.cpp:117
Protocol::~Protocol
virtual ~Protocol()
Definition: protocol.cpp:35
uint8
uint8_t uint8
Definition: types.h:37
InputMessage::getMessageSize
uint16 getMessageSize()
Definition: inputmessage.h:62
application.h