mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-06 11:24:07 +08:00
✨ Sync 2026-06-07 00:43:45
This commit is contained in:
1 parent
75dec81517
commit
85f7b195f8
18 files changed
+4294
-2
No files matched your search
@@ -0,0 +1,2 @@
|
||||
config daede 'config'
|
||||
option active_backend 'daed'
|
||||
@@ -0,0 +1,153 @@
|
||||
#!/bin/sh
|
||||
# Patch the daed package's /etc/init.d/daed to clean up /run/netns/daens on
|
||||
# stop_service(). Without this, restarting daed fails with:
|
||||
# fatal: failed to create netns: open /run/netns/daens: file exists
|
||||
#
|
||||
# This script lives under /etc/uci-defaults/ which OpenWrt runs once on
|
||||
# every package install/upgrade and then removes - so each time luci-app-daede
|
||||
# is (re-)installed we re-patch the daed init.d even if it was overwritten
|
||||
# by a daed package upgrade in between.
|
||||
#
|
||||
# The patch is idempotent: a marker comment guards against double-injection.
|
||||
|
||||
set -e
|
||||
|
||||
# luci-app-daede owns its own LuCI menu and view path. Remove stale files from
|
||||
# the official luci-app-daed package and earlier daede builds that reused
|
||||
# /view/daed.
|
||||
rm -f /usr/share/luci/menu.d/luci-app-daed.json
|
||||
rm -f /usr/share/rpcd/acl.d/luci-app-daed.json
|
||||
rm -rf /usr/share/luci-app-daed
|
||||
rm -rf /www/luci-static/resources/view/daed
|
||||
rm -f /usr/lib/lua/luci/i18n/daed.zh-cn.lmo
|
||||
rm -f /tmp/luci-indexcache.* 2>/dev/null || true
|
||||
rm -rf /tmp/luci-modulecache/* 2>/dev/null || true
|
||||
|
||||
if [ -f /etc/config/daed_luci ]; then
|
||||
old_backend="$(uci -q get daed_luci.config.active_backend || true)"
|
||||
if ! uci -q get daede.config >/dev/null 2>&1; then
|
||||
touch /etc/config/daede
|
||||
uci -q set daede.config=daede
|
||||
case "$old_backend" in
|
||||
dae|daed) uci -q set daede.config.active_backend="$old_backend" ;;
|
||||
esac
|
||||
uci -q commit daede
|
||||
elif ! uci -q get daede.config.active_backend >/dev/null 2>&1; then
|
||||
case "$old_backend" in
|
||||
dae|daed)
|
||||
uci -q set daede.config.active_backend="$old_backend"
|
||||
uci -q commit daede
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
rm -f /etc/config/daed_luci
|
||||
fi
|
||||
|
||||
if ! uci -q get daede.config >/dev/null 2>&1; then
|
||||
touch /etc/config/daede
|
||||
uci -q set daede.config=daede
|
||||
if [ -f /etc/init.d/daed ]; then
|
||||
uci -q set daede.config.active_backend='daed'
|
||||
else
|
||||
uci -q set daede.config.active_backend='dae'
|
||||
fi
|
||||
uci -q commit daede
|
||||
fi
|
||||
|
||||
# Remove --disable-timestamp from dae init so log lines carry timestamps
|
||||
DAE_INIT="/etc/init.d/dae"
|
||||
if [ -f "$DAE_INIT" ]; then
|
||||
sed -i 's/--disable-timestamp//' "$DAE_INIT"
|
||||
sed -i '/procd_append_param command *$/d' "$DAE_INIT"
|
||||
fi
|
||||
|
||||
# dae expects geoip.dat / geosite.dat under /usr/share/dae/;
|
||||
# v2ray-geoip installs them to /usr/share/v2ray/. Create symlinks.
|
||||
if [ -f /usr/share/v2ray/geoip.dat ] && [ ! -f /usr/share/dae/geoip.dat ]; then
|
||||
mkdir -p /usr/share/dae
|
||||
ln -sf /usr/share/v2ray/geoip.dat /usr/share/dae/geoip.dat
|
||||
fi
|
||||
if [ -f /usr/share/v2ray/geosite.dat ] && [ ! -f /usr/share/dae/geosite.dat ]; then
|
||||
mkdir -p /usr/share/dae
|
||||
ln -sf /usr/share/v2ray/geosite.dat /usr/share/dae/geosite.dat
|
||||
fi
|
||||
if [ -f /usr/share/v2ray/geoip.dat ] && [ ! -f /usr/share/daed/geoip.dat ]; then
|
||||
mkdir -p /usr/share/daed
|
||||
ln -sf /usr/share/v2ray/geoip.dat /usr/share/daed/geoip.dat
|
||||
fi
|
||||
if [ -f /usr/share/v2ray/geosite.dat ] && [ ! -f /usr/share/daed/geosite.dat ]; then
|
||||
mkdir -p /usr/share/daed
|
||||
ln -sf /usr/share/v2ray/geosite.dat /usr/share/daed/geosite.dat
|
||||
fi
|
||||
|
||||
# Seed default dae form sections (group/routing/dns) for the LuCI editor.
|
||||
# Idempotent: only add what's missing. Lives in the `dae` UCI package,
|
||||
# consumed by gen-dae-config.sh.
|
||||
if [ -f /etc/config/dae ]; then
|
||||
if ! uci -q show dae | grep -q "=group$"; then
|
||||
g="$(uci add dae group)"
|
||||
uci -q set "dae.$g.name=proxy"
|
||||
uci -q set "dae.$g.policy=min_moving_avg"
|
||||
fi
|
||||
if ! uci -q get dae.routing >/dev/null 2>&1; then
|
||||
uci -q set dae.routing=routing
|
||||
uci -q set dae.routing.private_direct=1
|
||||
uci -q set dae.routing.cn_direct=1
|
||||
uci -q set dae.routing.block_ads=0
|
||||
uci -q set dae.routing.fallback=proxy
|
||||
fi
|
||||
if ! uci -q get dae.dns >/dev/null 2>&1; then
|
||||
uci -q set dae.dns=dns
|
||||
uci -q set dae.dns.cn_upstream='udp://dns.alidns.com:53'
|
||||
uci -q set dae.dns.fallback_upstream='tcp+udp://dns.google:53'
|
||||
fi
|
||||
# Migrate legacy group filters to the unified source / name_filter fields,
|
||||
# then drop the old keys so the form shows real, editable values (the old
|
||||
# fields used to re-populate the new ones and looked un-deletable).
|
||||
# `uci get` joins a list into one space-separated line; the unquoted $() word-
|
||||
# splits it back into items. dae tags/keywords are whitespace-free, so this is
|
||||
# the intended split (do NOT set IFS=newline here — it would merge the items).
|
||||
i=0
|
||||
while uci -q get "dae.@group[$i]" >/dev/null 2>&1; do
|
||||
if [ -z "$(uci -q get dae.@group[$i].source)" ]; then
|
||||
for v in $(uci -q get dae.@group[$i].filter_sub 2>/dev/null); do
|
||||
uci -q add_list dae.@group[$i].source="$v"
|
||||
done
|
||||
fi
|
||||
if [ -z "$(uci -q get dae.@group[$i].name_filter)" ]; then
|
||||
for v in $(uci -q get dae.@group[$i].filter_node 2>/dev/null); do
|
||||
uci -q add_list dae.@group[$i].name_filter="$v"
|
||||
done
|
||||
fi
|
||||
uci -q delete dae.@group[$i].filter_sub 2>/dev/null
|
||||
uci -q delete dae.@group[$i].filter_node 2>/dev/null
|
||||
i=$((i+1))
|
||||
done
|
||||
uci -q commit dae
|
||||
fi
|
||||
|
||||
INIT="/etc/init.d/daed"
|
||||
MARKER="# luci-app-daede: netns-cleanup"
|
||||
|
||||
[ -f "$INIT" ] || exit 0
|
||||
grep -q "$MARKER" "$INIT" && exit 0
|
||||
|
||||
# Inject `ip netns del daens 2>/dev/null || true` right after `rm -f "$LOG"`
|
||||
# inside stop_service(). awk is more reliable than sed for multiline contexts.
|
||||
awk -v marker="$MARKER" '
|
||||
BEGIN { in_stop = 0; injected = 0 }
|
||||
/^stop_service\(\)/ { in_stop = 1 }
|
||||
in_stop && /^}/ { in_stop = 0 }
|
||||
{ print }
|
||||
in_stop && !injected && /rm -f "\$LOG"/ {
|
||||
print "\tip netns del daens 2>/dev/null || true " marker
|
||||
injected = 1
|
||||
}
|
||||
' "$INIT" > "${INIT}.new" && mv "${INIT}.new" "$INIT"
|
||||
chmod +x "$INIT"
|
||||
|
||||
exit 0
|
||||
|
||||
reload_service() {
|
||||
restart
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# gen-dae-config.sh - build /etc/dae/config.dae from the dae UCI form sections,
|
||||
# validate with `dae validate`, then overwrite + hot reload only on success.
|
||||
#
|
||||
# generate read UCI -> render config.dae -> validate -> overwrite + hot reload
|
||||
# import parse existing config.dae subscription{}/node{} -> fill UCI form
|
||||
#
|
||||
# UCI model (package dae):
|
||||
# config subscription tag/url, enabled
|
||||
# config node tag/link, enabled
|
||||
# config group name/policy, list source, list name_filter
|
||||
# (old list filter_sub/filter_node still read as fallback)
|
||||
# config routing 'routing' private_direct/cn_direct/block_ads/fallback, list custom
|
||||
# config dns 'dns' cn_upstream/fallback_upstream
|
||||
|
||||
. /lib/functions.sh
|
||||
|
||||
CONFIG_DAE="/etc/dae/config.dae"
|
||||
TMP_GEN="/tmp/dae-gen.dae"
|
||||
DAE_BIN="/usr/bin/dae"
|
||||
DAE_INITD="/etc/init.d/dae"
|
||||
|
||||
# dae single-quoted strings have no escaping; strip quotes/newlines defensively.
|
||||
sanitize() {
|
||||
printf '%s' "$1" | tr -d "'" | tr -d '\n\r'
|
||||
}
|
||||
|
||||
# ===================== generate =====================
|
||||
|
||||
SUB_BUF=""
|
||||
NODE_BUF=""
|
||||
GROUP_BUF=""
|
||||
FIRST_GROUP=""
|
||||
|
||||
emit_sub() {
|
||||
local s="$1" tag url enabled
|
||||
config_get tag "$s" tag ""
|
||||
config_get url "$s" url ""
|
||||
config_get_bool enabled "$s" enabled 1
|
||||
[ "$enabled" = "1" ] || return 0
|
||||
url="$(sanitize "$url")"
|
||||
[ -n "$url" ] || return 0
|
||||
tag="$(sanitize "$tag")"
|
||||
if [ -n "$tag" ]; then
|
||||
SUB_BUF="${SUB_BUF} ${tag}: '${url}'
|
||||
"
|
||||
else
|
||||
SUB_BUF="${SUB_BUF} '${url}'
|
||||
"
|
||||
fi
|
||||
}
|
||||
|
||||
emit_node() {
|
||||
local s="$1" tag link enabled
|
||||
config_get tag "$s" tag ""
|
||||
config_get link "$s" link ""
|
||||
config_get_bool enabled "$s" enabled 1
|
||||
[ "$enabled" = "1" ] || return 0
|
||||
link="$(sanitize "$link")"
|
||||
[ -n "$link" ] || return 0
|
||||
tag="$(sanitize "$tag")"
|
||||
if [ -n "$tag" ]; then
|
||||
NODE_BUF="${NODE_BUF} ${tag}: '${link}'
|
||||
"
|
||||
else
|
||||
NODE_BUF="${NODE_BUF} '${link}'
|
||||
"
|
||||
fi
|
||||
}
|
||||
|
||||
emit_group() {
|
||||
local s="$1" name policy
|
||||
config_get name "$s" name ""
|
||||
config_get policy "$s" policy "min_moving_avg"
|
||||
name="$(sanitize "$name")"
|
||||
[ -n "$name" ] || return 0
|
||||
[ -n "$FIRST_GROUP" ] || FIRST_GROUP="$name"
|
||||
|
||||
GROUP_BUF="${GROUP_BUF} ${name} {
|
||||
"
|
||||
# Build the source: 'source' entries that match a subscription tag become
|
||||
# subtag(), the rest (node tags / keywords) become name(). Old filter_sub /
|
||||
# filter_node configs still work as a fallback when 'source' is unset.
|
||||
SUBS_ACC=""; NODES_ACC=""; KW_ACC=""
|
||||
# source: new field, else old filter_sub (decoupled from the name filter so
|
||||
# editing one never silently drops the other)
|
||||
config_list_foreach "$s" source _collect_source
|
||||
[ -z "$SUBS_ACC" ] && [ -z "$NODES_ACC" ] && config_list_foreach "$s" filter_sub _collect_source
|
||||
# optional name filter narrows the subscription pool only (AND); hand-picked
|
||||
# nodes are explicit choices and stay in unconditionally
|
||||
config_list_foreach "$s" name_filter _collect_kw
|
||||
[ -z "$KW_ACC" ] && config_list_foreach "$s" filter_node _collect_kw
|
||||
local andclause=""
|
||||
[ -n "$KW_ACC" ] && andclause=" && name(${KW_ACC})"
|
||||
|
||||
local emitted=0
|
||||
# multiple filter lines = OR, so subtag and name lines union the sources
|
||||
if [ -n "$SUBS_ACC" ]; then
|
||||
GROUP_BUF="${GROUP_BUF} filter: subtag(${SUBS_ACC})${andclause}
|
||||
"
|
||||
emitted=1
|
||||
fi
|
||||
if [ -n "$NODES_ACC" ]; then
|
||||
GROUP_BUF="${GROUP_BUF} filter: name(${NODES_ACC})
|
||||
"
|
||||
emitted=1
|
||||
fi
|
||||
# name filter with no source = filter the whole pool by name
|
||||
if [ "$emitted" = 0 ] && [ -n "$KW_ACC" ]; then
|
||||
GROUP_BUF="${GROUP_BUF} filter: name(${KW_ACC})
|
||||
"
|
||||
fi
|
||||
GROUP_BUF="${GROUP_BUF} policy: ${policy}
|
||||
}
|
||||
"
|
||||
SUBS_ACC=""; NODES_ACC=""; KW_ACC=""
|
||||
}
|
||||
|
||||
SUBS_ACC=""
|
||||
NODES_ACC=""
|
||||
KW_ACC=""
|
||||
# Subscription tags, space-padded for membership tests (" tag " match).
|
||||
SUB_TAGS=""
|
||||
collect_subtag() {
|
||||
local t; config_get t "$1" tag ""
|
||||
t="$(sanitize "$t")"
|
||||
[ -n "$t" ] && SUB_TAGS="${SUB_TAGS} ${t} "
|
||||
}
|
||||
is_subtag() {
|
||||
case "$SUB_TAGS" in *" $1 "*) return 0 ;; *) return 1 ;; esac
|
||||
}
|
||||
# dae filter args are bare only for plain identifiers; anything with regex
|
||||
# chars / CJK / spaces must be wrapped as regex: '...' or the lexer chokes.
|
||||
filter_arg() {
|
||||
case "$1" in
|
||||
*[!A-Za-z0-9_]*) printf "regex: '%s'" "$1" ;;
|
||||
*) printf '%s' "$1" ;;
|
||||
esac
|
||||
}
|
||||
# name-filter args do substring matching: regex metachars -> regex:, else keyword:.
|
||||
# Intentionally stricter than filter_arg: a plain CJK keyword here stays a
|
||||
# substring `keyword:` match, whereas filter_arg wraps any non-word char as regex.
|
||||
kw_arg() {
|
||||
if printf '%s' "$1" | grep -q '[][|().*+?^$\\]'; then
|
||||
printf "regex: '%s'" "$1"
|
||||
else
|
||||
printf "keyword: '%s'" "$1"
|
||||
fi
|
||||
}
|
||||
# classify a source entry: subscription tag -> subtag bucket, else name bucket
|
||||
_collect_source() {
|
||||
local v="$(sanitize "$1")"
|
||||
[ -n "$v" ] || return 0
|
||||
if is_subtag "$v"; then
|
||||
SUBS_ACC="${SUBS_ACC:+$SUBS_ACC, }$(filter_arg "$v")"
|
||||
else
|
||||
NODES_ACC="${NODES_ACC:+$NODES_ACC, }$(filter_arg "$v")"
|
||||
fi
|
||||
}
|
||||
_collect_kw() {
|
||||
local v="$(sanitize "$1")"
|
||||
[ -n "$v" ] || return 0
|
||||
KW_ACC="${KW_ACC:+$KW_ACC, }$(kw_arg "$v")"
|
||||
}
|
||||
|
||||
generate() {
|
||||
config_load dae
|
||||
|
||||
# global overridable knobs (fall back to defaults)
|
||||
local dial_mode log_level wan_interface lan_interface
|
||||
config_get dial_mode config dial_mode "domain"
|
||||
config_get log_level config log_level "info"
|
||||
config_get wan_interface config wan_interface "auto"
|
||||
config_get lan_interface config lan_interface ""
|
||||
|
||||
SUB_BUF=""; NODE_BUF=""; GROUP_BUF=""; FIRST_GROUP=""
|
||||
SUB_TAGS=""
|
||||
config_foreach collect_subtag subscription
|
||||
config_foreach emit_sub subscription
|
||||
config_foreach emit_node node
|
||||
config_foreach emit_group group
|
||||
|
||||
# fall back to a default group when none defined
|
||||
if [ -z "$GROUP_BUF" ]; then
|
||||
GROUP_BUF=" proxy {
|
||||
policy: min_moving_avg
|
||||
}
|
||||
"
|
||||
FIRST_GROUP="proxy"
|
||||
fi
|
||||
|
||||
# routing / dns singletons
|
||||
local private_direct cn_direct block_ads fallback cn_up fb_up
|
||||
config_get_bool private_direct routing private_direct 1
|
||||
config_get_bool cn_direct routing cn_direct 1
|
||||
config_get_bool block_ads routing block_ads 0
|
||||
config_get fallback routing fallback "$FIRST_GROUP"
|
||||
[ -n "$fallback" ] || fallback="$FIRST_GROUP"
|
||||
fallback="$(sanitize "$fallback")"
|
||||
config_get cn_up dns cn_upstream "udp://dns.alidns.com:53"
|
||||
config_get fb_up dns fallback_upstream "tcp+udp://dns.google:53"
|
||||
cn_up="$(sanitize "$cn_up")"
|
||||
fb_up="$(sanitize "$fb_up")"
|
||||
|
||||
{
|
||||
echo "# Auto-generated by luci-app-daede form. Manual edits are overwritten on next save."
|
||||
echo "global {"
|
||||
echo " tproxy_port: 12345"
|
||||
echo " tproxy_port_protect: true"
|
||||
echo " log_level: ${log_level}"
|
||||
[ -n "$lan_interface" ] && echo " lan_interface: ${lan_interface}"
|
||||
echo " wan_interface: ${wan_interface}"
|
||||
echo " auto_config_kernel_parameter: true"
|
||||
echo " dial_mode: ${dial_mode}"
|
||||
echo " tcp_check_url: 'http://cp.cloudflare.com,1.1.1.1,2606:4700:4700::1111'"
|
||||
echo " udp_check_dns: 'dns.google:53,8.8.8.8,2001:4860:4860::8888'"
|
||||
echo " check_interval: 30s"
|
||||
echo " check_tolerance: 50ms"
|
||||
echo "}"
|
||||
echo ""
|
||||
|
||||
if [ -n "$SUB_BUF" ]; then
|
||||
echo "subscription {"
|
||||
printf '%s' "$SUB_BUF"
|
||||
echo "}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [ -n "$NODE_BUF" ]; then
|
||||
echo "node {"
|
||||
printf '%s' "$NODE_BUF"
|
||||
echo "}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "dns {"
|
||||
echo " ipversion_prefer: 4"
|
||||
echo " upstream {"
|
||||
echo " cndns: '${cn_up}'"
|
||||
echo " fallbackdns: '${fb_up}'"
|
||||
echo " }"
|
||||
echo " routing {"
|
||||
echo " request {"
|
||||
echo " qname(geosite:cn) -> cndns"
|
||||
echo " fallback: fallbackdns"
|
||||
echo " }"
|
||||
echo " }"
|
||||
echo "}"
|
||||
echo ""
|
||||
|
||||
echo "group {"
|
||||
printf '%s' "$GROUP_BUF"
|
||||
echo "}"
|
||||
echo ""
|
||||
|
||||
echo "routing {"
|
||||
echo " pname(NetworkManager) -> direct"
|
||||
# multicast / broadcast direct (geoip:private doesn't cover these)
|
||||
echo " dip(224.0.0.0/3) -> direct"
|
||||
echo " dip(255.255.255.255/32) -> direct"
|
||||
echo " dip('ff00::/8') -> direct"
|
||||
[ "$private_direct" = "1" ] && echo " dip(geoip:private) -> direct"
|
||||
# NTP time sync direct
|
||||
echo " l4proto(udp) && dport(123) -> direct"
|
||||
# OS connectivity checks direct (avoid captive-portal false positives)
|
||||
echo " domain(connectivitycheck.gstatic.com) -> direct"
|
||||
echo " domain(msftconnecttest.com) -> direct"
|
||||
if [ "$cn_direct" = "1" ]; then
|
||||
echo " dip(geoip:cn) -> direct"
|
||||
echo " domain(geosite:cn) -> direct"
|
||||
fi
|
||||
[ "$block_ads" = "1" ] && echo " domain(geosite:category-ads-all) -> block"
|
||||
config_list_foreach routing custom _emit_custom
|
||||
echo " fallback: ${fallback}"
|
||||
echo "}"
|
||||
} > "$TMP_GEN"
|
||||
chmod 600 "$TMP_GEN" # dae rejects world/group-accessible config files
|
||||
|
||||
# overwrite live config only if validation passes
|
||||
local out
|
||||
out="$("$DAE_BIN" validate -c "$TMP_GEN" 2>&1)"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "validate failed: $(echo "$out" | head -1)" >&2
|
||||
rm -f "$TMP_GEN"
|
||||
return 1
|
||||
fi
|
||||
|
||||
mkdir -p /etc/dae
|
||||
cat "$TMP_GEN" > "$CONFIG_DAE"
|
||||
chmod 600 "$CONFIG_DAE"
|
||||
rm -f "$TMP_GEN"
|
||||
|
||||
if /usr/bin/pgrep -x dae >/dev/null 2>&1; then
|
||||
"$DAE_INITD" hot_reload >/dev/null 2>&1
|
||||
fi
|
||||
echo "ok"
|
||||
return 0
|
||||
}
|
||||
|
||||
_emit_custom() {
|
||||
local line="$(printf '%s' "$1" | tr -d '\n\r')"
|
||||
[ -n "$line" ] || return 0
|
||||
echo " ${line}"
|
||||
}
|
||||
|
||||
# ===================== import =====================
|
||||
# Fill the UCI form from an existing config.dae subscription{}/node{} block.
|
||||
# Idempotent: wipe same-type sections first, then rebuild from file.
|
||||
|
||||
import_block() {
|
||||
# $1=block name (subscription|node) $2=uci section type $3=link/url field
|
||||
local block="$1" stype="$2" field="$3"
|
||||
awk -v blk="$block" '
|
||||
$0 ~ "^[[:space:]]*"blk"[[:space:]]*\\{" { inb=1; next }
|
||||
inb && /^[[:space:]]*\}/ { inb=0 }
|
||||
inb { print }
|
||||
' "$CONFIG_DAE" 2>/dev/null | while IFS= read -r raw; do
|
||||
# drop comment, trim
|
||||
line="$(printf '%s' "$raw" | sed 's/#.*$//' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
|
||||
[ -n "$line" ] || continue
|
||||
tag=""; val=""
|
||||
case "$line" in
|
||||
\'*|\"*)
|
||||
# starts with a quote -> untagged link
|
||||
val="$line"
|
||||
;;
|
||||
[A-Za-z0-9_]*:*)
|
||||
# identifier ':' link -> tagged (split on first colon)
|
||||
tag="$(printf '%s' "$line" | sed "s/[[:space:]]*:.*//")"
|
||||
val="$(printf '%s' "$line" | sed "s/^[^:]*:[[:space:]]*//")"
|
||||
;;
|
||||
*)
|
||||
val="$line"
|
||||
;;
|
||||
esac
|
||||
# strip surrounding quotes
|
||||
val="$(printf '%s' "$val" | sed "s/^['\"]//; s/['\"]$//")"
|
||||
[ -n "$val" ] || continue
|
||||
# skip dae example placeholders (ss://LINK, server-ip:port, localhost:1080 ...)
|
||||
case "$val" in
|
||||
*://LINK|*LINK*|*server-ip*|*://localhost:*|*example.com*) continue ;;
|
||||
esac
|
||||
sid="$(uci add dae "$stype")"
|
||||
[ -n "$tag" ] && uci set "dae.$sid.tag=$tag"
|
||||
uci set "dae.$sid.$field=$val"
|
||||
uci set "dae.$sid.enabled=1"
|
||||
done
|
||||
}
|
||||
|
||||
do_import() {
|
||||
[ -f "$CONFIG_DAE" ] || { echo "no config.dae" >&2; return 1; }
|
||||
while uci -q delete dae.@subscription[0] >/dev/null 2>&1; do :; done
|
||||
while uci -q delete dae.@node[0] >/dev/null 2>&1; do :; done
|
||||
import_block subscription subscription url
|
||||
import_block node node link
|
||||
uci commit dae
|
||||
echo "ok"
|
||||
}
|
||||
|
||||
# ===================== entry =====================
|
||||
|
||||
case "$1" in
|
||||
generate) generate ;;
|
||||
import) do_import ;;
|
||||
*) echo "usage: $0 {generate|import}" >&2; exit 2 ;;
|
||||
esac
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
# pkg-info.sh <package>
|
||||
# Prints "<installed>\t<latest>" for the named package, where either field is
|
||||
# empty when unknown. Used by the Updates view to decide whether to enable
|
||||
# the [Upgrade] button.
|
||||
|
||||
PKG="$1"
|
||||
case "$PKG" in
|
||||
dae|daed|luci-app-daede) ;;
|
||||
*) echo "" ; exit 64 ;;
|
||||
esac
|
||||
|
||||
installed=""
|
||||
latest=""
|
||||
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
if apk info -e "$PKG" >/dev/null 2>&1; then
|
||||
installed=$(apk list -I "$PKG" 2>/dev/null | awk -v p="$PKG" '
|
||||
$1 ~ "^" p "-" {
|
||||
sub("^" p "-", "", $1);
|
||||
print $1;
|
||||
exit
|
||||
}
|
||||
')
|
||||
fi
|
||||
latest=$(apk search "^${PKG}\$" 2>/dev/null | sort -V | tail -1 | sed "s/^${PKG}-//")
|
||||
elif command -v opkg >/dev/null 2>&1; then
|
||||
installed=$(opkg status "$PKG" 2>/dev/null | awk -F': ' '$1=="Version"{print $2; exit}')
|
||||
latest=$(opkg info "$PKG" 2>/dev/null | awk -F': ' '$1=="Version"{print $2; exit}')
|
||||
fi
|
||||
|
||||
printf '%s\t%s\n' "$installed" "$latest"
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
# proxy-check.sh - test whether dae's transparent proxy can actually reach a
|
||||
# blocked site (default YouTube). dae proxies the router's own WAN-bound traffic
|
||||
# (wan_interface), so a plain request from here is routed through dae just like a
|
||||
# LAN client would be. Prints one line: ok=<0|1> code=<http> ms=<n>
|
||||
|
||||
URL="${1:-https://www.youtube.com/generate_204}"
|
||||
|
||||
code=000
|
||||
ms=0
|
||||
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
out="$(curl -4 -sL --max-time 8 --connect-timeout 4 \
|
||||
-o /dev/null -w '%{http_code} %{time_total}' "$URL" 2>/dev/null)"
|
||||
code="$(printf '%s' "$out" | awk '{print $1}')"
|
||||
t="$(printf '%s' "$out" | awk '{print $2}')"
|
||||
[ -n "$code" ] || code=000
|
||||
ms="$(awk -v x="${t:-0}" 'BEGIN{printf "%d", x*1000}')"
|
||||
else
|
||||
# busybox wget has no status/timing output — success is exit 0
|
||||
if wget -q -T 8 -O /dev/null "$URL" 2>/dev/null; then code=204; else code=000; fi
|
||||
fi
|
||||
|
||||
ok=0
|
||||
case "$code" in 200|204|301|302) ok=1 ;; esac
|
||||
|
||||
echo "ok=$ok code=$code ms=$ms"
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/bin/sh
|
||||
# update-geo.sh <geoip|geosite>
|
||||
# Fork-and-detach so the LuCI RPC call returns immediately. The diagnostics
|
||||
# card polls the .dat file's mtime/size to detect completion. A lock file
|
||||
# in /tmp prevents concurrent runs.
|
||||
|
||||
TYPE="$1"
|
||||
case "$TYPE" in
|
||||
geoip)
|
||||
URL="https://github.com/v2fly/geoip/releases/latest/download/geoip.dat"
|
||||
DEST="/usr/share/v2ray/geoip.dat"
|
||||
;;
|
||||
geosite)
|
||||
URL="https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat"
|
||||
DEST="/usr/share/v2ray/geosite.dat"
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 <geoip|geosite>" >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
|
||||
LOCK="/tmp/luci-app-daede.${TYPE}.lock"
|
||||
LOG="/tmp/luci-app-daede.${TYPE}.log"
|
||||
|
||||
if [ -f "$LOCK" ]; then
|
||||
# Lock is fresh (< 5 min)? Refuse. Stale? Remove and proceed.
|
||||
mtime=$(date -r "$LOCK" +%s 2>/dev/null || echo 0)
|
||||
age=$(( $(date +%s) - mtime ))
|
||||
if [ "$age" -lt 300 ]; then
|
||||
echo "${TYPE} update already in progress (PID $(cat "$LOCK" 2>/dev/null), age ${age}s)" >&2
|
||||
exit 75
|
||||
fi
|
||||
rm -f "$LOCK"
|
||||
fi
|
||||
|
||||
if ! ( set -C; echo "$$" >"$LOCK" ) 2>/dev/null; then
|
||||
echo "${TYPE} update already in progress" >&2
|
||||
exit 75
|
||||
fi
|
||||
|
||||
# Spawn detached worker — parent returns immediately to LuCI.
|
||||
(
|
||||
exec >"$LOG" 2>&1
|
||||
trap 'rm -f "$LOCK"' EXIT INT TERM
|
||||
|
||||
TMP="${DEST}.new"
|
||||
mkdir -p "$(dirname "$DEST")"
|
||||
echo "$(date '+%F %T') begin: $URL"
|
||||
|
||||
if ! curl -fsSL --connect-timeout 15 --max-time 240 -o "$TMP" "$URL"; then
|
||||
echo "$(date '+%F %T') download failed"
|
||||
rm -f "$TMP"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
size=$(wc -c < "$TMP" 2>/dev/null || echo 0)
|
||||
if [ "$size" -lt 102400 ]; then
|
||||
echo "$(date '+%F %T') file too small ($size bytes)"
|
||||
rm -f "$TMP"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
mv "$TMP" "$DEST"
|
||||
echo "$(date '+%F %T') updated $DEST ($size bytes)"
|
||||
) </dev/null >/dev/null 2>&1 &
|
||||
|
||||
echo "started in background, see $LOG"
|
||||
exit 0
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/bin/sh
|
||||
# update-pkg.sh <dae|daed|luci-app-daede>
|
||||
# Refresh package indexes and upgrade the named package via apk (25.12+) or
|
||||
# opkg (24.10). Forks the work to background so the LuCI RPC call returns
|
||||
# immediately; the result is streamed to /tmp/luci-app-daede.pkg.<name>.log.
|
||||
|
||||
PKG="$1"
|
||||
case "$PKG" in
|
||||
dae|daed|luci-app-daede) ;;
|
||||
*)
|
||||
echo "usage: $0 <dae|daed|luci-app-daede>" >&2
|
||||
exit 64
|
||||
;;
|
||||
esac
|
||||
|
||||
LOCK="/tmp/luci-app-daede.pkg-${PKG}.lock"
|
||||
LOG="/tmp/luci-app-daede.pkg-${PKG}.log"
|
||||
|
||||
if [ -f "$LOCK" ]; then
|
||||
mtime=$(date -r "$LOCK" +%s 2>/dev/null || echo 0)
|
||||
age=$(( $(date +%s) - mtime ))
|
||||
if [ "$age" -lt 300 ]; then
|
||||
echo "${PKG} update already in progress (PID $(cat "$LOCK" 2>/dev/null), age ${age}s)" >&2
|
||||
exit 75
|
||||
fi
|
||||
rm -f "$LOCK"
|
||||
fi
|
||||
|
||||
if ! ( set -C; echo "$$" >"$LOCK" ) 2>/dev/null; then
|
||||
echo "${PKG} update already in progress" >&2
|
||||
exit 75
|
||||
fi
|
||||
|
||||
(
|
||||
exec >"$LOG" 2>&1
|
||||
trap 'rm -f "$LOCK"' EXIT INT TERM
|
||||
|
||||
echo "$(date '+%F %T') begin upgrade: $PKG"
|
||||
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
echo "--- apk update ---"
|
||||
apk update 2>&1
|
||||
echo "--- apk upgrade $PKG ---"
|
||||
# --no-self-upgrade so apk itself does not jump versions mid-op
|
||||
apk upgrade --no-self-upgrade "$PKG" 2>&1
|
||||
rc=$?
|
||||
elif command -v opkg >/dev/null 2>&1; then
|
||||
echo "--- opkg update ---"
|
||||
opkg update 2>&1
|
||||
echo "--- opkg upgrade $PKG ---"
|
||||
opkg upgrade "$PKG" 2>&1
|
||||
rc=$?
|
||||
else
|
||||
echo "no package manager found"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
echo "$(date '+%F %T') done (rc=$rc)"
|
||||
|
||||
# luci-app-daede upgrade replaces ACL JSON — reload rpcd so changes apply.
|
||||
if [ "$PKG" = "luci-app-daede" ] && [ "$rc" = "0" ]; then
|
||||
echo "reloading rpcd to pick up new ACL"
|
||||
/etc/init.d/rpcd reload 2>&1
|
||||
fi
|
||||
) </dev/null >/dev/null 2>&1 &
|
||||
|
||||
echo "started in background, see $LOG"
|
||||
exit 0
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"admin/services/daede": {
|
||||
"title": "daede",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "firstchild"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-daede" ]
|
||||
}
|
||||
},
|
||||
"admin/services/daede/config": {
|
||||
"title": "Settings",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "daede/config"
|
||||
}
|
||||
},
|
||||
"admin/services/daede/updates": {
|
||||
"title": "Updates",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "daede/updates"
|
||||
}
|
||||
},
|
||||
"admin/services/daede/log": {
|
||||
"title": "Log",
|
||||
"order": 30,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "daede/log"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"luci-app-daede": {
|
||||
"description": "Grant access to dae/daed configuration",
|
||||
"read": {
|
||||
"file": {
|
||||
"/etc/init.d/dae": [ "read" ],
|
||||
"/etc/init.d/daed": [ "read" ],
|
||||
"/etc/config/daede": [ "read" ],
|
||||
"/etc/dae/config.dae": [ "read" ],
|
||||
"/etc/dae/example.dae": [ "read" ],
|
||||
"/var/log/dae/dae.log": [ "read" ],
|
||||
"/var/log/daed/daed.log": [ "read" ],
|
||||
"/usr/share/v2ray/geoip.dat": [ "read" ],
|
||||
"/usr/share/v2ray/geosite.dat": [ "read" ],
|
||||
"/sys/kernel/btf/vmlinux": [ "read" ],
|
||||
"/run/netns/daens": [ "read" ],
|
||||
"/tmp/luci-app-daede.geoip.log": [ "read" ],
|
||||
"/tmp/luci-app-daede.geosite.log": [ "read" ],
|
||||
"/tmp/luci-app-daede.pkg-dae.log": [ "read" ],
|
||||
"/tmp/luci-app-daede.pkg-daed.log": [ "read" ],
|
||||
"/tmp/luci-app-daede.pkg-luci-app-daede.log": [ "read" ],
|
||||
"/usr/bin/pgrep -x dae": [ "exec" ],
|
||||
"/usr/bin/pgrep -x daed": [ "exec" ],
|
||||
"/usr/bin/dae validate -c /tmp/dae-validate.dae": [ "exec" ],
|
||||
"/usr/bin/dae --version": [ "exec" ],
|
||||
"/bin/rm -f /tmp/dae-validate.dae": [ "exec" ],
|
||||
"/usr/share/luci-app-daede/pkg-info.sh dae": [ "exec" ],
|
||||
"/usr/share/luci-app-daede/pkg-info.sh daed": [ "exec" ],
|
||||
"/usr/share/luci-app-daede/pkg-info.sh luci-app-daede": [ "exec" ],
|
||||
"/usr/share/luci-app-daede/proxy-check.sh": [ "exec" ]
|
||||
},
|
||||
"ubus": {
|
||||
"service": [ "list" ]
|
||||
},
|
||||
"uci": [ "dae", "daed", "daede" ]
|
||||
},
|
||||
"write": {
|
||||
"file": {
|
||||
"/etc/dae/config.dae": [ "write" ],
|
||||
"/tmp/dae-validate.dae": [ "write" ],
|
||||
"/etc/config/daede": [ "write" ],
|
||||
"/var/log/dae/dae.log": [ "write" ],
|
||||
"/var/log/daed/daed.log": [ "write" ],
|
||||
"/etc/init.d/dae start": [ "exec" ],
|
||||
"/etc/init.d/dae stop": [ "exec" ],
|
||||
"/etc/init.d/dae restart": [ "exec" ],
|
||||
"/etc/init.d/dae enable": [ "exec" ],
|
||||
"/etc/init.d/dae disable": [ "exec" ],
|
||||
"/etc/init.d/dae hot_reload": [ "exec" ],
|
||||
"/etc/init.d/daed start": [ "exec" ],
|
||||
"/etc/init.d/daed stop": [ "exec" ],
|
||||
"/etc/init.d/daed restart": [ "exec" ],
|
||||
"/etc/init.d/daed enable": [ "exec" ],
|
||||
"/etc/init.d/daed disable": [ "exec" ],
|
||||
"/sbin/uci set dae.config.enabled=*": [ "exec" ],
|
||||
"/sbin/uci set daed.config.enabled=*": [ "exec" ],
|
||||
"/sbin/uci set daede.config=daede": [ "exec" ],
|
||||
"/sbin/uci set daede.config.active_backend=*": [ "exec" ],
|
||||
"/sbin/uci commit dae": [ "exec" ],
|
||||
"/sbin/uci commit daed": [ "exec" ],
|
||||
"/sbin/uci commit daede": [ "exec" ],
|
||||
"/sbin/ip netns del daens": [ "exec" ],
|
||||
"/usr/share/luci-app-daede/update-geo.sh geoip": [ "exec" ],
|
||||
"/usr/share/luci-app-daede/update-geo.sh geosite": [ "exec" ],
|
||||
"/usr/share/luci-app-daede/update-pkg.sh dae": [ "exec" ],
|
||||
"/usr/share/luci-app-daede/update-pkg.sh daed": [ "exec" ],
|
||||
"/usr/share/luci-app-daede/update-pkg.sh luci-app-daede": [ "exec" ],
|
||||
"/usr/share/luci-app-daede/gen-dae-config.sh generate": [ "exec" ],
|
||||
"/usr/share/luci-app-daede/gen-dae-config.sh import": [ "exec" ]
|
||||
},
|
||||
"uci": [ "dae", "daed", "daede" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user