summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMadCamel <madcamel@gmail.com>2010-04-04 13:48:37 -0400
committerMadCamel <madcamel@gmail.com>2010-04-04 13:48:37 -0400
commit30728fa5dca858af79038878d597ffd24cb1fc26 (patch)
treeed2c7e54c6b70128a9d57ac03b837b32996dc9ed
parent6d2fb0bf050c95a6ea1dbed09c7faef05b77253f (diff)
downloadtmwa-30728fa5dca858af79038878d597ffd24cb1fc26.tar.gz
tmwa-30728fa5dca858af79038878d597ffd24cb1fc26.tar.bz2
tmwa-30728fa5dca858af79038878d597ffd24cb1fc26.tar.xz
tmwa-30728fa5dca858af79038878d597ffd24cb1fc26.zip
Added packet rate limiter and flood prevention system.
The idea behind this is to keep abusive clients from flooding the server. Everything from over-voracious auto pick-up to exploitation of NPC vulnerabilities is mitigated by this. It will also eventually reduce server traffic, load, and possibly lag. Config options in battle_athena.conf: packet_spam_flood (default 30) packet_spam_threshold (default 2) packet_spam_kick (default 1) Each packet type has a specified allowed incoming rate in milliseconds, if this rate is exceeded the packet is ignored. If over 'flood' overruns in 'threshold' seconds are detected, a flood is logged and a kick may be triggered depending on the 'kick' config boolean. The packet rates and flood thresholds have been tested and tweaked on a live server, this should only affect abusive clients. Only extremely abusive clients will trigger a log/kick condition. This probably obsoletes the trade and kick spam code.
-rw-r--r--src/map/battle.c28
-rw-r--r--src/map/battle.h4
-rw-r--r--src/map/clif.c762
-rw-r--r--src/map/map.h4
-rw-r--r--src/map/pc.c3
5 files changed, 671 insertions, 130 deletions
diff --git a/src/map/battle.c b/src/map/battle.c
index 579ea55..1cee690 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -5662,6 +5662,11 @@ int battle_config_read (const char *cfgName)
battle_config.sit_spam_flood = 15;
battle_config.sit_spam_ban = 0;
battle_config.sit_spam_warn = 3;
+
+ battle_config.packet_spam_threshold = 2;
+ battle_config.packet_spam_flood = 30;
+ battle_config.packet_spam_kick = 1;
+
}
fp = fopen_ (cfgName, "r");
@@ -6122,7 +6127,13 @@ int battle_config_read (const char *cfgName)
{
"sit_spam_ban", &battle_config.sit_spam_ban},
{
- "sit_spam_warn", &battle_config.sit_spam_warn}
+ "sit_spam_warn", &battle_config.sit_spam_warn},
+ {
+ "packet_spam_threshold", &battle_config.packet_spam_threshold},
+ {
+ "packet_spam_flood", &battle_config.packet_spam_flood},
+ {
+ "packet_spam_kick", &battle_config.packet_spam_kick}
};
if (line[0] == '/' && line[1] == '/')
@@ -6304,6 +6315,21 @@ int battle_config_read (const char *cfgName)
else if (battle_config.sit_spam_threshold > 32767)
battle_config.sit_spam_threshold = 32767;
+ if (battle_config.packet_spam_threshold < 0)
+ battle_config.packet_spam_threshold = 0;
+ else if (battle_config.packet_spam_threshold > 32767)
+ battle_config.packet_spam_threshold = 32767;
+
+ if (battle_config.packet_spam_flood < 0)
+ battle_config.packet_spam_flood = 0;
+ else if (battle_config.packet_spam_flood > 32767)
+ battle_config.packet_spam_flood = 32767;
+
+ if (battle_config.packet_spam_kick < 0)
+ battle_config.packet_spam_kick = 0;
+ else if (battle_config.packet_spam_kick > 1)
+ battle_config.packet_spam_kick = 1;
+
// at least 1 client must be accepted
if ((battle_config.packet_ver_flag & 63) == 0) // added by [Yor]
battle_config.packet_ver_flag = 63; // accept all clients
diff --git a/src/map/battle.h b/src/map/battle.h
index 4270b5b..9862df6 100644
--- a/src/map/battle.h
+++ b/src/map/battle.h
@@ -350,6 +350,10 @@ extern struct Battle_Config
int sit_spam_ban;
int sit_spam_warn;
+ int packet_spam_threshold;
+ int packet_spam_flood;
+ int packet_spam_kick;
+
int drop_pickup_safety_zone; // [Fate] Max. distance to an object dropped by a kill by self in which dropsteal protection works
int itemheal_regeneration_factor; // [Fate] itemheal speed factor
diff --git a/src/map/clif.c b/src/map/clif.c
index 0593b95..61410e9 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -9171,133 +9171,630 @@ void clif_parse_sn_explosionspirits (int fd, struct map_session_data *sd)
return;
}
-// functions list
-static void (*clif_parse_func_table[0x220]) () =
-{
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- // 40
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL,
- // 70
- NULL, NULL, clif_parse_WantToConnection, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, clif_parse_LoadEndAck,
- clif_parse_TickSend, NULL,
- // 80
- NULL, NULL, NULL, NULL, NULL, clif_parse_WalkToXY, NULL, NULL,
- NULL, clif_parse_ActionRequest, NULL, NULL, clif_parse_GlobalMessage,
- NULL, NULL, NULL,
- // 90
- clif_parse_NpcClicked, NULL, NULL, NULL,
- clif_parse_GetCharNameRequest, NULL, clif_parse_Wis, NULL, NULL,
- clif_parse_GMmessage, NULL, clif_parse_ChangeDir, NULL, NULL, NULL,
- clif_parse_TakeItem,
- // a0
- NULL, NULL, clif_parse_DropItem, NULL, NULL, NULL, NULL,
- clif_parse_UseItem, NULL, clif_parse_EquipItem, NULL,
- clif_parse_UnequipItem, NULL, NULL, NULL, NULL,
- // b0
- NULL, NULL, clif_parse_Restart, NULL, NULL, NULL, NULL, NULL,
- clif_parse_NpcSelectMenu, clif_parse_NpcNextClicked, NULL,
- clif_parse_StatusUp, NULL, NULL, NULL, clif_parse_Emotion,
- // c0
- NULL, clif_parse_HowManyConnections, NULL, NULL, NULL,
- clif_parse_NpcBuySellSelected, NULL, NULL, clif_parse_NpcBuyListSend,
- clif_parse_NpcSellListSend, NULL, NULL, clif_parse_GMKick, NULL, NULL,
- clif_parse_PMIgnore,
- // d0
- clif_parse_PMIgnoreAll, NULL, NULL, NULL, NULL,
- clif_parse_CreateChatRoom, NULL, NULL, NULL, clif_parse_ChatAddMember,
- NULL, NULL, NULL, NULL, clif_parse_ChatRoomStatusChange, NULL,
- // e0
- clif_parse_ChangeChatOwner, NULL, clif_parse_KickFromChat,
- clif_parse_ChatLeave, clif_parse_TradeRequest, NULL,
- clif_parse_TradeAck, NULL, clif_parse_TradeAddItem, NULL, NULL,
- clif_parse_TradeOk, NULL, clif_parse_TradeCansel, NULL,
- clif_parse_TradeCommit,
- // f0
- NULL, NULL, NULL, clif_parse_MoveToKafra, NULL,
- clif_parse_MoveFromKafra, NULL, clif_parse_CloseKafra, NULL,
- clif_parse_CreateParty, NULL, NULL, clif_parse_PartyInvite, NULL,
- NULL, clif_parse_ReplyPartyInvite,
- // 100
- clif_parse_LeaveParty, NULL, clif_parse_PartyChangeOption,
- clif_parse_RemovePartyMember, NULL, NULL, NULL, NULL,
- clif_parse_PartyMessage, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- // 110
- NULL, NULL, clif_parse_SkillUp, clif_parse_UseSkillToId, NULL, NULL,
- clif_parse_UseSkillToPos, NULL, clif_parse_StopAttack, NULL, NULL,
- clif_parse_UseSkillMap, NULL, clif_parse_RequestMemo, NULL, NULL,
- // 120
- NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_PutItemToCart,
- clif_parse_GetItemFromCart, clif_parse_MoveFromKafraToCart,
- clif_parse_MoveToKafraFromCart, clif_parse_RemoveOption, NULL, NULL,
- NULL, NULL, NULL,
- // 130
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_GM_Monster_Item,
- // 140
- clif_parse_MapMove, NULL, NULL, clif_parse_NpcAmountInput, NULL, NULL,
- clif_parse_NpcCloseClicked, NULL, NULL, clif_parse_GMReqNoChat, NULL,
- NULL, NULL, clif_parse_GuildCheckMaster, NULL,
- clif_parse_GuildReqeustInfo,
- // 150
- NULL, clif_parse_GuildRequestEmblem, NULL,
- clif_parse_GuildChangeEmblem, NULL,
- clif_parse_GuildChangeMemberPosition, NULL, NULL, NULL,
- clif_parse_GuildLeave, NULL, clif_parse_GuildExplusion, NULL,
- clif_parse_GuildBreak, NULL, NULL,
- // 160
- NULL, clif_parse_GuildChangePositionInfo, NULL, NULL, NULL,
- clif_parse_CreateGuild, NULL, NULL, clif_parse_GuildInvite, NULL,
- NULL, clif_parse_GuildReplyInvite, NULL, NULL,
- clif_parse_GuildChangeNotice, NULL,
- // 170
- clif_parse_GuildRequestAlliance, NULL, clif_parse_GuildReplyAlliance,
- NULL, NULL, NULL, NULL, NULL, clif_parse_ItemIdentify, NULL,
- clif_parse_UseCard, NULL, clif_parse_InsertCard, NULL,
- clif_parse_GuildMessage, NULL,
- // 180
- clif_parse_GuildOpposition, NULL, NULL, clif_parse_GuildDelAlliance,
- NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_QuitGame, NULL, NULL,
- NULL, NULL, NULL,
- // 190
- clif_parse_UseSkillToPos, NULL, NULL, clif_parse_SolveCharName, NULL,
- NULL, NULL, clif_parse_ResetChar, NULL, NULL, NULL, NULL,
- clif_parse_LGMmessage, clif_parse_GMHide, NULL, NULL,
- // 1a0
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_ChangeCart,
- // 1b0
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, clif_parse_Shift, clif_parse_Shift, clif_parse_Recall,
- clif_parse_Recall, NULL, NULL,
- // 1c0
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_AutoSpell, NULL,
- // 1d0
- NULL, NULL, NULL, NULL, NULL, clif_parse_NpcStringInput, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_GMReqNoChatCount,
- // 1e0
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, clif_parse_sn_doridori,
- clif_parse_CreateParty2, NULL, NULL, NULL, NULL,
- clif_parse_sn_explosionspirits, NULL, NULL,
- // 1f0
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL,
- // 200
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL,
- // 210
-NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL};
+// functions list. Rate is how many milliseconds are required between
+// calls. Packets exceeding this rate will be dropped. flood_rates in
+// map.h must be the same length as this table. rate 0 is default
+// rate -1 is unlimited
+typedef struct func_table
+{
+ void (*func)();
+ int rate;
+} func_table;
+// *INDENT-OFF*
+func_table clif_parse_func_table[0x220] =
+{
+ { NULL, 0 }, // 0
+ { NULL, 0 }, // 1
+ { NULL, 0 }, // 2
+ { NULL, 0 }, // 3
+ { NULL, 0 }, // 4
+ { NULL, 0 }, // 5
+ { NULL, 0 }, // 6
+ { NULL, 0 }, // 7
+ { NULL, 0 }, // 8
+ { NULL, 0 }, // 9
+ { NULL, 0 }, // a
+ { NULL, 0 }, // b
+ { NULL, 0 }, // c
+ { NULL, 0 }, // d
+ { NULL, 0 }, // e
+ { NULL, 0 }, // f
+ { NULL, 0 }, // 10
+ { NULL, 0 }, // 11
+ { NULL, 0 }, // 12
+ { NULL, 0 }, // 13
+ { NULL, 0 }, // 14
+ { NULL, 0 }, // 15
+ { NULL, 0 }, // 16
+ { NULL, 0 }, // 17
+ { NULL, 0 }, // 18
+ { NULL, 0 }, // 19
+ { NULL, 0 }, // 1a
+ { NULL, 0 }, // 1b
+ { NULL, 0 }, // 1c
+ { NULL, 0 }, // 1d
+ { NULL, 0 }, // 1e
+ { NULL, 0 }, // 1f
+ { NULL, 0 }, // 20
+ { NULL, 0 }, // 21
+ { NULL, 0 }, // 22
+ { NULL, 0 }, // 23
+ { NULL, 0 }, // 24
+ { NULL, 0 }, // 25
+ { NULL, 0 }, // 26
+ { NULL, 0 }, // 27
+ { NULL, 0 }, // 28
+ { NULL, 0 }, // 29
+ { NULL, 0 }, // 2a
+ { NULL, 0 }, // 2b
+ { NULL, 0 }, // 2c
+ { NULL, 0 }, // 2d
+ { NULL, 0 }, // 2e
+ { NULL, 0 }, // 2f
+ { NULL, 0 }, // 30
+ { NULL, 0 }, // 31
+ { NULL, 0 }, // 32
+ { NULL, 0 }, // 33
+ { NULL, 0 }, // 34
+ { NULL, 0 }, // 35
+ { NULL, 0 }, // 36
+ { NULL, 0 }, // 37
+ { NULL, 0 }, // 38
+ { NULL, 0 }, // 39
+ { NULL, 0 }, // 3a
+ { NULL, 0 }, // 3b
+ { NULL, 0 }, // 3c
+ { NULL, 0 }, // 3d
+ { NULL, 0 }, // 3e
+ { NULL, 0 }, // 3f
+ { NULL, 0 }, // 40
+ { NULL, 0 }, // 41
+ { NULL, 0 }, // 42
+ { NULL, 0 }, // 43
+ { NULL, 0 }, // 44
+ { NULL, 0 }, // 45
+ { NULL, 0 }, // 46
+ { NULL, 0 }, // 47
+ { NULL, 0 }, // 48
+ { NULL, 0 }, // 49
+ { NULL, 0 }, // 4a
+ { NULL, 0 }, // 4b
+ { NULL, 0 }, // 4c
+ { NULL, 0 }, // 4d
+ { NULL, 0 }, // 4e
+ { NULL, 0 }, // 4f
+ { NULL, 0 }, // 50
+ { NULL, 0 }, // 51
+ { NULL, 0 }, // 52
+ { NULL, 0 }, // 53
+ { NULL, 0 }, // 54
+ { NULL, 0 }, // 55
+ { NULL, 0 }, // 56
+ { NULL, 0 }, // 57
+ { NULL, 0 }, // 58
+ { NULL, 0 }, // 59
+ { NULL, 0 }, // 5a
+ { NULL, 0 }, // 5b
+ { NULL, 0 }, // 5c
+ { NULL, 0 }, // 5d
+ { NULL, 0 }, // 5e
+ { NULL, 0 }, // 5f
+ { NULL, 0 }, // 60
+ { NULL, 0 }, // 61
+ { NULL, 0 }, // 62
+ { NULL, 0 }, // 63
+ { NULL, 0 }, // 64
+ { NULL, 0 }, // 65
+ { NULL, 0 }, // 66
+ { NULL, 0 }, // 67
+ { NULL, 0 }, // 68
+ { NULL, 0 }, // 69
+ { NULL, 0 }, // 6a
+ { NULL, 0 }, // 6b
+ { NULL, 0 }, // 6c
+ { NULL, 0 }, // 6d
+ { NULL, 0 }, // 6e
+ { NULL, 0 }, // 6f
+ { NULL, 0 }, // 70
+ { NULL, 0 }, // 71
+ { clif_parse_WantToConnection, 0 }, // 72
+ { NULL, 0 }, // 73
+ { NULL, 0 }, // 74
+ { NULL, 0 }, // 75
+ { NULL, 0 }, // 76
+ { NULL, 0 }, // 77
+ { NULL, 0 }, // 78
+ { NULL, 0 }, // 79
+ { NULL, 0 }, // 7a
+ { NULL, 0 }, // 7b
+ { NULL, 0 }, // 7c
+ { clif_parse_LoadEndAck, -1 }, // 7d
+ { clif_parse_TickSend, 0 }, // 7e
+ { NULL, 0 }, // 7f
+ { NULL, 0 }, // 80
+ { NULL, 0 }, // 81
+ { NULL, 0 }, // 82
+ { NULL, 0 }, // 83
+ { NULL, 0 }, // 84
+ { clif_parse_WalkToXY, -1 }, // 85 Walk code limits this on it's own
+ { NULL, 0 }, // 86
+ { NULL, 0 }, // 87
+ { NULL, 0 }, // 88
+ { clif_parse_ActionRequest, 1000 }, // 89 Special case - see below
+ { NULL, 0 }, // 8a
+ { NULL, 0 }, // 8b
+ { clif_parse_GlobalMessage, 300 }, // 8c
+ { NULL, 0 }, // 8d
+ { NULL, 0 }, // 8e
+ { NULL, 0 }, // 8f
+ { clif_parse_NpcClicked, 500 }, // 90
+ { NULL, 0 }, // 91
+ { NULL, 0 }, // 92
+ { NULL, 0 }, // 93
+ { clif_parse_GetCharNameRequest, -1 }, // 94
+ { NULL, 0 }, // 95
+ { clif_parse_Wis, 300 }, // 96
+ { NULL, 0 }, // 97
+ { NULL, 0 }, // 98
+ { clif_parse_GMmessage, 300 }, // 99
+ { NULL, 0 }, // 9a
+ { clif_parse_ChangeDir, -1 }, // 9b .29 clients spam this packet when walking into a blocked tile
+ { NULL, 0 }, // 9c
+ { NULL, 0 }, // 9d
+ { NULL, 0 }, // 9e
+ { clif_parse_TakeItem, 400 }, // 9f
+ { NULL, 0 }, // a0
+ { NULL, 0 }, // a1
+ { clif_parse_DropItem, 200 }, // a2
+ { NULL, 0 }, // a3
+ { NULL, 0 }, // a4
+ { NULL, 0 }, // a5
+ { NULL, 0 }, // a6
+ { clif_parse_UseItem, 0 }, // a7
+ { NULL, 0 }, // a8
+ { clif_parse_EquipItem, -1 }, // a9 Special case - outfit window (not implemented yet - needs to allow bursts)
+ { NULL, 0 }, // aa
+ { clif_parse_UnequipItem, -1 }, // ab Special case - outfit window (not implemented yet - needs to allow bursts)
+ { NULL, 0 }, // ac
+ { NULL, 0 }, // ad
+ { NULL, 0 }, // ae
+ { NULL, 0 }, // af
+ { NULL, 0 }, // b0
+ { NULL, 0 }, // b1
+ { clif_parse_Restart, 0 }, // b2
+ { NULL, 0 }, // b3
+ { NULL, 0 }, // b4
+ { NULL, 0 }, // b5
+ { NULL, 0 }, // b6
+ { NULL, 0 }, // b7
+ { clif_parse_NpcSelectMenu, 300 }, // b8
+ { clif_parse_NpcNextClicked, 300 }, // b9
+ { NULL, 0 }, // ba
+ { clif_parse_StatusUp, -1 }, // bb People click this very quickly
+ { NULL, 0 }, // bc
+ { NULL, 0 }, // bd
+ { NULL, 0 }, // be
+ { clif_parse_Emotion, 1000 }, // bf
+ { NULL, 0 }, // c0
+ { clif_parse_HowManyConnections, 0 }, // c1
+ { NULL, 0 }, // c2
+ { NULL, 0 }, // c3
+ { NULL, 0 }, // c4
+ { clif_parse_NpcBuySellSelected, 300 }, // c5
+ { NULL, 0 }, // c6
+ { NULL, 0 }, // c7
+ { clif_parse_NpcBuyListSend, 300 }, // c8
+ { clif_parse_NpcSellListSend, -1 }, // c9 Selling multiple 1-slot items
+ { NULL, 0 }, // ca
+ { NULL, 0 }, // cb
+ { clif_parse_GMKick, 0 }, // cc
+ { NULL, 0 }, // cd
+ { NULL, 0 }, // ce
+ { clif_parse_PMIgnore, 0 }, // cf
+ { clif_parse_PMIgnoreAll, 0 }, // d0
+ { NULL, 0 }, // d1
+ { NULL, 0 }, // d2
+ { NULL, 0 }, // d3
+ { NULL, 0 }, // d4
+ { clif_parse_CreateChatRoom, 1000 }, // d5
+ { NULL, 0 }, // d6
+ { NULL, 0 }, // d7
+ { NULL, 0 }, // d8
+ { clif_parse_ChatAddMember, 0 }, // d9
+ { NULL, 0 }, // da
+ { NULL, 0 }, // db
+ { NULL, 0 }, // dc
+ { NULL, 0 }, // dd
+ { clif_parse_ChatRoomStatusChange, 0 }, // de
+ { NULL, 0 }, // df
+ { clif_parse_ChangeChatOwner, 0 }, // e0
+ { NULL, 0 }, // e1
+ { clif_parse_KickFromChat, 0 }, // e2
+ { clif_parse_ChatLeave, 0 }, // e3
+ { clif_parse_TradeRequest, 2000 }, // e4
+ { NULL, 0 }, // e5
+ { clif_parse_TradeAck, 0 }, // e6
+ { NULL, 0 }, // e7
+ { clif_parse_TradeAddItem, 0 }, // e8
+ { NULL, 0 }, // e9
+ { NULL, 0 }, // ea
+ { clif_parse_TradeOk, 0 }, // eb
+ { NULL, 0 }, // ec
+ { clif_parse_TradeCansel, 0 }, // ed
+ { NULL, 0 }, // ee
+ { clif_parse_TradeCommit, 0 }, // ef
+ { NULL, 0 }, // f0
+ { NULL, 0 }, // f1
+ { NULL, 0 }, // f2
+ { clif_parse_MoveToKafra, -1 }, // f3
+ { NULL, 0 }, // f4
+ { clif_parse_MoveFromKafra, -1 }, // f5
+ { NULL, 0 }, // f6
+ { clif_parse_CloseKafra, 0 }, // f7
+ { NULL, 0 }, // f8
+ { clif_parse_CreateParty, 2000 }, // f9
+ { NULL, 0 }, // fa
+ { NULL, 0 }, // fb
+ { clif_parse_PartyInvite, 2000 }, // fc
+ { NULL, 0 }, // fd
+ { NULL, 0 }, // fe
+ { clif_parse_ReplyPartyInvite, 0 }, // ff
+ { clif_parse_LeaveParty, 0 }, // 100
+ { NULL, 0 }, // 101
+ { clif_parse_PartyChangeOption, 0 }, // 102
+ { clif_parse_RemovePartyMember, 0 }, // 103
+ { NULL, 0 }, // 104
+ { NULL, 0 }, // 105
+ { NULL, 0 }, // 106
+ { NULL, 0 }, // 107
+ { clif_parse_PartyMessage, 300 }, // 108
+ { NULL, 0 }, // 109
+ { NULL, 0 }, // 10a
+ { NULL, 0 }, // 10b
+ { NULL, 0 }, // 10c
+ { NULL, 0 }, // 10d
+ { NULL, 0 }, // 10e
+ { NULL, 0 }, // 10f
+ { NULL, 0 }, // 110
+ { NULL, 0 }, // 111
+ { clif_parse_SkillUp, -1 }, // 112
+ { clif_parse_UseSkillToId, 0 }, // 113
+ { NULL, 0 }, // 114
+ { NULL, 0 }, // 115
+ { clif_parse_UseSkillToPos, 0 }, // 116
+ { NULL, 0 }, // 117
+ { clif_parse_StopAttack, 0 }, // 118
+ { NULL, 0 }, // 119
+ { NULL, 0 }, // 11a
+ { clif_parse_UseSkillMap, 0 }, // 11b
+ { NULL, 0 }, // 11c
+ { clif_parse_RequestMemo, 0 }, // 11d
+ { NULL, 0 }, // 11e
+ { NULL, 0 }, // 11f
+ { NULL, 0 }, // 120
+ { NULL, 0 }, // 121
+ { NULL, 0 }, // 122
+ { NULL, 0 }, // 123
+ { NULL, 0 }, // 124
+ { NULL, 0 }, // 125
+ { clif_parse_PutItemToCart, 0 }, // 126
+ { clif_parse_GetItemFromCart, 0 }, // 127
+ { clif_parse_MoveFromKafraToCart, 0 }, // 128
+ { clif_parse_MoveToKafraFromCart, 0 }, // 129
+ { clif_parse_RemoveOption, 0 }, // 12a
+ { NULL, 0 }, // 12b
+ { NULL, 0 }, // 12c
+ { NULL, 0 }, // 12d
+ { NULL, 0 }, // 12e
+ { NULL, 0 }, // 12f
+ { NULL, 0 }, // 130
+ { NULL, 0 }, // 131
+ { NULL, 0 }, // 132
+ { NULL, 0 }, // 133
+ { NULL, 0 }, // 134
+ { NULL, 0 }, // 135
+ { NULL, 0 }, // 136
+ { NULL, 0 }, // 137
+ { NULL, 0 }, // 138
+ { NULL, 0 }, // 139
+ { NULL, 0 }, // 13a
+ { NULL, 0 }, // 13b
+ { NULL, 0 }, // 13c
+ { NULL, 0 }, // 13d
+ { NULL, 0 }, // 13e
+ { clif_parse_GM_Monster_Item, 0 }, // 13f
+ { clif_parse_MapMove, 0 }, // 140
+ { NULL, 0 }, // 141
+ { NULL, 0 }, // 142
+ { clif_parse_NpcAmountInput, 300 }, // 143
+ { NULL, 0 }, // 144
+ { NULL, 0 }, // 145
+ { clif_parse_NpcCloseClicked, 300 }, // 146
+ { NULL, 0 }, // 147
+ { NULL, 0 }, // 148
+ { clif_parse_GMReqNoChat, 0 }, // 149
+ { NULL, 0 }, // 14a
+ { NULL, 0 }, // 14b
+ { NULL, 0 }, // 14c
+ { clif_parse_GuildCheckMaster, 0 }, // 14d
+ { NULL, 0 }, // 14e
+ { clif_parse_GuildReqeustInfo, 0 }, // 14f
+ { NULL, 0 }, // 150
+ { clif_parse_GuildRequestEmblem, 0 }, // 151
+ { NULL, 0 }, // 152
+ { clif_parse_GuildChangeEmblem, 0 }, // 153
+ { NULL, 0 }, // 154
+ { clif_parse_GuildChangeMemberPosition, 0 }, // 155
+ { NULL, 0 }, // 156
+ { NULL, 0 }, // 157
+ { NULL, 0 }, // 158
+ { clif_parse_GuildLeave, 0 }, // 159
+ { NULL, 0 }, // 15a
+ { clif_parse_GuildExplusion, 0 }, // 15b
+ { NULL, 0 }, // 15c
+ { clif_parse_GuildBreak, 0 }, // 15d
+ { NULL, 0 }, // 15e
+ { NULL, 0 }, // 15f
+ { NULL, 0 }, // 160
+ { clif_parse_GuildChangePositionInfo, 0 }, // 161
+ { NULL, 0 }, // 162
+ { NULL, 0 }, // 163
+ { NULL, 0 }, // 164
+ { clif_parse_CreateGuild, 0 }, // 165
+ { NULL, 0 }, // 166
+ { NULL, 0 }, // 167
+ { clif_parse_GuildInvite, 2000 }, // 168
+ { NULL, 0 }, // 169
+ { NULL, 0 }, // 16a
+ { clif_parse_GuildReplyInvite, 0 }, // 16b
+ { NULL, 0 }, // 16c
+ { NULL, 0 }, // 16d
+ { clif_parse_GuildChangeNotice, 0 }, // 16e
+ { NULL, 0 }, // 16f
+ { clif_parse_GuildRequestAlliance, 0 }, // 170
+ { NULL, 0 }, // 171
+ { clif_parse_GuildReplyAlliance, 0 }, // 172
+ { NULL, 0 }, // 173
+ { NULL, 0 }, // 174
+ { NULL, 0 }, // 175
+ { NULL, 0 }, // 176
+ { NULL, 0 }, // 177
+ { clif_parse_ItemIdentify, 0 }, // 178
+ { NULL, 0 }, // 179
+ { clif_parse_UseCard, 0 }, // 17a
+ { NULL, 0 }, // 17b
+ { clif_parse_InsertCard, 0 }, // 17c
+ { NULL, 0 }, // 17d
+ { clif_parse_GuildMessage, 300 }, // 17e
+ { NULL, 0 }, // 17f
+ { clif_parse_GuildOpposition, 0 }, // 180
+ { NULL, 0 }, // 181
+ { NULL, 0 }, // 182
+ { clif_parse_GuildDelAlliance, 0 }, // 183
+ { NULL, 0 }, // 184
+ { NULL, 0 }, // 185
+ { NULL, 0 }, // 186
+ { NULL, 0 }, // 187
+ { NULL, 0 }, // 188
+ { NULL, 0 }, // 189
+ { clif_parse_QuitGame, 0 }, // 18a
+ { NULL, 0 }, // 18b
+ { NULL, 0 }, // 18c
+ { NULL, 0 }, // 18d
+ { NULL, 0 }, // 18e
+ { NULL, 0 }, // 18f
+ { clif_parse_UseSkillToPos, 0 }, // 190
+ { NULL, 0 }, // 191
+ { NULL, 0 }, // 192
+ { clif_parse_SolveCharName, 0 }, // 193
+ { NULL, 0 }, // 194
+ { NULL, 0 }, // 195
+ { NULL, 0 }, // 196
+ { clif_parse_ResetChar, 0 }, // 197
+ { NULL, 0 }, // 198
+ { NULL, 0 }, // 199
+ { NULL, 0 }, // 19a
+ { NULL, 0 }, // 19b
+ { clif_parse_LGMmessage, 0 }, // 19c
+ { clif_parse_GMHide, 300 }, // 19d
+ { NULL, 0 }, // 19e
+ { NULL, 0 }, // 19f
+ { NULL, 0 }, // 1a0
+ { NULL, 0 }, // 1a1
+ { NULL, 0 }, // 1a2
+ { NULL, 0 }, // 1a3
+ { NULL, 0 }, // 1a4
+ { NULL, 0 }, // 1a5
+ { NULL, 0 }, // 1a6
+ { NULL, 0 }, // 1a7
+ { NULL, 0 }, // 1a8
+ { NULL, 0 }, // 1a9
+ { NULL, 0 }, // 1aa
+ { NULL, 0 }, // 1ab
+ { NULL, 0 }, // 1ac
+ { NULL, 0 }, // 1ad
+ { NULL, 0 }, // 1ae
+ { clif_parse_ChangeCart, 0 }, // 1af
+ { NULL, 0 }, // 1b0
+ { NULL, 0 }, // 1b1
+ { NULL, 0 }, // 1b2
+ { NULL, 0 }, // 1b3
+ { NULL, 0 }, // 1b4
+ { NULL, 0 }, // 1b5
+ { NULL, 0 }, // 1b6
+ { NULL, 0 }, // 1b7
+ { NULL, 0 }, // 1b8
+ { NULL, 0 }, // 1b9
+ { clif_parse_Shift, 300 }, // 1ba
+ { clif_parse_Shift, 300 }, // 1bb
+ { clif_parse_Recall, 300 }, // 1bc
+ { clif_parse_Recall, 300 }, // 1bd
+ { NULL, 0 }, // 1be
+ { NULL, 0 }, // 1bf
+ { NULL, 0 }, // 1c0
+ { NULL, 0 }, // 1c1
+ { NULL, 0 }, // 1c2
+ { NULL, 0 }, // 1c3
+ { NULL, 0 }, // 1c4
+ { NULL, 0 }, // 1c5
+ { NULL, 0 }, // 1c6
+ { NULL, 0 }, // 1c7
+ { NULL, 0 }, // 1c8
+ { NULL, 0 }, // 1c9
+ { NULL, 0 }, // 1ca
+ { NULL, 0 }, // 1cb
+ { NULL, 0 }, // 1cc
+ { NULL, 0 }, // 1cd
+ { clif_parse_AutoSpell, 0 }, // 1ce
+ { NULL, 0 }, // 1cf
+ { NULL, 0 }, // 1d0
+ { NULL, 0 }, // 1d1
+ { NULL, 0 }, // 1d2
+ { NULL, 0 }, // 1d3
+ { NULL, 0 }, // 1d4
+ { clif_parse_NpcStringInput, 300 }, // 1d5
+ { NULL, 0 }, // 1d6
+ { NULL, 0 }, // 1d7
+ { NULL, 0 }, // 1d8
+ { NULL, 0 }, // 1d9
+ { NULL, 0 }, // 1da
+ { NULL, 0 }, // 1db
+ { NULL, 0 }, // 1dc
+ { NULL, 0 }, // 1dd
+ { NULL, 0 }, // 1de
+ { clif_parse_GMReqNoChatCount, 0 }, // 1df
+ { NULL, 0 }, // 1e0
+ { NULL, 0 }, // 1e1
+ { NULL, 0 }, // 1e2
+ { NULL, 0 }, // 1e3
+ { NULL, 0 }, // 1e4
+ { NULL, 0 }, // 1e5
+ { NULL, 0 }, // 1e6
+ { clif_parse_sn_doridori, 0 }, // 1e7
+ { clif_parse_CreateParty2, 1000 }, // 1e8
+ { NULL, 0 }, // 1e9
+ { NULL, 0 }, // 1ea
+ { NULL, 0 }, // 1eb
+ { NULL, 0 }, // 1ec
+ { clif_parse_sn_explosionspirits, 0 }, // 1ed
+ { NULL, 0 }, // 1ee
+ { NULL, 0 }, // 1ef
+ { NULL, 0 }, // 1f0
+ { NULL, 0 }, // 1f1
+ { NULL, 0 }, // 1f2
+ { NULL, 0 }, // 1f3
+ { NULL, 0 }, // 1f4
+ { NULL, 0 }, // 1f5
+ { NULL, 0 }, // 1f6
+ { NULL, 0 }, // 1f7
+ { NULL, 0 }, // 1f8
+ { NULL, 0 }, // 1f9
+ { NULL, 0 }, // 1fa
+ { NULL, 0 }, // 1fb
+ { NULL, 0 }, // 1fc
+ { NULL, 0 }, // 1fd
+ { NULL, 0 }, // 1fe
+ { NULL, 0 }, // 1ff
+ { NULL, 0 }, // 200
+ { NULL, 0 }, // 201
+ { NULL, 0 }, // 202
+ { NULL, 0 }, // 203
+ { NULL, 0 }, // 204
+ { NULL, 0 }, // 205
+ { NULL, 0 }, // 206
+ { NULL, 0 }, // 207
+ { NULL, 0 }, // 208
+ { NULL, 0 }, // 209
+ { NULL, 0 }, // 20a
+ { NULL, 0 }, // 20b
+ { NULL, 0 }, // 20c
+ { NULL, 0 }, // 20d
+ { NULL, 0 }, // 20e
+ { NULL, 0 }, // 20f
+ { NULL, 0 }, // 210
+ { NULL, 0 }, // 211
+ { NULL, 0 }, // 212
+ { NULL, 0 }, // 213
+ { NULL, 0 }, // 214
+ { NULL, 0 }, // 215
+ { NULL, 0 }, // 216
+ { NULL, 0 }, // 217
+ { NULL, 0 }, // 218
+ { NULL, 0 }, // 219
+ { NULL, 0 }, // 21a
+ { NULL, 0 }, // 21b
+ { NULL, 0 }, // 21c
+ { NULL, 0 }, // 21d
+ { NULL, 0 }, // 21e
+ { NULL, 0 }, // 21f
+};
+// *INDENT-ON*
+
+// Checks for packet flooding
+int clif_check_packet_flood(fd, cmd)
+{
+ struct map_session_data *sd = session[fd]->session_data;
+ unsigned int rate, tick = gettick();
+
+ // sd will not be set if the client hasn't requested
+ // WantToConnection yet. Do not apply flood logic to GMs
+ // as approved bots (GMlvl1) should not have to work around
+ // flood logic.
+ if (!sd || pc_isGM(sd) || clif_parse_func_table[cmd].rate == -1)
+ return 0;
+
+ // Timer has wrapped
+ if (tick < sd->flood_rates[cmd])
+ {
+ sd->flood_rates[cmd] = tick;
+ return 0;
+ }
+
+ // Default rate is 100ms
+ if ((rate = clif_parse_func_table[cmd].rate) == 0)
+ rate = 100;
+
+ // ActionRequest - attacks are allowed a faster rate than sit/stand
+ if (cmd == 0x89)
+ {
+ int action_type = RFIFOB (fd, 6);
+ if (action_type == 0x00 || action_type == 0x07)
+ rate = 20;
+ else
+ rate = 1000;
+ }
+
+ // They are flooding
+ if (tick < sd->flood_rates[cmd] + rate)
+ {
+ time_t now = time(NULL);
+
+ // If it's a nasty flood we log and possibly kick
+ if (now > sd->packet_flood_reset_due)
+ {
+ sd->packet_flood_reset_due = now + battle_config.packet_spam_threshold;
+ sd->packet_flood_in = 0;
+ }
+
+ sd->packet_flood_in++;
+
+ if (sd->packet_flood_in >= battle_config.packet_spam_flood)
+ {
+ printf("packet flood detected from %s [0x%x]\n", sd->status.name, cmd);
+ if (battle_config.packet_spam_kick)
+ {
+ session[fd]->eof = 1; // Kick
+ return 1;
+ }
+ sd->packet_flood_in = 0;
+ }
+
+ return 1;
+ }
+
+ sd->flood_rates[cmd] = tick;
+ return 0;
+}
/*==========================================
* �N���C�A���g�����̃p�P�b�g����
@@ -9403,9 +9900,16 @@ static int clif_parse (int fd)
{ // �ؒf�҂��̏ꍇ�p�P�b�g������Ȃ�
}
- else if (clif_parse_func_table[cmd])
+ else if (clif_parse_func_table[cmd].func)
{
- clif_parse_func_table[cmd] (fd, sd);
+ if (clif_check_packet_flood(fd, cmd))
+ {
+ // Flood triggered. Skip packet.
+ RFIFOSKIP(sd->fd, packet_len);
+ return 0;
+ }
+
+ clif_parse_func_table[cmd].func (fd, sd);
}
else
{
diff --git a/src/map/map.h b/src/map/map.h
index 2ba31b1..936513b 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -402,6 +402,10 @@ struct map_session_data
time_t sit_reset_due;
int sits_in;
+
+ unsigned int flood_rates[0x220];
+ time_t packet_flood_reset_due;
+ int packet_flood_in;
};
struct npc_timerevent_list
diff --git a/src/map/pc.c b/src/map/pc.c
index 8d1066d..fd30066 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -961,6 +961,9 @@ int pc_authok (int id, int login_id2, time_t connect_until_time,
sd->trade_reset_due = sd->trades_in = 0;
sd->sit_reset_due = sd->sits_in = 0;
+ memset(sd->flood_rates, 0, sizeof(sd->flood_rates));
+ sd->packet_flood_reset_due = sd->packet_flood_in = 0;
+
// message of the limited time of the account
if (connect_until_time != 0)
{ // don't display if it's unlimited or unknow value