Compare commits
No commits in common. "master" and "networks-upgrade" have entirely different histories.
master
...
networks-u
15
README.md
15
README.md
|
|
@ -1,5 +1,18 @@
|
||||||
# Protected Resources
|
# Protected Resources
|
||||||
Список ресурсов для настройки родительского контроля чтобы заблокировать доступ детям до запрещённых в РФ вражеских сервисов.
|
Список ресурсов для настройки родительского контроля чтобы заблокировать доступ детям до запрещённых в РФ вражеских сервисов.
|
||||||
|
|
||||||
## resources
|
## networks
|
||||||
Каталог с файлами сервисов и их подсетей
|
Каталог с файлами сервисов и их подсетей
|
||||||
|
|
||||||
|
### Установка
|
||||||
|
```shell
|
||||||
|
apk add php php-session php-curl jq git
|
||||||
|
cd /opt
|
||||||
|
git clone https://git.kirillius.ru/kirillius/protected-resources-list.git
|
||||||
|
cd /opt/protected-resources-list/
|
||||||
|
chmod -R +x ./bin
|
||||||
|
ln -s /opt/protected-resources-list/bin/webui /etc/init.d/webui
|
||||||
|
cp config.json.example config.json
|
||||||
|
rc-update add webui
|
||||||
|
/etc/init.d/webui start
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
import {JSONRPC} from "./jrpc.js";
|
||||||
|
|
||||||
|
const App = {
|
||||||
|
config: {},
|
||||||
|
networks: {},
|
||||||
|
RPC: JSONRPC
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
App.auth = async function () {
|
||||||
|
let authorized = await JSONRPC.__invoke("auth");
|
||||||
|
if (!authorized) {
|
||||||
|
do {
|
||||||
|
let pass = prompt("Password");
|
||||||
|
authorized = await JSONRPC.__invoke("auth", {
|
||||||
|
"password": pass
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!authorized) {
|
||||||
|
alert("Wrong password");
|
||||||
|
}
|
||||||
|
} while (!authorized);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.config = await JSONRPC.__invoke("getConfig");
|
||||||
|
this.networks = await JSONRPC.__invoke("getNetworks");
|
||||||
|
|
||||||
|
|
||||||
|
for (const key of this.config.plugins) {
|
||||||
|
$("body").append("<" + "script type='module' src='/plugins/" + key + "/plugin.js'><" + "/script>");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#loading").hide();
|
||||||
|
|
||||||
|
this.fillNetworks();
|
||||||
|
|
||||||
|
$("#panel").show();
|
||||||
|
|
||||||
|
let invalidNetworks = await JSONRPC.__invoke("getInvalidNetworks");
|
||||||
|
|
||||||
|
if (invalidNetworks.length > 0) {
|
||||||
|
$("body").append(`<span>There are invalid networks <BR>` + invalidNetworks.join("<BR>") + ` <BR></span>`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
App.fillNetworks = function () {
|
||||||
|
const that = this;
|
||||||
|
let proto = $("#net-table tr");
|
||||||
|
proto.detach();
|
||||||
|
|
||||||
|
for (const net in this.networks) {
|
||||||
|
let item = proto.clone();
|
||||||
|
item.find("input").prop('checked', this.config.networks.indexOf(net) !== -1).change(function () {
|
||||||
|
if ($(this).prop('checked')) {
|
||||||
|
that.config.networks.push(net);
|
||||||
|
} else {
|
||||||
|
that.config.networks = that.config.networks.filter(e => e !== net);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item.find("span").text(net);
|
||||||
|
$("#net-table").append(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
App.render = async function () {
|
||||||
|
await this.auth();
|
||||||
|
$("#save").click(function () {
|
||||||
|
const self = $(this);
|
||||||
|
self.prop("disabled", true);
|
||||||
|
(async function () {
|
||||||
|
await JSONRPC.__invoke("setConfig", App.config);
|
||||||
|
alert("Config saved!");
|
||||||
|
self.prop("disabled", false);
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export {App};
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Routing config</title>
|
||||||
|
<script src="jquery-3.7.1.min.js" type="text/javascript"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<span id="loading">Loading...</span>
|
||||||
|
|
||||||
|
<div style="display: none" id="panel">
|
||||||
|
Selected networks:
|
||||||
|
<table id="net-table">
|
||||||
|
<tr>
|
||||||
|
<td><input type="checkbox"></td>
|
||||||
|
<td><span>Network name</span></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div id="buttons">
|
||||||
|
<button id="save">Save</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import {App} from "./App.js";
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
App.render();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,59 @@
|
||||||
|
export const JSONRPC = {
|
||||||
|
url: "/rpc",
|
||||||
|
__id: 1,
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param method
|
||||||
|
* @param params
|
||||||
|
* @returns Object
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
__invoke: async function (method, params) {
|
||||||
|
if(params === undefined){
|
||||||
|
params = {};
|
||||||
|
}
|
||||||
|
const request = await JSONRPC.__performRequest(method, params);
|
||||||
|
|
||||||
|
if (!request.success) {
|
||||||
|
console.error(request.result);
|
||||||
|
throw new Error("Failed to invoke method " + method + " with params " + JSON.stringify(params));
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.result;
|
||||||
|
},
|
||||||
|
__performRequest: async function (method, params) {
|
||||||
|
const __this = this;
|
||||||
|
const resp = await fetch(
|
||||||
|
__this.url,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
mode: "cors",
|
||||||
|
cache: "no-cache",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
redirect: "follow",
|
||||||
|
referrerPolicy: "no-referrer",
|
||||||
|
body: JSON.stringify({
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
method: method,
|
||||||
|
params: params,
|
||||||
|
id: __this.__id++
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const success = resp.status === 200;
|
||||||
|
const result = (success ? (await resp.json()).result : {
|
||||||
|
"error": true,
|
||||||
|
"code": resp.status,
|
||||||
|
"status": resp.statusText,
|
||||||
|
"body": await resp.text()
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
"result": result,
|
||||||
|
"success": success
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/php
|
||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__) . "/loader.php";
|
||||||
|
require_once dirname(__DIR__) . "/plugins/openvpn/Openvpn.php";
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (!isset($argv[1])) {
|
||||||
|
throw new RuntimeException("Output config is not set");
|
||||||
|
}
|
||||||
|
|
||||||
|
$rpc = new StaticRPC();
|
||||||
|
$instance = $rpc->getPlugins()["openvpn"] ?? null;
|
||||||
|
if ($instance === null) {
|
||||||
|
throw new RuntimeException("Plugin is not enabled");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @var Custom $instance
|
||||||
|
*/
|
||||||
|
|
||||||
|
$config = $instance->getRoutingConfig();
|
||||||
|
$outfile = $argv[1];
|
||||||
|
file_put_contents($outfile, implode("\n", $config));
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "\nError:" . $e->getMessage() . "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/php
|
||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__) . "/loader.php";
|
||||||
|
require_once dirname(__DIR__) . "/plugins/netsync/Netsync.php";
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
|
||||||
|
$rpc = new StaticRPC();
|
||||||
|
$instance = $rpc->getPlugins()["netsync"] ?? null;
|
||||||
|
if ($instance === null) {
|
||||||
|
throw new RuntimeException("Plugin is not enabled");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @var Netsync $instance
|
||||||
|
*/
|
||||||
|
|
||||||
|
$config = $instance->sync();
|
||||||
|
|
||||||
|
var_dump($config);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "\nError:" . $e->getMessage() . "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/sbin/openrc-run
|
||||||
|
command="/opt/protected-resources-list/bin/webui-server"
|
||||||
|
command_background=true
|
||||||
|
pidfile="/run/${RC_SVCNAME}.pid"
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/sh
|
||||||
|
SELFDIR=`dirname $0`
|
||||||
|
ROOT=`realpath $SELFDIR/..`
|
||||||
|
CFGFILE=$ROOT/config.json
|
||||||
|
|
||||||
|
if ! test -f "$CFGFILE"; then
|
||||||
|
echo Config file $CFGFILE not found
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
HOST=`jq -r .web.host $CFGFILE`
|
||||||
|
PORT=`jq -r .web.port $CFGFILE`
|
||||||
|
#TODO FIXME !!!!
|
||||||
|
killall php
|
||||||
|
cd $ROOT
|
||||||
|
php $ROOT/loader.php --init
|
||||||
|
php -S $HOST:$PORT $ROOT/server.php
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Config implements ArrayAccess
|
||||||
|
{
|
||||||
|
private string $path;
|
||||||
|
|
||||||
|
public function asArray(): array
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fromArray($a)
|
||||||
|
{
|
||||||
|
$this->data = $a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->path = dirname(__DIR__) . "/config.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read(): void
|
||||||
|
{
|
||||||
|
$this->data = @json_decode(@file_get_contents($this->path), true);
|
||||||
|
if ($this->data == null) {
|
||||||
|
throw new RuntimeException("Failed to read or parse config file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save(): void
|
||||||
|
{
|
||||||
|
file_put_contents($this->path, json_encode($this->data,JSON_PRETTY_PRINT));
|
||||||
|
}
|
||||||
|
|
||||||
|
private mixed $data = [];
|
||||||
|
|
||||||
|
|
||||||
|
public function offsetExists(mixed $offset): bool
|
||||||
|
{
|
||||||
|
return isset($this->data[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetGet(mixed $offset): mixed
|
||||||
|
{
|
||||||
|
return $this->data[$offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetSet(mixed $offset, mixed $value): void
|
||||||
|
{
|
||||||
|
$this->data[$offset] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function offsetUnset(mixed $offset): void
|
||||||
|
{
|
||||||
|
unset($this->data[$offset]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
interface IPluggable
|
||||||
|
{
|
||||||
|
public function onServerStarted();
|
||||||
|
public function onInit(PluginContext $context);
|
||||||
|
public function onSync(array $remote_config);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class IPv4Subnet
|
||||||
|
{
|
||||||
|
|
||||||
|
private string $address;
|
||||||
|
private int $prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $subnetAddress
|
||||||
|
* @param int $prefix
|
||||||
|
*/
|
||||||
|
public function __construct(string $subnetAddress, int $prefix)
|
||||||
|
{
|
||||||
|
$this->address = $subnetAddress;
|
||||||
|
if (ip2long($subnetAddress) === false) {
|
||||||
|
throw new RuntimeException("Invalid subnet address: " . $subnetAddress);
|
||||||
|
}
|
||||||
|
$this->prefix = $prefix;
|
||||||
|
if ($prefix < 0 or $prefix > 32) {
|
||||||
|
throw new RuntimeException("Invalid subnet prefix: " . $prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFirstAddress()
|
||||||
|
{
|
||||||
|
$a = ip2long($this->address);
|
||||||
|
$mask = ip2long($this->getNetMask());
|
||||||
|
return long2ip($a & $mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLastAddress()
|
||||||
|
{
|
||||||
|
return long2ip(ip2long($this->getFirstAddress()) + $this->getAddressCount() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAddressCount()
|
||||||
|
{
|
||||||
|
return pow(2, 32 - $this->prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNetMask()
|
||||||
|
{
|
||||||
|
return long2ip(-1 << (32 - $this->prefix));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkConfigReader
|
||||||
|
{
|
||||||
|
private array $configs = [];
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$path = dirname(__DIR__) . "/networks";
|
||||||
|
foreach (new IteratorIterator(new DirectoryIterator($path)) as $file) {
|
||||||
|
/**
|
||||||
|
* @var SplFileInfo $file
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ($file->getExtension() === "json") {
|
||||||
|
$key = $file->getBasename(".json");
|
||||||
|
$value = @json_decode(@file_get_contents($file->getPathname()), true);
|
||||||
|
|
||||||
|
if ($value === null) {
|
||||||
|
throw new RuntimeException("Network file " . $file->getBasename() . " is invalid or cannot be read");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->configs[$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConfigs(): array
|
||||||
|
{
|
||||||
|
return $this->configs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class Plugin implements IPluggable
|
||||||
|
{
|
||||||
|
protected PluginContext $context;
|
||||||
|
protected array $config;
|
||||||
|
|
||||||
|
public function onServerStarted()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onSync($remote_config)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onInit(PluginContext $context): void
|
||||||
|
{
|
||||||
|
$this->context = $context;
|
||||||
|
$this->checkConfig();
|
||||||
|
$this->config = $this->context->getConfig()[$context->getName()];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkConfig(): void
|
||||||
|
{
|
||||||
|
$config = $this->context->getConfig();
|
||||||
|
$defaults = $this->context->getMetadata()["config"];
|
||||||
|
$name = $this->context->getName();
|
||||||
|
|
||||||
|
if (!isset($config[$name])) {
|
||||||
|
$config[$name] = $defaults;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($defaults as $key => $value) {
|
||||||
|
if (!isset($config[$name][$key])) {
|
||||||
|
$config[$name][$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function saveConfig()
|
||||||
|
{
|
||||||
|
$wrapper = $this->context->getConfig();
|
||||||
|
$wrapper[$this->context->getName()] = $this->config;
|
||||||
|
$wrapper->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class PluginContext
|
||||||
|
{
|
||||||
|
private RPC $RPC;
|
||||||
|
private Config $config;
|
||||||
|
private array $metadata;
|
||||||
|
private string $name;
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RPC $RPC
|
||||||
|
* @param Config $config
|
||||||
|
* @param array $metadata
|
||||||
|
* @param string $name
|
||||||
|
*/
|
||||||
|
public function __construct(RPC $RPC, Config $config, array $metadata, string $name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->RPC = $RPC;
|
||||||
|
$this->config = $config;
|
||||||
|
$this->metadata = $metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRPC(): RPC
|
||||||
|
{
|
||||||
|
return $this->RPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConfig(): Config
|
||||||
|
{
|
||||||
|
return $this->config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMetadata(): array
|
||||||
|
{
|
||||||
|
return $this->metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class RPC
|
||||||
|
{
|
||||||
|
protected Config $config;
|
||||||
|
protected array $plugins = [];
|
||||||
|
|
||||||
|
public function getPlugins(): array
|
||||||
|
{
|
||||||
|
return $this->plugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->config = new Config();
|
||||||
|
$this->config->read();
|
||||||
|
|
||||||
|
foreach ($this->config["plugins"] as $plugin) {
|
||||||
|
try {
|
||||||
|
$meta = $this->getPluginMetadata($plugin);
|
||||||
|
$inst = $this->loadPlugin($plugin, $meta["class"]);
|
||||||
|
$inst->onInit(new PluginContext($this, $this->config, $meta, $plugin));
|
||||||
|
} catch (Error $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPluginMetadata($name): array
|
||||||
|
{
|
||||||
|
$root = dirname(__DIR__) . "/plugins/" . $name;
|
||||||
|
if (!file_exists($root)) {
|
||||||
|
throw new RuntimeException("Plugin $name dir not found");
|
||||||
|
}
|
||||||
|
$metafile = $root . "/metadata.json";
|
||||||
|
if (!file_exists($metafile)) {
|
||||||
|
throw new RuntimeException("Plugin $name metadata not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
$meta = @json_decode(@file_get_contents($metafile), true);
|
||||||
|
|
||||||
|
if ($meta === null) {
|
||||||
|
throw new RuntimeException("Unable to parse $name plugin metadata");
|
||||||
|
}
|
||||||
|
return $meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadPlugin($name, $classname): IPluggable
|
||||||
|
{
|
||||||
|
$file = dirname(__DIR__) . "/plugins/" . $name . "/" . $classname . ".php";
|
||||||
|
if (!file_exists($file)) {
|
||||||
|
throw new RuntimeException("Plugin $name class $classname not found");
|
||||||
|
}
|
||||||
|
require_once $file;
|
||||||
|
|
||||||
|
$instance = new $classname();
|
||||||
|
if (!($instance instanceof IPluggable)) {
|
||||||
|
throw new RuntimeException("Class $classname have to implement IPluggable");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->plugins[$name] = $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConfig(): array
|
||||||
|
{
|
||||||
|
$this->checkAuth();
|
||||||
|
return $this->config->asArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setConfig($config): bool
|
||||||
|
{
|
||||||
|
$this->config->fromArray($config);
|
||||||
|
$this->config->save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkAuth(): void
|
||||||
|
{
|
||||||
|
$auth = $_SESSION["auth"] ?? false;
|
||||||
|
if (!$auth) {
|
||||||
|
throw new RuntimeException("Unauthorized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNetworks(): array
|
||||||
|
{
|
||||||
|
$this->checkAuth();
|
||||||
|
return (new NetworkConfigReader())->getConfigs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInvalidNetworks(): array
|
||||||
|
{
|
||||||
|
$this->checkAuth();
|
||||||
|
$invalid = [];
|
||||||
|
foreach ((new NetworkConfigReader())->getConfigs() as $config) {
|
||||||
|
foreach ($config["networks"] as $network) {
|
||||||
|
if (!RouteUtil::validateSubnet($network)) {
|
||||||
|
$invalid[] = $network;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function logout(): void
|
||||||
|
{
|
||||||
|
$_SESSION["auth"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function auth($params): bool
|
||||||
|
{
|
||||||
|
if (isset($params["password"])) {
|
||||||
|
if ($this->comparePassword($params["password"])) {
|
||||||
|
return $_SESSION["auth"] = true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $_SESSION["auth"] ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function comparePassword($passwd): bool
|
||||||
|
{
|
||||||
|
$pass = $this->config["password"];
|
||||||
|
if ($pass["type"] == "plaintext") {
|
||||||
|
return $pass["data"] == $passwd;
|
||||||
|
} else if ($pass["type"] == "hash") {
|
||||||
|
return $this->hash($passwd) == $pass["data"];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function hash($what): string
|
||||||
|
{
|
||||||
|
return md5(sha1($what) . md5($what));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isInternalMethod($name)
|
||||||
|
{
|
||||||
|
$cls = new ReflectionClass(IPluggable::class);
|
||||||
|
return $cls->hasMethod($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke($method, $args)
|
||||||
|
{
|
||||||
|
$parts = explode("::", $method);
|
||||||
|
$isPlugin = count($parts) > 1;
|
||||||
|
|
||||||
|
if ($isPlugin) {
|
||||||
|
$this->checkAuth();
|
||||||
|
$plugin = $this->plugins[$parts[0]];
|
||||||
|
$methodname = $parts[1];
|
||||||
|
if ($this->isInternalMethod($methodname)) {
|
||||||
|
throw new RuntimeException("Unable to invoke internal methods");
|
||||||
|
}
|
||||||
|
return call_user_func([$plugin, $methodname], $args);
|
||||||
|
} else {
|
||||||
|
$cls = new ReflectionClass(__CLASS__);
|
||||||
|
$method = $cls->getMethod($method);
|
||||||
|
if (!$method or !$method->isPublic()) {
|
||||||
|
throw new RuntimeException("Unable to find method");
|
||||||
|
}
|
||||||
|
return $method->invoke($this, $args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class RouteUtil
|
||||||
|
{
|
||||||
|
public static function validateSubnet($subnet)
|
||||||
|
{
|
||||||
|
$parts = explode("/", $subnet);
|
||||||
|
if (count($parts) != 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$ipv4subnet = new IPv4Subnet($parts[0], $parts[1]);
|
||||||
|
return $parts[0] == $ipv4subnet->getFirstAddress();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
class RoutingTableReader
|
||||||
|
{
|
||||||
|
private $routes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getRoutes(): array
|
||||||
|
{
|
||||||
|
return $this->routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$result = @shell_exec("ip --json route show");
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
throw new RuntimeException("Failed to read routing table");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$this->routes = @json_decode($result, true);
|
||||||
|
|
||||||
|
if ($this->routes === null) {
|
||||||
|
throw new RuntimeException("Failed to parse json output");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->routes as $key => &$route) {
|
||||||
|
if ($route["dst"] === "default") {
|
||||||
|
$route["dst"] = "0.0.0.0/0";
|
||||||
|
} elseif (!str_contains($route["dst"], "/")) {
|
||||||
|
$route["dst"] .= "/32";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class StaticRPC extends RPC
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlugins(): array
|
||||||
|
{
|
||||||
|
return $this->plugins;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class WebRouter
|
||||||
|
{
|
||||||
|
private string $requestBody;
|
||||||
|
private string $requestedFile;
|
||||||
|
private string $URI;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->requestBody = @file_get_contents('php://input');
|
||||||
|
$this->requestedFile = dirname(__DIR__) . "/" . $_SERVER["REQUEST_URI"];
|
||||||
|
$this->URI = $_SERVER["REQUEST_URI"];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleRequest(): void
|
||||||
|
{
|
||||||
|
@session_start();
|
||||||
|
try {
|
||||||
|
if (str_starts_with($this->URI, "/assets") or str_starts_with($this->URI, "/plugins") and str_ends_with($this->URI, ".js")) {
|
||||||
|
$this->handleAsset();
|
||||||
|
} elseif (!str_starts_with($this->URI, "/rpc")) {
|
||||||
|
$this->redirect("/assets/index.html");
|
||||||
|
} else {
|
||||||
|
$this->handleJRPC();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
session_write_close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleJRPC(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$request = @json_decode($this->requestBody, true);
|
||||||
|
if ($request === null) {
|
||||||
|
throw new RuntimeException("Failed to parse JRPC");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (["id", "jsonrpc", "method", "params"] as $param) {
|
||||||
|
if (!isset($request[$param])) {
|
||||||
|
throw new RuntimeException("Bad JRPC structure");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rpc = new RPC();
|
||||||
|
$response = $rpc($request["method"], $request["params"]);
|
||||||
|
|
||||||
|
header("content-type: application/json");
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
"jsonrpc" => "2.0",
|
||||||
|
"id" => $request["id"] ?? 0,
|
||||||
|
"result" => $response
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode([
|
||||||
|
"jsonrpc" => "2.0",
|
||||||
|
"id" => $request["id"],
|
||||||
|
"error" => $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleAsset(): void
|
||||||
|
{
|
||||||
|
if (!file_exists($this->requestedFile)) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo "File not found: " . $this->URI;
|
||||||
|
} else {
|
||||||
|
header("content-type: " . mime_content_type($this->requestedFile));
|
||||||
|
echo file_get_contents($this->requestedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function redirect($where): void
|
||||||
|
{
|
||||||
|
http_response_code(302);
|
||||||
|
header("location: " . $where);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
if (!function_exists('mime_content_type')) {
|
||||||
|
|
||||||
|
function mime_content_type($filename): bool|string
|
||||||
|
{
|
||||||
|
$mime_types = array(
|
||||||
|
'txt' => 'text/plain',
|
||||||
|
'htm' => 'text/html',
|
||||||
|
'html' => 'text/html',
|
||||||
|
'php' => 'text/html',
|
||||||
|
'css' => 'text/css',
|
||||||
|
'js' => 'application/javascript',
|
||||||
|
'json' => 'application/json',
|
||||||
|
'xml' => 'application/xml',
|
||||||
|
'swf' => 'application/x-shockwave-flash',
|
||||||
|
'flv' => 'video/x-flv',
|
||||||
|
|
||||||
|
// images
|
||||||
|
'png' => 'image/png',
|
||||||
|
'jpe' => 'image/jpeg',
|
||||||
|
'jpeg' => 'image/jpeg',
|
||||||
|
'jpg' => 'image/jpeg',
|
||||||
|
'gif' => 'image/gif',
|
||||||
|
'bmp' => 'image/bmp',
|
||||||
|
'ico' => 'image/vnd.microsoft.icon',
|
||||||
|
'tiff' => 'image/tiff',
|
||||||
|
'tif' => 'image/tiff',
|
||||||
|
'svg' => 'image/svg+xml',
|
||||||
|
'svgz' => 'image/svg+xml',
|
||||||
|
|
||||||
|
// archives
|
||||||
|
'zip' => 'application/zip',
|
||||||
|
'rar' => 'application/x-rar-compressed',
|
||||||
|
'exe' => 'application/x-msdownload',
|
||||||
|
'msi' => 'application/x-msdownload',
|
||||||
|
'cab' => 'application/vnd.ms-cab-compressed',
|
||||||
|
|
||||||
|
// audio/video
|
||||||
|
'mp3' => 'audio/mpeg',
|
||||||
|
'qt' => 'video/quicktime',
|
||||||
|
'mov' => 'video/quicktime',
|
||||||
|
|
||||||
|
// adobe
|
||||||
|
'pdf' => 'application/pdf',
|
||||||
|
'psd' => 'image/vnd.adobe.photoshop',
|
||||||
|
'ai' => 'application/postscript',
|
||||||
|
'eps' => 'application/postscript',
|
||||||
|
'ps' => 'application/postscript',
|
||||||
|
|
||||||
|
// ms office
|
||||||
|
'doc' => 'application/msword',
|
||||||
|
'rtf' => 'application/rtf',
|
||||||
|
'xls' => 'application/vnd.ms-excel',
|
||||||
|
'ppt' => 'application/vnd.ms-powerpoint',
|
||||||
|
|
||||||
|
// open office
|
||||||
|
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||||
|
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||||
|
);
|
||||||
|
|
||||||
|
$parts = explode('.', $filename);
|
||||||
|
$ext = strtolower(array_pop($parts));
|
||||||
|
if (array_key_exists($ext, $mime_types)) {
|
||||||
|
return $mime_types[$ext];
|
||||||
|
} elseif (function_exists('finfo_open')) {
|
||||||
|
$finfo = finfo_open(FILEINFO_MIME);
|
||||||
|
$mimetype = finfo_file($finfo, $filename);
|
||||||
|
finfo_close($finfo);
|
||||||
|
return $mimetype;
|
||||||
|
} else {
|
||||||
|
return 'application/octet-stream';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"password": {
|
||||||
|
"type": "plaintext",
|
||||||
|
"data": "admin"
|
||||||
|
},
|
||||||
|
"networks": [
|
||||||
|
"google"
|
||||||
|
],
|
||||||
|
"web": {
|
||||||
|
"port":8000,
|
||||||
|
"host":"0.0.0.0"
|
||||||
|
},
|
||||||
|
"plugins":["updates"]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/usr/bin/php
|
||||||
|
<?php
|
||||||
|
require_once __DIR__ . "/common.inc.php";
|
||||||
|
spl_autoload_register(function ($classname) {
|
||||||
|
require_once __DIR__ . "/classes/" . $classname . ".php";
|
||||||
|
});
|
||||||
|
if (isset($argv) and in_array("--init", $argv)) {
|
||||||
|
$rpc = new StaticRPC();
|
||||||
|
foreach ($rpc->getPlugins() as $plugin => $instance) {
|
||||||
|
/**
|
||||||
|
* @var IPluggable $instance
|
||||||
|
*/
|
||||||
|
$instance->onServerStarted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"description": "facebook, instagram, oculus",
|
||||||
|
"domains": [],
|
||||||
|
"networks": [
|
||||||
|
"213.102.128.0/24",
|
||||||
|
"204.15.20.0/22",
|
||||||
|
"199.201.0.0/16",
|
||||||
|
"185.89.0.0/16",
|
||||||
|
"185.60.216.0/21",
|
||||||
|
"179.60.0.0/16",
|
||||||
|
"173.252.0.0/16",
|
||||||
|
"173.194.10.0/24",
|
||||||
|
"164.163.191.64/26",
|
||||||
|
"163.70.0.0/16",
|
||||||
|
"157.240.0.0/16",
|
||||||
|
"147.75.0.0/16",
|
||||||
|
"142.250.0.0/15",
|
||||||
|
"129.134.0.0/16",
|
||||||
|
"103.4.0.0/16",
|
||||||
|
"102.221.0.0/16",
|
||||||
|
"102.132.0.0/16",
|
||||||
|
"99.84.0.0/16",
|
||||||
|
"87.245.208.0/24",
|
||||||
|
"77.240.43.0/24",
|
||||||
|
"74.119.0.0/16",
|
||||||
|
"69.171.0.0/16",
|
||||||
|
"69.63.0.0/16",
|
||||||
|
"66.220.0.0/16",
|
||||||
|
"57.141.0.0/16",
|
||||||
|
"57.144.222.0/24",
|
||||||
|
"45.64.0.0/16",
|
||||||
|
"45.130.4.0/24",
|
||||||
|
"31.13.0.0/16"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"description": "broadcom.com",
|
||||||
|
"domains": ["broadcom.com","omnissa.com"],
|
||||||
|
"networks": [
|
||||||
|
"172.66.0.165/32",
|
||||||
|
"162.159.140.167/32",
|
||||||
|
"54.68.22.26/32",
|
||||||
|
"50.112.202.115/32",
|
||||||
|
"52.13.171.212/32",
|
||||||
|
"2.19.183.16/32",
|
||||||
|
"2.19.183.47/32",
|
||||||
|
"129.153.117.201/32",
|
||||||
|
"23.73.4.0/24",
|
||||||
|
"184.50.200.7/32"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"description": "cloudflare",
|
||||||
|
"domains": [],
|
||||||
|
"networks": [
|
||||||
|
"173.245.48.0/20",
|
||||||
|
"103.21.244.0/22",
|
||||||
|
"103.22.200.0/22",
|
||||||
|
"103.31.4.0/22",
|
||||||
|
"141.101.64.0/18",
|
||||||
|
"108.162.192.0/18",
|
||||||
|
"190.93.240.0/20",
|
||||||
|
"188.114.96.0/20",
|
||||||
|
"197.234.240.0/22",
|
||||||
|
"198.41.128.0/17",
|
||||||
|
"162.158.0.0/15",
|
||||||
|
"104.16.0.0/13",
|
||||||
|
"104.24.0.0/14",
|
||||||
|
"172.64.0.0/13",
|
||||||
|
"131.0.72.0/22"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
{
|
{
|
||||||
"description": "amazon cloudfront",
|
"description": "amazon cloudfront",
|
||||||
"domains": [],
|
"domains": [],
|
||||||
"ASN": [],
|
"networks": [
|
||||||
"subnets": [
|
|
||||||
"108.138.0.0/15",
|
"108.138.0.0/15",
|
||||||
"108.156.0.0/14",
|
"108.156.0.0/14",
|
||||||
"111.13.0.0/16",
|
"111.13.0.0/16",
|
||||||
|
|
@ -3,8 +3,7 @@
|
||||||
"domains": [
|
"domains": [
|
||||||
"ultimaker.com"
|
"ultimaker.com"
|
||||||
],
|
],
|
||||||
"ASN": [],
|
"networks": [
|
||||||
"subnets": [
|
|
||||||
"188.114.98.0/23"
|
"188.114.98.0/23"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
{
|
{
|
||||||
"description": "discord",
|
"description": "discord",
|
||||||
"domains": [],
|
"domains": [],
|
||||||
"ASN": [],
|
"networks": [
|
||||||
"subnets": [
|
|
||||||
"104.16.0.0/12",
|
"104.16.0.0/12",
|
||||||
"108.177.14.207/32",
|
"108.177.14.207/32",
|
||||||
"138.128.140.240/28",
|
"138.128.140.240/28",
|
||||||
|
|
@ -3,8 +3,7 @@
|
||||||
"domains": [
|
"domains": [
|
||||||
"flibusta.is"
|
"flibusta.is"
|
||||||
],
|
],
|
||||||
"ASN": [],
|
"networks": [
|
||||||
"subnets": [
|
|
||||||
"179.43.150.83/32"
|
"179.43.150.83/32"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"description": "facebook, instagram, oculus",
|
||||||
|
"domains": ["githubusercontent.com", "github.com"],
|
||||||
|
"networks": [
|
||||||
|
"185.199.0.0/16"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
{
|
||||||
|
"description": "google services (youtube, mail)",
|
||||||
|
"domains": [
|
||||||
|
"youtube.com",
|
||||||
|
"googlevideo.com",
|
||||||
|
"ytimg.com",
|
||||||
|
"youtu.be",
|
||||||
|
"ggpht.com",
|
||||||
|
"nhacmp3youtube.com",
|
||||||
|
"googleusercontent.com",
|
||||||
|
"googleapis.com",
|
||||||
|
"gstatic.com"
|
||||||
|
],
|
||||||
|
"networks": [
|
||||||
|
"188.43.61.0/24",
|
||||||
|
"157.240.252.0/23",
|
||||||
|
"87.245.216.0/24",
|
||||||
|
"85.249.244.0/23",
|
||||||
|
"213.221.56.0/27",
|
||||||
|
"104.154.0.0/15",
|
||||||
|
"104.196.0.0/14",
|
||||||
|
"104.237.160.0/19",
|
||||||
|
"104.237.160.0/19",
|
||||||
|
"107.167.160.0/19",
|
||||||
|
"107.178.192.0/18",
|
||||||
|
"108.170.192.0/18",
|
||||||
|
"108.177.0.0/17",
|
||||||
|
"108.59.80.0/20",
|
||||||
|
"130.211.0.0/16",
|
||||||
|
"136.124.0.0/15",
|
||||||
|
"136.22.0.0/16",
|
||||||
|
"142.250.0.0/15",
|
||||||
|
"146.148.0.0/17",
|
||||||
|
"152.65.0.0/16",
|
||||||
|
"162.120.128.0/17",
|
||||||
|
"162.216.148.0/22",
|
||||||
|
"162.222.176.0/21",
|
||||||
|
"172.110.32.0/21",
|
||||||
|
"172.217.0.0/16",
|
||||||
|
"172.253.0.0/16",
|
||||||
|
"173.194.0.0/16",
|
||||||
|
"173.255.112.0/20",
|
||||||
|
"178.66.83.0/24",
|
||||||
|
"192.158.28.0/22",
|
||||||
|
"192.178.0.0/15",
|
||||||
|
"193.186.4.0/24",
|
||||||
|
"195.95.178.0/24",
|
||||||
|
"199.192.112.0/22",
|
||||||
|
"199.223.232.0/21",
|
||||||
|
"199.36.154.0/23",
|
||||||
|
"199.36.156.0/24",
|
||||||
|
"207.223.160.0/20",
|
||||||
|
"208.117.224.0/19",
|
||||||
|
"208.65.152.0/22",
|
||||||
|
"208.68.108.0/22",
|
||||||
|
"208.81.188.0/22",
|
||||||
|
"209.85.0.0/16",
|
||||||
|
"216.239.32.0/19",
|
||||||
|
"216.58.192.0/19",
|
||||||
|
"216.73.80.0/20",
|
||||||
|
"23.236.48.0/20",
|
||||||
|
"23.251.128.0/19",
|
||||||
|
"34.0.0.0/7",
|
||||||
|
"57.140.192.0/18",
|
||||||
|
"64.15.112.0/20",
|
||||||
|
"64.233.0.0/16",
|
||||||
|
"66.102.0.0/20",
|
||||||
|
"66.22.228.0/23",
|
||||||
|
"66.249.64.0/19",
|
||||||
|
"70.32.128.0/19",
|
||||||
|
"72.14.192.0/18",
|
||||||
|
"74.125.0.0/16",
|
||||||
|
"8.34.208.0/20",
|
||||||
|
"8.35.192.0/20",
|
||||||
|
"8.8.4.0/24",
|
||||||
|
"8.8.8.0/24"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"description": "habrahabr",
|
||||||
|
"domains": [
|
||||||
|
"habr.com"
|
||||||
|
],
|
||||||
|
"networks": [
|
||||||
|
"178.248.237.68/32"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"description": "intel website",
|
||||||
|
"domains": [
|
||||||
|
"intel.com"
|
||||||
|
],
|
||||||
|
"networks": [
|
||||||
|
"23.42.171.108/32"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
{
|
{
|
||||||
"description": "jetbrains market",
|
"description": "jetbrains market",
|
||||||
"domains": ["jetbrains.com"],
|
"domains": ["jetbrains.com"],
|
||||||
"ASN": [],
|
"networks": [
|
||||||
"subnets": [
|
|
||||||
"108.157.229.0/24",
|
"108.157.229.0/24",
|
||||||
"18.245.46.0/24",
|
"18.245.46.0/24",
|
||||||
"18.238.243.0/24",
|
"18.238.243.0/24",
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"description": "lostfilm",
|
||||||
|
"domains": [],
|
||||||
|
"networks": [
|
||||||
|
"104.21.0.0/17"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -3,8 +3,7 @@
|
||||||
"domains": [
|
"domains": [
|
||||||
"notion.so"
|
"notion.so"
|
||||||
],
|
],
|
||||||
"ASN": [],
|
"networks": [
|
||||||
"subnets": [
|
|
||||||
"104.18.39.102/32",
|
"104.18.39.102/32",
|
||||||
"172.64.148.154/32",
|
"172.64.148.154/32",
|
||||||
"208.103.161.0/30"
|
"208.103.161.0/30"
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"description": "google & cloudflare public dns",
|
||||||
|
"domains": [],
|
||||||
|
"networks": [
|
||||||
|
"1.1.1.1/32",
|
||||||
|
"1.0.0.1/32",
|
||||||
|
"8.8.8.8/32",
|
||||||
|
"8.8.4.4/32"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
{
|
{
|
||||||
"description": "rutor",
|
"description": "rutor",
|
||||||
"domains": ["rutor.info"],
|
"domains": ["rutor.info"],
|
||||||
"ASN": [],
|
"networks": [
|
||||||
"subnets": [
|
|
||||||
"193.46.255.29/32"
|
"193.46.255.29/32"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"description": "rutracker site & trackers",
|
||||||
|
"domains": [
|
||||||
|
"rutracker.org",
|
||||||
|
"rutracker.cc",
|
||||||
|
"t-ru.org"
|
||||||
|
],
|
||||||
|
"networks": [
|
||||||
|
"104.21.32.39/32",
|
||||||
|
"172.67.182.196/32",
|
||||||
|
"188.114.97.0/24",
|
||||||
|
"188.114.96.0/24",
|
||||||
|
"104.21.50.150/32",
|
||||||
|
"172.67.163.237/32"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"description": "www.snapeda.com",
|
||||||
|
"domains": ["www.snapeda.com"],
|
||||||
|
"networks": [
|
||||||
|
"104.26.0.159/32",
|
||||||
|
"104.26.1.159/32",
|
||||||
|
"172.67.71.169/32"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
class API extends Plugin
|
||||||
|
{
|
||||||
|
public function onInit(PluginContext $context): void
|
||||||
|
{
|
||||||
|
parent::onInit($context);
|
||||||
|
if (!isset($this->config["key"])) {
|
||||||
|
$this->generateNewKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers = getallheaders();
|
||||||
|
|
||||||
|
if ($headers and isset($headers["X-Auth"]) and $headers["X-Auth"] == md5($this->config["key"])) {
|
||||||
|
$_SESSION["auth"] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateNewKey(): string
|
||||||
|
{
|
||||||
|
$this->config["key"] = sha1(rand() . uniqid());
|
||||||
|
$this->saveConfig();
|
||||||
|
return $this->config["key"];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey(): string
|
||||||
|
{
|
||||||
|
return $this->config["key"];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"class": "API",
|
||||||
|
"config": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import {App} from "/assets/App.js";
|
||||||
|
|
||||||
|
(async function () {
|
||||||
|
let key = await App.RPC.__invoke("api::getKey");
|
||||||
|
|
||||||
|
$("body").append("<span>API Key: " + key + "</span>")
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Custom extends Plugin
|
||||||
|
{
|
||||||
|
public function onSync($remote_config)
|
||||||
|
{
|
||||||
|
if (!isset($remote_config["custom"])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->config = $remote_config["custom"];
|
||||||
|
$this->saveConfig();
|
||||||
|
|
||||||
|
$this->updateConfigFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateConfigFile()
|
||||||
|
{
|
||||||
|
$path = dirname(__DIR__, 2) . "/networks/custom.json";
|
||||||
|
$current_config = @file_get_contents($path);
|
||||||
|
$new_config = json_encode($this->config);
|
||||||
|
if($current_config !== $new_config) {
|
||||||
|
file_put_contents($path, $new_config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onInit(PluginContext $context): void
|
||||||
|
{
|
||||||
|
parent::onInit($context);
|
||||||
|
$this->updateConfigFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"class": "Custom",
|
||||||
|
"config": {
|
||||||
|
"description": "Custom routes",
|
||||||
|
"domains": [
|
||||||
|
],
|
||||||
|
"networks": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import {App} from "/assets/App.js";
|
||||||
|
|
||||||
|
(async function(){
|
||||||
|
await App.RPC.__invoke("custom::updateConfigFile")
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class BindPlugin extends Plugin
|
||||||
|
{
|
||||||
|
public function restart(): string|bool|null
|
||||||
|
{
|
||||||
|
$selectedDomains = [];
|
||||||
|
|
||||||
|
$networks = (new NetworkConfigReader())->getConfigs();
|
||||||
|
|
||||||
|
//add new routes
|
||||||
|
foreach ($this->context->getConfig()["networks"] as $key) {
|
||||||
|
|
||||||
|
if (isset($networks[$key])) {
|
||||||
|
foreach ($networks[$key]["domains"] as $domain) {
|
||||||
|
$selectedDomains[] = $domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$selectedDomains = array_unique($selectedDomains);
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach ($selectedDomains as $domain) {
|
||||||
|
$data[] = $this->createForwardRecord($domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($this->config["file"], implode("\n", $data));
|
||||||
|
|
||||||
|
|
||||||
|
return shell_exec($this->config["restart_cmd"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createForwardRecord($domain)
|
||||||
|
{
|
||||||
|
$fwd = implode(";", $this->config["forwarders"]) . ";";
|
||||||
|
return <<<TXT
|
||||||
|
zone "{$domain}" IN {
|
||||||
|
type forward;
|
||||||
|
forward only;
|
||||||
|
forwarders{{$fwd}};
|
||||||
|
};
|
||||||
|
TXT;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onServerStarted()
|
||||||
|
{
|
||||||
|
$this->restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onSync($remote_config)
|
||||||
|
{
|
||||||
|
$this->restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"class": "BindPlugin",
|
||||||
|
"config": {
|
||||||
|
"restart_cmd": "/etc/init.d/named restart",
|
||||||
|
"file": "/var/bind/forward.dns",
|
||||||
|
"forwarders": ["8.8.8.8", "1.1.1.1"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import {App} from "/assets/App.js";
|
||||||
|
|
||||||
|
(async function () {
|
||||||
|
$("#buttons").append(`<button id="restart-bind">Restart named</button>`);
|
||||||
|
$("#restart-bind").click(function () {
|
||||||
|
|
||||||
|
const self = $(this);
|
||||||
|
self.prop("disabled", true);
|
||||||
|
(async function () {
|
||||||
|
try {
|
||||||
|
alert(await App.RPC.__invoke("named::restart"));
|
||||||
|
} finally {
|
||||||
|
setTimeout(() => {
|
||||||
|
self.prop("disabled", false);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Netsync extends Plugin
|
||||||
|
{
|
||||||
|
public function sync()
|
||||||
|
{
|
||||||
|
$host = $this->config["master"];
|
||||||
|
$key = $this->config["key"];
|
||||||
|
|
||||||
|
if (empty($key)) {
|
||||||
|
throw new RuntimeException("API key is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
$ch = curl_init("http://" . $host . "/rpc");
|
||||||
|
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||||
|
"jsonrpc" => "2.0",
|
||||||
|
"id" => "1",
|
||||||
|
"method" => "getConfig",
|
||||||
|
"params" => []
|
||||||
|
]));
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"X-Auth: " . md5($key),
|
||||||
|
"Content-type: application/json"
|
||||||
|
]);
|
||||||
|
|
||||||
|
$output = curl_exec($ch);
|
||||||
|
try {
|
||||||
|
if ($err = curl_error($ch)) {
|
||||||
|
throw new RuntimeException("Failed to fetch remote API: " . $err);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
curl_close($ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
$header = @json_decode($output, true);
|
||||||
|
|
||||||
|
$remote_config = $header["result"] ?? null;
|
||||||
|
|
||||||
|
$networks = $remote_config["networks"] ?? null;
|
||||||
|
if ($remote_config === null or $networks === null) {
|
||||||
|
throw new RuntimeException("Response has invalid data");
|
||||||
|
}
|
||||||
|
|
||||||
|
$available = array_keys((new NetworkConfigReader())->getConfigs());
|
||||||
|
|
||||||
|
$remote_enabled = array_filter($networks, function ($e) use ($available) {
|
||||||
|
return in_array($e, $available);
|
||||||
|
});
|
||||||
|
$wrapper = $this->context->getConfig();
|
||||||
|
$local_enabled = $wrapper["networks"];
|
||||||
|
$diff = array_diff($remote_enabled, $local_enabled);
|
||||||
|
if (count($diff) > 0) {
|
||||||
|
$wrapper["networks"] = array_values($remote_enabled);
|
||||||
|
$wrapper->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$last_hash = $this->config["last_hash"] ?? "";
|
||||||
|
$current_hash = md5(json_encode($remote_config));
|
||||||
|
if ($last_hash != $current_hash) {
|
||||||
|
foreach ($this->context->getRPC()->getPlugins() as $plugin) {
|
||||||
|
/**
|
||||||
|
* @var IPluggable $plugin
|
||||||
|
*/
|
||||||
|
|
||||||
|
$plugin->onSync($remote_config);
|
||||||
|
}
|
||||||
|
$this->config["last_hash"] = $current_hash;
|
||||||
|
$this->saveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"class": "Netsync",
|
||||||
|
"config": {
|
||||||
|
"master": "127.0.0.1:8001",
|
||||||
|
"key": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import {App} from "/assets/App.js";
|
||||||
|
|
||||||
|
(async function () {
|
||||||
|
$("#buttons").append(`<button id="sync">Force sync</button>`);
|
||||||
|
$("#sync").click(function () {
|
||||||
|
|
||||||
|
const self = $(this);
|
||||||
|
self.prop("disabled", true);
|
||||||
|
(async function () {
|
||||||
|
try {
|
||||||
|
alert(await App.RPC.__invoke("netsync::sync"));
|
||||||
|
} finally {
|
||||||
|
setTimeout(() => {
|
||||||
|
location.reload();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Openvpn extends Plugin
|
||||||
|
{
|
||||||
|
|
||||||
|
public function restart()
|
||||||
|
{
|
||||||
|
//restart ovpn
|
||||||
|
return shell_exec($this->config["restart_cmd"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getRoutingConfig(): array
|
||||||
|
{
|
||||||
|
$networks = (new NetworkConfigReader())->getConfigs();
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
//add new routes
|
||||||
|
foreach ($this->context->getConfig() ["networks"] as $key) {
|
||||||
|
if (isset($networks[$key])) {
|
||||||
|
foreach ($networks[$key]["networks"] as $route) {
|
||||||
|
$parts = explode("/", $route);
|
||||||
|
$mask = long2ip(-1 << (32 - (int)$parts[1]));
|
||||||
|
$dst = $parts[0];
|
||||||
|
$data[] = "push \"route {$dst} {$mask}\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onServerStarted()
|
||||||
|
{
|
||||||
|
$this->restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onSync($remote_config)
|
||||||
|
{
|
||||||
|
$this->restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"class": "Openvpn",
|
||||||
|
"config": {
|
||||||
|
"restart_cmd": "/etc/init.d/openvpn restart"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import {App} from "/assets/App.js";
|
||||||
|
(async function () {
|
||||||
|
$("#buttons").append(`<button id="restart-ovpn">Restart openvpn server</button>`);
|
||||||
|
$("#restart-ovpn").click(function () {
|
||||||
|
if (confirm("Are you sure?")) {
|
||||||
|
const self = $(this);
|
||||||
|
self.prop("disabled", true);
|
||||||
|
(async function () {
|
||||||
|
try {
|
||||||
|
alert(await App.RPC.__invoke("openvpn::restart"));
|
||||||
|
} finally {
|
||||||
|
setTimeout(() => {
|
||||||
|
self.prop("disabled", false);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
class QuaggaPlugin extends Plugin
|
||||||
|
{
|
||||||
|
|
||||||
|
const REM_PREFIX = "! routes from file ";
|
||||||
|
|
||||||
|
public function restart(): string
|
||||||
|
{
|
||||||
|
$configfile = $this->config["file"];
|
||||||
|
|
||||||
|
if (!file_exists($configfile)) {
|
||||||
|
throw new RuntimeException("Quagga config file not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
$networks = (new NetworkConfigReader())->getConfigs();
|
||||||
|
$routeParser = new RoutingTableReader();
|
||||||
|
$routes = $routeParser->getRoutes();
|
||||||
|
$defGatewayInterface = "";
|
||||||
|
$defGateway = "";
|
||||||
|
|
||||||
|
foreach ($routes as $route) {
|
||||||
|
if ($route["dst"] === "0.0.0.0/0") {
|
||||||
|
$defGatewayInterface = $route["dev"];
|
||||||
|
$defGateway = $route["gateway"];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$defGatewayInterface) {
|
||||||
|
throw new RuntimeException("Failed to detect default gateway interface");
|
||||||
|
}
|
||||||
|
|
||||||
|
$contents = file_get_contents($configfile);
|
||||||
|
$lines = explode("\n", $contents);
|
||||||
|
|
||||||
|
//remove existing routes
|
||||||
|
foreach ($lines as $key => $line) {
|
||||||
|
if (str_starts_with($line, self::REM_PREFIX) or str_starts_with($line, "ip route ") and str_contains($line . " ", $defGateway . " ")) {
|
||||||
|
unset($lines[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//add new routes
|
||||||
|
foreach ($this->context->getConfig()["networks"] as $key) {
|
||||||
|
$lines[] = self::REM_PREFIX . $key;
|
||||||
|
if (isset($networks[$key])) {
|
||||||
|
foreach ($networks[$key]["networks"] as $route) {
|
||||||
|
$lines[] = "ip route " . $route . " " . $defGateway;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($lines as $key => $line) {
|
||||||
|
if (trim($line) === "") {
|
||||||
|
unset($lines[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$backupFile = $configfile . ".sav";
|
||||||
|
|
||||||
|
unlink($backupFile);
|
||||||
|
rename($configfile, $backupFile);
|
||||||
|
file_put_contents($configfile, implode("\n", $lines));
|
||||||
|
|
||||||
|
sleep(10);
|
||||||
|
|
||||||
|
//restart zebra
|
||||||
|
return shell_exec($this->config["restart_cmd"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onServerStarted()
|
||||||
|
{
|
||||||
|
$this->restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onSync($remote_config)
|
||||||
|
{
|
||||||
|
$this->restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"class": "QuaggaPlugin",
|
||||||
|
"config": {
|
||||||
|
"restart_cmd": "/etc/init.d/zebra restart",
|
||||||
|
"file": "/etc/quagga/zebra.conf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import {App} from "/assets/App.js";
|
||||||
|
(async function () {
|
||||||
|
$("#buttons").append(`<button id="restart-quagga">Restart quagga</button>`);
|
||||||
|
$("#restart-quagga").click(function () {
|
||||||
|
if (confirm("Are you sure?")) {
|
||||||
|
const self = $(this);
|
||||||
|
self.prop("disabled", true);
|
||||||
|
(async function () {
|
||||||
|
try {
|
||||||
|
alert(await App.RPC.__invoke("quagga::restart"));
|
||||||
|
} finally {
|
||||||
|
setTimeout(() => {
|
||||||
|
self.prop("disabled", false);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Updates extends Plugin
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public function check(): ?bool
|
||||||
|
{
|
||||||
|
$data = str_replace("=", "", @shell_exec("git --no-pager fetch --dry-run --porcelain --verbose 2>&1 | grep refs"));
|
||||||
|
$parts = explode(" ", trim($data));
|
||||||
|
if (count($parts) < 3) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parts[0] != $parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function install(): string|bool|null
|
||||||
|
{
|
||||||
|
return @shell_exec("git --no-pager pull --verbose 2>&1");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"class": "Updates",
|
||||||
|
"config": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import {JSONRPC} from "/assets/jrpc.js";
|
||||||
|
|
||||||
|
(async function () {
|
||||||
|
$("#panel").prepend(`<div id="update-panel">Checking for updates...</div><hr>`);
|
||||||
|
let state = (await JSONRPC.__invoke("updates::check"));
|
||||||
|
if (state === null) {
|
||||||
|
$("#update-panel").html(`<span style="color:red;">Error checking updates</span>`);
|
||||||
|
} else if (state === false) {
|
||||||
|
$("#update-panel").html(`<span style="color:black;">There is no updates</span>`);
|
||||||
|
} else if (state === true) {
|
||||||
|
$("#update-panel").html(`<span style="color:green;">Some updates are available</span> <button>Update</button>`);
|
||||||
|
$("#update-panel button").click(async function () {
|
||||||
|
$("#panel").hide();
|
||||||
|
$("#loading").show().text("Installing updates...");
|
||||||
|
try {
|
||||||
|
alert(await JSONRPC.__invoke("updates::install"));
|
||||||
|
} finally {
|
||||||
|
setTimeout(() => location.reload(), 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
{
|
|
||||||
"description": "MEGA.nz",
|
|
||||||
"domains": [
|
|
||||||
"mega.nz",
|
|
||||||
"mega.co.nz",
|
|
||||||
"mega.io"
|
|
||||||
],
|
|
||||||
"ASN": [
|
|
||||||
205809,
|
|
||||||
203055
|
|
||||||
],
|
|
||||||
"subnets": [
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
32934,
|
32934,
|
||||||
149642
|
149642
|
||||||
],
|
],
|
||||||
"subnets": [
|
"networks": [
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"description": "cloudflare",
|
|
||||||
"domains": [],
|
|
||||||
"ASN": [395747, 394536, 209242, 203898, 202623, 14789, 139242, 133877, 13335, 132892],
|
|
||||||
"subnets": [
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
{
|
|
||||||
"description": "factory AI",
|
|
||||||
"domains": [
|
|
||||||
"factory.ai"
|
|
||||||
],
|
|
||||||
"ASN": [],
|
|
||||||
"subnets": [
|
|
||||||
"76.76.21.0/24",
|
|
||||||
"66.33.60.0/24",
|
|
||||||
"216.150.1.1/32"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"description": "github",
|
|
||||||
"domains": ["githubusercontent.com", "github.com"],
|
|
||||||
"ASN": [36459],
|
|
||||||
"subnets": [
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -13,202 +13,13 @@
|
||||||
"gmail.com",
|
"gmail.com",
|
||||||
"doubleclick.net",
|
"doubleclick.net",
|
||||||
"google-analytics.com",
|
"google-analytics.com",
|
||||||
|
"docs.google.com",
|
||||||
|
"drive.google.com",
|
||||||
"blogger.com",
|
"blogger.com",
|
||||||
"android.com",
|
"android.com",
|
||||||
"firebaseapp.com",
|
"firebaseapp.com",
|
||||||
"appspot.com",
|
"appspot.com",
|
||||||
"g.co",
|
"g.co"
|
||||||
"google.ac",
|
|
||||||
"google.ad",
|
|
||||||
"google.ae",
|
|
||||||
"google.al",
|
|
||||||
"google.am",
|
|
||||||
"google.as",
|
|
||||||
"google.at",
|
|
||||||
"google.az",
|
|
||||||
"google.ba",
|
|
||||||
"google.be",
|
|
||||||
"google.bf",
|
|
||||||
"google.bg",
|
|
||||||
"google.bi",
|
|
||||||
"google.bj",
|
|
||||||
"google.bs",
|
|
||||||
"google.bt",
|
|
||||||
"google.by",
|
|
||||||
"google.ca",
|
|
||||||
"google.cat",
|
|
||||||
"google.cd",
|
|
||||||
"google.cf",
|
|
||||||
"google.cg",
|
|
||||||
"google.ch",
|
|
||||||
"google.ci",
|
|
||||||
"google.cl",
|
|
||||||
"google.cm",
|
|
||||||
"google.cn",
|
|
||||||
"google.co.ao",
|
|
||||||
"google.co.bw",
|
|
||||||
"google.co.ck",
|
|
||||||
"google.co.cr",
|
|
||||||
"google.co.id",
|
|
||||||
"google.co.il",
|
|
||||||
"google.co.in",
|
|
||||||
"google.co.jp",
|
|
||||||
"google.co.ke",
|
|
||||||
"google.co.kr",
|
|
||||||
"google.co.ls",
|
|
||||||
"google.co.ma",
|
|
||||||
"google.co.mz",
|
|
||||||
"google.co.nz",
|
|
||||||
"google.co.th",
|
|
||||||
"google.co.tz",
|
|
||||||
"google.co.ug",
|
|
||||||
"google.co.uk",
|
|
||||||
"google.co.uz",
|
|
||||||
"google.co.ve",
|
|
||||||
"google.co.vi",
|
|
||||||
"google.co.za",
|
|
||||||
"google.co.zm",
|
|
||||||
"google.co.zw",
|
|
||||||
"google.com.af",
|
|
||||||
"google.com.ag",
|
|
||||||
"google.com.ai",
|
|
||||||
"google.com.ar",
|
|
||||||
"google.com.au",
|
|
||||||
"google.com.bd",
|
|
||||||
"google.com.bh",
|
|
||||||
"google.com.bn",
|
|
||||||
"google.com.bo",
|
|
||||||
"google.com.br",
|
|
||||||
"google.com.bz",
|
|
||||||
"google.com.co",
|
|
||||||
"google.com.cu",
|
|
||||||
"google.com.cy",
|
|
||||||
"google.com.do",
|
|
||||||
"google.com.ec",
|
|
||||||
"google.com.eg",
|
|
||||||
"google.com.et",
|
|
||||||
"google.com.fj",
|
|
||||||
"google.com.gh",
|
|
||||||
"google.com.gi",
|
|
||||||
"google.com.gt",
|
|
||||||
"google.com.hk",
|
|
||||||
"google.com.jm",
|
|
||||||
"google.com.jo",
|
|
||||||
"google.com.kh",
|
|
||||||
"google.com.kw",
|
|
||||||
"google.com.lb",
|
|
||||||
"google.com.ly",
|
|
||||||
"google.com.mm",
|
|
||||||
"google.com.mt",
|
|
||||||
"google.com.mx",
|
|
||||||
"google.com.my",
|
|
||||||
"google.com.na",
|
|
||||||
"google.com.ng",
|
|
||||||
"google.com.ni",
|
|
||||||
"google.com.np",
|
|
||||||
"google.com.om",
|
|
||||||
"google.com.pa",
|
|
||||||
"google.com.pe",
|
|
||||||
"google.com.pg",
|
|
||||||
"google.com.ph",
|
|
||||||
"google.com.pk",
|
|
||||||
"google.com.pr",
|
|
||||||
"google.com.py",
|
|
||||||
"google.com.qa",
|
|
||||||
"google.com.sa",
|
|
||||||
"google.com.sb",
|
|
||||||
"google.com.sg",
|
|
||||||
"google.com.sl",
|
|
||||||
"google.com.sv",
|
|
||||||
"google.com.tj",
|
|
||||||
"google.com.tr",
|
|
||||||
"google.com.tw",
|
|
||||||
"google.com.ua",
|
|
||||||
"google.com.uy",
|
|
||||||
"google.com.vc",
|
|
||||||
"google.com.vn",
|
|
||||||
"google.cv",
|
|
||||||
"google.cz",
|
|
||||||
"google.de",
|
|
||||||
"google.dj",
|
|
||||||
"google.dk",
|
|
||||||
"google.dm",
|
|
||||||
"google.dz",
|
|
||||||
"google.ee",
|
|
||||||
"google.es",
|
|
||||||
"google.fi",
|
|
||||||
"google.fm",
|
|
||||||
"google.fr",
|
|
||||||
"google.ga",
|
|
||||||
"google.ge",
|
|
||||||
"google.gg",
|
|
||||||
"google.gl",
|
|
||||||
"google.gm",
|
|
||||||
"google.gp",
|
|
||||||
"google.gr",
|
|
||||||
"google.gy",
|
|
||||||
"google.hn",
|
|
||||||
"google.hr",
|
|
||||||
"google.ht",
|
|
||||||
"google.hu",
|
|
||||||
"google.ie",
|
|
||||||
"google.im",
|
|
||||||
"google.iq",
|
|
||||||
"google.is",
|
|
||||||
"google.it",
|
|
||||||
"google.je",
|
|
||||||
"google.jo",
|
|
||||||
"google.kg",
|
|
||||||
"google.ki",
|
|
||||||
"google.kz",
|
|
||||||
"google.la",
|
|
||||||
"google.li",
|
|
||||||
"google.lk",
|
|
||||||
"google.lt",
|
|
||||||
"google.lu",
|
|
||||||
"google.lv",
|
|
||||||
"google.md",
|
|
||||||
"google.me",
|
|
||||||
"google.mg",
|
|
||||||
"google.mk",
|
|
||||||
"google.ml",
|
|
||||||
"google.mn",
|
|
||||||
"google.ms",
|
|
||||||
"google.mu",
|
|
||||||
"google.mv",
|
|
||||||
"google.mw",
|
|
||||||
"google.ne",
|
|
||||||
"google.nl",
|
|
||||||
"google.no",
|
|
||||||
"google.nr",
|
|
||||||
"google.nu",
|
|
||||||
"google.pl",
|
|
||||||
"google.pn",
|
|
||||||
"google.ps",
|
|
||||||
"google.pt",
|
|
||||||
"google.ro",
|
|
||||||
"google.rs",
|
|
||||||
"google.ru",
|
|
||||||
"google.rw",
|
|
||||||
"google.sc",
|
|
||||||
"google.se",
|
|
||||||
"google.sh",
|
|
||||||
"google.si",
|
|
||||||
"google.sk",
|
|
||||||
"google.sn",
|
|
||||||
"google.so",
|
|
||||||
"google.sr",
|
|
||||||
"google.st",
|
|
||||||
"google.td",
|
|
||||||
"google.tg",
|
|
||||||
"google.tl",
|
|
||||||
"google.tm",
|
|
||||||
"google.tn",
|
|
||||||
"google.to",
|
|
||||||
"google.tt",
|
|
||||||
"google.vg",
|
|
||||||
"google.vu",
|
|
||||||
"google.ws"
|
|
||||||
],
|
],
|
||||||
"ASN": [
|
"ASN": [
|
||||||
6432,
|
6432,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"description": "lostfilm",
|
|
||||||
"domains": ["lostfilm.tv", "lostfilm.one"],
|
|
||||||
"ASN": [],
|
|
||||||
"subnets": [
|
|
||||||
"104.21.0.0/17"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -6,13 +6,12 @@
|
||||||
"t-ru.org"
|
"t-ru.org"
|
||||||
],
|
],
|
||||||
"ASN": [],
|
"ASN": [],
|
||||||
"subnets": [
|
"networks": [
|
||||||
"104.21.32.39/32",
|
"104.21.32.39/32",
|
||||||
"172.67.182.196/32",
|
"172.67.182.196/32",
|
||||||
"188.114.97.0/24",
|
"188.114.97.0/24",
|
||||||
"188.114.96.0/24",
|
"188.114.96.0/24",
|
||||||
"104.21.50.150/32",
|
"104.21.50.150/32",
|
||||||
"172.67.163.237/32",
|
"172.67.163.237/32"
|
||||||
"188.186.154.0/24"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"description": "snapeda",
|
|
||||||
"domains": ["snapeda.com"],
|
|
||||||
"ASN": [],
|
|
||||||
"subnets": [
|
|
||||||
"104.20.16.0/20",
|
|
||||||
"172.66.144.0/20"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
{
|
|
||||||
"description": "telegram",
|
|
||||||
"domains": [
|
|
||||||
"t.me",
|
|
||||||
"telegram.me",
|
|
||||||
"telegram.org",
|
|
||||||
"nicegram.app",
|
|
||||||
"telesco.pe",
|
|
||||||
"tg.dev"
|
|
||||||
],
|
|
||||||
"ASN": [
|
|
||||||
62041,
|
|
||||||
62014,
|
|
||||||
59930,
|
|
||||||
44907,
|
|
||||||
211157
|
|
||||||
],
|
|
||||||
"subnets": [
|
|
||||||
"91.108.4.0/22",
|
|
||||||
"91.108.8.0/22",
|
|
||||||
"91.108.12.0/22",
|
|
||||||
"91.108.16.0/22",
|
|
||||||
"91.108.20.0/22",
|
|
||||||
"91.108.56.0/22",
|
|
||||||
"91.105.192.0/23",
|
|
||||||
"95.161.64.0/20",
|
|
||||||
"149.154.160.0/20",
|
|
||||||
"185.76.151.0/24"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$memory = trim(shell_exec("free -b | grep Mem:"));
|
||||||
|
$memory = str_replace(" ", " ", $memory);
|
||||||
|
while (str_contains($memory, " ")) $memory = str_replace(" ", " ", $memory);
|
||||||
|
$items = explode(" ", $memory);
|
||||||
|
$available = intval($items[count($items) - 1]) / 1024 / 1024;
|
||||||
|
|
||||||
|
if ($available < 25) {
|
||||||
|
shell_exec("systemctl restart grass");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
require_once __DIR__ . "/loader.php";
|
||||||
|
$router = new WebRouter();
|
||||||
|
$router->handleRequest();
|
||||||
Loading…
Reference in New Issue