mirror of
https://github.com/opnsense/plugins.git
synced 2026-05-28 04:34:15 -04:00
Merge 88d9411b02 into 4d7a938c13
This commit is contained in:
commit
fb7ce721bd
20 changed files with 1248 additions and 0 deletions
7
net/rtsphelper/Makefile
Normal file
7
net/rtsphelper/Makefile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
PLUGIN_NAME= rtsphelper
|
||||
PLUGIN_VERSION= 1.2
|
||||
#PLUGIN_DEPENDS=
|
||||
PLUGIN_COMMENT= RTSP Port Forwarder Helper
|
||||
PLUGIN_MAINTAINER= quentin.canel@o2r.fr
|
||||
|
||||
.include "../../Mk/plugins.mk"
|
||||
1
net/rtsphelper/pkg-descr
Normal file
1
net/rtsphelper/pkg-descr
Normal file
|
|
@ -0,0 +1 @@
|
|||
A simple helper script that opens ports to allow mis-configured RTSP servers to work behind NAT environment
|
||||
112
net/rtsphelper/src/etc/inc/plugins.inc.d/rtsphelper.inc
Normal file
112
net/rtsphelper/src/etc/inc/plugins.inc.d/rtsphelper.inc
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
use OPNsense\RTSPHelper\General;
|
||||
|
||||
function rtsphelper_enabled()
|
||||
{
|
||||
$model = new General();
|
||||
return (string)$model->general->enabled == '1';
|
||||
}
|
||||
|
||||
function rtsphelper_firewall($fw)
|
||||
{
|
||||
if (!rtsphelper_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fw->registerAnchor('rtsphelper', 'rdr');
|
||||
$fw->registerAnchor('rtsphelper', 'fw');
|
||||
}
|
||||
|
||||
function rtsphelper_services()
|
||||
{
|
||||
$services = array();
|
||||
|
||||
if (!rtsphelper_enabled()) {
|
||||
return $services;
|
||||
}
|
||||
|
||||
$pconfig = array();
|
||||
$pconfig['name'] = 'rtsphelper';
|
||||
$pconfig['description'] = gettext('RTSP Helper');
|
||||
$pconfig['php']['restart'] = array('rtsphelper_stop', 'rtsphelper_start');
|
||||
$pconfig['php']['start'] = array('rtsphelper_start');
|
||||
$pconfig['php']['stop'] = array('rtsphelper_stop');
|
||||
$pconfig['pidfile'] = '/var/run/rtsphelper.pid';
|
||||
$services[] = $pconfig;
|
||||
|
||||
return $services;
|
||||
}
|
||||
|
||||
function rtsphelper_start()
|
||||
{
|
||||
if (!rtsphelper_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isvalidpid('/var/run/rtsphelper.pid')) {
|
||||
return;
|
||||
}
|
||||
|
||||
mwexec_bg('/usr/local/bin/python3 /usr/local/opnsense/scripts/net/rtsphelper/rtsphelper.py');
|
||||
}
|
||||
|
||||
function rtsphelper_stop()
|
||||
{
|
||||
killbypid('/var/run/rtsphelper.pid', 'TERM', true);
|
||||
mwexec('/sbin/pfctl -artsphelper -Fr 2>&1 >/dev/null');
|
||||
mwexec('/sbin/pfctl -artsphelper -Fn 2>&1 >/dev/null');
|
||||
}
|
||||
|
||||
function rtsphelper_configure()
|
||||
{
|
||||
return array('bootup' => array('rtsphelper_configure_do'));
|
||||
}
|
||||
|
||||
function rtsphelper_configure_do($verbose = false)
|
||||
{
|
||||
rtsphelper_stop();
|
||||
|
||||
if (!rtsphelper_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($verbose) {
|
||||
echo 'Starting RTSP Helper...';
|
||||
flush();
|
||||
}
|
||||
|
||||
$model = new General();
|
||||
$ext_iface = (string)$model->general->ext_iface;
|
||||
$ext_ifname = get_real_interface($ext_iface);
|
||||
|
||||
// Log a warning if interface couldn't be resolved
|
||||
// This can happen if the selected interface was deleted from the system
|
||||
if ($ext_ifname == $ext_iface) {
|
||||
syslog(LOG_WARNING, "rtsphelper: Interface '{$ext_iface}' could not be resolved to a device name");
|
||||
}
|
||||
|
||||
$config_text = "ext_ifname={$ext_ifname}\n";
|
||||
|
||||
/* RTSP Helper access restrictions */
|
||||
foreach ($model->permissions->permission->iterateItems() as $perm) {
|
||||
$network = (string)$perm->network;
|
||||
$port = (string)$perm->port;
|
||||
$config_text .= "allow={$network} {$port}\n";
|
||||
}
|
||||
|
||||
foreach ($model->hosts->host->iterateItems() as $host) {
|
||||
$ip = (string)$host->ip;
|
||||
$port = (string)$host->port;
|
||||
$config_text .= "forward={$ip}:{$port}\n";
|
||||
}
|
||||
|
||||
/* write out the configuration */
|
||||
file_put_contents('/var/etc/rtsphelper.conf', $config_text);
|
||||
rtsphelper_start();
|
||||
|
||||
if ($verbose) {
|
||||
echo "done.\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace OPNsense\RTSPHelper\Api;
|
||||
|
||||
use OPNsense\Base\ApiControllerBase;
|
||||
use OPNsense\Core\Backend;
|
||||
|
||||
class ServiceController extends ApiControllerBase
|
||||
{
|
||||
public function startAction()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$backend = new Backend();
|
||||
$response = $backend->configdRun('rtsphelper start');
|
||||
return array("response" => $response);
|
||||
}
|
||||
return array("response" => array());
|
||||
}
|
||||
|
||||
public function stopAction()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$backend = new Backend();
|
||||
$response = $backend->configdRun('rtsphelper stop');
|
||||
return array("response" => $response);
|
||||
}
|
||||
return array("response" => array());
|
||||
}
|
||||
|
||||
public function restartAction()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$backend = new Backend();
|
||||
$response = $backend->configdRun('rtsphelper restart');
|
||||
return array("response" => $response);
|
||||
}
|
||||
return array("response" => array());
|
||||
}
|
||||
|
||||
public function statusAction()
|
||||
{
|
||||
$backend = new Backend();
|
||||
$response = $backend->configdRun('rtsphelper status');
|
||||
return array("status" => trim($response));
|
||||
}
|
||||
|
||||
public function reconfigureAction()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$backend = new Backend();
|
||||
$response = $backend->configdRun('rtsphelper configure');
|
||||
return array("response" => $response);
|
||||
}
|
||||
return array("response" => array());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace OPNsense\RTSPHelper\Api;
|
||||
|
||||
use OPNsense\Base\ApiMutableModelControllerBase;
|
||||
|
||||
class SettingsController extends ApiMutableModelControllerBase
|
||||
{
|
||||
protected static $internalModelClass = '\OPNsense\RTSPHelper\General';
|
||||
protected static $internalModelName = 'rtsphelper';
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace OPNsense\RTSPHelper\Api;
|
||||
|
||||
use OPNsense\Base\ApiControllerBase;
|
||||
use OPNsense\Core\Backend;
|
||||
|
||||
class StatusController extends ApiControllerBase
|
||||
{
|
||||
public function connectionsAction()
|
||||
{
|
||||
$backend = new Backend();
|
||||
$response = $backend->configdRun('rtsphelper connections');
|
||||
$rows = array();
|
||||
|
||||
foreach (explode("\n", $response) as $line) {
|
||||
if (preg_match("/on (.*) inet proto (.*) from (.*) to (.*) port = (.*) -> (.*)/", $line, $matches)) {
|
||||
$rows[] = array(
|
||||
"interface" => $matches[1],
|
||||
"proto" => $matches[2],
|
||||
"source" => $matches[3],
|
||||
"destination" => $matches[4],
|
||||
"port" => $matches[5],
|
||||
"redirect_to" => $matches[6]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return array("rows" => $rows);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace OPNsense\RTSPHelper;
|
||||
|
||||
use OPNsense\Base\IndexController;
|
||||
|
||||
class SettingsController extends IndexController
|
||||
{
|
||||
public function indexAction()
|
||||
{
|
||||
$this->view->pick('OPNsense/RTSPHelper/index');
|
||||
$this->view->formGeneral = $this->getForm("general");
|
||||
$this->view->formDialogHost = $this->getForm("dialog_host");
|
||||
$this->view->formDialogPermission = $this->getForm("dialog_permission");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<form>
|
||||
<field>
|
||||
<id>host.ip</id>
|
||||
<label>IP Address</label>
|
||||
<type>text</type>
|
||||
<help>Internal IP address.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>host.port</id>
|
||||
<label>Port</label>
|
||||
<type>text</type>
|
||||
<help>Port number.</help>
|
||||
</field>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<form>
|
||||
<field>
|
||||
<id>permission.network</id>
|
||||
<label>Network</label>
|
||||
<type>text</type>
|
||||
<help>Network (CIDR) or IP address.</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>permission.port</id>
|
||||
<label>Port / Range</label>
|
||||
<type>text</type>
|
||||
<help>Port or port range (e.g. 1024-65535).</help>
|
||||
</field>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<form>
|
||||
<field>
|
||||
<id>general.enabled</id>
|
||||
<label>Enable</label>
|
||||
<type>checkbox</type>
|
||||
<help>Enable RTSP Helper</help>
|
||||
</field>
|
||||
<field>
|
||||
<id>general.ext_iface</id>
|
||||
<label>External Interface</label>
|
||||
<type>dropdown</type>
|
||||
<help>Select your primary WAN interface.</help>
|
||||
</field>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<acl>
|
||||
<page-service-rtsphelper>
|
||||
<name>Service: RTSP Helper</name>
|
||||
<patterns>
|
||||
<pattern>ui/rtsphelper/*</pattern>
|
||||
<pattern>api/rtsphelper/*</pattern>
|
||||
</patterns>
|
||||
</page-service-rtsphelper>
|
||||
</acl>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace OPNsense\RTSPHelper;
|
||||
|
||||
use OPNsense\Base\BaseModel;
|
||||
|
||||
class General extends BaseModel
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<model>
|
||||
<mount>//OPNsense/RTSPHelper</mount>
|
||||
<version>1.0.0</version>
|
||||
<items>
|
||||
<general>
|
||||
<enabled type="BooleanField">
|
||||
<default>0</default>
|
||||
<Required>Y</Required>
|
||||
</enabled>
|
||||
<ext_iface type="InterfaceField">
|
||||
<Required>Y</Required>
|
||||
<multiple>N</multiple>
|
||||
</ext_iface>
|
||||
</general>
|
||||
<hosts>
|
||||
<host type="ArrayField">
|
||||
<ip type="NetworkField">
|
||||
<Required>Y</Required>
|
||||
<ValidationMessage>Please specify a valid IP address.</ValidationMessage>
|
||||
</ip>
|
||||
<port type="PortField">
|
||||
<Required>Y</Required>
|
||||
<ValidationMessage>Please specify a valid port number.</ValidationMessage>
|
||||
</port>
|
||||
</host>
|
||||
</hosts>
|
||||
<permissions>
|
||||
<permission type="ArrayField">
|
||||
<network type="NetworkField">
|
||||
<Required>Y</Required>
|
||||
<ValidationMessage>Please specify a valid network (CIDR) or IP address.</ValidationMessage>
|
||||
</network>
|
||||
<port type="TextField">
|
||||
<Required>Y</Required>
|
||||
<mask>/^(\d{1,5})(?:-(\d{1,5}))?$/</mask>
|
||||
<ValidationMessage>Please specify a valid port or port range (e.g. 1024-65535).</ValidationMessage>
|
||||
</port>
|
||||
</permission>
|
||||
</permissions>
|
||||
</items>
|
||||
</model>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<menu>
|
||||
<Services>
|
||||
<RTSPHelper VisibleName="RTSP Helper" cssClass="fa fa-plug fa-fw">
|
||||
<Settings url="/ui/rtsphelper/settings"/>
|
||||
</RTSPHelper>
|
||||
</Services>
|
||||
</menu>
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
|
||||
<li class="active"><a data-toggle="tab" href="#settings">{{ lang._('Settings') }}</a></li>
|
||||
<li><a data-toggle="tab" href="#status">{{ lang._('Status') }}</a></li>
|
||||
</ul>
|
||||
<div class="tab-content content-box">
|
||||
<div id="settings" class="tab-pane fade in active">
|
||||
{{ partial("layout_partials/base_form",['fields':formGeneral,'id':'frm_general_settings'])}}
|
||||
|
||||
<hr />
|
||||
<h3>{{ lang._('Hosts to enable') }}</h3>
|
||||
<table id="grid-hosts" class="table table-condensed table-hover table-striped" data-editDialog="DialogHost"
|
||||
data-editAlert="RTSP Helper Host Change">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="ip" data-type="string" data-identifier="true">{{ lang._('IP Address') }}</th>
|
||||
<th data-column-id="port" data-type="string">{{ lang._('Port') }}</th>
|
||||
<th data-column-id="commands" data-formatter="commands" data-sortable="false">{{ lang._('Commands')
|
||||
}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="5"></td>
|
||||
<td>
|
||||
<button data-action="add" type="button" class="btn btn-xs btn-default"><span
|
||||
class="fa fa-plus"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<hr />
|
||||
<h3>{{ lang._('User specified permissions') }}</h3>
|
||||
<table id="grid-permissions" class="table table-condensed table-hover table-striped"
|
||||
data-editDialog="DialogPermission" data-editAlert="RTSP Helper Permission Change">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="network" data-type="string" data-identifier="true">{{ lang._('Network') }}</th>
|
||||
<th data-column-id="port" data-type="string">{{ lang._('Port / Range') }}</th>
|
||||
<th data-column-id="commands" data-formatter="commands" data-sortable="false">{{ lang._('Commands')
|
||||
}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="5"></td>
|
||||
<td>
|
||||
<button data-action="add" type="button" class="btn btn-xs btn-default"><span
|
||||
class="fa fa-plus"></span></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
<div class="col-md-12">
|
||||
<hr />
|
||||
<button class="btn btn-primary" id="saveAct" type="button"><b>{{ lang._('Save') }}</b> <i
|
||||
id="saveAct_progress"></i></button>
|
||||
<br /><br />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="status" class="tab-pane fade">
|
||||
<table id="grid-status" class="table table-condensed table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-column-id="interface" data-type="string">{{ lang._('Interface') }}</th>
|
||||
<th data-column-id="proto" data-type="string">{{ lang._('Protocol') }}</th>
|
||||
<th data-column-id="source" data-type="string">{{ lang._('Source') }}</th>
|
||||
<th data-column-id="destination" data-type="string">{{ lang._('Destination') }}</th>
|
||||
<th data-column-id="port" data-type="string">{{ lang._('Port') }}</th>
|
||||
<th data-column-id="redirect_to" data-type="string">{{ lang._('Redirect To') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="col-md-12">
|
||||
<br />
|
||||
<button class="btn btn-primary" id="refreshAct" type="button"><b>{{ lang._('Refresh') }}</b></button>
|
||||
<br /><br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ partial("layout_partials/base_dialog",['fields':formDialogHost,'id':'DialogHost','label':lang._('Edit Host')])}}
|
||||
{{ partial("layout_partials/base_dialog",['fields':formDialogPermission,'id':'DialogPermission','label':lang._('Edit
|
||||
Permission')])}}
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
var data_get_map = { 'frm_general_settings': "/api/rtsphelper/settings/get" };
|
||||
mapDataToFormUI(data_get_map).done(function (data) {
|
||||
formatTokenizersUI();
|
||||
$('.selectpicker').selectpicker('refresh');
|
||||
});
|
||||
|
||||
$("#grid-hosts").bootgrid({
|
||||
ajax: true,
|
||||
selection: true,
|
||||
multiSelect: true,
|
||||
rowCount: [10, 25, 50, -1],
|
||||
url: '/api/rtsphelper/settings/searchHost',
|
||||
formatters: {
|
||||
"commands": function (column, row) {
|
||||
return "<button type=\"button\" class=\"btn btn-xs btn-default command-edit bootgrid-tooltip\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-pencil\"></span></button> " +
|
||||
"<button type=\"button\" class=\"btn btn-xs btn-default command-delete bootgrid-tooltip\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-trash-o\"></span></button>";
|
||||
}
|
||||
}
|
||||
}).on("loaded.rs.jquery.bootgrid", function (e) {
|
||||
$("#grid-hosts").find(".command-edit").on("click", function (e) {
|
||||
var uuid = $(this).data("row-id");
|
||||
mapDataToFormUI({ 'DialogHost': "/api/rtsphelper/settings/getHost/" + uuid }).done(function () {
|
||||
$("#DialogHost").attr('data-uuid', uuid);
|
||||
$("#DialogHost").modal({ backdrop: 'static', keyboard: false });
|
||||
});
|
||||
});
|
||||
$("#grid-hosts").find(".command-delete").on("click", function (e) {
|
||||
var uuid = $(this).data("row-id");
|
||||
stdDialogConfirm('{{ lang._('Confirm') }}', '{{ lang._('Do you want to delete this host ? ') }}', function () {
|
||||
ajaxCall(url = "/api/rtsphelper/settings/delHost/" + uuid, sendData = {}, callback = function (data, status) {
|
||||
$("#grid-hosts").bootgrid("reload");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$("#grid-hosts").find("tfoot button[data-action='add']").on("click", function (e) {
|
||||
$("#DialogHost").attr('data-uuid', '');
|
||||
$("#DialogHost").modal({ backdrop: 'static', keyboard: false });
|
||||
$("#DialogHost").find("input").val("");
|
||||
});
|
||||
|
||||
$("#btn_DialogHost_save").unbind('click').click(function () {
|
||||
var uuid = $("#DialogHost").attr('data-uuid');
|
||||
var url = "/api/rtsphelper/settings/addHost";
|
||||
if (uuid) {
|
||||
url = "/api/rtsphelper/settings/setHost/" + uuid;
|
||||
}
|
||||
saveFormToEndpoint(url = url, formid = 'DialogHost', callback_ok = function () {
|
||||
$("#DialogHost").modal('hide');
|
||||
$("#grid-hosts").bootgrid("reload");
|
||||
});
|
||||
});
|
||||
|
||||
$("#grid-permissions").bootgrid({
|
||||
ajax: true,
|
||||
selection: true,
|
||||
multiSelect: true,
|
||||
rowCount: [10, 25, 50, -1],
|
||||
url: '/api/rtsphelper/settings/searchPermission',
|
||||
formatters: {
|
||||
"commands": function (column, row) {
|
||||
return "<button type=\"button\" class=\"btn btn-xs btn-default command-edit bootgrid-tooltip\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-pencil\"></span></button> " +
|
||||
"<button type=\"button\" class=\"btn btn-xs btn-default command-delete bootgrid-tooltip\" data-row-id=\"" + row.uuid + "\"><span class=\"fa fa-trash-o\"></span></button>";
|
||||
}
|
||||
}
|
||||
}).on("loaded.rs.jquery.bootgrid", function (e) {
|
||||
$("#grid-permissions").find(".command-edit").on("click", function (e) {
|
||||
var uuid = $(this).data("row-id");
|
||||
mapDataToFormUI({ 'DialogPermission': "/api/rtsphelper/settings/getPermission/" + uuid }).done(function () {
|
||||
$("#DialogPermission").attr('data-uuid', uuid);
|
||||
$("#DialogPermission").modal({ backdrop: 'static', keyboard: false });
|
||||
});
|
||||
});
|
||||
$("#grid-permissions").find(".command-delete").on("click", function (e) {
|
||||
var uuid = $(this).data("row-id");
|
||||
stdDialogConfirm('{{ lang._('Confirm') }}', '{{ lang._('Do you want to delete this permission ? ') }}', function () {
|
||||
ajaxCall(url = "/api/rtsphelper/settings/delPermission/" + uuid, sendData = {}, callback = function (data, status) {
|
||||
$("#grid-permissions").bootgrid("reload");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$("#grid-permissions").find("tfoot button[data-action='add']").on("click", function (e) {
|
||||
$("#DialogPermission").attr('data-uuid', '');
|
||||
$("#DialogPermission").modal({ backdrop: 'static', keyboard: false });
|
||||
$("#DialogPermission").find("input").val("");
|
||||
});
|
||||
|
||||
$("#btn_DialogPermission_save").unbind('click').click(function () {
|
||||
var uuid = $("#DialogPermission").attr('data-uuid');
|
||||
var url = "/api/rtsphelper/settings/addPermission";
|
||||
if (uuid) {
|
||||
url = "/api/rtsphelper/settings/setPermission/" + uuid;
|
||||
}
|
||||
saveFormToEndpoint(url = url, formid = 'DialogPermission', callback_ok = function () {
|
||||
$("#DialogPermission").modal('hide');
|
||||
$("#grid-permissions").bootgrid("reload");
|
||||
});
|
||||
});
|
||||
|
||||
$("#grid-status").bootgrid({
|
||||
ajax: true,
|
||||
selection: false,
|
||||
multiSelect: false,
|
||||
rowCount: [10, 25, 50, -1],
|
||||
url: '/api/rtsphelper/status/connections',
|
||||
});
|
||||
|
||||
$("#saveAct").click(function () {
|
||||
saveFormToEndpoint(url = "/api/rtsphelper/settings/set", formid = 'frm_general_settings', callback_ok = function () {
|
||||
$("#saveAct_progress").addClass("fa fa-spinner fa-pulse");
|
||||
ajaxCall(url = "/api/rtsphelper/service/reconfigure", sendData = {}, callback = function (data, status) {
|
||||
$("#saveAct_progress").removeClass("fa fa-spinner fa-pulse");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$("#refreshAct").click(function () {
|
||||
$("#grid-status").bootgrid("reload");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/local/bin/php
|
||||
<?php
|
||||
|
||||
require_once(__DIR__ . "/../../../../etc/inc/plugins.inc.d/rtsphelper.inc");
|
||||
require_once("config.inc");
|
||||
require_once("util.inc");
|
||||
|
||||
rtsphelper_configure_do(true);
|
||||
291
net/rtsphelper/src/opnsense/scripts/net/rtsphelper/rtsphelper.py
Normal file
291
net/rtsphelper/src/opnsense/scripts/net/rtsphelper/rtsphelper.py
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
#!/usr/bin/python
|
||||
from __future__ import annotations
|
||||
import socket
|
||||
import select
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
from typing import Any
|
||||
|
||||
buffer_size = 4096
|
||||
delay = 0.0001
|
||||
|
||||
config_file = '/var/etc/rtsphelper.conf'
|
||||
config: dict[str, Any] = {}
|
||||
|
||||
FNULL = open(os.devnull, 'w')
|
||||
|
||||
# Type alias for permissions structure: ((mask, net), (port_min, port_max))
|
||||
PermType = tuple[tuple[int, int], tuple[str, str]]
|
||||
|
||||
class Forward:
|
||||
def __init__(self) -> None:
|
||||
self.forward: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
def start(self, host: str, port: int) -> socket.socket | bool:
|
||||
try:
|
||||
self.forward.connect((host, port))
|
||||
return self.forward
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
|
||||
class ProxyServer:
|
||||
input_list: list[Any] = []
|
||||
channel: dict[Any, Any] = {}
|
||||
clients: list[list[Any]] = []
|
||||
forward_to: list[str | int] = []
|
||||
perms: list[PermType] = []
|
||||
|
||||
def __init__(self, remoteHost: str, remotePort: int, portManager: PortManager, perms: list[PermType]) -> None:
|
||||
self.pm: PortManager = portManager
|
||||
self.forward_to = [remoteHost, remotePort]
|
||||
self.perms = perms
|
||||
self.server: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.server.bind(('127.0.0.1', 0))
|
||||
self.server.listen(200)
|
||||
self.pm.addLocalBinding(remoteHost, remotePort, self.server.getsockname()[1])
|
||||
self.input_list.append(self.server)
|
||||
|
||||
def main_loop(self) -> None:
|
||||
ss = select.select
|
||||
inputready, outputready, exceptready = ss(self.input_list, [], [])
|
||||
for self.s in inputready:
|
||||
if self.s == self.server:
|
||||
self.on_accept()
|
||||
break
|
||||
|
||||
try:
|
||||
self.data = self.s.recv(buffer_size)
|
||||
if len(self.data) == 0:
|
||||
self.on_close()
|
||||
break
|
||||
else:
|
||||
self.on_recv()
|
||||
except socket.error as e:
|
||||
self.on_close()
|
||||
|
||||
def on_accept(self) -> None:
|
||||
clientsock, clientaddr = self.server.accept()
|
||||
|
||||
if allowedIP(clientaddr[0], self.perms):
|
||||
forward = Forward().start(self.forward_to[0], self.forward_to[1]) # type: ignore
|
||||
if forward:
|
||||
self.clients.append([clientaddr,clientsock,forward])
|
||||
self.pm.addClient(clientaddr)
|
||||
self.input_list.append(clientsock)
|
||||
self.input_list.append(forward)
|
||||
self.channel[clientsock] = forward
|
||||
self.channel[forward] = clientsock
|
||||
else:
|
||||
print("Can't establish connection with remote server.")
|
||||
print("Closing connection with client side", clientaddr)
|
||||
clientsock.close()
|
||||
else:
|
||||
print("Forbidden client IP")
|
||||
clientsock.close()
|
||||
|
||||
def on_close(self) -> None:
|
||||
#remove objects from input_list
|
||||
self.input_list.remove(self.s)
|
||||
self.input_list.remove(self.channel[self.s])
|
||||
out = self.channel[self.s]
|
||||
# close the connection with client
|
||||
self.channel[out].close() # equivalent to do self.s.close()
|
||||
# close the connection with remote server
|
||||
self.channel[self.s].close()
|
||||
# delete both objects from channel dict
|
||||
del self.channel[out]
|
||||
del self.channel[self.s]
|
||||
|
||||
for c in self.clients:
|
||||
if c[1] == self.s:
|
||||
break
|
||||
self.clients.remove(c)
|
||||
self.pm.removeClient(c[0])
|
||||
|
||||
def on_recv(self) -> None:
|
||||
data = self.data
|
||||
# here we can parse and/or modify the data before send forward
|
||||
self.channel[self.s].send(data)
|
||||
for c in self.clients:
|
||||
if c[1] == self.s:
|
||||
self.parseData(data, c)
|
||||
break
|
||||
|
||||
def parseData(self, data: bytes, client: list[Any]) -> None:
|
||||
for line in data.splitlines():
|
||||
lineSplit = line.decode().split(':', 1)
|
||||
if lineSplit[0] == "Transport":
|
||||
for transportOpt in lineSplit[1].split(';'):
|
||||
if transportOpt.split('=')[0] == "client_port":
|
||||
askedPorts = transportOpt.split('=')[1].split('-')
|
||||
allowedPorts = []
|
||||
for port in askedPorts:
|
||||
if allowedPortForward(client[0][0], port, self.perms):
|
||||
allowedPorts.append(port)
|
||||
self.pm.updatePorts(client[0], allowedPorts)
|
||||
|
||||
class PortManager:
|
||||
forwardedPorts: dict[Any, list[str]] = {}
|
||||
localBindings: list[list[str | int]] = []
|
||||
allowedNets: list[str] = []
|
||||
|
||||
def __init__(self, perms: list[list[str]]) -> None:
|
||||
for perm in perms:
|
||||
network = perm[0]
|
||||
self.allowedNets.append(network)
|
||||
self.removeAll()
|
||||
self.applyRules()
|
||||
|
||||
def addClient(self, client: Any) -> None:
|
||||
self.forwardedPorts[client] = []
|
||||
|
||||
def updatePorts(self, client: Any, ports: list[str]) -> None:
|
||||
print("Forwarding ports for client " + client[0] + ". New list of ports is: {0}".format(ports))
|
||||
self.forwardedPorts[client] = ports
|
||||
self.applyRules()
|
||||
|
||||
|
||||
def removeClient(self, client: Any) -> None:
|
||||
print("Remove client: " + client[0])
|
||||
self.forwardedPorts.pop(client)
|
||||
self.applyRules()
|
||||
|
||||
def removeAll(self) -> None:
|
||||
f = open('/tmp/rtsphelper.rules', 'w')
|
||||
f.close()
|
||||
subprocess.call(['pfctl', '-a', 'rtsphelper', '-F', 'nat'], stdout=FNULL, stderr=subprocess.STDOUT)
|
||||
subprocess.call(['pfctl', '-a', 'rtsphelper', '-F', 'rules'], stdout=FNULL, stderr=subprocess.STDOUT)
|
||||
subprocess.call(['pfctl', '-a', 'rtsphelper', '-F', 'state'], stdout=FNULL, stderr=subprocess.STDOUT)
|
||||
|
||||
def addLocalBinding(self, ip: str, port: int, local_port: int) -> None:
|
||||
self.localBindings.append([ip, port, local_port])
|
||||
self.applyRules()
|
||||
|
||||
def applyRules(self) -> None:
|
||||
config_rule_1 = 'rdr inet proto tcp from any to {} port {} -> {} port {}\n'
|
||||
config_rule_2 = 'block in quick on {} proto tcp from any to {} port {}\n'
|
||||
config_rule_3 = 'pass in quick proto tcp from {} to {} port {}\n'
|
||||
|
||||
pass_rule = 'pass in quick on {} inet proto udp from any to {} port {} keep state label "{}"\n'
|
||||
rdr_rule = 'rdr on {} inet proto udp from any to any port {} -> {}\n'
|
||||
|
||||
f = open('/tmp/rtsphelper.rules', 'w')
|
||||
|
||||
for localBinding in self.localBindings:
|
||||
f.write(config_rule_1.format(localBinding[0], localBinding[1], '127.0.0.1', localBinding[2]))
|
||||
|
||||
for client,ports in self.forwardedPorts.items():
|
||||
ip = client[0]
|
||||
for port in ports:
|
||||
f.write(rdr_rule.format(config['ext_if'], port, ip))
|
||||
|
||||
f.write('\n')
|
||||
for localBinding in self.localBindings:
|
||||
f.write(config_rule_2.format(config['ext_if'], '127.0.0.1', localBinding[2]))
|
||||
for network in self.allowedNets:
|
||||
f.write(config_rule_3.format(network, '127.0.0.1', localBinding[2]))
|
||||
|
||||
for client,ports in self.forwardedPorts.items():
|
||||
ip = client[0]
|
||||
for port in ports:
|
||||
f.write(pass_rule.format(config['ext_if'], ip, port, 'RTSP'))
|
||||
|
||||
f.close()
|
||||
subprocess.call(['pfctl', '-a', 'rtsphelper', '-f', '/tmp/rtsphelper.rules'], stdout=FNULL)
|
||||
|
||||
|
||||
def writePidFile() -> None:
|
||||
pid = str(os.getpid())
|
||||
f = open('/var/run/rtsphelper.pid', 'w')
|
||||
f.write(pid)
|
||||
f.close()
|
||||
|
||||
def ip_to_u32(ip: str) -> int:
|
||||
return int(''.join('%02x' % int(d) for d in ip.split('.')), 16)
|
||||
|
||||
def allowedIP(ipstr: str, perms: list[PermType]) -> bool:
|
||||
ip = ip_to_u32(ipstr)
|
||||
for perm in perms:
|
||||
mask, net = perm[0]
|
||||
if ip & mask == net:
|
||||
return True
|
||||
return False
|
||||
|
||||
def allowedPortForward(ipstr: str, port: str, perms: list[PermType]) -> bool:
|
||||
if not allowedIP(ipstr, perms):
|
||||
return False
|
||||
else:
|
||||
ip = ip_to_u32(ipstr)
|
||||
for perm in perms:
|
||||
mask, net = perm[0]
|
||||
ports = perm[1]
|
||||
if ip & mask == net:
|
||||
if int(port) >= int(ports[0]) and int(port) <= int(ports[1]):
|
||||
return True
|
||||
return False
|
||||
|
||||
def buildPerms(perms: list[list[str]]) -> list[PermType]:
|
||||
masks: list[PermType] = []
|
||||
for perm in perms:
|
||||
cidr = perm[0]
|
||||
portRange = perm[1]
|
||||
if '/' in cidr:
|
||||
netstr, bits = cidr.split('/')
|
||||
mask: int = (0xffffffff << (32 - int(bits))) & 0xffffffff
|
||||
net: int = ip_to_u32(netstr) & mask
|
||||
else:
|
||||
mask = 0xffffffff
|
||||
net = ip_to_u32(cidr)
|
||||
masks.append(((mask, net), (min(portRange.split('-')[0],portRange.split('-')[1]),max(portRange.split('-')[0],portRange.split('-')[1]))))
|
||||
return masks
|
||||
|
||||
if __name__ == '__main__':
|
||||
writePidFile()
|
||||
|
||||
config['forward_to'] = []
|
||||
config['perms'] = []
|
||||
|
||||
with open(config_file, 'r') as cf:
|
||||
line = cf.readline()
|
||||
while line:
|
||||
key,value = line.strip().split('=')
|
||||
if key == 'ext_ifname':
|
||||
config['ext_if'] = value
|
||||
elif key == 'forward':
|
||||
config['forward_to'].append([value.split(':')[0],int(value.split(':')[1])])
|
||||
elif key == 'allow':
|
||||
config['perms'].append(value.split(' '))
|
||||
|
||||
line = cf.readline()
|
||||
|
||||
perms = buildPerms(config['perms'])
|
||||
servers: list[ProxyServer] = []
|
||||
|
||||
pm = PortManager(config['perms'])
|
||||
|
||||
for forward in config['forward_to']:
|
||||
servers.append(ProxyServer(forward[0], forward[1], pm, perms))
|
||||
|
||||
def handle_exit_signal(sig: int, frame: Any) -> None:
|
||||
handle_exit()
|
||||
|
||||
def handle_exit() -> None:
|
||||
print("Exiting...")
|
||||
pm.removeAll()
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGTERM, handle_exit_signal)
|
||||
try:
|
||||
while 1:
|
||||
time.sleep(delay)
|
||||
for server in servers:
|
||||
server.main_loop()
|
||||
except KeyboardInterrupt:
|
||||
print("Ctrl C - Stopping server")
|
||||
handle_exit()
|
||||
sys.exit(1)
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
[start]
|
||||
command:/usr/local/bin/python3 /usr/local/opnsense/scripts/net/rtsphelper/rtsphelper.py
|
||||
type:script
|
||||
message:starting rtsphelper
|
||||
|
||||
[stop]
|
||||
command:kill -TERM $(cat /var/run/rtsphelper.pid) 2> /dev/null; /sbin/pfctl -artsphelper -Fr 2> /dev/null; /sbin/pfctl -artsphelper -Fn 2> /dev/null; exit 0
|
||||
type:script
|
||||
message:stopping rtsphelper
|
||||
|
||||
[restart]
|
||||
command:kill -TERM $(cat /var/run/rtsphelper.pid) 2> /dev/null; /sbin/pfctl -artsphelper -Fr 2> /dev/null; /sbin/pfctl -artsphelper -Fn 2> /dev/null; /usr/local/bin/python3 /usr/local/opnsense/scripts/net/rtsphelper/rtsphelper.py
|
||||
type:script
|
||||
message:restarting rtsphelper
|
||||
|
||||
[status]
|
||||
command:if [ -f /var/run/rtsphelper.pid ] && pgrep -F /var/run/rtsphelper.pid > /dev/null; then echo "running"; else echo "stopped"; fi
|
||||
type:script_output
|
||||
message:get rtsphelper status
|
||||
|
||||
[connections]
|
||||
command:/sbin/pfctl -artsphelper -sn 2> /dev/null
|
||||
type:script_output
|
||||
message:list rtsphelper connections
|
||||
|
||||
[configure]
|
||||
command:/usr/local/bin/php /usr/local/opnsense/scripts/net/rtsphelper/configure.php
|
||||
type:script
|
||||
message:configuring rtsphelper
|
||||
279
net/rtsphelper/src/www/services_rtsphelper.php
Normal file
279
net/rtsphelper/src/www/services_rtsphelper.php
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
<?php
|
||||
|
||||
require_once("guiconfig.inc");
|
||||
require_once("interfaces.inc");
|
||||
require_once("filter.inc");
|
||||
require_once("system.inc");
|
||||
require_once("plugins.inc.d/rtsphelper.inc");
|
||||
|
||||
function rtsphelper_validate_ip($ip)
|
||||
{
|
||||
/* validate cidr */
|
||||
$ip_array = array();
|
||||
$ip_array = explode('/', $ip);
|
||||
if (count($ip_array) == 2) {
|
||||
if ($ip_array[1] < 1 || $ip_array[1] > 32) {
|
||||
return false;
|
||||
}
|
||||
} elseif (count($ip_array) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* validate ip */
|
||||
if (!is_ipaddr($ip_array[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function rtsphelper_validate_forward($forward)
|
||||
{
|
||||
$fw_array = array();
|
||||
$fw_array = explode(':', $forward);
|
||||
|
||||
if (!is_ipaddr($fw_array[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sub = $fw_array[1];
|
||||
if ($sub < 0 || $sub > 65535 || !is_numeric($sub)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function rtsphelper_validate_port($port)
|
||||
{
|
||||
foreach (explode('-', $port) as $sub) {
|
||||
if ($sub < 0 || $sub > 65535 || !is_numeric($sub)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
$pconfig = array();
|
||||
|
||||
$copy_fields = array('enable', 'ext_iface');
|
||||
|
||||
foreach (rtsphelper_permuser_list() as $permuser) {
|
||||
$copy_fields[] = $permuser;
|
||||
}
|
||||
|
||||
foreach (rtsphelper_forward_list() as $forward) {
|
||||
$copy_fields[] = $forward;
|
||||
}
|
||||
|
||||
foreach ($copy_fields as $fieldname) {
|
||||
if (isset($config['installedpackages']['rtsphelper']['config'][0][$fieldname])) {
|
||||
$pconfig[$fieldname] = $config['installedpackages']['rtsphelper']['config'][0][$fieldname];
|
||||
}
|
||||
}
|
||||
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$input_errors = array();
|
||||
$pconfig = $_POST;
|
||||
|
||||
/* user permissions validation */
|
||||
foreach (rtsphelper_permuser_list() as $i => $permuser) {
|
||||
if (!empty($pconfig[$permuser])) {
|
||||
$perm = explode(' ', $pconfig[$permuser]);
|
||||
/* should explode to 2 args */
|
||||
if (count($perm) != 2) {
|
||||
$input_errors[] = sprintf(gettext("You must follow the specified format in the 'User specified permissions %s' field"), $i);
|
||||
} else {
|
||||
/* verify port or port range */
|
||||
if (!rtsphelper_validate_port($perm[1]) ) {
|
||||
$input_errors[] = sprintf(gettext("You must specify a port or port range between 0 and 65535 in the 'User specified permissions %s' field"), $i);
|
||||
}
|
||||
/* verify ip address */
|
||||
if (!rtsphelper_validate_ip($perm[0])) {
|
||||
$input_errors[] = sprintf(gettext("You must specify a valid ip address in the 'User specified permissions %s' field"), $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (rtsphelper_forward_list() as $i => $forward) {
|
||||
if (!empty($pconfig[$forward])) {
|
||||
if (!rtsphelper_validate_forward($pconfig[$forward])) {
|
||||
$input_errors[] = sprintf(gettext("You must specify a valid ip and port in the 'Hosts to enable %s' field"), $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($input_errors) == 0) {
|
||||
// save form data
|
||||
$rtsp = array();
|
||||
// boolean types
|
||||
foreach (array('enable') as $fieldname) {
|
||||
$rtsp[$fieldname] = !empty($pconfig[$fieldname]);
|
||||
}
|
||||
// text field types
|
||||
foreach (array('ext_iface') as $fieldname) {
|
||||
$rtsp[$fieldname] = $pconfig[$fieldname];
|
||||
}
|
||||
foreach (rtsphelper_permuser_list() as $fieldname) {
|
||||
$rtsp[$fieldname] = $pconfig[$fieldname];
|
||||
}
|
||||
foreach (rtsphelper_forward_list() as $forward) {
|
||||
$rtsp[$forward] = $pconfig[$forward];
|
||||
}
|
||||
// sync to config
|
||||
$config['installedpackages']['rtsphelper']['config'] = $rtsp;
|
||||
|
||||
write_config('Modified RTSP Helper settings');
|
||||
rtsphelper_configure_do();
|
||||
filter_configure();
|
||||
header(url_safe('Location: /services_rtsphelper.php'));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$service_hook = 'rtsphelper';
|
||||
legacy_html_escape_form_data($pconfig);
|
||||
include("head.inc");
|
||||
?>
|
||||
<body>
|
||||
<?php include("fbegin.inc"); ?>
|
||||
<section class="page-content-main">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<?php if (isset($input_errors) && count($input_errors) > 0) print_input_errors($input_errors); ?>
|
||||
<form method="post" name="iform" id="iform">
|
||||
<section class="col-xs-12">
|
||||
<div class="content-box">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped opnsense_standard_table_form">
|
||||
<thead>
|
||||
<tr>
|
||||
<td style="width:22%">
|
||||
<strong><?=gettext("RTSP Helper Settings");?></strong>
|
||||
</td>
|
||||
<td style="width:78%; text-align:right">
|
||||
<small><?=gettext("full help"); ?> </small>
|
||||
<i class="fa fa-toggle-off text-danger" style="cursor: pointer;" id="show_all_help_page"></i>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><i class="fa fa-info-circle text-muted"></i> <?=gettext("Enable");?></td>
|
||||
<td>
|
||||
<input name="enable" type="checkbox" value="yes" <?=!empty($pconfig['enable']) ? "checked=\"checked\"" : ""; ?> />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="help_for_ext_iface" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext("External Interface");?></td>
|
||||
<td>
|
||||
<select class="selectpicker" name="ext_iface">
|
||||
<?php
|
||||
foreach (get_configured_interface_with_descr() as $iface => $ifacename):?>
|
||||
<option value="<?=$iface;?>" <?=$pconfig['ext_iface'] == $iface ? "selected=\"selected\"" : "";?>>
|
||||
<?=htmlspecialchars($ifacename);?>
|
||||
</option>
|
||||
<?php
|
||||
endforeach;?>
|
||||
</select>
|
||||
<div class="hidden" data-for="help_for_ext_iface">
|
||||
<?=gettext("Select only your primary WAN interface (interface with your default route). Only one interface is allowed here, not multiple.");?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="col-xs-12">
|
||||
<div class="content-box">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped opnsense_standard_table_form">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2"><?=gettext("Hosts to enable");?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach (rtsphelper_forward_list() as $i => $forward): ?>
|
||||
<tr>
|
||||
<?php if ($i == 1): ?>
|
||||
<td style="width:22%"><a id="help_for_forward" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext('Entry') . ' ' . $i ?></td>
|
||||
<?php else: ?>
|
||||
<td style="width:22%"><i class="fa fa-info-circle text-muted"></i> <?=gettext('Entry') . ' ' . $i ?></td>
|
||||
<?php endif ?>
|
||||
<td style="width:78%">
|
||||
<input name="<?= html_safe($forward) ?>" type="text" value="<?= $pconfig[$forward] ?>" />
|
||||
<?php if ($i == 1): ?>
|
||||
<div class="hidden" data-for="help_for_forward">
|
||||
<?=gettext("Format: [ip:port]");?><br/>
|
||||
<?=gettext("Example: 1.2.3.4:554");?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="col-xs-12">
|
||||
<div class="content-box">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped opnsense_standard_table_form">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2"><?=gettext("User specified permissions");?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach (rtsphelper_permuser_list() as $i => $permuser): ?>
|
||||
<tr>
|
||||
<?php if ($i == 1): ?>
|
||||
<td style="width:22%"><a id="help_for_permuser" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a> <?=gettext('Entry') . ' ' . $i ?></td>
|
||||
<?php else: ?>
|
||||
<td style="width:22%"><i class="fa fa-info-circle text-muted"></i> <?=gettext('Entry') . ' ' . $i ?></td>
|
||||
<?php endif ?>
|
||||
<td style="width:78%">
|
||||
<input name="<?= html_safe($permuser) ?>" type="text" value="<?= $pconfig[$permuser] ?>" />
|
||||
<?php if ($i == 1): ?>
|
||||
<div class="hidden" data-for="help_for_permuser">
|
||||
<?=gettext("Format: [int ipaddr or ipaddr/cdir] [int port or range]");?><br/>
|
||||
<?=gettext("Example: 192.168.0.0/24 1024-65535");?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="col-xs-12">
|
||||
<div class="content-box">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width:22%; vertical-align:top"> </td>
|
||||
<td style="width:78%">
|
||||
<input name="Submit" type="submit" class="btn btn-primary" value="<?=gettext("Save");?>" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php include("foot.inc"); ?>
|
||||
80
net/rtsphelper/src/www/status_rtsphelper.php
Normal file
80
net/rtsphelper/src/www/status_rtsphelper.php
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
require_once("guiconfig.inc");
|
||||
require_once("interfaces.inc");
|
||||
require_once("plugins.inc.d/rtsphelper.inc");
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (!empty($_POST['clear'])) {
|
||||
rtsphelper_stop();
|
||||
rtsphelper_start();
|
||||
header(url_safe('Location: /status_rtsphelper.php'));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$rdr_entries = array();
|
||||
exec("/sbin/pfctl -artsphelper -sn", $rdr_entries, $pf_ret);
|
||||
|
||||
$service_hook = 'rtsphelper';
|
||||
include("head.inc");
|
||||
?>
|
||||
<body>
|
||||
<?php include("fbegin.inc"); ?>
|
||||
|
||||
<section class="page-content-main">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<section class="col-xs-12">
|
||||
<div class="content-box">
|
||||
<?php
|
||||
if (empty($config['installedpackages']['rtsphelper']['config'][0]['enable'])): ?>
|
||||
<header class="content-box-head container-fluid">
|
||||
<h3><?= gettext('RTSP Helper is currently disabled.') ?></h3>
|
||||
</header>
|
||||
<?php
|
||||
else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?=gettext("Internal IP");?></td>
|
||||
<td><?=gettext("Int. Port");?></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ($rdr_entries as $rdr_entry):
|
||||
if (!preg_match("/on (.*) inet proto (.*) from (.*) to (.*) port = (.*) -> (.*)/", $rdr_entry, $matches)) {
|
||||
continue;
|
||||
}
|
||||
$rdr_ip = $matches[6];
|
||||
$rdr_iport = $matches[5];
|
||||
?>
|
||||
<tr>
|
||||
<td><?=$rdr_ip;?></td>
|
||||
<td><?=$rdr_iport;?></td>
|
||||
</tr>
|
||||
<?php
|
||||
endforeach;?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<form method="post">
|
||||
<button type="submit" name="clear" id="clear" class="btn btn-primary" value="Clear"><?=gettext("Clear");?></button>
|
||||
<?=gettext("all currently connected sessions");?>.
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<?php
|
||||
endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php include("foot.inc"); ?>
|
||||
Loading…
Reference in a new issue