protocol.cpp

main 426 lines · 12.9 KB Raw
Alex Alex Commit Client Upload Part 2 19/10/2026 00:06
1/*
2 * Copyright (c) 2010-2017 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"
25#include <framework/core/application.h>
26#include <random>
27
28#include <framework/net/packet_player.h>
29#include <framework/net/packet_recorder.h>
30
31extern asio::io_service g_ioService;
32
33Protocol::Protocol()
34{
35 m_xteaEncryptionEnabled = false;
36 m_checksumEnabled = false;
37 m_sequencedPackets = false;
38 m_bigPackets = false;
39 m_compression = false;
40 m_inputMessage = InputMessagePtr(new InputMessage);
41 m_packetNumber = 0;
42
43 // compression
44 m_zstreamBuffer.resize(InputMessage::BUFFER_MAXSIZE);
45 m_zstream.next_in = m_inputMessage->getDataBuffer();
46 m_zstream.next_out = m_zstreamBuffer.data();
47 m_zstream.avail_in = 0;
48 m_zstream.avail_out = 0;
49 m_zstream.total_in = 0;
50 m_zstream.total_out = 0;
51 m_zstream.zalloc = nullptr;
52 m_zstream.zfree = nullptr;
53 m_zstream.opaque = nullptr;
54 m_zstream.data_type = Z_BINARY;
55 inflateInit2(&m_zstream, -15);
56}
57
58Protocol::~Protocol()
59{
60 VALIDATE(!g_app.isTerminated());
61 disconnect();
62 inflateEnd(&m_zstream);
63}
64
65void Protocol::connect(const std::string& host, uint16 port)
66{
67 if (host == "proxy" || host == "0.0.0.0" || (host == "127.0.0.1" && g_proxy.isActive())) {
68 m_disconnected = false;
69 m_proxy = g_proxy.addSession(port,
70 std::bind(&Protocol::onProxyPacket, asProtocol(), std::placeholders::_1),
71 std::bind(&Protocol::onLocalDisconnected, asProtocol(), std::placeholders::_1));
72 return onConnect();
73 }
74 m_connection = ConnectionPtr(new Connection);
75 m_connection->setErrorCallback(std::bind(&Protocol::onError, asProtocol(), std::placeholders::_1));
76 m_connection->connect(host, port, std::bind(&Protocol::onConnect, asProtocol()));
77}
78
79void Protocol::disconnect()
80{
81 m_disconnected = true;
82 if (m_player) {
83 m_player->stop();
84 return;
85 }
86 if (m_proxy) {
87 g_proxy.removeSession(m_proxy);
88 return;
89 }
90 if (m_connection) {
91 m_connection->close();
92 m_connection.reset();
93 }
94}
95
96
97void Protocol::playRecord(PacketPlayerPtr player)
98{
99 m_disconnected = false;
100 m_player = player;
101 m_player->start(std::bind(&Protocol::onPlayerPacket, asProtocol(), std::placeholders::_1),
102 std::bind(&Protocol::onLocalDisconnected, asProtocol(), std::placeholders::_1));
103 return onConnect();
104}
105
106void Protocol::setRecorder(PacketRecorderPtr recorder)
107{
108 m_recorder = recorder;
109}
110
111bool Protocol::isConnected()
112{
113 if (m_player)
114 return !m_disconnected;
115 if (m_proxy)
116 return !m_disconnected;
117 if (m_connection && m_connection->isConnected())
118 return true;
119 return false;
120}
121
122bool Protocol::isConnecting()
123{
124 if (m_player)
125 return false;
126 if (m_proxy)
127 return false;
128 if (m_connection && m_connection->isConnecting())
129 return true;
130 return false;
131}
132
133void Protocol::send(const OutputMessagePtr& outputMessage, bool rawPacket)
134{
135 if (m_player) {
136 m_player->onOutputPacket(outputMessage);
137 return;
138 }
139
140 if (m_recorder) {
141 m_recorder->addOutputPacket(outputMessage);
142 }
143
144 if (!rawPacket) {
145 // encrypt
146 if (m_xteaEncryptionEnabled)
147 xteaEncrypt(outputMessage);
148
149 // write checksum
150 if (m_sequencedPackets)
151 outputMessage->writeSequence(m_packetNumber++);
152 else if (m_checksumEnabled)
153 outputMessage->writeChecksum();
154
155 // write message size
156 outputMessage->writeMessageSize(m_bigPackets);
157 }
158
159 if (m_proxy) {
160 auto packet = std::make_shared<ProxyPacket>(outputMessage->getHeaderBuffer(), outputMessage->getWriteBuffer());
161 g_proxy.send(m_proxy, packet);
162 outputMessage->reset();
163 return;
164 }
165
166 // send
167 if (m_connection)
168 m_connection->write(outputMessage->getHeaderBuffer(), outputMessage->getMessageSize());
169
170 // reset message to allow reuse
171 outputMessage->reset();
172}
173
174void Protocol::recv()
175{
176 if (m_player)
177 return;
178
179 if (m_proxy) {
180 return;
181 }
182
183 m_inputMessage->reset();
184
185 // first update message header size
186 int headerSize = m_bigPackets ? 4 : 2; // 2 or 4 bytes for message size
187 if (m_checksumEnabled)
188 headerSize += 4; // 4 bytes for checksum
189 if (m_xteaEncryptionEnabled)
190 headerSize += m_bigPackets ? 4 : 2; // 2 or 4 bytes for XTEA encrypted message size
191 m_inputMessage->setHeaderSize(headerSize);
192
193 // read the first 2 bytes which contain the message size
194 if (m_connection)
195 m_connection->read(m_bigPackets ? 4 : 2, std::bind(&Protocol::internalRecvHeader, asProtocol(), std::placeholders::_1, std::placeholders::_2));
196}
197
198void Protocol::internalRecvHeader(uint8* buffer, uint32 size)
199{
200 // read message size
201 m_inputMessage->fillBuffer(buffer, size);
202 uint32 remainingSize = m_inputMessage->readSize(m_bigPackets);
203
204 // read remaining message data
205 if (m_connection)
206 m_connection->read(remainingSize, std::bind(&Protocol::internalRecvData, asProtocol(), std::placeholders::_1, std::placeholders::_2));
207}
208
209void Protocol::internalRecvData(uint8* buffer, uint32 size)
210{
211 // process data only if really connected
212 if (!isConnected()) {
213 g_logger.traceError("received data while disconnected");
214 return;
215 }
216
217 m_inputMessage->fillBuffer(buffer, size);
218
219 bool decompress = false;
220 if (m_sequencedPackets) {
221 if (m_inputMessage->getU32() >= 0xC0000000) {
222 decompress = true;
223 }
224 } else if (m_checksumEnabled) {
225 if (m_inputMessage->peekU32() == 0) { // compressed data
226 m_inputMessage->getU32();
227 decompress = true;
228 } else if (!m_inputMessage->readChecksum()) {
229 g_logger.traceError(stdext::format("got a network message with invalid checksum, size: %i", (int)m_inputMessage->getMessageSize()));
230 return;
231 }
232 }
233
234 if (m_xteaEncryptionEnabled) {
235 if (!xteaDecrypt(m_inputMessage)) {
236 g_logger.traceError("failed to decrypt message");
237 return;
238 }
239 }
240
241 if (decompress || m_compression) {
242 m_inputMessage->addZlibFooter();
243 m_zstream.next_in = m_inputMessage->getDataBuffer();
244 m_zstream.next_out = m_zstreamBuffer.data();
245 m_zstream.avail_in = m_inputMessage->getUnreadSize();
246 m_zstream.avail_out = m_zstreamBuffer.size();
247 if (inflate(&m_zstream, Z_SYNC_FLUSH) != Z_OK) {
248 g_logger.traceError("failed to decompress message");
249 return;
250 }
251 int decryptedSize = m_zstreamBuffer.size() - m_zstream.avail_out;
252 if (decryptedSize == 0) {
253 g_logger.traceError(stdext::format("invalid size of decompressed message - %i", (int)decryptedSize));
254 return;
255 }
256 m_inputMessage->fillBuffer(m_zstreamBuffer.data(), decryptedSize);
257 m_inputMessage->setMessageSize(m_inputMessage->getHeaderSize() + decryptedSize);
258 }
259
260 if (m_recorder) {
261 m_recorder->addInputPacket(m_inputMessage);
262 }
263 onRecv(m_inputMessage);
264}
265
266void Protocol::generateXteaKey()
267{
268 std::mt19937 eng(std::time(NULL));
269 std::uniform_int_distribution<uint32> unif(0, 0xFFFFFFFF);
270 m_xteaKey[0] = unif(eng);
271 m_xteaKey[1] = unif(eng);
272 m_xteaKey[2] = unif(eng);
273 m_xteaKey[3] = unif(eng);
274}
275
276void Protocol::setXteaKey(uint32 a, uint32 b, uint32 c, uint32 d)
277{
278 m_xteaKey[0] = a;
279 m_xteaKey[1] = b;
280 m_xteaKey[2] = c;
281 m_xteaKey[3] = d;
282}
283
284std::vector<uint32> Protocol::getXteaKey()
285{
286 std::vector<uint32> xteaKey;
287 xteaKey.resize(4);
288 for (int i = 0; i < 4; ++i)
289 xteaKey[i] = m_xteaKey[i];
290 return xteaKey;
291}
292
293bool Protocol::xteaDecrypt(const InputMessagePtr& inputMessage)
294{
295 uint32 encryptedSize = inputMessage->getUnreadSize();
296 if (encryptedSize % 8 != 0) {
297 g_logger.traceError(stdext::format("invalid encrypted network message %i", (int)encryptedSize));
298 return false;
299 }
300
301 uint32* buffer = (uint32*)(inputMessage->getReadBuffer());
302 uint32_t readPos = 0;
303
304 while (readPos < encryptedSize / 4) {
305 uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1];
306 uint32 delta = 0x61C88647;
307 uint32 sum = 0xC6EF3720;
308
309 for (int32 i = 0; i < 32; i++) {
310 v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + m_xteaKey[sum >> 11 & 3]);
311 sum += delta;
312 v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + m_xteaKey[sum & 3]);
313 }
314 buffer[readPos] = v0; buffer[readPos + 1] = v1;
315 readPos = readPos + 2;
316 }
317
318 uint32 decryptedSize = m_bigPackets ? (inputMessage->getU32() + 4) : (inputMessage->getU16() + 2);
319 int sizeDelta = decryptedSize - encryptedSize;
320 if (sizeDelta > 0 || -sizeDelta > (int)encryptedSize) {
321 g_logger.traceError("invalid decrypted network message");
322 return false;
323 }
324
325 inputMessage->setMessageSize(inputMessage->getMessageSize() + sizeDelta);
326 return true;
327}
328
329void Protocol::xteaEncrypt(const OutputMessagePtr& outputMessage)
330{
331 outputMessage->writeMessageSize(m_bigPackets);
332 uint32 encryptedSize = outputMessage->getMessageSize();
333
334 //add bytes until reach 8 multiple
335 if ((encryptedSize % 8) != 0) {
336 uint32 n = 8 - (encryptedSize % 8);
337 outputMessage->addPaddingBytes(n);
338 encryptedSize += n;
339 }
340
341 uint32_t readPos = 0;
342 uint32* buffer = (uint32*)(outputMessage->getDataBuffer() - (m_bigPackets ? 4 : 2));
343 while (readPos < encryptedSize / 4) {
344 uint32 v0 = buffer[readPos], v1 = buffer[readPos + 1];
345 uint32 delta = 0x61C88647;
346 uint32 sum = 0;
347
348 for (int32 i = 0; i < 32; i++) {
349 v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + m_xteaKey[sum & 3]);
350 sum -= delta;
351 v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + m_xteaKey[sum >> 11 & 3]);
352 }
353 buffer[readPos] = v0; buffer[readPos + 1] = v1;
354 readPos = readPos + 2;
355 }
356}
357
358void Protocol::onConnect()
359{
360 callLuaField("onConnect");
361}
362
363void Protocol::onRecv(const InputMessagePtr& inputMessage)
364{
365 callLuaField("onRecv", inputMessage);
366}
367
368void Protocol::onError(const boost::system::error_code& err)
369{
370 callLuaField("onError", err.message(), err.value());
371 disconnect();
372}
373
374void Protocol::onPlayerPacket(const std::shared_ptr<std::vector<uint8_t>>& packet)
375{
376 if (m_disconnected)
377 return;
378 auto self(asProtocol());
379 boost::asio::post(g_ioService, [&, self, packet] {
380 if (m_disconnected)
381 return;
382 m_inputMessage->reset();
383
384 m_inputMessage->setHeaderSize(0);
385 m_inputMessage->fillBuffer(packet->data(), packet->size());
386 m_inputMessage->setMessageSize(packet->size());
387 onRecv(m_inputMessage);
388 });
389}
390
391void Protocol::onProxyPacket(const std::shared_ptr<std::vector<uint8_t>>& packet)
392{
393 if (m_disconnected)
394 return;
395 auto self(asProtocol());
396 boost::asio::post(g_ioService, [&, self, packet] {
397 if (m_disconnected)
398 return;
399 m_inputMessage->reset();
400
401 // first update message header size
402 int headerSize = m_bigPackets ? 4 : 2; // 2 bytes for message size
403 if (m_checksumEnabled)
404 headerSize += 4; // 4 bytes for checksum
405 if (m_xteaEncryptionEnabled)
406 headerSize += m_bigPackets ? 4 : 2; // 2 bytes for XTEA encrypted message size
407 m_inputMessage->setHeaderSize(headerSize);
408 m_inputMessage->fillBuffer(packet->data(), m_bigPackets ? 4 : 2);
409 m_inputMessage->readSize(m_bigPackets);
410 internalRecvData(packet->data() + (m_bigPackets ? 4 : 2), packet->size() - (m_bigPackets ? 4 : 2));
411 });
412}
413
414void Protocol::onLocalDisconnected(boost::system::error_code ec)
415{
416 if (m_disconnected)
417 return;
418 auto self(asProtocol());
419 boost::asio::post(g_ioService, [&, self, ec] {
420 if (m_disconnected)
421 return;
422 m_disconnected = true;
423 onError(ec);
424 });
425}
426
Top