protocol.cpp

main 154 lines · 3.9 KB Raw
Alex Alex Commit Server upload 18/10/2026 23:38
1/**
2 * The Forgotten Server - a free and open-source MMORPG server emulator
3 * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#include "otpch.h"
21
22#include "protocol.h"
23#include "outputmessage.h"
24#include "rsa.h"
25#include "xtea.h"
26
27extern RSA g_RSA;
28
29Protocol::~Protocol()
30{
31 if (compression) {
32 deflateEnd(&zstream);
33 }
34}
35
36void Protocol::onSendMessage(const OutputMessage_ptr& msg) const
37{
38 if (!rawMessages) {
39 bool compressed = false;
40 if (compression && msg->getLength() > 64) {
41 compress(*msg);
42 compressed = true;
43 }
44
45 msg->writeMessageLength();
46
47 if (encryptionEnabled) {
48 XTEA_encrypt(*msg);
49 msg->addCryptoHeader(checksumEnabled, compressed);
50 }
51 }
52}
53
54void Protocol::onRecvMessage(NetworkMessage& msg)
55{
56 if (encryptionEnabled && !XTEA_decrypt(msg)) {
57 return;
58 }
59
60 parsePacket(msg);
61}
62
63OutputMessage_ptr Protocol::getOutputBuffer(int32_t size)
64{
65 //dispatcher thread
66 if (!outputBuffer) {
67 outputBuffer = OutputMessagePool::getOutputMessage();
68 } else if ((outputBuffer->getLength() + size) > NetworkMessage::MAX_PROTOCOL_BODY_LENGTH) {
69 send(outputBuffer);
70 outputBuffer = OutputMessagePool::getOutputMessage();
71 }
72 return outputBuffer;
73}
74
75void Protocol::XTEA_encrypt(OutputMessage& msg) const
76{
77 // The message must be a multiple of 8
78 size_t paddingBytes = msg.getLength() % 8u;
79 if (paddingBytes != 0) {
80 msg.addPaddingBytes(8 - paddingBytes);
81 }
82
83 uint8_t* buffer = msg.getOutputBuffer();
84 xtea::encrypt(buffer, msg.getLength(), key);
85}
86
87bool Protocol::XTEA_decrypt(NetworkMessage& msg) const
88{
89 if (((msg.getLength() - 6) & 7) != 0) {
90 return false;
91 }
92
93 uint8_t* buffer = msg.getBuffer() + msg.getBufferPosition();
94 xtea::decrypt(buffer, msg.getLength() - 6, key);
95
96 uint16_t innerLength = msg.get<uint16_t>();
97 if (innerLength + 8 > msg.getLength()) {
98 return false;
99 }
100
101 msg.setLength(innerLength);
102 return true;
103}
104
105bool Protocol::RSA_decrypt(NetworkMessage& msg)
106{
107 if ((msg.getLength() - msg.getBufferPosition()) < 128) {
108 return false;
109 }
110
111 g_RSA.decrypt(reinterpret_cast<char*>(msg.getBuffer()) + msg.getBufferPosition()); //does not break strict aliasing
112 return msg.getByte() == 0;
113}
114
115uint32_t Protocol::getIP() const
116{
117 if (auto connection = getConnection()) {
118 return connection->getIP();
119 }
120
121 return 0;
122}
123
124void Protocol::enableCompression()
125{
126 if (compression)
127 return;
128 if (deflateInit2(&zstream, 6, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
129 std::cerr << "ZLIB initialization error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
130 }
131 compression = true;
132}
133
134void Protocol::compress(OutputMessage& msg) const
135{
136 static thread_local std::vector<uint8_t> buffer(NETWORKMESSAGE_MAXSIZE);
137 zstream.next_in = msg.getOutputBuffer();
138 zstream.avail_in = msg.getLength();
139 zstream.next_out = buffer.data();
140 zstream.avail_out = buffer.size();
141 if (deflate(&zstream, Z_SYNC_FLUSH) != Z_OK) {
142 std::cerr << "ZLIB deflate error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
143 return;
144 }
145 int finalSize = buffer.size() - zstream.avail_out - 4;
146 if (finalSize < 0) {
147 std::cerr << "Packet compression error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
148 return;
149 }
150
151 msg.reset();
152 msg.addBytes((const char*)buffer.data(), finalSize);
153}
154
Top