Compare commits

..

10 Commits

Author SHA1 Message Date
mercury 8ba7ddedee update version 2024-11-28 20:04:53 +04:00
mercury 19ef567873 fix createruleset 2024-11-28 19:51:21 +04:00
mercury 4f4e36e1cb doh ip exclude for analyze ip 2024-11-28 15:17:42 +04:00
mercury 6267931475 fix bug createruleset for singbox 2024-11-28 12:59:06 +04:00
mercury 7e2d90e7da update prioritet clash rules 2024-11-28 10:36:10 +04:00
mercury 28906176f5 update clash links 2024-11-28 00:21:22 +04:00
mercury 968d72ce81 autoscan call fix 2024-11-28 00:12:12 +04:00
mercury 66e204e5ba import url for clash 2024-11-27 23:37:39 +04:00
mercury 36e8acd2aa xray: template for clash 2024-11-27 22:00:30 +04:00
mercury cdc20adccd consistent template logic 2024-11-27 16:52:30 +04:00
5 changed files with 327 additions and 139 deletions
+195 -131
View File
@@ -37,7 +37,7 @@ class Bot
'xray',
];
$this->reg = '~' . implode('|', [
'GET /ws(?:.+)? HTTP',
'GET /ws HTTP',
'GET /adguard/(?:.+)? HTTP',
'GET /webapp(?:.+)? HTTP',
'GET /pac(?:.+)? HTTP',
@@ -1192,10 +1192,10 @@ class Bot
try {
$pac = $this->getPacConf();
if (!empty($pac['autoscan'])) {
$r = $this->analysisIp(return: 1);
require __DIR__ . '/config.php';
if (!empty($c['admin']) && (empty($this->time3) || ((time() - $this->time3) > $pac['autoscan_timeout']))) {
$this->time3 = time();
$r = $this->analysisIp(return: 1);
if (!empty($r)) {
foreach ($r as $k => $v) {
foreach ($v as $i) {
@@ -3828,12 +3828,6 @@ DNS-over-HTTPS with IP:
$p = $this->getPacConf();
$text[] = "Menu -> " . $this->i18n('xray') . ' -> ' . $this->i18n('routes') . ' -> proxy list';
[$data] = $this->listPac('includelist', $page, 'xtlsproxy');
$data[] = [
[
'text' => 'set to ' . ($p['domains_outbound'] ? ($p['outbound'] ?: 'proxy') : 'direct'),
'callback_data' => "/domainsOutbound",
],
];
$data[] = [
[
'text' => $this->i18n('back'),
@@ -3886,12 +3880,6 @@ DNS-over-HTTPS with IP:
[$data] = $this->listPac('packagelist', $page, 'xtlsapp');
$p = $this->getPacConf();
$data[] = [
[
'text' => 'set to ' . ($p['app_outbound'] ? ($p['outbound'] ?: 'proxy') : 'direct'),
'callback_data' => "/appOutbound",
],
];
$data[] = [
[
'text' => $this->i18n('back'),
@@ -3912,12 +3900,6 @@ DNS-over-HTTPS with IP:
[$data] = $this->listPac('processlist', $page, 'xtlsprocess');
$p = $this->getPacConf();
$data[] = [
[
'text' => 'set to ' . ($p['process_outbound'] ? ($p['outbound'] ?: 'proxy') : 'direct'),
'callback_data' => "/processOutbound",
],
];
$data[] = [
[
'text' => $this->i18n('back'),
@@ -4413,6 +4395,17 @@ DNS-over-HTTPS with IP:
}
fclose($r);
}
if ($r = fopen('/logs/nginx_doh_access', 'r')) {
while (feof($r) === false) {
$l = fgets($r);
if (preg_match('~(\d+\.\d+\.\d+\.\d+)~', $l, $m)) {
if (!in_array("{$m[1]}/32", $xr)) {
$xr[] = "{$m[1]}/32";
}
}
}
fclose($r);
}
if ($r = fopen('/logs/xray', 'r')) {
while (feof($r) === false) {
$l = fgets($r);
@@ -5342,6 +5335,10 @@ DNS-over-HTTPS with IP:
$text[] = <<<TEXT
<code>~outbound~</code>
<code>"~pac~"</code>
<code>~package~</code>
<code>~process~</code>
<code>~block~</code>
<code>~warp~</code>
<code>~dns~</code>
<code>~uid~</code>
<code>~domain~</code>
@@ -5350,10 +5347,6 @@ DNS-over-HTTPS with IP:
<code>~short_id~</code>
<code>~public_key~</code>
<code>~server_name~</code>
<code>~app_outbound~</code>
<code>~process_outbound~</code>
<code>~domains_outbound~</code>
<code>~final_outbound~</code>
<code>~ip~</code>
TEXT;
$templates = $pac["{$type}templates"];
@@ -5500,6 +5493,10 @@ DNS-over-HTTPS with IP:
'text' => $this->i18n('sing-box templates'),
'callback_data' => "/templates sing",
],
[
'text' => $this->i18n('clash templates'),
'callback_data' => "/templates clash",
],
];
$data[] = [
[
@@ -5589,24 +5586,20 @@ DNS-over-HTTPS with IP:
'callback_data' => "/xtlswarp",
]],
[[
'text' => $this->i18n('rulesset'),
'callback_data' => "/xtlsrulesset",
]],
[[
'text' => 'domains: ' . ($p['domains_outbound'] ? 'direct' : $outbound),
'text' => 'domains',
'callback_data' => "/xtlsproxy",
]],
[[
'text' => 'process: ' . ($p['process_outbound'] ? 'direct' : $outbound),
'text' => 'process',
'callback_data' => "/xtlsprocess",
]],
[[
'text' => 'package: ' . ($p['app_outbound'] ? 'direct' : $outbound),
'text' => 'package',
'callback_data' => "/xtlsapp",
]],
[[
'text' => 'final: ' . ($p['final_outbound'] ? $outbound : 'direct'),
'callback_data' => "/finalOutbound",
'text' => $this->i18n('rulesset'),
'callback_data' => "/xtlsrulesset",
]],
];
$data[] = [
@@ -5795,6 +5788,7 @@ DNS-over-HTTPS with IP:
$text[] = "<a href='$scheme://{$domain}/pac?h=$hash&t=s&r=st&s={$c['id']}#{$c['email']}'>import://streisand</a>";
$text[] = "<a href='$scheme://{$domain}/pac?h=$hash&t=si&r=h&s={$c['id']}#{$c['email']}'>import://hiddify</a>";
$text[] = "<a href='$scheme://{$domain}/pac?h=$hash&t=si&r=k&s={$c['id']}#{$c['email']}'>import://karing</a>";
$text[] = "<a href='$scheme://{$domain}/pac?h=$hash&t=si&r=c&s={$c['id']}#{$c['email']}'>import://clash</a>";
$si = "$scheme://{$domain}/pac/" . base64_encode(serialize([
'h' => $hash,
@@ -5806,9 +5800,15 @@ DNS-over-HTTPS with IP:
't' => 's',
's' => $c['id'],
]));
$cl = "$scheme://{$domain}/pac/" . base64_encode(serialize([
'h' => $hash,
't' => 'cl',
's' => $c['id'],
]));
$text[] = "\nxray config: <pre><code>$xr</code></pre>";
$text[] = "sing-box config: <pre><code>$si</code></pre>";
$text[] = "clash config: <pre><code>$cl</code></pre>";
$text[] = "sing-box windows: <a href='$scheme://{$domain}/pac?h=$hash&t=si&r=w&s={$c['id']}'>windows service</a>";
@@ -5834,6 +5834,7 @@ DNS-over-HTTPS with IP:
];
$singtemplate = $c['singtemplate'] ? base64_decode($c['singtemplate']) : 'default(' . ($pac['defaultsingtemplate'] && !empty($pac['singtemplates'][base64_decode($pac['defaultsingtemplate'])]) ? base64_decode($pac['defaultsingtemplate']) : 'origin') . ')';
$v2raytemplate = $c['v2raytemplate'] ? base64_decode($c['v2raytemplate']) : 'default(' . ($pac['defaultv2raytemplate'] && !empty($pac['v2raytemplates'][base64_decode($pac['defaultv2raytemplate'])]) ? base64_decode($pac['defaultv2raytemplate']) : 'origin') . ')';
$clashtemplate = $c['clashtemplate'] ? base64_decode($c['clashtemplate']) : 'default(' . ($pac['defaultclashtemplate'] && !empty($pac['clashtemplates'][base64_decode($pac['defaultclashtemplate'])]) ? base64_decode($pac['defaultclashtemplate']) : 'origin') . ')';
$data[] = [
[
'text' => $this->i18n('v2ray') . ": $v2raytemplate",
@@ -5843,6 +5844,10 @@ DNS-over-HTTPS with IP:
'text' => $this->i18n('singbox') . ": $singtemplate",
'callback_data' => "/templateUser sing $i",
],
[
'text' => $this->i18n('clash') . ": $clashtemplate",
'callback_data' => "/templateUser clash $i",
],
];
$data[] = [
[
@@ -5893,7 +5898,17 @@ DNS-over-HTTPS with IP:
public function subscription()
{
$type = $_GET['t'] == 's' ? 'v2ray' : 'sing';
switch ($_GET['t']) {
case 's':
$type = 'v2ray';
break;
case 'si':
$type = 'sing';
break;
case 'cl':
$type = 'clash';
break;
}
$pac = $this->getPacConf();
$domain = $_GET['cdn'] ?: ($_SERVER['SERVER_NAME'] ?: $this->getDomain($pac['transport'] == 'Websocket'));
$xr = $this->getXray();
@@ -5927,6 +5942,11 @@ DNS-over-HTTPS with IP:
't' => 's',
's' => $uid,
]));
$cl = "$scheme://{$domain}/pac/" . base64_encode(serialize([
'h' => $hash,
't' => 'cl',
's' => $uid,
]));
switch ($_GET['r']) {
case 'si':
header("Location: sing-box://import-remote-profile/?url=$si");
@@ -5943,6 +5963,9 @@ DNS-over-HTTPS with IP:
case 'h':
header("Location: hiddify://install-config/?url=$si");
exit;
case 'c':
header("Location: clash://install-config/?url=$cl&name=$email");
exit;
case 'w':
$link = htmlspecialchars($si, ENT_XML1, 'UTF-8');
$n = "singbox_$uid.zip";
@@ -5983,6 +6006,14 @@ DNS-over-HTTPS with IP:
break;
}
}
if (!isset($index)) {
foreach ($c['proxies'] as $k => $v) {
if ($v['name'] == $outbound) {
$index = $k;
break;
}
}
}
switch ($_GET['t']) {
case 's':
@@ -6041,32 +6072,101 @@ DNS-over-HTTPS with IP:
$c['outbounds'][$index]['tls']['server_name'] = '~server_name~';
$c['outbounds'][$index]['tls']['reality']['short_id'] = '~short_id~';
}
break;
case 'cl':
$c['proxies'][$index]['server'] = '~domain~';
$c['proxies'][$index]['uuid'] = '~uid~';
if ($pac['transport'] == 'Websocket') {
unset($c['proxies'][$index]['flow']);
unset($c['proxies'][$index]['reality-opts']);
$c['proxies'][$index]["network"] = "ws";
$c['proxies'][$index]["ws-opts"]['path'] = '/ws';
$c['proxies'][$index]["skip-cert-verify"] = false;
$c['proxies'][$index]['servername'] = '~domain~';
} else {
unset($c['proxies'][$index]["ws-opts"]);
unset($c['proxies'][$index]["skip-cert-verify"]);
$c['proxies'][$index]["network"] = "tcp";
$c['proxies'][$index]['flow'] = 'xtls-rprx-vision';
$c['proxies'][$index]['servername'] = '~server_name~';
$c['proxies'][$index]['reality-opts'] = [
'public-key' => '~public_key~',
'short-id' => '~short_id~',
];
}
break;
}
$c = json_decode($this->replaceTags(json_encode($c), [
'"~pac~"' => json_encode(array_keys(array_filter($pac['includelist'] ?: []))),
'"~block~"' => json_encode(array_keys(array_filter($pac['blocklist'] ?: []))),
'"~warp~"' => json_encode(array_keys(array_filter($pac['warplist'] ?: []))),
'"~process~"' => json_encode(array_keys(array_filter($pac['processlist'] ?: []))),
'"~package~"' => json_encode(array_keys(array_filter($pac['packagelist'] ?: []))),
'~dns~' => "https://$domain/dns-query/$uid",
'~uid~' => $uid,
'~domain~' => $domain,
'~directdomain~' => $pac['domain'],
'~cdndomain~' => $pac['linkdomain'],
'~short_id~' => $xr['inbounds'][0]['streamSettings']['realitySettings']['shortIds'][0],
'~public_key~' => $pac['xray'],
'~server_name~' => $xr['inbounds'][0]['streamSettings']['realitySettings']['serverNames'][0],
'~ip~' => $this->ip,
]), true);
switch ($_GET['t']) {
case 's':
if (!empty($c['routing']['rules'])) {
foreach ($c['routing']['rules'] as $k => $v) {
if (array_key_exists('domain', $v) && empty($v['domain'])) {
unset($c['routing']['rules'][$k]);
}
}
$c['routing']['rules'] = array_values($c['routing']['rules']);
}
break;
case 'si':
$c['route'] = $this->addRuleSet($c['route']);
$c['route'] = $this->createRuleSet($c['route'], $uid, $domain);
if (!empty($c['route']['rules'])) {
foreach ($c['route']['rules'] as $k => $v) {
if (count($v) < 2) {
unset($c['route']['rules'][$k]);
}
}
$c['route']['rules'] = array_values($c['route']['rules']);
}
break;
case 'cl':
if (!empty($c['rules'])) {
$c['rules'] = $this->clashRules($c['rules']);
}
break;
}
$json = $this->replaceTags(json_encode($c), [
'"~pac~"' => json_encode(array_keys(array_filter($pac['includelist'] ?: []))),
'~dns~' => "https://$domain/dns-query/$uid",
'~uid~' => $uid,
'~domain~' => $domain,
'~directdomain~' => $pac['domain'],
'~cdndomain~' => $pac['linkdomain'],
'~short_id~' => $xr['inbounds'][0]['streamSettings']['realitySettings']['shortIds'][0],
'~public_key~' => $pac['xray'],
'~server_name~' => $xr['inbounds'][0]['streamSettings']['realitySettings']['serverNames'][0],
'~app_outbound~' => $pac['app_outbound'] ? 'direct' : $outbound,
'~process_outbound~' => $pac['process_outbound'] ? 'direct' : $outbound,
'~domains_outbound~' => $pac['domains_outbound'] ? 'direct' : $outbound,
'~final_outbound~' => $pac['final_outbound'] ? $outbound : 'direct',
'~ip~' => $this->ip,
]);
$json = $this->addRuleSet($json);
$json = $this->clearEmptyRules($json);
if ($_GET['t'] == 'cl') {
header('Content-type: text/yaml');
echo yaml_emit($c);
return;
}
header('Content-type: application/json');
echo $json;
echo json_encode($c);
}
public function clashRules($rules)
{
foreach ($rules as $v) {
if (isset($v['list'])) {
if (!empty($v['list'])) {
foreach ($v['list'] as $j) {
$tmp[] = "{$v['type']}, $j, {$v['action']}";
}
}
} else {
$tmp[] = "{$v['type']}, {$v['action']}";
}
}
return $tmp;
}
public function replaceTags($subject, $tags)
@@ -6074,33 +6174,10 @@ DNS-over-HTTPS with IP:
return str_replace(array_keys($tags), array_values($tags), $subject);
}
public function clearEmptyRules($json)
public function addRuleSet($route)
{
$json = json_decode($json, 1);
if (!empty($json['routing']['rules'])) {
foreach ($json['routing']['rules'] as $k => $v) {
if (array_key_exists('domain', $v) && empty($v['domain'])) {
unset($json['routing']['rules'][$k]);
}
}
$json['routing']['rules'] = array_values($json['routing']['rules']);
}
if (!empty($json['route']['rules'])) {
foreach ($json['route']['rules'] as $k => $v) {
if (count($v) < 2) {
unset($json['route']['rules'][$k]);
}
}
$json['route']['rules'] = array_values($json['route']['rules']);
}
return json_encode($json);
}
public function addRuleSet($json)
{
$json = json_decode($json, 1);
if (!empty($json['route']['rules'])) {
foreach ($json['route']['rules'] as $k => $v) {
if (!empty($route['rules'])) {
foreach ($route['rules'] as $k => $v) {
if (!empty($v['addruleset'])) {
$t[$v['outbound']] = $k;
}
@@ -6110,8 +6187,8 @@ DNS-over-HTTPS with IP:
foreach ($p['rulessetlist'] as $k => $v) {
if (!empty($v)) {
[$type, $time, $url] = explode(':', $k, 3);
if (!empty($json['route']['rules'][$t[$type]])) {
$json['route']['rule_set'][] = [
if (!empty($route['rules'][$t[$type]])) {
$route['rule_set'][] = [
"tag" => $k,
"type" => "remote",
"format" => "binary",
@@ -6119,73 +6196,60 @@ DNS-over-HTTPS with IP:
"download_detour" => "direct",
"update_interval" => $time
];
$json['route']['rules'][$t[$type]]['rule_set'][] = $k;
$route['rules'][$t[$type]]['rule_set'][] = $k;
}
}
}
}
foreach ($json['route']['rules'] as $k => $v) {
unset($json['route']['rules'][$k]['addruleset']);
foreach ($route['rules'] as $k => $v) {
unset($route['rules'][$k]['addruleset']);
}
}
return json_encode($json);
return $route;
}
public function cleanEmptyKeys(array $arr)
{
foreach ($arr as $k => $v) {
if (empty($v)) {
unset($arr[$k]);
} elseif (is_array($v)) {
$arr[$k] = $this->cleanEmptyKeys($v);
if (empty($arr[$k])) {
unset($arr[$k]);
}
}
}
return $arr;
}
public function createSrs(string $name, array $rules)
{
$rules = $this->cleanEmptyKeys($rules);
header("Content-Disposition: attachment; filename=$name.srs");
header('Content-Type: application/binary');
$f = "/tmp/$name" . time() . rand(1, 100);
file_put_contents($f, json_encode([
'version' => 1,
'rules' => $rules ?: [],
]));
exec("sing-box rule-set compile $f");
echo file_get_contents("$f.srs");
unlink($f);
unlink("$f.srs");
exit;
}
public function createRuleSet($route, $uid, $domain)
{
$pac = $this->getPacConf();
$scheme = empty($this->nginxGetTypeCert()) ? 'http' : 'https';
$hash = substr(md5($this->key), 0, 8);
foreach ($route['rules'] as $k => $v) {
if (!empty($v['createruleset'])) {
foreach ($v['createruleset'] as $r) {
foreach ($r['rules'] as $l => $n) {
switch (true) {
case array_key_exists('domain_suffix', $n):
switch ($n['domain_suffix']) {
case '~pac~':
$t = 'includelist';
break;
case '~warp~':
$t = 'warplist';
break;
case '~block~':
$t = 'blocklist';
break;
}
$r['rules'][$l]['domain_suffix'] = array_keys(array_filter($pac[$t] ?: []));
if (empty($r['rules'][$l]['domain_suffix'])) {
unset($r['rules'][$l]);
}
break;
case array_key_exists('package_name', $n):
$r['rules'][$l]['package_name'] = array_keys(array_filter($pac['packagelist'] ?: []));
if (empty($r['rules'][$l]['package_name'])) {
unset($r['rules'][$l]);
}
break;
case array_key_exists('process_name', $n):
$r['rules'][$l]['process_name'] = array_keys(array_filter($pac['processlist'] ?: []));
if (empty($r['rules'][$l]['process_name'])) {
unset($r['rules'][$l]);
}
break;
}
}
if (!empty($_GET['r']) && $r['name'] == $_GET['r']) {
header("Content-Disposition: attachment; filename={$r['name']}.srs");
header('Content-Type: application/binary');
$f = "/tmp/{$r['name']}" . time() . rand(1, 100);
file_put_contents($f, json_encode([
'version' => 1,
'rules' => $r['rules'] ?: [],
]));
exec("sing-box rule-set compile $f");
echo file_get_contents("$f.srs");
unlink($f);
unlink("$f.srs");
exit;
$this->createSrs($r['name'], $r['rules']);
}
$ruleset[] = [
"tag" => $r['name'],
+1
View File
@@ -37,6 +37,7 @@ if ($hash == substr(md5($c['key']), 0, 8)) {
break;
case 's':
case 'si':
case 'cl':
$bot->subscription();
exit;
+124
View File
@@ -0,0 +1,124 @@
{
"mixed-port": 2080,
"allow-lan": true,
"tcp-concurrent": true,
"enable-process": true,
"find-process-mode": "strict",
"global-client-fingerprint": "chrome",
"mode": "rule",
"log-level": "debug",
"ipv6": false,
"keep-alive-interval": 30,
"unified-delay": false,
"profile": {
"store-selected": true,
"store-fake-ip": true
},
"sniffer": {
"enable": true,
"sniff": {
"HTTP": {
"ports": [
80,
"8080-8880"
],
"override-destination": true
},
"TLS": {
"ports": [
443,
8443
]
},
"QUIC": {
"ports": [
443,
8443
]
}
}
},
"tun": {
"enable": true,
"stack": "mixed",
"dns-hijack": [
"any:53"
],
"auto-route": true,
"auto-detect-interface": true,
"strict-route": true
},
"dns": {
"enable": true,
"listen": ":1053",
"prefer-h3": false,
"ipv6": false,
"enhanced-mode": "fake-ip",
"fake-ip-filter": [
"~domain~",
"+.lan",
"+.local"
],
"nameserver": [
"~dns~"
]
},
"proxies": [
{
"name": "~outbound~",
"type": "vless",
"server": "~domain~",
"port": 443,
"uuid": "~uid~",
"network": "tcp",
"flow": "xtls-rprx-vision",
"tls": true,
"reality-opts": {
"public-key": "~public_key~",
"short-id": "~short_id~"
},
"servername": "~server_name~",
"client-fingerprint": "chrome"
}
],
"proxy-groups": [
{
"name": "PROXY",
"type": "select",
"proxies": [
"~outbound~"
]
}
],
"rules": [
{
"type": "DOMAIN-SUFFIX",
"list": "~block~",
"action": "REJECT"
},
{
"type": "PROCESS-NAME",
"list": "~process~",
"action": "PROXY"
},
{
"type": "PROCESS-NAME",
"list": "~package~",
"action": "PROXY"
},
{
"type": "DOMAIN-SUFFIX",
"list": "~warp~",
"action": "PROXY"
},
{
"type": "DOMAIN-SUFFIX",
"list": "~pac~",
"action": "PROXY"
},
{
"type": "MATCH",
"action": "DIRECT"
}
]
}
+4 -8
View File
@@ -123,7 +123,7 @@
]
}
],
"outbound": "~domains_outbound~"
"outbound": "~outbound~"
},
{
"addruleset": true,
@@ -138,7 +138,7 @@
]
}
],
"outbound": "~app_outbound~"
"outbound": "~outbound~"
},
{
"addruleset": true,
@@ -153,7 +153,7 @@
]
}
],
"outbound": "~process_outbound~"
"outbound": "~outbound~"
},
{
"addruleset": true,
@@ -184,12 +184,8 @@
}
],
"outbound": "~outbound~"
},
{
"addruleset": true,
"outbound": "direct"
}
],
"final": "~final_outbound~"
"final": "direct"
}
}
+3
View File
@@ -1,3 +1,6 @@
28.11.2024 v1.109
- фикс автоскана
- добавление clash-шаблонов
21.11.2024 v1.108
- xray: addruleset для direct
- openconnect:ограничение формата при добавлении подсети