mirror of
https://github.com/kiddin9/openwrt-packages.git
synced 2025-01-08 12:57:28 +08:00
🐤 Sync 2024-07-25 18:50:52
This commit is contained in:
parent
7fa16dced5
commit
4b5a8d5468
42
hickory-dns/Makefile
Normal file
42
hickory-dns/Makefile
Normal file
@ -0,0 +1,42 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=hickory-dns
|
||||
PKG_VERSION:=master
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/hickory-dns/hickory-dns.git
|
||||
PKG_SOURCE_VERSION:=f9da0e946dd1bc62259a2b7dc5c05d7b740fd9a7
|
||||
PKG_BUILD_DEPENDS:=rust/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
PKG_BUILD_FLAGS:=no-lto
|
||||
RUST_PKG_FEATURES:=resolver,dns-over-https-rustls,dns-over-quic,dns-over-h3,native-certs,dnssec-ring,recursor
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/rust/rust-package.mk
|
||||
|
||||
define Package/hickory-dns
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=IP Addresses and Names
|
||||
TITLE:=A plug-in DNS forwarder/splitter
|
||||
URL:=https://github.com/hickory-dns/hickory-dns
|
||||
DEPENDS:=$(RUST_ARCH_DEPENDS)
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
$(call Build/Compile/Cargo,bin,--no-default-features)
|
||||
endef
|
||||
|
||||
define Package/hickory-dns/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin/
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/bin/* $(1)/usr/bin/
|
||||
$(INSTALL_DIR) $(1)/etc/init.d/
|
||||
$(INSTALL_DIR) $(1)/etc/hickory-dns/
|
||||
$(INSTALL_BIN) ./files/etc/init.d/hickory-dns $(1)/etc/init.d/hickory-dns
|
||||
$(INSTALL_BIN) ./files/etc/hickory-dns/forwarder.toml $(1)/etc/hickory-dns/forwarder.toml
|
||||
endef
|
||||
|
||||
$(eval $(call RustBinPackage,hickory-dns))
|
||||
$(eval $(call BuildPackage,hickory-dns))
|
12
hickory-dns/files/etc/hickory-dns/forwarder.toml
Normal file
12
hickory-dns/files/etc/hickory-dns/forwarder.toml
Normal file
@ -0,0 +1,12 @@
|
||||
listen_addrs_ipv6 = ["::0"]
|
||||
|
||||
[[zones]]
|
||||
zone = "."
|
||||
|
||||
zone_type = "Forward"
|
||||
stores = { type = "forward", name_servers = [
|
||||
{ socket_addr = "[2400:3200::1]:443", protocol = "h3", trust_nx_responses = true, tls_dns_name = "dns.alidns.com" },
|
||||
{ socket_addr = "[2400:3200:baba::1]:443", protocol = "h3", trust_nx_responses = true, tls_dns_name = "dns.alidns.com" },
|
||||
{ socket_addr = "1.12.12.12:443", protocol = "https", trust_nx_responses = true, tls_dns_name = "1.12.12.12" },
|
||||
{ socket_addr = "120.53.53.53:443", protocol = "https", trust_nx_responses = true, tls_dns_name = "120.53.53.53" },
|
||||
], options = { rotate = true, edns0 = true, ip_strategy = "Ipv6thenIpv4", cache_size = 0, use_hosts_file = true, server_ordering_strategy = "QueryStatistics", shuffle_dns_servers = true }}
|
18
hickory-dns/files/etc/init.d/hickory-dns
Executable file
18
hickory-dns/files/etc/init.d/hickory-dns
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
USE_PROCD=1
|
||||
START=99
|
||||
|
||||
PROG=/usr/bin/hickory-dns
|
||||
CONF=/etc/hickory-dns/forwarder.toml
|
||||
|
||||
start_service() {
|
||||
procd_open_instance hickory-dns
|
||||
procd_set_param command $PROG -c $CONF -p 5335
|
||||
procd_set_param env RUST_LOG=error
|
||||
procd_set_param user root
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
procd_set_param respawn "${respawn_threshold:-3600}" "${respawn_timeout:-5}" "${respawn_retry:-5}"
|
||||
procd_close_instance hickory-dns
|
||||
}
|
@ -212,6 +212,7 @@ return view.extend({
|
||||
o = s.option(widgets.NetworkSelect, 'wan_interfaces', _('WAN Interfaces'));
|
||||
o.multiple = true;
|
||||
o.optional = false;
|
||||
o.retain = true;
|
||||
o.rmempty = false;
|
||||
o.depends('transparent_proxy', '1');
|
||||
|
||||
|
39
luci-app-sblite/po/templates/sblite.pot
Normal file
39
luci-app-sblite/po/templates/sblite.pot
Normal file
@ -0,0 +1,39 @@
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2024-06-19 13:42+0800\n"
|
||||
"PO-Revision-Date: 2024-06-19 11:44+0800\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 3.4.4\n"
|
||||
"X-Poedit-Basepath: ../../root\n"
|
||||
"X-Poedit-SearchPath-0: .\n"
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:12
|
||||
msgid "Main Settings"
|
||||
msgstr ""
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:53
|
||||
msgid "Enable Server"
|
||||
msgstr ""
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:55
|
||||
msgid "Include Interface"
|
||||
msgstr ""
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:55
|
||||
msgid "A list of interfaces for which the transparent proxy takes effect"
|
||||
msgstr ""
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:61
|
||||
msgid "DNS Listen Port"
|
||||
msgstr ""
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:61
|
||||
msgid "The port number on which the DNS service runs"
|
||||
msgstr ""
|
1
luci-app-sblite/po/zh-cn
Symbolic link
1
luci-app-sblite/po/zh-cn
Symbolic link
@ -0,0 +1 @@
|
||||
zh_Hans
|
50
luci-app-sblite/po/zh_Hans/sblite.po
Normal file
50
luci-app-sblite/po/zh_Hans/sblite.po
Normal file
@ -0,0 +1,50 @@
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2024-06-19 13:42+0800\n"
|
||||
"PO-Revision-Date: 2024-06-19 13:42+0800\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: zh_CN\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 3.4.4\n"
|
||||
"X-Poedit-Basepath: ../../root\n"
|
||||
"X-Poedit-SearchPath-0: .\n"
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:12
|
||||
msgid "Main Settings"
|
||||
msgstr "基本设置"
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:53
|
||||
msgid "Enable Server"
|
||||
msgstr "启用服务"
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:55
|
||||
msgid "Include Interface"
|
||||
msgstr "包含的接口"
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:55
|
||||
msgid "A list of interfaces for which the transparent proxy takes effect"
|
||||
msgstr "透明代理生效的内网接口列表"
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:61
|
||||
#, fuzzy
|
||||
#| msgid "DNS Port"
|
||||
msgid "DNS Listen Port"
|
||||
msgstr "DNS 监听端口"
|
||||
|
||||
#: www/luci-static/resources/view/sblite/main.js:61
|
||||
msgid "The port number on which the DNS service runs"
|
||||
msgstr "DNS 服务运行的端口号"
|
||||
|
||||
#~ msgid "TProxy Listen Port"
|
||||
#~ msgstr "透明代理监听端口"
|
||||
|
||||
#~ msgid "The port number on which the proxy service runs"
|
||||
#~ msgstr "透明代理服务运行的端口号"
|
||||
|
||||
#~ msgid "sing-box lite"
|
||||
#~ msgstr "sing-box lite"
|
@ -0,0 +1,18 @@
|
||||
{
|
||||
"admin/services/sblite": {
|
||||
"title": "sing-box lite",
|
||||
"order": 60,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "sblite/main"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [
|
||||
"luci-app-sblite"
|
||||
],
|
||||
"uci": {
|
||||
"sblite": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
{
|
||||
"luci-app-sblite": {
|
||||
"description": "Grant access to sblite configuration",
|
||||
"read": {
|
||||
"file": {
|
||||
"/tmp/log/sblite.log": [
|
||||
"read"
|
||||
]
|
||||
},
|
||||
"ubus": {
|
||||
"luci.sblite": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
"uci": [
|
||||
"sblite"
|
||||
]
|
||||
},
|
||||
"write": {
|
||||
"uci": [
|
||||
"sblite"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
14
luci-app-sblite/root/usr/share/rpcd/ucode/luci.sblite
Normal file
14
luci-app-sblite/root/usr/share/rpcd/ucode/luci.sblite
Normal file
@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
import * as app from '/usr/share/sblite/export.uc';
|
||||
|
||||
const methods = {
|
||||
subscribe: {
|
||||
args: { section_id: 'section_id' },
|
||||
call: function (req) {
|
||||
return app.subscribe(req.args?.section_id);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return { 'luci.sblite': methods };
|
1149
luci-app-sblite/root/www/luci-static/resources/view/sblite/main.js
Normal file
1149
luci-app-sblite/root/www/luci-static/resources/view/sblite/main.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=mihomo
|
||||
PKG_VERSION:=1.18.6
|
||||
PKG_RELEASE:=17
|
||||
PKG_RELEASE:=18
|
||||
PKG_BUILD_TIME=$(shell date -u -Iseconds)
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
|
@ -214,7 +214,7 @@ start_service() {
|
||||
yq -M -i 'del(.tun)' "$run_profile_path"
|
||||
# test profile
|
||||
log "Profile testing..."
|
||||
if (/usr/bin/mihomo -d "$run_dir" -t > /dev/null 2>&1); then
|
||||
if (/usr/bin/mihomo -d "$run_dir" -t >> "$run_core_log_path" 2>&1); then
|
||||
log "Profile test passed!"
|
||||
else
|
||||
log "Profile test failed! Exiting..."
|
||||
|
@ -5,8 +5,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=naiveproxy
|
||||
PKG_VERSION:=122.0.6261.43-1
|
||||
PKG_RELEASE:=100
|
||||
PKG_VERSION:=125.0.6422.35-1
|
||||
PKG_RELEASE:=101
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/klzgrad/naiveproxy/tar.gz/v$(PKG_VERSION)?
|
||||
@ -20,17 +20,6 @@ PKG_BUILD_DEPENDS:=gn/host
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
PKG_BUILD_FLAGS:=no-mips16
|
||||
|
||||
ifeq ($(strip $(NINJA)),)
|
||||
ifneq ($(wildcard $(TOPDIR)/feeds/packages/devel/ninja/ninja.mk),)
|
||||
PKG_BUILD_DEPENDS+=ninja/host
|
||||
NINJA = \
|
||||
MAKEFLAGS="$(MAKE_JOBSERVER)" \
|
||||
$(STAGING_DIR_HOSTPKG)/bin/ninja \
|
||||
$(if $(findstring c,$(OPENWRT_VERBOSE)),-v) \
|
||||
$(if $(MAKE_JOBSERVER),,-j1)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_CPU_TYPE)," ")
|
||||
CPU_TYPE:=$(word 1, $(subst +," ,$(CONFIG_CPU_TYPE)))
|
||||
CPU_SUBTYPE:=$(word 2, $(subst +, ",$(CONFIG_CPU_TYPE)))
|
||||
@ -66,7 +55,7 @@ ifneq ($(CONFIG_CCACHE),)
|
||||
export naive_ccache_flags=cc_wrapper="$(CCACHE)"
|
||||
endif
|
||||
|
||||
CLANG_VER:=18-init-16072-gc4146121e940-5
|
||||
CLANG_VER:=19-init-8091-gab037c4f-1
|
||||
CLANG_FILE:=clang-llvmorg-$(CLANG_VER).tgz
|
||||
define Download/CLANG
|
||||
URL:=https://commondatastorage.googleapis.com/chromium-browser-clang/Linux_x64
|
||||
@ -75,7 +64,7 @@ define Download/CLANG
|
||||
HASH:=skip
|
||||
endef
|
||||
|
||||
PGO_VER:=6261-1707846690-1391fcc4772c0b31e214f533af5cafa87e4ccf40
|
||||
PGO_VER:=6422-1715102072-9bdbfa29f2bb1ff28f0f031b98501a1193b8d03b-13cfbf145656b369f9c23bff70ab2fb07e1e2fdb
|
||||
PGO_FILE:=chrome-linux-$(PGO_VER).profdata
|
||||
define Download/PGO_PROF
|
||||
URL:=https://storage.googleapis.com/chromium-optimization-profiles/pgo_profiles
|
||||
|
@ -55,6 +55,7 @@ use_gio=false
|
||||
use_gtk=false
|
||||
use_platform_icu_alternatives=true
|
||||
use_glib=false
|
||||
enable_js_protobuf=false
|
||||
|
||||
disable_file_support=true
|
||||
enable_websockets=false
|
||||
|
5
sblite/root/etc/hotplug.d/iface/99-sblite
Normal file
5
sblite/root/etc/hotplug.d/iface/99-sblite
Normal file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ "$ACTION" = "ifup" -o "$ACTION" = "ifupdate" ] && [ $(uci get sblite.main.enable) = "1" ] && {
|
||||
# if "$DEVICE" in outbounds restart sblite
|
||||
}
|
23
sblite/root/etc/init.d/sblite
Normal file
23
sblite/root/etc/init.d/sblite
Normal file
@ -0,0 +1,23 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
USE_PROCD=1
|
||||
|
||||
START=99
|
||||
STOP=15
|
||||
|
||||
CONFIG=sblite
|
||||
$APP_FILE=/usr/share/${CONFIG}/app.sh
|
||||
|
||||
start_service() {
|
||||
procd_open_instance $CONFIG
|
||||
procd_set_param command $APP_FILE start && sing-box run -c /tmp/sblite/config.json
|
||||
procd_set_param user root
|
||||
procd_set_param limits core="unlimited"
|
||||
procd_set_param limits nofile="1000000 1000000"
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
|
||||
procd_set_param pidfile /var/run/${CONFIG}.pid
|
||||
|
||||
procd_close_instance
|
||||
}
|
17
sblite/root/etc/uci-defaults/sblite
Normal file
17
sblite/root/etc/uci-defaults/sblite
Normal file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
config_name="sblite"
|
||||
|
||||
touch /etc/config/$config_name
|
||||
|
||||
section_type="sing_box"
|
||||
uci set $config_name.main=$section_type
|
||||
uci set $config_name.access_control=$section_type
|
||||
uci set $config_name.route=$section_type
|
||||
uci set $config_name.dns=$section_type
|
||||
uci set $config_name.subscribe=$section_type
|
||||
|
||||
uci commit
|
||||
|
||||
/etc/init.d/rpcd reload
|
||||
exit 0
|
26
sblite/root/usr/share/sblite/app.sh
Normal file
26
sblite/root/usr/share/sblite/app.sh
Normal file
@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORKDIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
APP_FILE="$WORKDIR/app.uc"
|
||||
|
||||
method=$1
|
||||
shift
|
||||
case "$method" in
|
||||
start)
|
||||
rm -rf /tmp/sblite
|
||||
mkdir -p /tmp/sblite
|
||||
mkdir -p /tmp/sblite/rule_sets
|
||||
ucode -D action=start $APP_FILE
|
||||
if [ -e '/tmp/sblite/config.json' ]; then
|
||||
sing-box format -w -c /tmp/sblite/config.json
|
||||
return 0
|
||||
fi
|
||||
return -1
|
||||
;;
|
||||
subscribe) ucode -D action=subscribe -D params="$1" $APP_FILE ;;
|
||||
stop)
|
||||
rm -rf /tmp/sblite
|
||||
ucode -D action=stop $APP_FILE
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
100
sblite/root/usr/share/sblite/app.uc
Normal file
100
sblite/root/usr/share/sblite/app.uc
Normal file
@ -0,0 +1,100 @@
|
||||
'use strict';
|
||||
|
||||
import { cursor } from 'uci';
|
||||
import { CONF_NAME } from './const.uc';
|
||||
import { log_t, log_tab } from './utils.uc';
|
||||
import { start as start_crontab, stop as stop_crontab } from './cron.uc';
|
||||
import { subscribe } from './subscribe.uc';
|
||||
import { SingBoxOption } from './singbox.uc';
|
||||
import * as LOGLEVEL from './loglevel.uc';
|
||||
import { Outbound } from './outbound.uc';
|
||||
import { RuleSet } from './rule.uc';
|
||||
import { Route } from './route.uc';
|
||||
import { DNS } from './dns.uc';
|
||||
|
||||
function start() {
|
||||
const uci = cursor();
|
||||
|
||||
// purne node config
|
||||
uci.foreach(CONF_NAME, 'node', section => {
|
||||
const group = section.group;
|
||||
if (group == null || group == '') {
|
||||
uci.set(CONF_NAME, section['.name'], 'hashkey', null);
|
||||
}
|
||||
});
|
||||
|
||||
uci.commit();
|
||||
|
||||
const enable = uci.get(CONF_NAME, 'main', 'enable') == '1';
|
||||
|
||||
if (enable) {
|
||||
log_t('starting...');
|
||||
start_crontab();
|
||||
const outbounds = Outbound(uci);
|
||||
const rule_sets = RuleSet(uci, outbounds);
|
||||
const config = SingBoxOption();
|
||||
if (uci.get(CONF_NAME, 'main', 'loglevel') != '0') {
|
||||
let level;
|
||||
switch (uci.get(CONF_NAME, 'main', 'log')) {
|
||||
case 'trace': level = LOGLEVEL.TRACE; break;
|
||||
case 'debug': level = LOGLEVEL.DEBUG; break;
|
||||
case 'info': level = LOGLEVEL.INFO; break;
|
||||
case 'warn': level = LOGLEVEL.WARN; break;
|
||||
case 'error': level = LOGLEVEL.ERROR; break;
|
||||
case 'fatal': level = LOGLEVEL.FATAL; break;
|
||||
case 'panic': level = LOGLEVEL.PANIC; break;
|
||||
default: level = LOGLEVEL.ERROR; break;
|
||||
}
|
||||
config.logOption(true, level);
|
||||
} else {
|
||||
config.logOption(false);
|
||||
}
|
||||
config.logOption(true, LOGLEVEL.ERROR);
|
||||
config.cacheFileOption(true);
|
||||
// 出站
|
||||
config.outbounds = values(outbounds);
|
||||
const inbounds = {
|
||||
redirect_tcp: {
|
||||
type: 'redirect',
|
||||
tag: 'redirect_tcp',
|
||||
listen: '::',
|
||||
listen_port: 18008,
|
||||
sniff: true,
|
||||
sniff_override_destination: true,
|
||||
},
|
||||
tproxy_udp: {
|
||||
type: 'tproxy',
|
||||
tag: 'tproxy_udp',
|
||||
network: 'udp',
|
||||
listen: '::',
|
||||
listen_port: 18008,
|
||||
sniff: true,
|
||||
sniff_override_destination: true,
|
||||
},
|
||||
};
|
||||
config.dns = DNS(uci, rule_sets, outbounds);
|
||||
// 然后是重头戏,路由
|
||||
config.route = Route(uci, rule_sets, outbounds);
|
||||
config.inbounds = values(inbounds);
|
||||
config.write();
|
||||
|
||||
log_t('Done.');
|
||||
} else {
|
||||
stop_crontab();
|
||||
}
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case 'start':
|
||||
start();
|
||||
break;
|
||||
case 'stop':
|
||||
const uci = cursor();
|
||||
uci.set(CONF_NAME, 'main', 'enable', '0');
|
||||
uci.commit();
|
||||
start();
|
||||
break;
|
||||
case 'subscribe':
|
||||
subscribe(params);
|
||||
break;
|
||||
}
|
19
sblite/root/usr/share/sblite/const.uc
Normal file
19
sblite/root/usr/share/sblite/const.uc
Normal file
@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
export const APP_FILE = '/usr/share/sblite/app.sh';
|
||||
export const PKG_NAME = 'sing-box lite';
|
||||
export const CONF_NAME = 'sblite';
|
||||
|
||||
export const LOG_PATH = '/tmp/sblite/singbox.log';
|
||||
export const DB_PATH = '/tmp/sblite/singbox.db';
|
||||
export const CONFIG_PATH = '/tmp/sblite/config.json';
|
||||
|
||||
export const TCP_IN_TAG = 'tcp-in';
|
||||
export const UDP_IN_TAG = 'udp-in';
|
||||
export const DNS_IN_TAG = 'dns-in';
|
||||
|
||||
export const FAKE_IP_TAG = 'fake_ip';
|
||||
|
||||
export const REJECT_OUTBOUND_TAG = 'reject';
|
||||
export const DNS_OUTBOUND_TAG = 'dns-out';
|
||||
export const DNS_BLOCK_TAG = 'block';
|
37
sblite/root/usr/share/sblite/cron.uc
Normal file
37
sblite/root/usr/share/sblite/cron.uc
Normal file
@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
import { cursor } from 'uci';
|
||||
import { APP_FILE, CONF_NAME } from './const.uc';
|
||||
|
||||
function clean() {
|
||||
system('touch /etc/crontabs/root');
|
||||
system(`sed -i "/sh ${replace(APP_FILE, '/', '\\/')} subscribe cfg.* > \\/dev\\/null 2>&1 &/d" /etc/crontabs/root`);
|
||||
}
|
||||
|
||||
export function start() {
|
||||
clean();
|
||||
|
||||
const uci = cursor();
|
||||
|
||||
uci.foreach(CONF_NAME, 'subscription',
|
||||
function (section) {
|
||||
if (section.auto_subscribe == '1') {
|
||||
let day = section.auto_subscribe_daily;
|
||||
let week = section.auto_subscribe_weekly;
|
||||
if(week == '0') {
|
||||
week = '*';
|
||||
}
|
||||
|
||||
const command =`echo "0 ${day} * * ${week} sh ${APP_FILE} subscribe ${section['.name']} > /dev/null 2>&1 &" >> /etc/crontabs/root`;
|
||||
system(command);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
system('/etc/init.d/cron restart');
|
||||
};
|
||||
|
||||
export function stop() {
|
||||
clean();
|
||||
system('/etc/init.d/cron restart');
|
||||
};
|
4
sblite/root/usr/share/sblite/default.uc
Normal file
4
sblite/root/usr/share/sblite/default.uc
Normal file
@ -0,0 +1,4 @@
|
||||
'use strict';
|
||||
|
||||
export const CLASH_HOST = "127.0.0.1";
|
||||
export const CLASH_PORT = 9090;
|
155
sblite/root/usr/share/sblite/dns.uc
Normal file
155
sblite/root/usr/share/sblite/dns.uc
Normal file
@ -0,0 +1,155 @@
|
||||
'use strict';
|
||||
|
||||
import { log_tab, asip, asport } from './utils.uc';
|
||||
import { CONF_NAME, DNS_BLOCK_TAG, FAKE_IP_TAG } from './const.uc';
|
||||
|
||||
export const PREFER_IPV4 = 'prefer_ipv4';
|
||||
export const PREFER_IPV6 = 'prefer_ipv6';
|
||||
export const ONLY_IPV4 = 'ipv4_only';
|
||||
export const ONLY_IPV6 = 'ipv6_only';
|
||||
|
||||
export function DNS(uci, rule_sets, outbounds) {
|
||||
const result = {
|
||||
final: '',
|
||||
strategy: '',
|
||||
disable_cache: false,
|
||||
disable_expire: false,
|
||||
independent_cache: false,
|
||||
reverse_mapping: false,
|
||||
};
|
||||
|
||||
const servers = {};
|
||||
|
||||
uci.foreach(CONF_NAME, 'dns_server', section => {
|
||||
let strategy = section.strategy;
|
||||
|
||||
if (strategy != PREFER_IPV4 && strategy != PREFER_IPV6 && strategy != ONLY_IPV4 && strategy != ONLY_IPV6) {
|
||||
log_tab('[DNS Server %s] Unkown strategy(%s), fallback to %s', section.tag, strategy, PREFER_IPV4);
|
||||
strategy = PREFER_IPV4;
|
||||
}
|
||||
|
||||
const server = {
|
||||
tag: section.tag,
|
||||
address: section.address,
|
||||
strategy: strategy,
|
||||
};
|
||||
|
||||
if (section.custom_detour == '1') {
|
||||
const detour = section.detour;
|
||||
|
||||
if (outbounds[detour]) {
|
||||
server.detour = detour;
|
||||
} else {
|
||||
log_tab('[DNS Server %s] Unkown outbound tag(%s)', detour);
|
||||
}
|
||||
}
|
||||
|
||||
if (section.resolver == '1') {
|
||||
const tag = section.resolver_tag;
|
||||
|
||||
if (outbounds[tag]) {
|
||||
server.address_resolver = tag;
|
||||
|
||||
let strategy = section.resolver_strategy;
|
||||
|
||||
if (strategy != PREFER_IPV4 && strategy != PREFER_IPV6 && strategy != ONLY_IPV4 && strategy != ONLY_IPV6) {
|
||||
log_tab('[DNS Server %s] Unkown resolver strategy(%s), fallback to %s', section.tag, strategy, PREFER_IPV4);
|
||||
strategy = PREFER_IPV4;
|
||||
}
|
||||
|
||||
server.address_strategy = strategy;
|
||||
} else {
|
||||
log_tab('[DNS Server %s] Unkown resolver tag(%s)', tag);
|
||||
}
|
||||
}
|
||||
|
||||
servers[section.tag] = server;
|
||||
});
|
||||
|
||||
servers[DNS_BLOCK_TAG] = {
|
||||
address: 'rcode://success',
|
||||
tag: DNS_BLOCK_TAG,
|
||||
};
|
||||
|
||||
let strategy = uci.get(CONF_NAME, 'dns', 'strategy');
|
||||
|
||||
if (strategy != PREFER_IPV4 && strategy != PREFER_IPV6 && strategy != ONLY_IPV4 && strategy != ONLY_IPV6) {
|
||||
log_tab('[DNS Setting] Unkown strategy(%s), fallback to %s', strategy, PREFER_IPV4);
|
||||
strategy = PREFER_IPV4;
|
||||
}
|
||||
|
||||
result.strategy = strategy;
|
||||
|
||||
if (uci.get(CONF_NAME, 'dns', 'custom_default') == 1) {
|
||||
const final = uci.get(CONF_NAME, 'dns', 'final');
|
||||
if (servers[final]) {
|
||||
result.final = final;
|
||||
} else {
|
||||
log_tab('[DNS Setting] Unkown custom defualt dns tag(%s)', final);
|
||||
}
|
||||
}
|
||||
|
||||
const rules = [];
|
||||
uci.foreach(CONF_NAME, 'dns_rule', section => {
|
||||
const server = section.server;
|
||||
|
||||
if (servers[server]) {
|
||||
if (section.rule_set && length(section.rule_set) > 0) {
|
||||
const current_rule_sets = [];
|
||||
for (let rule_set in section.rule_set) {
|
||||
if (rule_sets[rule_set]) {
|
||||
push(current_rule_sets, rule_set);
|
||||
} else {
|
||||
log_tab('[DNS Rule %s] Unkown rule set tag(%s)', rule_set);
|
||||
}
|
||||
}
|
||||
|
||||
if (length(current_rule_sets) > 0) {
|
||||
push(rules, {
|
||||
rule_set: current_rule_sets,
|
||||
server: server,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log_tab('[DNS Rule %s] Unkown dns server tag(%s)', server);
|
||||
}
|
||||
});
|
||||
|
||||
if (uci.get(CONF_NAME, 'dns', 'fake_ip') == '1') {
|
||||
const fake_ip_inet4_range = uci.get(CONF_NAME, 'dns', 'fake_ip_inet4_range');
|
||||
const fake_ip_inet6_range = uci.get(CONF_NAME, 'dns', 'fake_ip_inet4_range');
|
||||
const v4 = asip(fake_ip_inet4_range);
|
||||
const v6 = asip(fake_ip_inet6_range);
|
||||
|
||||
if (v4 && v4.version == 4 && v4.cidr != 32) {
|
||||
|
||||
if (v6 && v6.version == 6 && v6.cidr != 128) {
|
||||
servers[FAKE_IP_TAG] = {
|
||||
tag: FAKE_IP_TAG,
|
||||
adress: 'fakeip',
|
||||
};
|
||||
|
||||
push(rules, {
|
||||
query_type: ['A', 'AAAA'],
|
||||
server: FAKE_IP_TAG,
|
||||
});
|
||||
|
||||
result.fakeip = {
|
||||
enabled: true,
|
||||
inet4_range: `${v4.address}/${v4.cidr}`,
|
||||
inet6_range: `${v6.address}/${v6.cidr}`,
|
||||
};
|
||||
} else {
|
||||
log_tab('[DNS Setting] Invalid fake ip setting(%s)', fake_ip_inet6_range);
|
||||
}
|
||||
} else {
|
||||
log_tab('[DNS Setting] Invalid fake ip setting(%s)', fake_ip_inet4_range);
|
||||
}
|
||||
}
|
||||
|
||||
result.servers = values(servers);
|
||||
result.rules = rules;
|
||||
|
||||
return result;
|
||||
};
|
9
sblite/root/usr/share/sblite/export.uc
Normal file
9
sblite/root/usr/share/sblite/export.uc
Normal file
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
import * as CONST from './const.uc';
|
||||
import * as Subscribe from './subscribe.uc';
|
||||
import * as Utils from './utils.uc';
|
||||
|
||||
export const CONF_NAME = CONST.CONF_NAME;
|
||||
export const subscribe = Subscribe.subscribe;
|
||||
export const clear_log = Utils.clear_log;
|
9
sblite/root/usr/share/sblite/loglevel.uc
Normal file
9
sblite/root/usr/share/sblite/loglevel.uc
Normal file
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
export const TRACE = 'trace';
|
||||
export const DEBUG = 'debug';
|
||||
export const INFO = 'info';
|
||||
export const WARN = 'warn';
|
||||
export const ERROR = 'error';
|
||||
export const FATAL = 'fatal';
|
||||
export const PANIC = 'panic';
|
111
sblite/root/usr/share/sblite/outbound.uc
Normal file
111
sblite/root/usr/share/sblite/outbound.uc
Normal file
@ -0,0 +1,111 @@
|
||||
'use strict';
|
||||
|
||||
import { log_tab } from './utils.uc';
|
||||
import { CONF_NAME, REJECT_OUTBOUND_TAG, DNS_OUTBOUND_TAG } from './const.uc';
|
||||
import { TYPE as vmess_type, outbound as vmess_outbound } from './protocols/vmess.uc';
|
||||
|
||||
export function Outbound(uci) {
|
||||
const result = {};
|
||||
|
||||
// 先找 Direct
|
||||
uci.foreach(CONF_NAME, 'outbound', section => {
|
||||
if (section.type == 'direct') {
|
||||
if (result[section.tag]) {
|
||||
log_tab('Duplicate outbound tag(%s)', section.tag);
|
||||
} else {
|
||||
result[section.tag] = {
|
||||
type: 'direct',
|
||||
tag: section.tag,
|
||||
bind_interface: section.interface,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const nodes = {};
|
||||
// 再找 Node
|
||||
uci.foreach(CONF_NAME, 'node', section => {
|
||||
if (section.hashkey) {
|
||||
nodes[section.hashkey] = section;
|
||||
}
|
||||
});
|
||||
|
||||
uci.foreach(CONF_NAME, 'outbound', section => {
|
||||
if (section.type == 'node') {
|
||||
if (result[section.tag]) {
|
||||
log_tab('Duplicate outbound tag(%s)', section.tag);
|
||||
} else {
|
||||
const node = nodes[section.node];
|
||||
const detour = section.outbound;
|
||||
if (!result[detour]) {
|
||||
log_tab('There is no direct outbound(%s)', detour);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result[detour].type != 'direct') {
|
||||
log_tab('The outbound(%s) is not direct', detour);
|
||||
return;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
switch (node.type) {
|
||||
case vmess_type:
|
||||
result[section.tag] = vmess_outbound(node);
|
||||
break;
|
||||
default:
|
||||
log_tab('Unkown protocol(%s) in outbound node(%s)', node.type, section.tag);
|
||||
return;
|
||||
}
|
||||
|
||||
result[section.tag].tag = section.tag;
|
||||
result[section.tag].detour = detour;
|
||||
} else {
|
||||
log_tab('There is no node with hashkey(%s)', section.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 然后找 urltest
|
||||
uci.foreach(CONF_NAME, 'outbound', section => {
|
||||
if (section.type == 'urltest') {
|
||||
if (result[section.tag]) {
|
||||
log_tab('Duplicate outbound tag(%s)', section.tag);
|
||||
} else {
|
||||
result[section.tag] = {
|
||||
type: 'urltest',
|
||||
tag: section.tag,
|
||||
outbounds: [],
|
||||
url: section.url,
|
||||
};
|
||||
|
||||
for (let tag in section.include) {
|
||||
const outbound = result[tag];
|
||||
|
||||
if (!outbound) {
|
||||
log_tab('There is no outbound(%s)', tag);
|
||||
return;
|
||||
} else if (outbound.type == 'urltest') {
|
||||
log_tab('The outbound(%s) is urltest, should not be used in another urltest outbound.', tag);
|
||||
return;
|
||||
}
|
||||
|
||||
push(result[section.tag].outbounds, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 最后添加 BLOCK 出站和 DNS 出站
|
||||
result[REJECT_OUTBOUND_TAG] = {
|
||||
type: 'block',
|
||||
tag: REJECT_OUTBOUND_TAG,
|
||||
};
|
||||
|
||||
result[DNS_OUTBOUND_TAG] = {
|
||||
type: 'dns',
|
||||
tag: DNS_OUTBOUND_TAG,
|
||||
};
|
||||
|
||||
return result;
|
||||
};
|
62
sblite/root/usr/share/sblite/protocols/vmess.uc
Normal file
62
sblite/root/usr/share/sblite/protocols/vmess.uc
Normal file
@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
import { md5, log_t } from '../utils.uc';
|
||||
|
||||
// see: https://github.com/jarryson/singbox-subscribe/blob/main/parsers/vmess.py
|
||||
|
||||
export const TYPE = 'vmess';
|
||||
|
||||
export function parse(content, result) {
|
||||
const matches = match(content, /vmess:\/\/(.*)/);
|
||||
|
||||
if (matches) {
|
||||
let payload = matches[1];
|
||||
|
||||
const decode = b64dec(payload);
|
||||
|
||||
if (decode != null) {
|
||||
payload = decode;
|
||||
}
|
||||
|
||||
if (payload) {
|
||||
const info = json(payload);
|
||||
|
||||
if (info) {
|
||||
result.type = TYPE;
|
||||
result.server = info['add'];
|
||||
result.server_port = int(info['port']);
|
||||
result.uuid = info['id'];
|
||||
result.alter_id = info['aid'] ?? '0';
|
||||
result.security = info['scy'] ?? 'auto';
|
||||
if(result.security != 'http') {
|
||||
result.security = 'auto';
|
||||
}
|
||||
|
||||
result.alias = sprintf('[%s] %s', result.group, info['ps']);
|
||||
result.hashkey = md5(b64enc(sprintf('[%s] %s://%s:%s?id=%s',
|
||||
result.group,
|
||||
result.type,
|
||||
result.server,
|
||||
result.server_port,
|
||||
result.uuid)));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export function outbound(section) {
|
||||
return {
|
||||
type: TYPE,
|
||||
tag: section.tag,
|
||||
server: section.server,
|
||||
server_port: int(section.server_port),
|
||||
uuid: section.uuid,
|
||||
security: section.security,
|
||||
alter_id: int(section.alter_id),
|
||||
packet_encoding: 'xudp',
|
||||
};
|
||||
};
|
67
sblite/root/usr/share/sblite/route.uc
Normal file
67
sblite/root/usr/share/sblite/route.uc
Normal file
@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
import { CONF_NAME } from './const.uc';
|
||||
import { log_tab, delete_empty_arr } from './utils.uc';
|
||||
|
||||
export function Route(uci, rule_sets, outbounds) {
|
||||
const result = {
|
||||
rules: [],
|
||||
rule_set: [],
|
||||
};
|
||||
|
||||
for (let rule_set in values(rule_sets)) {
|
||||
push(result.rule_set, rule_set);
|
||||
}
|
||||
|
||||
const route_section = uci.get_all(CONF_NAME, 'route');
|
||||
|
||||
if (route_section.custom_default == '1') {
|
||||
const final_tag = route_section.final;
|
||||
|
||||
if (final_tag && rule_sets[final_tag]) {
|
||||
result.final = final_tag;
|
||||
}
|
||||
}
|
||||
|
||||
uci.foreach(CONF_NAME, 'route_rule', section => {
|
||||
const rule = {
|
||||
invert: section.invert == '1',
|
||||
};
|
||||
const outbound = rule.outbound = section.outbound;
|
||||
|
||||
if (!outbound || !outbounds[outbound]) {
|
||||
log_tab('[Route Rule %s] There is no outbound(%s)', section.tag, outbound);
|
||||
return;
|
||||
}
|
||||
|
||||
if (section.protocol && length(section.protocol) > 0) {
|
||||
rule.protocol = [];
|
||||
for (let protocol in section.protocol) {
|
||||
if (index(['HTTP', 'TLS', 'QUIC', 'STUN', 'BitTorrent'], protocol) > 0) {
|
||||
push(rule.protocol, protocol);
|
||||
} else {
|
||||
log_tab('[Route Rule %s] There is no protocol(%s)', section.tag, protocol);
|
||||
}
|
||||
}
|
||||
|
||||
delete_empty_arr(rule, 'protocol');
|
||||
}
|
||||
|
||||
if (section.rule_set && length(section.rule_set) > 0) {
|
||||
rule.rule_set = [];
|
||||
for (let rule_set in section.rule_set) {
|
||||
if (rule_sets[rule_set]) {
|
||||
push(rule.rule_set, rule_set);
|
||||
} else {
|
||||
log_tab('[Route Rule %s] There is no rule_set(%s)', section.tag, rule_set);
|
||||
}
|
||||
}
|
||||
|
||||
delete_empty_arr(rule, 'rule_set');
|
||||
}
|
||||
|
||||
push(result.rules, rule);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
278
sblite/root/usr/share/sblite/rule.uc
Normal file
278
sblite/root/usr/share/sblite/rule.uc
Normal file
@ -0,0 +1,278 @@
|
||||
'use strict';
|
||||
|
||||
import { log_tab, asip, asport, delete_empty_arr, call_system_command } from './utils.uc';
|
||||
import { CONF_NAME } from './const.uc';
|
||||
import { open as fopen } from 'fs';
|
||||
|
||||
function config_ip_and_port(section, section_ip, section_port, config, config_ip, config_port, config_port_range) {
|
||||
if (section[section_ip]) {
|
||||
config[config_ip] = [];
|
||||
|
||||
for (let line in section[section_ip]) {
|
||||
const ip = asip(line);
|
||||
if (ip) {
|
||||
push(config[config_ip], `${ip.address}/${ip.cidr}`);
|
||||
} else {
|
||||
log_tab('Unkown ip (%s) setting in %s', line, section.tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (length(config[config_ip]) == 0) {
|
||||
delete config[config_ip];
|
||||
}
|
||||
}
|
||||
|
||||
if (section[section_port]) {
|
||||
config[config_port] = [];
|
||||
config[config_port_range] = [];
|
||||
|
||||
for (let line in section[section_port]) {
|
||||
const port = asport(line);
|
||||
if (port) {
|
||||
if (port.range) {
|
||||
push(config[config_port_range], `${port.start}:${port.end}`);
|
||||
} else {
|
||||
push(config[config_port], port.value);
|
||||
}
|
||||
|
||||
} else {
|
||||
log_tab('Unkown port (%s) setting in %s', line, section.tag);
|
||||
}
|
||||
}
|
||||
|
||||
delete_empty_arr(config, config_port);
|
||||
delete_empty_arr(config, config_port_range);
|
||||
}
|
||||
}
|
||||
|
||||
function rule(section) {
|
||||
const result = { invert: section.invert == '1' };
|
||||
|
||||
switch (section.network) {
|
||||
case null:
|
||||
case '0': result.network = ['tcp', 'udp']; break;
|
||||
case '1': result.network = ['tcp']; break;
|
||||
case '2': result.network = ['udp']; break;
|
||||
default:
|
||||
log_tab('Unkown rule_set network (%s) setting in %s', section.network, section.tag);
|
||||
result.network = ['tcp', 'udp']; break;
|
||||
}
|
||||
|
||||
config_ip_and_port(section, 'source', 'source_port', result, 'source_ip_cidr', 'source_port', 'source_port_range');
|
||||
config_ip_and_port(section, 'dest', 'dest_port', result, 'ip_cidr', 'port', 'port_range');
|
||||
|
||||
if (section.domain) {
|
||||
result.domain = [];
|
||||
result.domain_suffix = [];
|
||||
result.domain_keyword = [];
|
||||
result.domain_regex = [];
|
||||
|
||||
const lines = split(section.domain, /[(\r\n)\r\n]+/);
|
||||
|
||||
for (let line in lines) {
|
||||
line = trim(line);
|
||||
let match_result;
|
||||
if (match_result = match(line, /#.*/)) {
|
||||
continue;
|
||||
} else if (match_result = match(line, /domain: *(.+)/)) {
|
||||
push(result.domain, trim(match_result[1]));
|
||||
} else if (match_result = match(line, /suffix: *(.+)/)) {
|
||||
push(result.domain_suffix, trim(match_result[1]));
|
||||
} else if (match_result = match(line, /keyword: *(.+)/)) {
|
||||
push(result.domain_keyword, trim(match_result[1]));
|
||||
} else if (match_result = match(line, /regex: *(.+)/)) {
|
||||
push(result.domain_regex, trim(match_result[1]));
|
||||
} else {
|
||||
log_tab('Unkown rule_set domain (%s) setting in %s', line, section.tag);
|
||||
}
|
||||
}
|
||||
|
||||
delete_empty_arr(result, 'domain');
|
||||
delete_empty_arr(result, 'domain_suffix');
|
||||
delete_empty_arr(result, 'domain_keyword');
|
||||
delete_empty_arr(result, 'domain_regex');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function headless(section, sub_rules) {
|
||||
if (section.logical_mode && section.logical_mode != '0') {
|
||||
const headless = {
|
||||
type: 'logical',
|
||||
invert: section.invert == '1',
|
||||
rules: []
|
||||
};
|
||||
if (section.logical_mode == '1') {
|
||||
headless.mode = 'and';
|
||||
} else if (section.logical_mode == '2') {
|
||||
headless.mode = 'or';
|
||||
} else {
|
||||
log_tab('[Rule Set %s] Unkown rule set logical_mode %s', section.tag, section.logical_mode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (section.sub_rule) {
|
||||
for (let sub_rule_tag in section.sub_rule) {
|
||||
const sub_rule = sub_rules[sub_rule_tag];
|
||||
if (sub_rule) {
|
||||
push(headless.rules, sub_rule);
|
||||
} else {
|
||||
log_tab('[Rule Set %s] Unkown sub rule set tag %s', section.tag, sub_rule_tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (length(headless.rules) > 0) {
|
||||
return headless;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const headless = rule(section);
|
||||
return headless;
|
||||
}
|
||||
}
|
||||
|
||||
function rule_set(section, headless_rules, sub_rules, outbounds) {
|
||||
switch (section.type) {
|
||||
case 'headless':
|
||||
return;
|
||||
case 'inline':
|
||||
const inline = {
|
||||
type: section.type,
|
||||
tag: section.tag,
|
||||
rules: [],
|
||||
};
|
||||
|
||||
if (section.advance != '1') {
|
||||
const rule = headless(section, sub_rules);
|
||||
push(inline.rules, rule);
|
||||
} else {
|
||||
for (let tag in section.headless) {
|
||||
const rule = headless_rules[tag];
|
||||
if (rule) {
|
||||
push(inline.rules, rule);
|
||||
} else {
|
||||
log_tab('[Rule Set %s] Unkown headless rule set tag %s', section.tag, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (length(inline.rules) > 0) {
|
||||
const raw = `/tmp/sblite/rule_sets/${section['.name']}.json`;
|
||||
const srs = `/tmp/sblite/rule_sets/${section['.name']}.srs`;
|
||||
|
||||
const fp = fopen(raw, 'w');
|
||||
|
||||
if (fp) {
|
||||
fp.write({
|
||||
version: 1,
|
||||
rules: inline.rules,
|
||||
});
|
||||
fp.write('\n');
|
||||
fp.close();
|
||||
} else {
|
||||
log_tab('[Rule Set %s] write headless rule to %s failed', section.tag, raw);
|
||||
return;
|
||||
}
|
||||
|
||||
const compile = call_system_command(`sing-box rule-set compile --output ${srs} ${raw}`);
|
||||
|
||||
if(compile && compile != '') {
|
||||
log_tab('[Rule Set %s] compile headless rule failed\n %s', section.tag, compile);
|
||||
return;
|
||||
}
|
||||
|
||||
//return inline;
|
||||
|
||||
return {
|
||||
type: 'local',
|
||||
tag: section.tag,
|
||||
format: 'binary',
|
||||
path: srs,
|
||||
};
|
||||
}
|
||||
return;
|
||||
case 'local':
|
||||
const local = {
|
||||
type: section.type,
|
||||
tag: section.tag,
|
||||
};
|
||||
if (section.format == 'binary' || section.format == 'source') {
|
||||
local.format = section.format;
|
||||
} else {
|
||||
log_tab('[Rule Set %s] Unkown rule set format %s', section.tag, section.format);
|
||||
return;
|
||||
}
|
||||
|
||||
if (section.path) {
|
||||
local.path = section.path;
|
||||
} else {
|
||||
log_tab('[Rule Set %s] Local rule set path undefined', section.tag);
|
||||
return;
|
||||
}
|
||||
|
||||
return local;
|
||||
case 'remote':
|
||||
const remote = {
|
||||
type: section.type,
|
||||
tag: section.tag,
|
||||
};
|
||||
|
||||
if (section.format == 'binary' || section.format == 'source') {
|
||||
remote.format = section.format;
|
||||
} else {
|
||||
log_tab('[Rule Set %s] Unkown rule set format %s', section.tag, section.format);
|
||||
return;
|
||||
}
|
||||
|
||||
if (section.url) {
|
||||
remote.url = section.url;
|
||||
} else {
|
||||
log_tab('[Rule Set %s] Remote rule set url undefined', section.tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (section.cutom_detour == '1' && section.download_detour) {
|
||||
if (outbounds[section.download_detour]) {
|
||||
remote.download_detour = section.download_detour;
|
||||
} else {
|
||||
log_tab('[Rule Set %s] There is no outbound(%s)', section.tag, section.download_detour);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return remote;
|
||||
default:
|
||||
log_tab('[Rule Set %s] Unkown rule set type %s', section.tag, section.type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export function RuleSet(uci, outbounds) {
|
||||
const sub_rules = {}, headless_rules = {}, rule_sets = {};
|
||||
|
||||
uci.foreach(CONF_NAME, 'rule_set', section => {
|
||||
if (section.type == 'headless' && section.sub == '1') {
|
||||
sub_rules[section.tag] = rule(section);
|
||||
}
|
||||
});
|
||||
|
||||
uci.foreach(CONF_NAME, 'rule_set', section => {
|
||||
if (section.type == 'headless' && section.sub != '1') {
|
||||
headless_rules[section.tag] = headless(section, sub_rules);
|
||||
}
|
||||
});
|
||||
|
||||
uci.foreach(CONF_NAME, 'rule_set', section => {
|
||||
if (section.type == 'headless') {
|
||||
return;
|
||||
}
|
||||
|
||||
const ruleSet = rule_set(section, headless_rules, sub_rules, outbounds);
|
||||
if (ruleSet) {
|
||||
rule_sets[section.tag] = ruleSet;
|
||||
}
|
||||
});
|
||||
|
||||
return rule_sets;
|
||||
};
|
122
sblite/root/usr/share/sblite/singbox.uc
Normal file
122
sblite/root/usr/share/sblite/singbox.uc
Normal file
@ -0,0 +1,122 @@
|
||||
'use strict';
|
||||
|
||||
import { popen, open as fopen } from 'fs';
|
||||
import { LOG_PATH, DB_PATH, CONFIG_PATH } from './const.uc';
|
||||
import { CLASH_PORT } from './default.uc';
|
||||
import { call_system_command, log_t } from './utils.uc';
|
||||
|
||||
function version() {
|
||||
let result = {
|
||||
version: 'unkown',
|
||||
features: {},
|
||||
};
|
||||
|
||||
const handle = popen('/usr/bin/sing-box version');
|
||||
|
||||
if (handle) {
|
||||
for (let line = handle.read('line'); length(line); line = handle.read('line')) {
|
||||
line = trim(line);
|
||||
|
||||
let tags = match(line, /Tags: (.*)/);
|
||||
if (tags) {
|
||||
for (let i in split(tags[1], ',')) {
|
||||
result.features[i] = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
let version = match(line, /sing-box version v(.*)/);
|
||||
if (version) {
|
||||
result.version = split(version[1], ' ')[0];
|
||||
}
|
||||
}
|
||||
|
||||
handle.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function SingBoxOption() {
|
||||
return proto({
|
||||
log: {},
|
||||
dns: {},
|
||||
inbounds: [],
|
||||
outbounds: [],
|
||||
route: {},
|
||||
experimental: {},
|
||||
}, {
|
||||
version: version(),
|
||||
|
||||
logOption: function (enable, level) {
|
||||
if (enable) {
|
||||
this.log.timestamp = true;
|
||||
this.log.level = level;
|
||||
this.log.disabled = false;
|
||||
this.log.output = LOG_PATH;
|
||||
} else {
|
||||
this.log.disabled = true;
|
||||
delete this.log.level;
|
||||
delete this.log.output;
|
||||
delete this.log.timestamp;
|
||||
}
|
||||
},
|
||||
cacheFileOption: function (enable) {
|
||||
if (enable) {
|
||||
this.experimental.cache_file = {
|
||||
enabled: true,
|
||||
path: DB_PATH,
|
||||
store_fakeip: true,
|
||||
};
|
||||
} else {
|
||||
this.experimental.cache_file = {
|
||||
enabled: false,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
clashOption: function (option) {
|
||||
// 必须传入 option 的同时 singbox 编译附带了 clash api 选项才能配置
|
||||
if (!option || !this.version.features.with_clash_api) {
|
||||
delete this.experimental.clash_api;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let port = int(option.port);
|
||||
|
||||
if (port == 'NaN' || port < 0 || port > 65535) {
|
||||
port = CLASH_PORT;
|
||||
}
|
||||
|
||||
let host = option.host;
|
||||
let ui = option.ui;
|
||||
let download_url = option.download_url;
|
||||
let download_detour = option.download_detour;
|
||||
let secret = option.secret;
|
||||
let default_mode = option.default_mode;
|
||||
|
||||
this.experimental.clash_api = {
|
||||
external_controller: host + ':' + port,
|
||||
external_ui: ui,
|
||||
external_ui_download_url: download_url,
|
||||
external_ui_download_detour: download_detour,
|
||||
secret: secret,
|
||||
default_mode: default_mode,
|
||||
};
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
write: function () {
|
||||
const fp = fopen(CONFIG_PATH, 'w');
|
||||
|
||||
if (fp) {
|
||||
fp.write(this);
|
||||
fp.write('\n');
|
||||
fp.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
211
sblite/root/usr/share/sblite/subscribe.uc
Normal file
211
sblite/root/usr/share/sblite/subscribe.uc
Normal file
@ -0,0 +1,211 @@
|
||||
'use strict';
|
||||
|
||||
import { cursor } from 'uci';
|
||||
import { PKG_NAME, CONF_NAME } from './const.uc';
|
||||
import { wget, log_t, log_tab } from './utils.uc';
|
||||
import { parse as vmess_parse } from './protocols/vmess.uc';
|
||||
|
||||
function filter(name, mode) {
|
||||
switch (mode.mode) {
|
||||
case '1': // 按关键字丢弃
|
||||
return !match(name, mode.excludes);
|
||||
case '2': // 按关键字保留
|
||||
return match(name, mode.includes);
|
||||
case '3': // 按关键字丢弃未匹配成功保留列表的项
|
||||
return match(name, mode.includes) || !match(name, mode.excludes);
|
||||
case '4': // 按关键字保留未匹配成功丢弃列表的项
|
||||
return !match(name, mode.excludes) && match(name, mode.includes);
|
||||
default:
|
||||
case '0': // 不过滤
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function get_filter_mode(mode, includes, excludes) {
|
||||
const result = {
|
||||
mode: mode,
|
||||
};
|
||||
|
||||
if (result.mode && result.mode != '0') {
|
||||
if (result.mode == '1' ||
|
||||
result.mode == '3' ||
|
||||
result.mode == '4' &&
|
||||
excludes &&
|
||||
length(excludes) > 0) {
|
||||
excludes = map(excludes, str => replace(str, /[\\.*+?^$|\[(){}]/g, '\\$&'));
|
||||
result.excludes = regexp(join('|', excludes));
|
||||
}
|
||||
|
||||
if (result.mode == '2' ||
|
||||
result.mode == '3' ||
|
||||
result.mode == '4' &&
|
||||
includes &&
|
||||
length(includes) > 0) {
|
||||
includes = map(includes, str => replace(str, /[\\.*+?^$|\[(){}]/g, '\\$&'));
|
||||
result.includes = regexp(join('|', includes));
|
||||
}
|
||||
|
||||
if (result.mode >= '5') {
|
||||
result.mode = '0';
|
||||
}
|
||||
}
|
||||
|
||||
if (result.mode == '3' && !result.includes) {
|
||||
result.mode = '1';
|
||||
}
|
||||
|
||||
if (result.mode == '4' && !result.excludes) {
|
||||
result.mode = '2';
|
||||
}
|
||||
|
||||
if (result.mode == '1' && !result.excludes) {
|
||||
result.mode = '0';
|
||||
}
|
||||
|
||||
if (result.mode == '2' && !result.includes) {
|
||||
result.mode = '0';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function subscribe_one(section, filter_mode, results) {
|
||||
if (!results) {
|
||||
results = {};
|
||||
}
|
||||
|
||||
const group = section.tag;
|
||||
const url = section.subscribe_url;
|
||||
const no_certificate = section.no_certificate;
|
||||
const ua = section.ua;
|
||||
|
||||
log_t('[%s] start...', group);
|
||||
log_tab('Filter mode %J', filter_mode);
|
||||
|
||||
const handle = wget(url, no_certificate, ua);
|
||||
|
||||
if (handle) {
|
||||
let content = handle.read('all');
|
||||
|
||||
const decode = b64dec(content);
|
||||
|
||||
if (decode != null) {
|
||||
content = decode;
|
||||
}
|
||||
|
||||
if (content) {
|
||||
const lines = split(content, /[\r\n]/g);
|
||||
|
||||
for (let line in lines) {
|
||||
line = trim(line);
|
||||
|
||||
if (line != '') {
|
||||
let result = {
|
||||
group: group,
|
||||
};
|
||||
|
||||
if (vmess_parse(line, result)) {
|
||||
//
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (filter(result.alias, filter_mode)) {
|
||||
log_tab('Get %s Hash: %s', result.alias, result.hashkey);
|
||||
results[result.hashkey] = result;
|
||||
} else {
|
||||
log_tab('Discard %s', result.alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_t('[%s] done.', group);
|
||||
handle.close();
|
||||
}
|
||||
else {
|
||||
log_t('[%s] error.', group);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
export function subscribe(section_id) {
|
||||
const uci = cursor();
|
||||
let results = {};
|
||||
|
||||
const subscribe_section = uci.get_all(CONF_NAME, 'subscribe');
|
||||
|
||||
|
||||
const filter_mode = get_filter_mode(
|
||||
subscribe_section.filter_mode,
|
||||
subscribe_section.whitelist,
|
||||
subscribe_section.blacklist
|
||||
);
|
||||
|
||||
log_tab('Global filter mode %J', filter_mode);
|
||||
|
||||
if (section_id) {
|
||||
const section = uci.get_all(CONF_NAME, section_id);
|
||||
if (section) {
|
||||
section_id = section.tag;
|
||||
|
||||
if (section.filter_mode == '5') {
|
||||
results = subscribe_one(section, filter_mode);
|
||||
} else {
|
||||
results = subscribe_one(section, get_filter_mode(
|
||||
section.filter_mode,
|
||||
section.whitelist,
|
||||
section.blacklist
|
||||
));
|
||||
}
|
||||
} else {
|
||||
log_t('can\'t get subscribe config %s...', section_id);
|
||||
}
|
||||
|
||||
} else {
|
||||
log_t('subscribe starting...');
|
||||
uci.foreach(CONF_NAME, 'subscription',
|
||||
function (section) {
|
||||
if (section.filter_mode == '5') {
|
||||
results = subscribe_one(section, filter_mode, results);
|
||||
} else {
|
||||
results = subscribe_one(section, get_filter_mode(
|
||||
section.filter_mode,
|
||||
section.whitelist,
|
||||
section.blacklist
|
||||
), results);
|
||||
}
|
||||
}
|
||||
);
|
||||
log_t('All done.');
|
||||
}
|
||||
|
||||
uci.foreach(CONF_NAME, 'node', section => {
|
||||
const group = section.group;
|
||||
if (group != null && group != '') {
|
||||
if (section_id && group != section_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
uci.delete(CONF_NAME, section['.name']);
|
||||
}
|
||||
});
|
||||
|
||||
for (let hashkey in keys(results)) {
|
||||
const result = results[hashkey];
|
||||
const sid = uci.add(CONF_NAME, 'node');
|
||||
for (let option in keys(result)) {
|
||||
uci.set(CONF_NAME, sid, option, result[option]);
|
||||
}
|
||||
}
|
||||
|
||||
print(results);
|
||||
|
||||
if (uci.commit(CONF_NAME)) {
|
||||
return results;
|
||||
}
|
||||
else {
|
||||
die('commit failed!');
|
||||
}
|
||||
};
|
155
sblite/root/usr/share/sblite/utils.uc
Normal file
155
sblite/root/usr/share/sblite/utils.uc
Normal file
@ -0,0 +1,155 @@
|
||||
'use strict';
|
||||
|
||||
import { popen, open as fopen } from 'fs';
|
||||
|
||||
const LOG_FILE = '/tmp/log/sblite.log';
|
||||
|
||||
export function call_system_command(command) {
|
||||
const handle = popen(command);
|
||||
if (handle) {
|
||||
let content = handle.read('all');
|
||||
handle.close();
|
||||
content = trim(content);
|
||||
|
||||
if (content != null) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// return the content of wget output
|
||||
export function wget(url, ua, no_certificate) {
|
||||
let addition_parameters = '';
|
||||
|
||||
if (ua) {
|
||||
addition_parameters += ('--user-agent="' + ua + '" ');
|
||||
}
|
||||
|
||||
if (no_certificate) {
|
||||
addition_parameters += ('--no-check-certificate ');
|
||||
}
|
||||
|
||||
return popen('wget -q ' + addition_parameters + '-t 3 -T 10 -O- ' + url);
|
||||
};
|
||||
|
||||
// return the md5 value of str
|
||||
export function md5(str) {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const handle = popen('echo -n "' + str + '" | md5sum');
|
||||
if (handle) {
|
||||
let content = handle.read(' ');
|
||||
handle.close();
|
||||
content = trim(content);
|
||||
|
||||
if (content != null) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function parse_port(port) {
|
||||
port = int(port);
|
||||
|
||||
if (port <= 0 || port >= 65535) {
|
||||
port = 'NaN';
|
||||
}
|
||||
|
||||
return port;
|
||||
};
|
||||
|
||||
export function get_loopback(ipv6) {
|
||||
let loopback = '127.0.0.1';
|
||||
if (ipv6) {
|
||||
loopback = '::1';
|
||||
}
|
||||
|
||||
return loopback;
|
||||
};
|
||||
|
||||
export function delete_empty_arr(obj, arr_name) {
|
||||
if (length(obj[arr_name]) == 0) {
|
||||
delete obj[arr_name];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 将字符串转化为 ip
|
||||
* @returns {Object} ip
|
||||
* @returns {number} ip.version
|
||||
* @returns {string} ip.address
|
||||
* @returns {number} ip.cidr
|
||||
*/
|
||||
export function asip(str) {
|
||||
const result = match(str, /^([0-9a-fA-F:.]+)(\/([0-9]+))?$/);
|
||||
if (result) {
|
||||
const ip = iptoarr(result[1]);
|
||||
|
||||
if (ip) {
|
||||
let cidr = (result[3] == null ? null : int(result[3]));
|
||||
|
||||
if (length(ip) == 4) {
|
||||
cidr ??= 32;
|
||||
|
||||
if (cidr <= 32) {
|
||||
return { version: 4, address: arrtoip(ip), cidr: cidr };
|
||||
}
|
||||
} else if (length(ip) == 16) {
|
||||
cidr ??= 128;
|
||||
|
||||
if (cidr <= 128) {
|
||||
return { version: 6, address: arrtoip(ip), cidr: cidr };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export function asport(str) {
|
||||
const result = match(str, /^([0-9]+)(-([0-9]+))?$/);
|
||||
if (result) {
|
||||
const port0 = int(result[1]);
|
||||
|
||||
if (port0 >= 0 && port0 <= 65535) {
|
||||
const port1 = result[3] == null ? null : int(result[3]);
|
||||
|
||||
if (port1) {
|
||||
if (port1 > port0 && port1 <= 65535) {
|
||||
return { range: true, start: port0, end: port1 };
|
||||
}
|
||||
|
||||
} else {
|
||||
return { range: false, value: port0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
function log(fmt, ...args) {
|
||||
const fp = fopen(LOG_FILE, 'a');
|
||||
|
||||
if (fp) {
|
||||
fp.write(sprintf(fmt, ...args));
|
||||
fp.write('\n');
|
||||
fp.close();
|
||||
}
|
||||
};
|
||||
|
||||
export function log_t(fmt, ...args) {
|
||||
let s = sprintf(fmt, ...args);
|
||||
const now = localtime(time());
|
||||
|
||||
log('[%04d-%02d-%02d %02d:%02d:%02d] %s', now.year, now.mon, now.mday, now.hour, now.min, now.sec, s);
|
||||
};
|
||||
|
||||
export function log_tab(fmt, ...args) {
|
||||
log(' %s', sprintf(fmt, ...args));
|
||||
};
|
||||
|
||||
export function clearlog() {
|
||||
system('echo "" > ' + LOG_FILE);
|
||||
};
|
@ -1,12 +1,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=sms-tool
|
||||
PKG_RELEASE:=22
|
||||
PKG_RELEASE:=23
|
||||
|
||||
PKG_SOURCE_URL:=https://github.com/obsy/sms_tool
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_DATE:=2022-03-21
|
||||
PKG_SOURCE_VERSION:=1b6ca03284fd65db8799dbf7c6224210093786b0
|
||||
PKG_SOURCE_VERSION:=fce2b931c8d749c28b8281363950e963c98324eb
|
||||
PKG_MIRROR_HASH:=skip
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
Loading…
Reference in New Issue
Block a user