@@ -47,6 +47,8 @@ class Server implements ServerInterface{
4747
4848 private const RAKLIB_TPS = 100 ;
4949 private const RAKLIB_TIME_PER_TICK = 1 / self ::RAKLIB_TPS ;
50+ private const BLOCK_MESSAGE_SUPPRESSION_THRESHOLD = 2 ;
51+ private const PACKET_ERROR_SUPPRESSION_THRESHOLD = 2 ;
5052
5153 protected int $ receiveBytes = 0 ;
5254 protected int $ sendBytes = 0 ;
@@ -71,6 +73,10 @@ class Server implements ServerInterface{
7173 /** @var int[] string (address) => int (number of packets) */
7274 protected array $ ipSec = [];
7375
76+ private int $ blockedSinceLastUpdate = 0 ;
77+
78+ private int $ packetErrorsSinceLastUpdate = 0 ;
79+
7480 /** @var string[] regex filters used to block out unwanted raw packets */
7581 protected array $ rawPacketFilters = [];
7682
@@ -92,7 +98,9 @@ public function __construct(
9298 private ServerEventListener $ eventListener ,
9399 private ExceptionTraceCleaner $ traceCleaner ,
94100 private int $ recvMaxSplitParts = ServerSession::DEFAULT_MAX_SPLIT_PART_COUNT ,
95- private int $ recvMaxConcurrentSplits = ServerSession::DEFAULT_MAX_CONCURRENT_SPLIT_COUNT
101+ private int $ recvMaxConcurrentSplits = ServerSession::DEFAULT_MAX_CONCURRENT_SPLIT_COUNT ,
102+ private int $ blockMessageSuppressionThreshold = self ::BLOCK_MESSAGE_SUPPRESSION_THRESHOLD ,
103+ private int $ packetErrorSuppressionThreshold = self ::PACKET_ERROR_SUPPRESSION_THRESHOLD
96104 ){
97105 if ($ maxMtuSize < Session::MIN_MTU_SIZE ){
98106 throw new \InvalidArgumentException ("MTU size must be at least " . Session::MIN_MTU_SIZE . ", got $ maxMtuSize " );
@@ -183,6 +191,18 @@ private function tick() : void{
183191 $ this ->receiveBytes = 0 ;
184192 }
185193
194+ $ packetErrorsWithoutMessage = $ this ->packetErrorsSinceLastUpdate - $ this ->packetErrorSuppressionThreshold ;
195+ if ($ packetErrorsWithoutMessage > 0 ){
196+ $ this ->logger ->warning ("$ packetErrorsWithoutMessage suppressed packet errors - RakLib may be under attack " );
197+ }
198+ $ this ->packetErrorsSinceLastUpdate = 0 ;
199+
200+ $ ipsBlockedWithoutMessage = $ this ->blockedSinceLastUpdate - $ this ->blockMessageSuppressionThreshold ;
201+ if ($ ipsBlockedWithoutMessage > 0 ){
202+ $ this ->logger ->warning ("$ ipsBlockedWithoutMessage more IP addresses were blocked - RakLib may be under attack " );
203+ }
204+ $ this ->blockedSinceLastUpdate = 0 ;
205+
186206 if (count ($ this ->block ) > 0 ){
187207 asort ($ this ->block );
188208 $ now = time ();
@@ -283,19 +303,22 @@ private function receivePacket() : bool{
283303 }
284304 }
285305 }catch (BinaryDataException $ e ){
286- $ logFn = function () use ($ address , $ e , $ buffer ) : void {
287- $ this ->logger ->debug ("Packet from $ address ( " . strlen ($ buffer ) . " bytes): 0x " . bin2hex ($ buffer ));
288- $ this ->logger ->debug (get_class ($ e ) . ": " . $ e ->getMessage () . " in " . $ e ->getFile () . " on line " . $ e ->getLine ());
289- foreach ($ this ->traceCleaner ->getTrace (0 , $ e ->getTrace ()) as $ line ){
290- $ this ->logger ->debug ($ line );
306+ if ($ this ->packetErrorsSinceLastUpdate < $ this ->packetErrorSuppressionThreshold ){
307+ $ logFn = function () use ($ address , $ e , $ buffer ) : void {
308+ $ this ->logger ->debug ("Packet from $ address ( " . strlen ($ buffer ) . " bytes): 0x " . bin2hex ($ buffer ));
309+ $ this ->logger ->debug (get_class ($ e ) . ": " . $ e ->getMessage () . " in " . $ e ->getFile () . " on line " . $ e ->getLine ());
310+ foreach ($ this ->traceCleaner ->getTrace (0 , $ e ->getTrace ()) as $ line ){
311+ $ this ->logger ->debug ($ line );
312+ }
313+ $ this ->logger ->error ("Bad packet from $ address: " . $ e ->getMessage ());
314+ };
315+ if ($ this ->logger instanceof \BufferedLogger){
316+ $ this ->logger ->buffer ($ logFn );
317+ }else {
318+ $ logFn ();
291319 }
292- $ this ->logger ->error ("Bad packet from $ address: " . $ e ->getMessage ());
293- };
294- if ($ this ->logger instanceof \BufferedLogger){
295- $ this ->logger ->buffer ($ logFn );
296- }else {
297- $ logFn ();
298320 }
321+ $ this ->packetErrorsSinceLastUpdate ++;
299322 $ this ->blockAddress ($ address ->getIp (), 5 );
300323 }
301324
@@ -354,10 +377,14 @@ public function blockAddress(string $address, int $timeout = 300) : void{
354377 if (!isset ($ this ->block [$ address ]) or $ timeout === -1 ){
355378 if ($ timeout === -1 ){
356379 $ final = PHP_INT_MAX ;
357- }else {
358- $ this ->logger ->notice ("Blocked $ address for $ timeout seconds " );
380+ }
381+ if ($ this ->blockedSinceLastUpdate < $ this ->blockMessageSuppressionThreshold ){
382+ //Suppress additional log messages if multiple IPs have been banned in quick succession
383+ //In the case of IP spoofing attacks we don't want log spam to slow down the server
384+ $ this ->logger ->notice ("Blocked $ address " . ($ timeout === -1 ? " forever " : " for $ timeout seconds " ));
359385 }
360386 $ this ->block [$ address ] = $ final ;
387+ $ this ->blockedSinceLastUpdate ++;
361388 }elseif ($ this ->block [$ address ] < $ final ){
362389 $ this ->block [$ address ] = $ final ;
363390 }
0 commit comments