From dc56886d4192a9243058fe65e7c9ba1c0dc2d5f5 Mon Sep 17 00:00:00 2001 From: frytimo Date: Tue, 5 Aug 2025 12:05:48 -0300 Subject: [PATCH] Fix websocket service disconnect on invalid handshake (#7447) --- .../classes/invalid_handshake_exception.php | 38 +++++++++++++++++++ .../resources/classes/socket_exception.php | 2 +- .../resources/classes/websocket_server.php | 2 +- .../resources/classes/websocket_service.php | 27 ++++++++----- 4 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 core/websockets/resources/classes/invalid_handshake_exception.php diff --git a/core/websockets/resources/classes/invalid_handshake_exception.php b/core/websockets/resources/classes/invalid_handshake_exception.php new file mode 100644 index 0000000000..dfb7a9feda --- /dev/null +++ b/core/websockets/resources/classes/invalid_handshake_exception.php @@ -0,0 +1,38 @@ + + * Portions created by the Initial Developer are Copyright (C) 2008-2025 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark J Crane + * Tim Fry + */ + +/** + * Description of invalid_handshake_exception + * + * @author Tim Fry + */ +class invalid_handshake_exception extends \socket_exception { + public function __construct($id = null, string $message = "Invalid handshake", int $code = 0, ?\Throwable $previous = null) { + return parent::__construct($id, $message, $code, $previous); + } +} diff --git a/core/websockets/resources/classes/socket_exception.php b/core/websockets/resources/classes/socket_exception.php index a53657cb55..46d05860b3 100644 --- a/core/websockets/resources/classes/socket_exception.php +++ b/core/websockets/resources/classes/socket_exception.php @@ -37,5 +37,5 @@ class socket_exception extends \Exception { $this->id = $id; return parent::__construct($message, $code, $previous); } - public function getResourceId() { return $this->resource_id; } + public function getResourceId() { return $this->id; } } diff --git a/core/websockets/resources/classes/websocket_server.php b/core/websockets/resources/classes/websocket_server.php index a1fb8b5090..56ee28c6b5 100644 --- a/core/websockets/resources/classes/websocket_server.php +++ b/core/websockets/resources/classes/websocket_server.php @@ -345,7 +345,7 @@ class websocket_server { } } if (!preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $request_header, $matches)) { - throw new \RuntimeException("Invalid WebSocket handshake"); + throw new \invalid_handshake_exception($socket, "Invalid WebSocket handshake"); } $key = trim($matches[1]); $accept_key = base64_encode( diff --git a/core/websockets/resources/classes/websocket_service.php b/core/websockets/resources/classes/websocket_service.php index 1989a9218a..2abe24635b 100644 --- a/core/websockets/resources/classes/websocket_service.php +++ b/core/websockets/resources/classes/websocket_service.php @@ -528,15 +528,22 @@ class websocket_service extends service { if ($client_socket === $this->server_socket) { $conn = @stream_socket_accept($this->server_socket, 0); if ($conn) { - // complete handshake on blocking socket - stream_set_blocking($conn, true); - $this->handshake($conn); - // switch to non-blocking for further reads - stream_set_blocking($conn, false); - // add them to the websocket list - $this->clients[] = $conn; - // notify websocket on_connect listeners - $this->trigger_connect($conn); + try { + // complete handshake on blocking socket + stream_set_blocking($conn, true); + $this->handshake($conn); + // switch to non-blocking for further reads + stream_set_blocking($conn, false); + // add them to the websocket list + $this->clients[] = $conn; + // notify websocket on_connect listeners + $this->trigger_connect($conn); + } catch (invalid_handshake_exception $ex) { + $resource = $ex->getResourceId(); + $this->warning('Invalid handshake from resource ' . $resource); + $this->disconnect_client($resource); + $this->warning('Disconnected resource ' . $resource); + } continue; } } @@ -756,7 +763,7 @@ class websocket_service extends service { } } if (!preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $request_header, $matches)) { - throw new \RuntimeException("Invalid WebSocket handshake"); + throw new invalid_handshake_exception($resource, "Invalid WebSocket handshake"); } $key = trim($matches[1]); $accept_key = base64_encode(