Skip to content
This repository was archived by the owner on Dec 4, 2020. It is now read-only.

Commit 96a1ae4

Browse files
authored
Merge pull request #1155 from zach2good/packet_guard
PacketGuard for Cutscenes
2 parents f0828d0 + 9036732 commit 96a1ae4

12 files changed

+157
-5
lines changed

conf/default/map.conf

+4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ max_time_lastupdate: 60
5757
#Game settings
5858
#--------------------------------
5959

60+
# PacketGuard will block and report any packets that aren't in the allow-list for a
61+
# player's current state.
62+
packetguard_enabled: 0
63+
6064
#Minimal number of 0x3A packets which uses for detect lightluggage (set 0 for disable)
6165
lightluggage_block: 4
6266

src/map/entities/charentity.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@ CCharEntity::CCharEntity()
212212
m_moghouseID = 0;
213213
m_moghancementID = 0;
214214

215+
m_Substate = CHAR_SUBSTATE::SUBSTATE_NONE;
216+
215217
PAI = std::make_unique<CAIContainer>(this, nullptr, std::make_unique<CPlayerController>(this),
216218
std::make_unique<CTargetFind>(this));
217219
}

src/map/entities/charentity.h

+12
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ along with this program. If not, see http://www.gnu.org/licenses/
2929
#include <deque>
3030
#include <mutex>
3131
#include <bitset>
32+
#include <unordered_map>
3233

3334
#include "battleentity.h"
3435
#include "petentity.h"
@@ -141,6 +142,13 @@ struct GearSetMod_t
141142
uint16 modValue;
142143
};
143144

145+
enum CHAR_SUBSTATE
146+
{
147+
SUBSTATE_NONE = 0,
148+
SUBSTATE_IN_CS,
149+
SUBSTATE_LAST,
150+
};
151+
144152
/************************************************************************
145153
* *
146154
* *
@@ -310,13 +318,17 @@ class CCharEntity : public CBattleEntity
310318
bool m_EffectsChanged;
311319
time_point m_LastSynthTime;
312320

321+
CHAR_SUBSTATE m_Substate;
322+
313323
int16 addTP(int16 tp) override;
314324
int32 addHP(int32 hp) override;
315325
int32 addMP(int32 mp) override;
316326

317327
std::vector<GearSetMod_t> m_GearSetMods; // The list of gear set mods currently applied to the character.
318328
std::vector<AuctionHistory_t> m_ah_history; // AH history list (in the future consider using UContainer)
319329

330+
std::unordered_map<uint16, uint32> m_PacketRecievedTimestamps;
331+
320332
void SetPlayTime(uint32 playTime); // Set playtime
321333
uint32 GetPlayTime(bool needUpdate = true); // Get playtime
322334

src/map/lua/lua_baseentity.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,9 @@ inline int32 CLuaBaseEntity::startEvent(lua_State *L)
10651065
{
10661066
PChar->m_event.Option = (int32)lua_tointeger(L, 10);
10671067
}
1068+
1069+
PChar->m_Substate = CHAR_SUBSTATE::SUBSTATE_IN_CS;
1070+
10681071
return 0;
10691072
}
10701073

src/map/map.cpp

+31-5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ along with this program. If not, see http://www.gnu.org/licenses/
4545
#include "linkshell.h"
4646
#include "map.h"
4747
#include "mob_spell_list.h"
48+
#include "packet_guard.h"
4849
#include "packet_system.h"
4950
#include "party.h"
5051
#include "utils/petutils.h"
@@ -251,6 +252,8 @@ int32 do_init(int32 argc, char** argv)
251252
g_PBuff = new int8[map_config.buffer_size + 20];
252253
PTempBuff = new int8[map_config.buffer_size + 20];
253254

255+
PacketGuard::Init();
256+
254257
ShowStatus("The map-server is " CL_GREEN"ready" CL_RESET" to work...\n");
255258
ShowMessage("=======================================================================\n");
256259
return 0;
@@ -584,18 +587,35 @@ int32 parse(int8* buff, size_t* buffsize, sockaddr_in* from, map_session_data_t*
584587

585588
if (PacketSize[SmallPD_Type] == SmallPD_Size || PacketSize[SmallPD_Type] == 0) // Tests incoming packets for the correct size prior to processing
586589
{
587-
// если код текущего пакета меньше либо равен последнему полученному
588-
// или больше глобального то игнорируем пакет
590+
// Google Translate:
591+
// if the code of the current package is less than or equal to the last received
592+
// or more global then ignore the package
589593

590594
if ((ref<uint16>(SmallPD_ptr, 2) <= map_session_data->client_packet_id) ||
591595
(ref<uint16>(SmallPD_ptr, 2) > SmallPD_Code))
592596
{
593597
continue;
594598
}
599+
595600
if (SmallPD_Type != 0x15)
596601
{
597602
ShowInfo("parse: %03hX | %04hX %04hX %02hX from user: %s\n", SmallPD_Type, ref<uint16>(SmallPD_ptr, 2), ref<uint16>(buff, 2), SmallPD_Size, PChar->GetName());
598603
}
604+
605+
if (map_config.packetguard_enabled && PacketGuard::IsRateLimitedPacket(PChar, SmallPD_Type))
606+
{
607+
ShowError(CL_RED "Rate-limiting packet: Player: %s - Packet: %03hX\n" CL_RESET, PChar->GetName(), SmallPD_Type);
608+
continue; // skip this packet
609+
}
610+
611+
if (map_config.packetguard_enabled && !PacketGuard::PacketIsValidForPlayerState(PChar, SmallPD_Type))
612+
{
613+
// TODO: Log exploit
614+
ShowError(CL_RED "Caught mismatch between player substate and recieved packet: Player: %s - Packet: %03hX\n" CL_RESET, PChar->GetName(), SmallPD_Type);
615+
// TODO: Plug in optional jailutils usage
616+
continue; // skip this packet
617+
}
618+
599619
if (PChar->loc.zone == nullptr && SmallPD_Type != 0x0A)
600620
{
601621
ShowWarning("This packet is unexpected from %s - Received %03hX earlier without matching 0x0A\n", PChar->GetName(), SmallPD_Type);
@@ -612,8 +632,9 @@ int32 parse(int8* buff, size_t* buffsize, sockaddr_in* from, map_session_data_t*
612632
}
613633
map_session_data->client_packet_id = SmallPD_Code;
614634

615-
// здесь мы проверяем, получил ли клиент предыдущий пакет
616-
// если не получил, то мы не создаем новый, а отправляем предыдущий
635+
// Google Translate:
636+
// here we check if the client received the previous package
637+
// if not received, then we do not create a new one, but send the previous one
617638

618639
if (ref<uint16>(buff, 2) != map_session_data->server_packet_id)
619640
{
@@ -627,7 +648,7 @@ int32 parse(int8* buff, size_t* buffsize, sockaddr_in* from, map_session_data_t*
627648
return -1;
628649
}
629650

630-
// увеличиваем номер отправленного пакета только в случае отправки новых данных
651+
// GT: increase the number of the sent packet only if new data is sent
631652

632653
map_session_data->server_packet_id += 1;
633654

@@ -1000,6 +1021,7 @@ int32 map_config_default()
10001021
map_config.blood_pact_shared_timer = 0;
10011022
map_config.vanadiel_time_epoch = 0;
10021023
map_config.lightluggage_block = 4;
1024+
map_config.packetguard_enabled = false;
10031025
map_config.max_time_lastupdate = 60000;
10041026
map_config.newstyle_skillups = 7;
10051027
map_config.drop_rate_multiplier = 1.0f;
@@ -1101,6 +1123,10 @@ int32 map_config_read(const int8* cfgName)
11011123
{
11021124
map_config.lightluggage_block = atoi(w2);
11031125
}
1126+
else if (strcmp(w1, "packetguard_enabled") == 0)
1127+
{
1128+
map_config.packetguard_enabled = atoi(w2);
1129+
}
11041130
else if (strcmp(w1, "ah_base_fee_single") == 0)
11051131
{
11061132
map_config.ah_base_fee_single = atoi(w2);

src/map/map.h

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ struct map_config_t
7272
uint32 max_time_lastupdate; // max interval wait of last update player char
7373
int32 vanadiel_time_epoch; // current timestamp - vanadiel_time_epoch = vana'diel time
7474
int32 lightluggage_block; // если значение отлично от нуля, то персонажи с lightluggage будут удаляться с сервера автоматически
75+
bool packetguard_enabled; // Block and report any packets that aren't in the allow-list for a player's current state.
7576

7677
uint16 ah_base_fee_single; // Base AH fee for single items
7778
uint16 ah_base_fee_stacks; // Base AH fee for stacks

src/map/packet_guard.cpp

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include "packet_guard.h"
2+
3+
#include "entities/charentity.h"
4+
5+
#include <unordered_map> // Lookup
6+
#include <unordered_set> // Capture
7+
8+
// #define PACKETGUARD_CAP_ENABLED 1
9+
10+
namespace PacketGuard
11+
{
12+
#ifdef PACKETGUARD_CAP_ENABLED
13+
std::unordered_map<CHAR_SUBSTATE, std::unordered_set<uint16>> regular_client_packets;
14+
#endif
15+
16+
std::unordered_map<CHAR_SUBSTATE, std::unordered_map<uint16, bool>> allowList;
17+
std::unordered_map<uint16, uint32> ratelimitList; // Default will be 0 - No Limit
18+
19+
void Init()
20+
{
21+
// Allow all non-substate packets
22+
for (uint16 i = 0; i < 512; ++i)
23+
{
24+
allowList[SUBSTATE_NONE][i] = true;
25+
}
26+
27+
// In Cutscene
28+
allowList[SUBSTATE_IN_CS][0x015] = true; // Player Sync
29+
allowList[SUBSTATE_IN_CS][0x016] = true; // Entity Information Request
30+
allowList[SUBSTATE_IN_CS][0x01A] = true; // Player Action
31+
allowList[SUBSTATE_IN_CS][0x03A] = true; // Sort Inventory
32+
allowList[SUBSTATE_IN_CS][0x05B] = true; // Event Update (Completion or Update)
33+
allowList[SUBSTATE_IN_CS][0x05C] = true; // Event Update (Update Player Position)
34+
allowList[SUBSTATE_IN_CS][0x0B5] = true; // Chat Message
35+
allowList[SUBSTATE_IN_CS][0x0B6] = true; // Tell Message
36+
allowList[SUBSTATE_IN_CS][0x0F2] = true; // Update Player Zone Boundary
37+
allowList[SUBSTATE_IN_CS][0x114] = true; // Map Marker Request
38+
}
39+
40+
bool PacketIsValidForPlayerState(CCharEntity* PChar, uint16 SmallPD_Type)
41+
{
42+
#if PACKETGUARD_CAP_ENABLED == 1
43+
regular_client_packets[PChar->m_Substate].insert(SmallPD_Type);
44+
for (uint8 state = SUBSTATE_IN_CS; state < SUBSTATE_LAST; ++state)
45+
{
46+
fmt::print("Substate {}: ", state);
47+
for (auto& entry : regular_client_packets[(CHAR_SUBSTATE)state])
48+
{
49+
fmt::print("{:#04x}, ", entry);
50+
}
51+
fmt::print("\n");
52+
}
53+
return true;
54+
#endif
55+
56+
return allowList[PChar->m_Substate][SmallPD_Type];
57+
}
58+
59+
bool IsRateLimitedPacket(CCharEntity* PChar, uint16 SmallPD_Type)
60+
{
61+
uint32 lastPacketRecievedTime = PChar->m_PacketRecievedTimestamps[SmallPD_Type];
62+
uint32 timeNowSeconds = static_cast<uint32>(std::chrono::time_point_cast<std::chrono::seconds>(server_clock::now()).time_since_epoch().count());
63+
uint32 ratelimitTime = ratelimitList[SmallPD_Type];
64+
65+
PChar->m_PacketRecievedTimestamps[SmallPD_Type] = timeNowSeconds;
66+
67+
if (lastPacketRecievedTime == 0 || ratelimitTime == 0)
68+
{
69+
return false;
70+
}
71+
72+
return timeNowSeconds - lastPacketRecievedTime < ratelimitList[SmallPD_Type];
73+
}
74+
75+
} // namespace PacketGuard

src/map/packet_guard.h

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef _PACKETGUARD_H
2+
#define _PACKETGUARD_H
3+
4+
#include "../common/cbasetypes.h"
5+
#include "packet_system.h"
6+
#include "packets/basic.h"
7+
8+
class CCharEntity;
9+
10+
namespace PacketGuard
11+
{
12+
void Init();
13+
bool PacketIsValidForPlayerState(CCharEntity* PChar, uint16 SmallPD_Type);
14+
bool IsRateLimitedPacket(CCharEntity* PChar, uint16 SmallPD_Type);
15+
}
16+
17+
#endif // _PACKETGUARD_H

src/map/packet_system.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -2787,6 +2787,7 @@ void SmallPacket0x05B(map_session_data_t* session, CCharEntity* PChar, CBasicPac
27872787
//reset if this event did not initiate another event
27882788
if (PChar->m_event.EventID == EventID)
27892789
{
2790+
PChar->m_Substate = CHAR_SUBSTATE::SUBSTATE_NONE;
27902791
PChar->m_event.reset();
27912792
}
27922793
}
@@ -2824,6 +2825,7 @@ void SmallPacket0x05C(map_session_data_t* session, CCharEntity* PChar, CBasicPac
28242825
updatePosition = luautils::OnEventFinish(PChar, EventID, Result) == 1;
28252826
if (PChar->m_event.EventID == EventID)
28262827
{
2828+
PChar->m_Substate = CHAR_SUBSTATE::SUBSTATE_NONE;
28272829
PChar->m_event.reset();
28282830
}
28292831
}

src/map/packets/release.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ CReleasePacket::CReleasePacket(CCharEntity * PChar, RELEASE_TYPE releaseType)
3636
{
3737
ref<uint16>(0x05) = PChar->m_event.EventID;
3838
}
39+
40+
PChar->m_Substate = CHAR_SUBSTATE::SUBSTATE_NONE;
3941
}
4042

4143
// типы release

win32/vcxproj/topaz_game.vcxproj

+2
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@
433433
<ClInclude Include="..\..\src\map\packets\wide_scan_track.h" />
434434
<ClInclude Include="..\..\src\map\packets\zone_in.h" />
435435
<ClInclude Include="..\..\src\map\packets\zone_visited.h" />
436+
<ClInclude Include="..\..\src\map\packet_guard.h" />
436437
<ClInclude Include="..\..\src\map\packet_system.h" />
437438
<ClInclude Include="..\..\src\map\party.h" />
438439
<ClInclude Include="..\..\src\map\recast_container.h" />
@@ -689,6 +690,7 @@
689690
<ClCompile Include="..\..\src\map\packets\wide_scan_track.cpp" />
690691
<ClCompile Include="..\..\src\map\packets\zone_in.cpp" />
691692
<ClCompile Include="..\..\src\map\packets\zone_visited.cpp" />
693+
<ClCompile Include="..\..\src\map\packet_guard.cpp" />
692694
<ClCompile Include="..\..\src\map\packet_system.cpp" />
693695
<ClCompile Include="..\..\src\map\party.cpp" />
694696
<ClCompile Include="..\..\src\map\recast_container.cpp" />

win32/vcxproj/topaz_game.vcxproj.filters

+6
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@
116116
<ClInclude Include="..\..\src\map\mob_modifier.h">
117117
<Filter>Header Files</Filter>
118118
</ClInclude>
119+
<ClInclude Include="..\..\src\map\packet_guard.h">
120+
<Filter>Header Files</Filter>
121+
</ClInclude>
119122
<ClInclude Include="..\..\src\map\packet_system.h">
120123
<Filter>Header Files</Filter>
121124
</ClInclude>
@@ -931,6 +934,9 @@
931934
<ClCompile Include="..\..\src\map\modifier.cpp">
932935
<Filter>Source Files</Filter>
933936
</ClCompile>
937+
<ClCompile Include="..\..\src\map\packet_guard.cpp">
938+
<Filter>Source Files</Filter>
939+
</ClCompile>
934940
<ClCompile Include="..\..\src\map\packet_system.cpp">
935941
<Filter>Source Files</Filter>
936942
</ClCompile>

0 commit comments

Comments
 (0)