2019-10-21 17:36:26 +02:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Extra functionality for PandoraFMS WebSockets.
|
|
|
|
*
|
|
|
|
* Register here your methods to handle different WebSocket steps.
|
|
|
|
* * connected
|
|
|
|
* * process
|
|
|
|
* * disconnect
|
|
|
|
* * tick
|
|
|
|
*
|
|
|
|
* DO NOT FORGET TO REGISTER THEM TO ws.php!!
|
|
|
|
*
|
|
|
|
* @category Websocket
|
|
|
|
* @package Pandora FMS
|
|
|
|
* @subpackage Console
|
|
|
|
* @version 1.0.0
|
|
|
|
* @license See below
|
|
|
|
*
|
|
|
|
* ______ ___ _______ _______ ________
|
|
|
|
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
|
|
|
|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
|
|
|
|
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
|
|
|
|
*
|
|
|
|
* ============================================================================
|
2023-06-08 11:53:13 +02:00
|
|
|
* Copyright (c) 2005-2023 Pandora FMS
|
2019-10-21 17:36:26 +02:00
|
|
|
* Please see http://pandorafms.org for full contribution list
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation for version 2.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
* ============================================================================
|
|
|
|
*/
|
|
|
|
|
2022-01-27 23:18:12 +01:00
|
|
|
use PandoraFMS\Websockets\WSManager;
|
|
|
|
|
2019-10-21 17:36:26 +02:00
|
|
|
/*
|
|
|
|
* ============================================================================
|
|
|
|
* * GOTTY PROTOCOL: PROXY
|
|
|
|
* ============================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Connects to internal socket.
|
|
|
|
*
|
|
|
|
* @param WSManager $ws_object Main WebSocket manager object.
|
|
|
|
* @param array $headers Communication headers.
|
|
|
|
* @param string $to_addr Target address (internal).
|
|
|
|
* @param integer $to_port Target port (internal).
|
2022-01-27 23:18:12 +01:00
|
|
|
* @param string $to_url Target url (internal).
|
2019-10-21 17:36:26 +02:00
|
|
|
*
|
|
|
|
* @return socket Active socket or null.
|
|
|
|
*/
|
|
|
|
function connectInt(
|
2022-01-27 23:18:12 +01:00
|
|
|
WSManager $ws_object,
|
|
|
|
array $headers,
|
|
|
|
string $to_addr,
|
|
|
|
int $to_port,
|
|
|
|
string $to_url
|
2019-10-21 17:36:26 +02:00
|
|
|
) {
|
|
|
|
$intSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
2022-01-26 18:40:45 +01:00
|
|
|
// Not sure.
|
2019-10-21 17:36:26 +02:00
|
|
|
$connect = socket_connect(
|
|
|
|
$intSocket,
|
|
|
|
$to_addr,
|
|
|
|
$to_port
|
|
|
|
);
|
2022-01-26 18:40:45 +01:00
|
|
|
|
|
|
|
if ($connect === false) {
|
|
|
|
$ws_object->stderr(socket_last_error($intSocket));
|
2019-10-21 17:36:26 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$c_str = 'GET '.$to_url." HTTP/1.1\r\n";
|
|
|
|
$c_str .= 'Host: '.$to_addr."\r\n";
|
|
|
|
$c_str .= "Upgrade: websocket\r\n";
|
|
|
|
$c_str .= "Connection: Upgrade\r\n";
|
|
|
|
$c_str .= 'Origin: http://'.$to_addr."\r\n";
|
|
|
|
$c_str .= 'Sec-WebSocket-Key: '.$headers['Sec-WebSocket-Key']."\r\n";
|
|
|
|
$c_str .= 'Sec-WebSocket-Version: '.$headers['Sec-WebSocket-Version']."\r\n";
|
2022-01-27 23:18:12 +01:00
|
|
|
if (isset($headers['Sec-WebSocket-Protocol']) === true) {
|
2019-10-21 17:36:26 +02:00
|
|
|
$c_str .= 'Sec-WebSocket-Protocol: '.$headers['Sec-WebSocket-Protocol']."\r\n";
|
|
|
|
}
|
|
|
|
|
2022-01-27 23:18:12 +01:00
|
|
|
$c_str .= "\r\n";
|
2019-10-21 17:36:26 +02:00
|
|
|
|
|
|
|
// Send.
|
|
|
|
// Register user - internal.
|
|
|
|
$intUser = new $ws_object->userClass('INTERNAL-'.uniqid('u'), $intSocket);
|
|
|
|
|
|
|
|
$intUser->headers = [
|
|
|
|
'get' => $to_url.' HTTP/1.1',
|
|
|
|
'host' => $to_addr,
|
|
|
|
'origin' => $to_addr,
|
|
|
|
'sec-websocket-protocol' => 'gotty',
|
|
|
|
];
|
2022-01-26 18:40:45 +01:00
|
|
|
|
2019-10-21 17:36:26 +02:00
|
|
|
$ws_object->writeSocket($intUser, $c_str);
|
|
|
|
|
|
|
|
return $intUser;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Process a connected step on proxied protocols.
|
|
|
|
*
|
|
|
|
* @param WSManager $ws_object Main WebSocket manager object.
|
|
|
|
* @param User $user WebSocketUser.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
function proxyConnected(
|
|
|
|
$ws_object,
|
|
|
|
$user
|
|
|
|
) {
|
|
|
|
global $config;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* $user->redirect is connected to internal (reflexive).
|
|
|
|
* $user->socket is connected to external.
|
|
|
|
*/
|
|
|
|
|
2022-01-27 23:18:12 +01:00
|
|
|
$failed = false;
|
|
|
|
|
2019-10-21 17:36:26 +02:00
|
|
|
// Gotty. Based on the command selected, redirect to a target port.
|
2022-01-26 18:40:45 +01:00
|
|
|
if ($user->requestedResource === '/ssh') {
|
2019-10-21 17:36:26 +02:00
|
|
|
$port = $config['gotty_ssh_port'];
|
2022-01-26 18:40:45 +01:00
|
|
|
} else if ($user->requestedResource === '/telnet') {
|
2019-10-21 17:36:26 +02:00
|
|
|
$port = $config['gotty_telnet_port'];
|
|
|
|
} else {
|
2022-01-27 23:18:12 +01:00
|
|
|
$failed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($failed === true
|
|
|
|
|| isset($config['gotty_host']) === false
|
|
|
|
|| isset($port) === false
|
|
|
|
) {
|
2019-10-21 17:36:26 +02:00
|
|
|
$ws_object->disconnect($user->socket);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Switch between ports...
|
|
|
|
// Create a new socket connection (internal).
|
|
|
|
$intUser = connectInt(
|
|
|
|
$ws_object,
|
|
|
|
$ws_object->rawHeaders,
|
|
|
|
$config['gotty_host'],
|
|
|
|
$port,
|
|
|
|
'/ws'
|
|
|
|
);
|
|
|
|
|
|
|
|
if ($intUser === null) {
|
|
|
|
$ws_object->disconnect($user->socket);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Map user.
|
|
|
|
$user->intUser = $intUser;
|
|
|
|
// And socket.
|
|
|
|
$user->intSocket = $intUser->socket;
|
|
|
|
$user->redirect = $intUser;
|
|
|
|
$intUser->redirect = $user;
|
|
|
|
|
|
|
|
// Keep an eye on changes.
|
|
|
|
$ws_object->remoteSockets[$intUser->id] = $intUser->socket;
|
|
|
|
$ws_object->remoteUsers[$intUser->id] = $intUser;
|
|
|
|
|
|
|
|
// Ignore. Cleanup socket.
|
2022-01-27 23:18:12 +01:00
|
|
|
$ws_object->readSocket($user->intUser);
|
2019-10-21 17:36:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Redirects input from user to redirection stabished.
|
|
|
|
*
|
|
|
|
* @param WSManager $ws_object Main WebSocket manager object.
|
|
|
|
* @param WebSocketUser $user WebSocket user.
|
|
|
|
* @param string $buffer Buffer.
|
|
|
|
*
|
|
|
|
* @return boolean Ok or not.
|
|
|
|
*/
|
|
|
|
function proxyProcessRaw($ws_object, $user, $buffer)
|
|
|
|
{
|
|
|
|
if (isset($user->redirect) !== true) {
|
|
|
|
$ws_object->disconnect($user->socket);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$ws_object->stderr($user->id.' >> '.$user->redirect->id);
|
|
|
|
$ws_object->stderr($ws_object->dump($buffer));
|
|
|
|
$ws_object->writeSocket($user->redirect, $buffer);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ============================================================================
|
|
|
|
* * ENDS: GOTTY PROTOCOL: PROXY
|
|
|
|
* ============================================================================
|
|
|
|
*/
|