mirror of
https://github.com/pandorafms/pandorafms.git
synced 2025-07-31 01:35:36 +02:00
wip: gotty redirection experiment p2
This commit is contained in:
parent
86170b3ad8
commit
3a9e5fa477
277
pandora_console/extensions/quick_shell/WSProxy.php
Normal file
277
pandora_console/extensions/quick_shell/WSProxy.php
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__.'/WebSocketServer.php';
|
||||||
|
require_once __DIR__.'/WSProxyUser.php';
|
||||||
|
|
||||||
|
use \PandoraFMS\WebSocketServer;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirects ws communication between two endpoints.
|
||||||
|
*/
|
||||||
|
class WSProxy extends WebSocketServer
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target host.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $intHost = '127.0.0.1';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Target port
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
private $intPort = 8080;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal URL.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $intUrl = '/ws';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1MB... overkill for an echo server, but potentially plausible for other
|
||||||
|
* applications.
|
||||||
|
*
|
||||||
|
* @var integer
|
||||||
|
*/
|
||||||
|
protected $maxBufferSize = 1048576;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interactive mode.
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $interative = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder.
|
||||||
|
*
|
||||||
|
* @param string $listen_addr Target address (external).
|
||||||
|
* @param integer $listen_port Target port (external).
|
||||||
|
* @param string $to_addr Target address (internal).
|
||||||
|
* @param integer $to_port Target port (internal).
|
||||||
|
* @param integer $to_url Target url (internal).
|
||||||
|
* @param integer $bufferLength Max buffer size.
|
||||||
|
* @param boolean $debug Enable traces.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
$listen_addr,
|
||||||
|
$listen_port,
|
||||||
|
$to_addr,
|
||||||
|
$to_port,
|
||||||
|
$to_url='/ws',
|
||||||
|
$bufferLength=1048576,
|
||||||
|
$debug=false
|
||||||
|
) {
|
||||||
|
$this->intHost = $to_addr;
|
||||||
|
$this->intPort = $to_port;
|
||||||
|
$this->intUrl = $to_url;
|
||||||
|
$this->maxBufferSize = $bufferLength;
|
||||||
|
$this->interactive = $debug;
|
||||||
|
$this->userClass = 'WSProxyUser';
|
||||||
|
parent::__construct($listen_addr, $listen_port, $bufferLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read from socket
|
||||||
|
*
|
||||||
|
* @param socket $socket Target connection.
|
||||||
|
*
|
||||||
|
* @return string Buffer.
|
||||||
|
*/
|
||||||
|
protected function readSocket($user)
|
||||||
|
{
|
||||||
|
$buffer;
|
||||||
|
|
||||||
|
$numBytes = socket_recv(
|
||||||
|
$user->socket,
|
||||||
|
$buffer,
|
||||||
|
$this->maxBufferSize,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
if ($numBytes === false) {
|
||||||
|
// Failed. Disconnect.
|
||||||
|
$this->disconnect($user->socket);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->splitPacket($numBytes, $buffer, $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write to socket.
|
||||||
|
*
|
||||||
|
* @param socket $socket Target socket.
|
||||||
|
* @param string $message Target message to be sent.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function writeSocket($user, $message)
|
||||||
|
{
|
||||||
|
if ($user->socket) {
|
||||||
|
socket_write(
|
||||||
|
$user->socket,
|
||||||
|
$message
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Failed. Disconnect.
|
||||||
|
$this->disconnect($user->socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to internal socket.
|
||||||
|
*
|
||||||
|
* @param array $headers Communication headers.
|
||||||
|
*
|
||||||
|
* @return socket Active socket or null.
|
||||||
|
*/
|
||||||
|
protected function connectInt($headers)
|
||||||
|
{
|
||||||
|
$intSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||||
|
$connect = socket_connect(
|
||||||
|
$intSocket,
|
||||||
|
$this->intHost,
|
||||||
|
$this->intPort
|
||||||
|
);
|
||||||
|
if (!$connect) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$c_str = 'GET '.$this->intUrl." HTTP/1.1\r\n";
|
||||||
|
$c_str .= 'Host: '.$this->intHost."\r\n";
|
||||||
|
$c_str .= "Upgrade: websocket\r\n";
|
||||||
|
$c_str .= "Connection: Upgrade\r\n";
|
||||||
|
$c_str .= 'Origin: http://'.$this->intHost."\r\n";
|
||||||
|
$c_str .= 'Sec-WebSocket-Key: '.$headers['Sec-WebSocket-Key']."\r\n";
|
||||||
|
$c_str .= 'Sec-WebSocket-Version: '.$headers['Sec-WebSocket-Version']."\r\n";
|
||||||
|
if (isset($headers['Sec-WebSocket-Protocol'])) {
|
||||||
|
$c_str .= 'Sec-WebSocket-Protocol: '.$headers['Sec-WebSocket-Protocol']."\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$c_str .= "\r\n";
|
||||||
|
|
||||||
|
// Send.
|
||||||
|
// Register user - internal.
|
||||||
|
$intUser = new $this->userClass('INTERNAL-'.uniqid('u'), $intSocket);
|
||||||
|
|
||||||
|
$intUser->headers = [
|
||||||
|
'get' => $this->intUrl.' HTTP/1.1',
|
||||||
|
'host' => $this->intHost,
|
||||||
|
'origin' => $this->intHost,
|
||||||
|
];
|
||||||
|
$this->writeSocket($intUser, $c_str);
|
||||||
|
|
||||||
|
return $intUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User already connected.
|
||||||
|
*
|
||||||
|
* @param object $user User.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function connected($user)
|
||||||
|
{
|
||||||
|
echo '** CONNECTED'."\n";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* $user->intSocket is connected to internal.
|
||||||
|
* $user->socket is connected to external.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Create a new socket connection (internal).
|
||||||
|
$intUser = $this->connectInt($this->rawHeaders);
|
||||||
|
|
||||||
|
// Map user.
|
||||||
|
$user->intUser = $intUser;
|
||||||
|
// And socket.
|
||||||
|
$user->intSocket = $intUser->socket;
|
||||||
|
|
||||||
|
$response = $this->readSocket($intUser);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protocol.
|
||||||
|
*
|
||||||
|
* @param string $protocol Protocol.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function processProtocol($protocol): string
|
||||||
|
{
|
||||||
|
return 'Sec-Websocket-Protocol: '.$protocol."\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From client to socket (internal);
|
||||||
|
*
|
||||||
|
* @param object $user Caller.
|
||||||
|
* @param string $message Message.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function process($user, $message)
|
||||||
|
{
|
||||||
|
echo '>> ['.$user->id.'] => ['.$user->intUser->id."]\n";
|
||||||
|
echo $this->dump($this->rawPacket);
|
||||||
|
$this->writeSocket(
|
||||||
|
$user->intUser,
|
||||||
|
$this->rawPacket
|
||||||
|
);
|
||||||
|
|
||||||
|
echo '<< ['.$user->intUser->id.'] => ['.$user->id."]\n";
|
||||||
|
$response = $this->readSocket($user->intUser);
|
||||||
|
$this->send($user, $response);
|
||||||
|
echo $this->dump($this->rawPacket);
|
||||||
|
print "\n********************************************\n";
|
||||||
|
print "\n\n\n";
|
||||||
|
print "\n\n\n";
|
||||||
|
print "\n\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Also close internal socket.
|
||||||
|
*
|
||||||
|
* @param object $user User.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
44
pandora_console/extensions/quick_shell/WSProxyUser.php
Normal file
44
pandora_console/extensions/quick_shell/WSProxyUser.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__.'/WebSocketServer.php';
|
||||||
|
require_once __DIR__.'/WebSocketUser.php';
|
||||||
|
|
||||||
|
use \PandoraFMS\WebSocketServer;
|
||||||
|
use \PandoraFMS\WebSocketUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket proxy user.
|
||||||
|
*/
|
||||||
|
class WSProxyUser extends WebSocketUser
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirection socket.
|
||||||
|
*
|
||||||
|
* @var socket
|
||||||
|
*/
|
||||||
|
public $intSocket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User identifier.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $myId;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder.
|
||||||
|
*
|
||||||
|
* @param string $id Identifier.
|
||||||
|
* @param socket $socket Socket (origin).
|
||||||
|
* @param socket $intSocket Socket (internal).
|
||||||
|
*/
|
||||||
|
public function __construct($id, $socket)
|
||||||
|
{
|
||||||
|
parent::__construct($id, $socket);
|
||||||
|
$this->myId = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -132,12 +132,19 @@ abstract class WebSocketServer
|
|||||||
protected $headerSecWebSocketExtensionsRequired = false;
|
protected $headerSecWebSocketExtensionsRequired = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stored raw headers for rediretion.
|
* Stored raw headers for redirection.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
public $rawHeaders = [];
|
public $rawHeaders = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw packet for redirection.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $rawPacket;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder.
|
* Builder.
|
||||||
@ -148,7 +155,7 @@ abstract class WebSocketServer
|
|||||||
* @param integer $maxConnections Max concurrent connections.
|
* @param integer $maxConnections Max concurrent connections.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $addr,
|
$addr,
|
||||||
int $port,
|
int $port,
|
||||||
int $bufferLength=2048,
|
int $bufferLength=2048,
|
||||||
int $maxConnections=20
|
int $maxConnections=20
|
||||||
@ -190,7 +197,7 @@ abstract class WebSocketServer
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
abstract protected function process($user, string $message);
|
abstract protected function process($user, $message);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -236,7 +243,7 @@ abstract class WebSocketServer
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function send($user, string $message)
|
protected function send($user, $message)
|
||||||
{
|
{
|
||||||
if ($user->handshake) {
|
if ($user->handshake) {
|
||||||
$message = $this->frame($message, $user);
|
$message = $this->frame($message, $user);
|
||||||
@ -394,6 +401,8 @@ abstract class WebSocketServer
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$user = $this->getUserBySocket($socket);
|
$user = $this->getUserBySocket($socket);
|
||||||
|
$pair = $this->getIntUserBySocket($socket);
|
||||||
|
|
||||||
if (!$user->handshake) {
|
if (!$user->handshake) {
|
||||||
$tmp = str_replace("\r", '', $buffer);
|
$tmp = str_replace("\r", '', $buffer);
|
||||||
if (strpos($tmp, "\n\n") === false) {
|
if (strpos($tmp, "\n\n") === false) {
|
||||||
@ -405,8 +414,13 @@ abstract class WebSocketServer
|
|||||||
|
|
||||||
$this->doHandshake($user, $buffer);
|
$this->doHandshake($user, $buffer);
|
||||||
} else {
|
} else {
|
||||||
// Split packet into frame and send it to deframe.
|
if (is_array($pair) && $pair['int']) {
|
||||||
$this->splitPacket($numBytes, $buffer, $user);
|
echo '~~ RESEND!'."\n";
|
||||||
|
socket_write($pair['ext']->socket, $buffer);
|
||||||
|
} else {
|
||||||
|
// Split packet into frame and send it to deframe.
|
||||||
|
$this->splitPacket($numBytes, $buffer, $user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -484,7 +498,7 @@ abstract class WebSocketServer
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function doHandshake($user, string $buffer)
|
protected function doHandshake($user, $buffer)
|
||||||
{
|
{
|
||||||
$magicGUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
$magicGUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
||||||
$headers = [];
|
$headers = [];
|
||||||
@ -688,7 +702,7 @@ abstract class WebSocketServer
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function processProtocol(string $protocol): string
|
protected function processProtocol($protocol): string
|
||||||
{
|
{
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -703,7 +717,7 @@ abstract class WebSocketServer
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function processExtensions(string $extensions): string
|
protected function processExtensions($extensions): string
|
||||||
{
|
{
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@ -728,6 +742,28 @@ abstract class WebSocketServer
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return INT user associated to target socket.
|
||||||
|
*
|
||||||
|
* @param Socket $socket Socket.
|
||||||
|
*
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
protected function getIntUserBySocket($socket)
|
||||||
|
{
|
||||||
|
foreach ($this->users as $user) {
|
||||||
|
if ($user->intSocket == $socket) {
|
||||||
|
return [
|
||||||
|
'ext' => $user,
|
||||||
|
'int' => $user->intUser,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dump to stdout.
|
* Dump to stdout.
|
||||||
*
|
*
|
||||||
@ -750,7 +786,7 @@ abstract class WebSocketServer
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function stderr(string $message=null)
|
public function stderr($message=null)
|
||||||
{
|
{
|
||||||
if ($this->interactive) {
|
if ($this->interactive) {
|
||||||
echo $message."\n";
|
echo $message."\n";
|
||||||
@ -769,9 +805,9 @@ abstract class WebSocketServer
|
|||||||
* @return string Framed message.
|
* @return string Framed message.
|
||||||
*/
|
*/
|
||||||
protected function frame(
|
protected function frame(
|
||||||
string $message,
|
$message,
|
||||||
$user,
|
$user,
|
||||||
string $messageType='text',
|
$messageType='text',
|
||||||
bool $messageContinues=false
|
bool $messageContinues=false
|
||||||
) {
|
) {
|
||||||
switch ($messageType) {
|
switch ($messageType) {
|
||||||
@ -858,7 +894,8 @@ abstract class WebSocketServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return chr($b1).chr($b2).$lengthField.$message;
|
$out = chr($b1).chr($b2).$lengthField.$message;
|
||||||
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -874,7 +911,7 @@ abstract class WebSocketServer
|
|||||||
*/
|
*/
|
||||||
protected function splitPacket(
|
protected function splitPacket(
|
||||||
int $length,
|
int $length,
|
||||||
string $packet,
|
$packet,
|
||||||
$user
|
$user
|
||||||
) {
|
) {
|
||||||
// Add PartialPacket and calculate the new $length.
|
// Add PartialPacket and calculate the new $length.
|
||||||
@ -884,6 +921,7 @@ abstract class WebSocketServer
|
|||||||
$length = strlen($packet);
|
$length = strlen($packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->rawPacket = $packet;
|
||||||
$fullpacket = $packet;
|
$fullpacket = $packet;
|
||||||
$frame_pos = 0;
|
$frame_pos = 0;
|
||||||
$frame_id = 1;
|
$frame_id = 1;
|
||||||
@ -958,7 +996,7 @@ abstract class WebSocketServer
|
|||||||
* @return boolean Process ok or not.
|
* @return boolean Process ok or not.
|
||||||
*/
|
*/
|
||||||
protected function deframe(
|
protected function deframe(
|
||||||
string $message,
|
$message,
|
||||||
&$user
|
&$user
|
||||||
) {
|
) {
|
||||||
/*
|
/*
|
||||||
@ -1052,7 +1090,7 @@ abstract class WebSocketServer
|
|||||||
*
|
*
|
||||||
* @return array Headers.
|
* @return array Headers.
|
||||||
*/
|
*/
|
||||||
protected function extractHeaders(string $message): array
|
protected function extractHeaders($message): array
|
||||||
{
|
{
|
||||||
$header = [
|
$header = [
|
||||||
'fin' => ($message[0] & chr(128)),
|
'fin' => ($message[0] & chr(128)),
|
||||||
@ -1115,7 +1153,7 @@ abstract class WebSocketServer
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function extractPayload(
|
protected function extractPayload(
|
||||||
string $message,
|
$message,
|
||||||
array $headers
|
array $headers
|
||||||
) {
|
) {
|
||||||
$offset = 2;
|
$offset = 2;
|
||||||
@ -1143,7 +1181,7 @@ abstract class WebSocketServer
|
|||||||
*/
|
*/
|
||||||
protected function applyMask(
|
protected function applyMask(
|
||||||
array $headers,
|
array $headers,
|
||||||
string $payload
|
$payload
|
||||||
) {
|
) {
|
||||||
$effectiveMask = '';
|
$effectiveMask = '';
|
||||||
if ($headers['hasmask']) {
|
if ($headers['hasmask']) {
|
||||||
@ -1211,7 +1249,7 @@ abstract class WebSocketServer
|
|||||||
* @return string HEX string.
|
* @return string HEX string.
|
||||||
*/
|
*/
|
||||||
protected function strtohex(
|
protected function strtohex(
|
||||||
string $str=''
|
$str=''
|
||||||
): string {
|
): string {
|
||||||
$strout = '';
|
$strout = '';
|
||||||
$len = strlen($str);
|
$len = strlen($str);
|
||||||
@ -1266,4 +1304,58 @@ abstract class WebSocketServer
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View any string as a hexdump.
|
||||||
|
*
|
||||||
|
* @param string $data The string to be dumped.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function dump($data)
|
||||||
|
{
|
||||||
|
// Init.
|
||||||
|
$hexi = '';
|
||||||
|
$ascii = '';
|
||||||
|
$dump = '';
|
||||||
|
$offset = 0;
|
||||||
|
$len = strlen($data);
|
||||||
|
|
||||||
|
// Iterate string.
|
||||||
|
for ($i = 0, $j = 0; $i < $len; $i++) {
|
||||||
|
// Convert to hexidecimal.
|
||||||
|
$hexi .= sprintf('%02x ', ord($data[$i]));
|
||||||
|
// Replace non-viewable bytes with '.'.
|
||||||
|
if (ord($data[$i]) >= 32) {
|
||||||
|
$ascii .= $data[$i];
|
||||||
|
} else {
|
||||||
|
$ascii .= '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add extra column spacing.
|
||||||
|
if ($j === 7 && $i !== ($len - 1)) {
|
||||||
|
$hexi .= ' ';
|
||||||
|
$ascii .= ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add row.
|
||||||
|
if (++$j === 16 || $i === ($len - 1)) {
|
||||||
|
// Join the hexi / ascii output.
|
||||||
|
$dump .= sprintf('%04x %-49s %s', $offset, $hexi, $ascii);
|
||||||
|
// Reset vars.
|
||||||
|
$hexi = $ascii = '';
|
||||||
|
$offset += 16;
|
||||||
|
$j = 0;
|
||||||
|
// Add newline.
|
||||||
|
if ($i !== ($len - 1)) {
|
||||||
|
$dump .= "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish dump.
|
||||||
|
$dump .= "\n";
|
||||||
|
return $dump;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHP WebocketServer from:
|
|
||||||
*
|
|
||||||
* Copyright (c) 2012, Adam Alexander
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
namespace \PandoraFMS\WebSocketServer;
|
|
||||||
class WebSocketUser
|
|
||||||
{
|
|
||||||
|
|
||||||
public $socket;
|
|
||||||
public $id;
|
|
||||||
public $headers = array();
|
|
||||||
public $handshake = false;
|
|
||||||
|
|
||||||
public $handlingPartialPacket = false;
|
|
||||||
public $partialBuffer = "";
|
|
||||||
|
|
||||||
public $sendingContinuous = false;
|
|
||||||
public $partialMessage = "";
|
|
||||||
|
|
||||||
public $hasSentClose = false;
|
|
||||||
|
|
||||||
function __construct($id, $socket)
|
|
||||||
{
|
|
||||||
$this->id = $id;
|
|
||||||
$this->socket = $socket;
|
|
||||||
}
|
|
||||||
}
|
|
97
pandora_console/extensions/quick_shell/test.html
Normal file
97
pandora_console/extensions/quick_shell/test.html
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>WebSocket</title>
|
||||||
|
<style type="text/css">
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
font: normal 0.9em arial, helvetica;
|
||||||
|
}
|
||||||
|
|
||||||
|
#log {
|
||||||
|
width: 600px;
|
||||||
|
height: 300px;
|
||||||
|
border: 1px solid #7f9db9;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#msg {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var socket;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
var host = "ws://172.16.0.2:8081/echobot"; // SET THIS TO YOUR SERVER
|
||||||
|
try {
|
||||||
|
socket = new WebSocket(host);
|
||||||
|
log("WebSocket - status " + socket.readyState);
|
||||||
|
socket.onopen = function(msg) {
|
||||||
|
log("Welcome - status " + this.readyState);
|
||||||
|
};
|
||||||
|
socket.onmessage = function(msg) {
|
||||||
|
log("Received: " + msg.data);
|
||||||
|
};
|
||||||
|
socket.onclose = function(msg) {
|
||||||
|
log("Disconnected - status " + this.readyState);
|
||||||
|
};
|
||||||
|
} catch (ex) {
|
||||||
|
log(ex);
|
||||||
|
}
|
||||||
|
$("msg").focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function send() {
|
||||||
|
var txt, msg;
|
||||||
|
txt = $("msg");
|
||||||
|
msg = txt.value;
|
||||||
|
if (!msg) {
|
||||||
|
alert("Message can not be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
txt.value = "";
|
||||||
|
txt.focus();
|
||||||
|
try {
|
||||||
|
socket.send(msg);
|
||||||
|
log("Sent: " + msg);
|
||||||
|
} catch (ex) {
|
||||||
|
log(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function quit() {
|
||||||
|
if (socket != null) {
|
||||||
|
log("Goodbye!");
|
||||||
|
socket.close();
|
||||||
|
socket = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reconnect() {
|
||||||
|
quit();
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utilities
|
||||||
|
function $(id) {
|
||||||
|
return document.getElementById(id);
|
||||||
|
}
|
||||||
|
function log(msg) {
|
||||||
|
$("log").innerHTML += "<br>" + msg;
|
||||||
|
}
|
||||||
|
function onkey(event) {
|
||||||
|
if (event.keyCode == 13) {
|
||||||
|
send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="init()">
|
||||||
|
<h3>WebSocket v2.00</h3>
|
||||||
|
<div id="log"></div>
|
||||||
|
<input id="msg" type="textbox" onkeypress="onkey(event)" />
|
||||||
|
<button onclick="send()">Send</button>
|
||||||
|
<button onclick="quit()">Quit</button>
|
||||||
|
<button onclick="reconnect()">Reconnect</button>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -4,7 +4,7 @@ $host = '127.0.0.1';
|
|||||||
$port = 8080;
|
$port = 8080;
|
||||||
|
|
||||||
$r = file_get_contents('http://'.$host.':'.$port.'/js/hterm.js');
|
$r = file_get_contents('http://'.$host.':'.$port.'/js/hterm.js');
|
||||||
$r .= file_get_contents('http://'.$host.':'.$port.'/hterms.js');
|
$r .= file_get_contents('http://'.$host.':'.$port.'/auth_token.js');
|
||||||
$gotty = file_get_contents('http://'.$host.':'.$port.'/js/gotty.js');
|
$gotty = file_get_contents('http://'.$host.':'.$port.'/js/gotty.js');
|
||||||
|
|
||||||
|
|
||||||
@ -18,11 +18,19 @@ $gotty = str_replace($url, $new, $gotty);
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>GoTTY</title>
|
<title>Quick Shell experiment</title>
|
||||||
<style>body, #terminal {position: absolute; height: 100%; width: 100%; margin: 0px;}</style>
|
<style>#terminal {
|
||||||
|
height: 590px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0;
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<link rel="icon" type="image/png" href="favicon.png">
|
<link rel="icon" type="image/png" href="favicon.png">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<h1>Prueba</h1>
|
||||||
<div id="terminal"></div>
|
<div id="terminal"></div>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
<?php echo $r; ?>
|
<?php echo $r; ?>
|
||||||
|
@ -4,190 +4,13 @@
|
|||||||
ini_set('display_errors', 1);
|
ini_set('display_errors', 1);
|
||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
require_once __DIR__.'/WebSocketServer.php';
|
require_once __DIR__.'/WSProxy.php';
|
||||||
require_once __DIR__.'/WebSocketUser.php';
|
|
||||||
|
|
||||||
use \PandoraFMS\WebSocketServer;
|
$wsproxy = new WSProxy('0.0.0.0', '8081', '127.0.0.1', '8080');
|
||||||
use \PandoraFMS\WebSocketUser;
|
|
||||||
|
|
||||||
class MyUser extends WebSocketUser
|
|
||||||
{
|
|
||||||
|
|
||||||
public $myId;
|
|
||||||
|
|
||||||
|
|
||||||
function __construct($id, $socket)
|
|
||||||
{
|
|
||||||
parent::__construct($id, $socket);
|
|
||||||
$this->myId = $id;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class echoServer extends WebSocketServer
|
|
||||||
{
|
|
||||||
|
|
||||||
private $redirectedSocket;
|
|
||||||
|
|
||||||
private $redirectedHost = '127.0.0.1';
|
|
||||||
|
|
||||||
|
|
||||||
function __construct($addr, $port, $bufferLength=2048)
|
|
||||||
{
|
|
||||||
parent::__construct($addr, $port, $bufferLength);
|
|
||||||
$this->userClass = 'MyUser';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1MB... overkill for an echo server, but potentially plausible for other
|
|
||||||
* applications.
|
|
||||||
*
|
|
||||||
* @var integer
|
|
||||||
*/
|
|
||||||
protected $maxBufferSize = 1048576;
|
|
||||||
|
|
||||||
|
|
||||||
protected function readSocket()
|
|
||||||
{
|
|
||||||
$buffer;
|
|
||||||
|
|
||||||
$numBytes = socket_recv(
|
|
||||||
$this->redirectedSocket,
|
|
||||||
$buffer,
|
|
||||||
$this->maxBufferSize,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
if ($numBytes === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function parseGottyHeaders($response)
|
|
||||||
{
|
|
||||||
$headers = [];
|
|
||||||
$lines = explode('\n', $response);
|
|
||||||
foreach ($lines as $l) {
|
|
||||||
$c = explode(':', $l);
|
|
||||||
|
|
||||||
$headers[trim($c[0])] = trim($c[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function translateGottyHeaders($headers)
|
|
||||||
{
|
|
||||||
// Redirect.
|
|
||||||
$h = $headers;
|
|
||||||
/*
|
|
||||||
$h['Sec-Websocket-Key'] = base64_encode($headers['sec-websocket-key']);
|
|
||||||
$h['Sec-Websocket-Extensions'] = $headers['sec-websocket-extensions'];
|
|
||||||
$h['Sec-Websocket-Protocol'] = $headers['sec-websocket-protocol'];
|
|
||||||
$h['Sec-Websocket-Version'] = $headers['sec-websocket-version'];
|
|
||||||
|
|
||||||
$h['Connection'] = $headers['connection'];
|
|
||||||
if ($headers['upgrade']) {
|
|
||||||
$h['Upgrade'] = $headers['upgrade'];
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
$h['Host'] = '127.0.0.1:8081';
|
|
||||||
$h['Origin'] = 'http://127.0.0.1:8081';
|
|
||||||
|
|
||||||
// Cleanup.
|
|
||||||
unset($h['get']);
|
|
||||||
unset($h['user-agent']);
|
|
||||||
unset($h['cache-control']);
|
|
||||||
unset($h['pragma']);
|
|
||||||
|
|
||||||
// Build.
|
|
||||||
$out = "GET /ws HTTP/1.1\r\n";
|
|
||||||
foreach ($h as $key => $value) {
|
|
||||||
$out .= ucfirst($key).': '.$value."\r\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$out .= "\r\n";
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function process($user, $message)
|
|
||||||
{
|
|
||||||
// What to do with received message.
|
|
||||||
echo 'Received from client> ['.$message."]\n";
|
|
||||||
|
|
||||||
socket_write($this->redirectedSocket, $message);
|
|
||||||
$out = $this->readSocket();
|
|
||||||
|
|
||||||
$this->send($user, $out);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function processProtocol(string $protocol): string
|
|
||||||
{
|
|
||||||
return 'Sec-Websocket-Protocol: '.$protocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function connected($user)
|
|
||||||
{
|
|
||||||
$this->redirectedSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
|
||||||
$connect = socket_connect($this->redirectedSocket, $this->redirectedHost, 8080);
|
|
||||||
if (!$connect) {
|
|
||||||
echo 'failed to open target socket redirection';
|
|
||||||
$this->disconnect($user->socket);
|
|
||||||
} else {
|
|
||||||
var_dump($this->rawHeaders);
|
|
||||||
$out = $this->translateGottyHeaders($this->rawHeaders);
|
|
||||||
echo '????';
|
|
||||||
socket_write($this->redirectedSocket, $out);
|
|
||||||
|
|
||||||
$response = '';
|
|
||||||
$response = $this->readSocket();
|
|
||||||
|
|
||||||
// Upgrade $user headers.
|
|
||||||
$new_headers = $this->parseGottyHeaders($response);
|
|
||||||
$user->headers += $new_headers;
|
|
||||||
|
|
||||||
print ">> Reenviando [gotty] [$response]\n";
|
|
||||||
|
|
||||||
// $this->send($user, $response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do nothing: This is just an echo server, there's no need to track the user.
|
|
||||||
// However, if we did care about the users, we would probably have a cookie to
|
|
||||||
// parse at this step, would be looking them up in permanent storage, etc.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function closed($user)
|
|
||||||
{
|
|
||||||
$out = "GET /ws HTTP/1.1\r\n";
|
|
||||||
$out .= 'Host: '.$this->redirectedHost."\r\n";
|
|
||||||
$out .= "Connection: Close\r\n\r\n";
|
|
||||||
socket_write($this->redirectedSocket, $out);
|
|
||||||
echo ">> Recibiendo respuesta de peticion de cierre:\n";
|
|
||||||
$out = $this->readSocket();
|
|
||||||
|
|
||||||
socket_close($this->redirectedSocket);
|
|
||||||
// Do nothing: This is where cleanup would go, in case the user had any sort of
|
|
||||||
// open files or other objects associated with them. This runs after the socket
|
|
||||||
// has been closed, so there is no need to clean up the socket itself here.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$echo = new echoServer('0.0.0.0', '8081');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$echo->run();
|
echo "Server running \n";
|
||||||
|
$wsproxy->run();
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$echo->stdout($e->getMessage());
|
$wsproxy->stdout($e->getMessage());
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user