1st RC WebSocketProxy

This commit is contained in:
fbsanchez 2019-10-15 22:16:54 +02:00
parent 06f8f920b8
commit 4cceb38762
4 changed files with 300 additions and 124 deletions

View File

@ -1,5 +1,45 @@
<?php
/**
* PHP WebSocketServer Proxy.
*
* Adapted to PandoraFMS by Fco de Borja Sanchez <fborja.sanchez@artica.es>
* Compatible with PHP >= 7.0
*
* @category External library
* @package Pandora FMS
* @subpackage WebSocketServer
* @version 1.0.0
* @license See below
* @filesource https://github.com/ghedipunk/PHP-Websockets
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of PHP WebSockets nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
// Begin.
require_once __DIR__.'/WebSocketServer.php';
require_once __DIR__.'/WSProxyUser.php';
@ -48,6 +88,13 @@ class WSProxy extends WebSocketServer
*/
protected $interative = false;
/**
* Use a timeout of 0.1 second to search for messages..
*
* @var integer
*/
public $timeout = 0.1;
/**
* Builder.
@ -80,9 +127,9 @@ class WSProxy extends WebSocketServer
/**
* Read from socket
* Read from user's socket.
*
* @param socket $socket Target connection.
* @param object $user Target user connection.
*
* @return string Buffer.
*/
@ -98,7 +145,13 @@ class WSProxy extends WebSocketServer
);
if ($numBytes === false) {
// Failed. Disconnect.
$this->handleSocketError($user->socket);
return false;
} else if ($numBytes == 0) {
$this->disconnect($user->socket);
$this->stderr(
'Client disconnected. TCP connection lost: '.$user->socket
);
return false;
}
@ -110,20 +163,21 @@ class WSProxy extends WebSocketServer
/**
* Write to socket.
*
* @param socket $socket Target socket.
* @param object $user Target user connection.
* @param string $message Target message to be sent.
*
* @return void
*/
protected function writeSocket($user, $message)
{
if ($user->socket) {
if (is_resource($user->socket)) {
if (!socket_write($user->socket, $message)) {
$this->disconnect($user->socket);
}
} else {
// Failed. Disconnect.
// Failed. Disconnect all.
$this->disconnect($user->socket);
$this->disconnect($user->redirect->socket);
}
}
@ -185,19 +239,20 @@ class WSProxy extends WebSocketServer
*/
protected function connected($user)
{
echo '** CONNECTED'."\n";
// Disconnect previous sessions.
$this->cleanupSocketByCookie($headers['cookie']);
$this->cleanupSocketByCookie($user);
/*
* $user->intSocket is connected to internal.
* $user->socket is connected to external.
*/
var_dump($user);
// Create a new socket connection (internal).
$intUser = $this->connectInt($this->rawHeaders);
if ($intUser === null) {
$this->disconnect($user);
return;
}
// Map user.
$user->intUser = $intUser;
@ -206,8 +261,12 @@ class WSProxy extends WebSocketServer
$user->redirect = $intUser;
$intUser->redirect = $user;
$response = $this->readSocket($intUser);
// Keep an eye on changes.
$this->remoteSockets[$intUser->id] = $intUser->socket;
$this->remoteUsers[$intUser->id] = $intUser;
// Ignore. Cleanup socket.
$response = $this->readSocket($user->intUser);
}
@ -234,37 +293,25 @@ class WSProxy extends WebSocketServer
*/
protected function processRaw($user, $buffer)
{
unset($user->intUser->lastRawPacket);
unset($user->lastRawPacket);
echo "\n****************** REDIRECT *********************\n";
echo date('D M j G:i:s').' - '.$user->id.' >> '.$user->intUser->id."\n";
echo $this->dump($buffer);
$this->writeSocket(
$user->intUser,
$buffer
);
echo date('D M j G:i:s').' - '.$user->intUser->id.'] << '.$user->id."\n";
$response = $this->readSocket($user->intUser);
$this->writeSocket($user, $user->intUser->lastRawPacket);
echo $this->dump($user->intUser->lastRawPacket);
$this->stdout(date('D M j G:i:s').' - '.$user->id.' >> '.$user->redirect->id);
$this->stdout($this->dump($buffer));
$this->writeSocket($user->redirect, $buffer);
return true;
}
/**
* From client to socket (internal);
* Process user message. Implement.
*
* @param object $user Caller.
* @param object $user User.
* @param string $message Message.
*
* @return void
*/
protected function process($user, $message)
{
echo 'Procesando..'."\n";
}
@ -277,22 +324,8 @@ class WSProxy extends WebSocketServer
*/
protected function closed($user)
{
$response = 'GET '.$this->intUrl." HTTP/1.1\r\n";
$response .= 'Host: '.$this->intHost."\r\n";
$response .= "Connection: Close\r\n\r\n";
$this->writeSocket($user->intUser, $response);
$response = $this->readSocket($user->intUser);
$this->disconnect($user->intSocket);
$this->disconnect($user->socket);
}
public function out($ss, $str)
{
echo $ss."\n";
echo $this->dump($str);
$this->disconnect($user);
$this->disconnect($user->redirect);
}

View File

@ -1,5 +1,45 @@
<?php
/**
* PHP WebSocketUser Proxy.
*
* Adapted to PandoraFMS by Fco de Borja Sanchez <fborja.sanchez@artica.es>
* Compatible with PHP >= 7.0
*
* @category External library
* @package Pandora FMS
* @subpackage WebSocketServer
* @version 1.0.0
* @license See below
* @filesource https://github.com/ghedipunk/PHP-Websockets
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of PHP WebSockets nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
// Begin.
require_once __DIR__.'/WebSocketServer.php';
require_once __DIR__.'/WebSocketUser.php';

View File

@ -1,6 +1,6 @@
<?php
/**
* PHP WebocketServer from:
* PHP WebSocketServer from:
*
* Copyright (c) 2012, Adam Alexander
* All rights reserved.
@ -83,19 +83,33 @@ abstract class WebSocketServer
protected $master;
/**
* Undocumented variable
* Incoming sockets.
*
* @var array
*/
protected $sockets = [];
/**
* Undocumented variable
* Outgoing sockets.
*
* @var array
*/
protected $remoteSockets = [];
/**
* Client list.
*
* @var array
*/
protected $users = [];
/**
* Servers list.
*
* @var array
*/
protected $remoteUsers = [];
/**
* Undocumented variable
*
@ -138,6 +152,13 @@ abstract class WebSocketServer
*/
public $rawHeaders = [];
/**
* Use a timeout of 1 second to search for messages..
*
* @var integer
*/
public $timeout = 1;
/**
* Builder.
@ -309,6 +330,75 @@ abstract class WebSocketServer
}
/**
* Manage behaviour on socket error.
*
* @param socket $socket Target socket.
*
* @return void
*/
public function handleSocketError($socket)
{
$sockErrNo = socket_last_error($socket);
switch ($sockErrNo) {
case 102:
// ENETRESET
// Network dropped connection because of reset.
case 103:
// ECONNABORTED
// Software caused connection abort.
case 104:
// ECONNRESET
// Connection reset by peer.
case 108:
// ESHUTDOWN
// Cannot send after transport endpoint shutdown
// Probably more of an error on our side,
// if we're trying to write after the socket is
// closed. Probably not a critical error,
// though.
case 110:
// ETIMEDOUT
// Connection timed out.
case 111:
// ECONNREFUSED
// Connection refused
// We shouldn't see this one, since we're
// listening... Still not a critical error.
case 112:
// EHOSTDOWN
// Host is down.
// Again, we shouldn't see this, and again,
// not critical because it's just one connection
// and we still want to listen to/for others.
case 113:
// EHOSTUNREACH
// No route to host.
case 121:
// EREMOTEIO
// Rempte I/O error
// Their hard drive just blew up.
case 125:
// ECANCELED
// Operation canceled.
$this->stderr(
'Unusual disconnect on socket '.$socket
);
// Disconnect before clearing error, in case
// someone with their own implementation wants
// to check for error conditions on the socket.
$this->disconnect($socket, true, $sockErrNo);
break;
default:
$this->stderr(
'Socket error: '.socket_strerror($sockErrNo)
);
break;
}
}
/**
* Main processing loop
*
@ -326,9 +416,10 @@ abstract class WebSocketServer
$write = null;
$this->pTick();
$this->tick();
socket_select($read, $write, $except, 1);
socket_select($read, $write, $except, $this->timeout);
foreach ($read as $socket) {
if ($socket == $this->master) {
// External to master connection. New client.
$client = socket_accept($socket);
if ($client < 0) {
$this->stderr('Failed: socket_accept()');
@ -338,6 +429,7 @@ abstract class WebSocketServer
$this->stdout('Client connected. '.$client);
}
} else {
// Updates on 'read' socket.
$numBytes = socket_recv(
$socket,
$buffer,
@ -345,63 +437,7 @@ abstract class WebSocketServer
0
);
if ($numBytes === false) {
$sockErrNo = socket_last_error($socket);
switch ($sockErrNo) {
case 102:
// ENETRESET
// Network dropped connection because of reset.
case 103:
// ECONNABORTED
// Software caused connection abort.
case 104:
// ECONNRESET
// Connection reset by peer.
case 108:
// ESHUTDOWN
// Cannot send after transport endpoint shutdown
// Probably more of an error on our side,
// if we're trying to write after the socket is
// closed. Probably not a critical error,
// though.
case 110:
// ETIMEDOUT
// Connection timed out.
case 111:
// ECONNREFUSED
// Connection refused
// We shouldn't see this one, since we're
// listening... Still not a critical error.
case 112:
// EHOSTDOWN
// Host is down.
// Again, we shouldn't see this, and again,
// not critical because it's just one connection
// and we still want to listen to/for others.
case 113:
// EHOSTUNREACH
// No route to host.
case 121:
// EREMOTEIO
// Rempte I/O error
// Their hard drive just blew up.
case 125:
// ECANCELED
// Operation canceled.
$this->stderr(
'Unusual disconnect on socket '.$socket
);
// Disconnect before clearing error, in case
// someone with their own implementation wants
// to check for error conditions on the socket.
$this->disconnect($socket, true, $sockErrNo);
break;
default:
$this->stderr(
'Socket error: '.socket_strerror($sockErrNo)
);
break;
}
$this->handleSocketError($socket);
} else if ($numBytes == 0) {
$this->disconnect($socket);
$this->stderr(
@ -422,12 +458,60 @@ abstract class WebSocketServer
} else {
if (!$this->processRaw($user, $buffer)) {
// Split packet into frame and send it to deframe.
$this->splitPacket($numBytes, $buffer, $user);
$this->splitPacket(
$numBytes,
$buffer,
$user
);
}
}
}
}
}
// Remote updates.
$remotes = $this->remoteSockets;
if (count($remotes) > 0) {
socket_select($remotes, $write, $except, $this->timeout);
foreach ($remotes as $socket) {
// Remote updates - internal. We're client of this sockets.
if (!$socket) {
continue;
}
$numBytes = socket_recv(
$socket,
$buffer,
$this->maxBufferSize,
0
);
if ($numBytes === false) {
$this->handleSocketError($socket);
} else if ($numBytes == 0) {
$this->disconnect($socket);
$this->stderr(
'Client disconnected. TCP connection lost: '.$socket
);
} else {
$user = $this->getUserBySocket($socket);
echo '>>>>> EEH, recibo:['.$user->id."]\n";
echo $this->dump($buffer);
if (!$user) {
$this->disconnect($socket);
$this->stderr(
'User was not connected: '.$socket
);
} else if (!$this->processRaw($user, $buffer)) {
// Split packet into frame and send it to deframe.
$this->splitPacket(
$numBytes,
$buffer,
$user
);
}
}
}
}
}
}
@ -462,13 +546,22 @@ abstract class WebSocketServer
bool $triggerClosed=true,
$sockErrNo=null
) {
$disconnectedUser = $this->getUserBySocket($socket);
$user = $this->getUserBySocket($socket);
if ($user !== null) {
if (array_key_exists($user->id, $this->users)) {
unset($this->users[$user->id]);
}
if ($disconnectedUser !== null) {
unset($this->users[$disconnectedUser->id]);
if (array_key_exists($user->id, $this->remoteUsers)) {
unset($this->remoteUsers[$user->id]);
}
if (array_key_exists($disconnectedUser->id, $this->sockets)) {
unset($this->sockets[$disconnectedUser->id]);
if (array_key_exists($user->id, $this->sockets)) {
unset($this->sockets[$user->id]);
}
if (array_key_exists($user->id, $this->remoteSockets)) {
unset($this->remoteSockets[$user->id]);
}
if ($sockErrNo !== null) {
@ -476,15 +569,15 @@ abstract class WebSocketServer
}
if ($triggerClosed) {
$this->closed($disconnectedUser);
$this->closed($user);
$this->stdout(
'Client disconnected. '.$disconnectedUser->socket
'Client disconnected. '.$user->socket
);
socket_close($disconnectedUser->socket);
socket_close($user->socket);
} else {
$message = $this->frame('', $disconnectedUser, 'close');
$message = $this->frame('', $user, 'close');
socket_write(
$disconnectedUser->socket,
$user->socket,
$message,
strlen($message)
);
@ -741,22 +834,31 @@ abstract class WebSocketServer
}
}
foreach ($this->remoteUsers as $user) {
if ($user->socket == $socket) {
return $user;
}
}
return null;
}
/**
* Disconnects all users matching target cookie but latest one.
* Disconnects all users matching target cookie but given one.
*
* @param string $cookie Cookie identifier.
* @param object $user Latest user.
*
* @return void
*/
protected function cleanupSocketByCookie($cookie)
protected function cleanupSocketByCookie($user)
{
foreach ($this->users as $user) {
if ($user->headers['cookie'] == $cookie) {
$this->disconnectUser($user->socket);
$cookie = $user->headers['cookie'];
foreach ($this->users as $u) {
if ($u->id != $user->id
&& $u->headers['cookie'] == $cookie
) {
$this->disconnect($u->socket);
}
}
@ -1379,7 +1481,8 @@ abstract class WebSocketServer
// Join the hexi / ascii output.
$dump .= sprintf('%04x %-49s %s', $offset, $hexi, $ascii);
// Reset vars.
$hexi = $ascii = '';
$hexi = '';
$ascii = '';
$offset += 16;
$j = 0;
// Add newline.

View File

@ -6,7 +6,7 @@ error_reporting(E_ALL);
require_once __DIR__.'/WSProxy.php';
$wsproxy = new WSProxy('0.0.0.0', '8081', '127.0.0.1', '8080');
$wsproxy = new WSProxy('0.0.0.0', '8081', '127.0.0.1', '8080', '/ws', 1048576, true);
try {
echo "Server running \n";