1818
1919use pocketmine \utils \BinaryDataException ;
2020use raklib \generic \DisconnectReason ;
21+ use raklib \generic \PacketHandlingException ;
2122use raklib \generic \Session ;
2223use raklib \generic \SocketException ;
23- use raklib \generic \PacketHandlingException ;
2424use raklib \protocol \ACK ;
2525use raklib \protocol \Datagram ;
2626use raklib \protocol \EncapsulatedPacket ;
3030use raklib \utils \ExceptionTraceCleaner ;
3131use raklib \utils \InternetAddress ;
3232use function asort ;
33+ use function assert ;
3334use function bin2hex ;
3435use function count ;
3536use function get_class ;
@@ -46,6 +47,8 @@ class Server implements ServerInterface{
4647
4748 private const RAKLIB_TPS = 100 ;
4849 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 ;
4952
5053 protected int $ receiveBytes = 0 ;
5154 protected int $ sendBytes = 0 ;
@@ -70,6 +73,10 @@ class Server implements ServerInterface{
7073 /** @var int[] string (address) => int (number of packets) */
7174 protected array $ ipSec = [];
7275
76+ private int $ blockedSinceLastUpdate = 0 ;
77+
78+ private int $ packetErrorsSinceLastUpdate = 0 ;
79+
7380 /** @var string[] regex filters used to block out unwanted raw packets */
7481 protected array $ rawPacketFilters = [];
7582
@@ -91,7 +98,10 @@ public function __construct(
9198 private ServerEventListener $ eventListener ,
9299 private ExceptionTraceCleaner $ traceCleaner ,
93100 private int $ recvMaxSplitParts = ServerSession::DEFAULT_MAX_SPLIT_PART_COUNT ,
94- 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 ,
104+ private bool $ blockIpOnPacketErrors = true
95105 ){
96106 if ($ maxMtuSize < Session::MIN_MTU_SIZE ){
97107 throw new \InvalidArgumentException ("MTU size must be at least " . Session::MIN_MTU_SIZE . ", got $ maxMtuSize " );
@@ -182,6 +192,18 @@ private function tick() : void{
182192 $ this ->receiveBytes = 0 ;
183193 }
184194
195+ $ packetErrorsWithoutMessage = $ this ->packetErrorsSinceLastUpdate - $ this ->packetErrorSuppressionThreshold ;
196+ if ($ packetErrorsWithoutMessage > 0 ){
197+ $ this ->logger ->warning ("$ packetErrorsWithoutMessage suppressed packet errors - RakLib may be under attack " );
198+ }
199+ $ this ->packetErrorsSinceLastUpdate = 0 ;
200+
201+ $ ipsBlockedWithoutMessage = $ this ->blockedSinceLastUpdate - $ this ->blockMessageSuppressionThreshold ;
202+ if ($ ipsBlockedWithoutMessage > 0 ){
203+ $ this ->logger ->warning ("$ ipsBlockedWithoutMessage more IP addresses were blocked - RakLib may be under attack " );
204+ }
205+ $ this ->blockedSinceLastUpdate = 0 ;
206+
185207 if (count ($ this ->block ) > 0 ){
186208 asort ($ this ->block );
187209 $ now = time ();
@@ -214,6 +236,9 @@ private function receivePacket() : bool{
214236 if ($ buffer === null ){
215237 return false ; //no data
216238 }
239+ assert ($ addressIp !== null , "Can't be null if we got a buffer " );
240+ assert ($ addressPort !== null , "Can't be null if we got a buffer " );
241+
217242 $ len = strlen ($ buffer );
218243
219244 $ this ->receiveBytes += $ len ;
@@ -279,20 +304,25 @@ private function receivePacket() : bool{
279304 }
280305 }
281306 }catch (BinaryDataException $ e ){
282- $ logFn = function () use ($ address , $ e , $ buffer ) : void {
283- $ this ->logger ->debug ("Packet from $ address ( " . strlen ($ buffer ) . " bytes): 0x " . bin2hex ($ buffer ));
284- $ this ->logger ->debug (get_class ($ e ) . ": " . $ e ->getMessage () . " in " . $ e ->getFile () . " on line " . $ e ->getLine ());
285- foreach ($ this ->traceCleaner ->getTrace (0 , $ e ->getTrace ()) as $ line ){
286- $ this ->logger ->debug ($ line );
307+ if ($ this ->packetErrorsSinceLastUpdate < $ this ->packetErrorSuppressionThreshold ){
308+ $ logFn = function () use ($ address , $ e , $ buffer ) : void {
309+ $ this ->logger ->debug ("Packet from $ address ( " . strlen ($ buffer ) . " bytes): 0x " . bin2hex ($ buffer ));
310+ $ this ->logger ->debug (get_class ($ e ) . ": " . $ e ->getMessage () . " in " . $ e ->getFile () . " on line " . $ e ->getLine ());
311+ foreach ($ this ->traceCleaner ->getTrace (0 , $ e ->getTrace ()) as $ line ){
312+ $ this ->logger ->debug ($ line );
313+ }
314+ $ this ->logger ->error ("Bad packet from $ address: " . $ e ->getMessage ());
315+ };
316+ if ($ this ->logger instanceof \BufferedLogger){
317+ $ this ->logger ->buffer ($ logFn );
318+ }else {
319+ $ logFn ();
287320 }
288- $ this ->logger ->error ("Bad packet from $ address: " . $ e ->getMessage ());
289- };
290- if ($ this ->logger instanceof \BufferedLogger){
291- $ this ->logger ->buffer ($ logFn );
292- }else {
293- $ logFn ();
294321 }
295- $ this ->blockAddress ($ address ->getIp (), 5 );
322+ $ this ->packetErrorsSinceLastUpdate ++;
323+ if ($ this ->blockIpOnPacketErrors ){
324+ $ this ->blockAddress ($ address ->getIp (), 5 );
325+ }
296326 }
297327
298328 return true ;
@@ -350,10 +380,14 @@ public function blockAddress(string $address, int $timeout = 300) : void{
350380 if (!isset ($ this ->block [$ address ]) or $ timeout === -1 ){
351381 if ($ timeout === -1 ){
352382 $ final = PHP_INT_MAX ;
353- }else {
354- $ this ->logger ->notice ("Blocked $ address for $ timeout seconds " );
383+ }
384+ if ($ this ->blockedSinceLastUpdate < $ this ->blockMessageSuppressionThreshold ){
385+ //Suppress additional log messages if multiple IPs have been banned in quick succession
386+ //In the case of IP spoofing attacks we don't want log spam to slow down the server
387+ $ this ->logger ->notice ("Blocked $ address " . ($ timeout === -1 ? " forever " : " for $ timeout seconds " ));
355388 }
356389 $ this ->block [$ address ] = $ final ;
390+ $ this ->blockedSinceLastUpdate ++;
357391 }elseif ($ this ->block [$ address ] < $ final ){
358392 $ this ->block [$ address ] = $ final ;
359393 }
0 commit comments