2026-02-14 19:52:31 +04:00
#!/bin/sh
. /lib/functions.sh
_log() {
2026-02-18 01:36:35 +04:00
local msg = " $1 " level = " ${ 2 :- info } " to_console = " ${ 3 :- 0 } "
2026-02-14 19:52:31 +04:00
logger -t xray-manager -p "daemon. $level " " $msg "
[ " $to_console " = "1" ] && echo "[ $level ] $msg "
}
_get_md5() { echo " $1 " | md5sum | cut -d' ' -f1; }
_nft_init() {
[ -z " $TABLE " ] && return 1
nft add table ip " $TABLE " 2>/dev/null
2026-02-18 01:36:35 +04:00
2026-02-14 19:52:31 +04:00
nft add chain ip " $TABLE " prerouting { type filter hook prerouting priority mangle \; policy accept \; } 2>/dev/null
nft flush chain ip " $TABLE " prerouting
2026-02-18 01:36:35 +04:00
nft add rule ip " $TABLE " prerouting fib daddr type local accept
[ -z " $TPROXY_MARK " ] && TPROXY_MARK = "10"
local mark_hex = $( printf '0x%x' " $TPROXY_MARK " )
ip route show table 110 | grep -q "local default" || ip route add local default dev lo table 110
ip rule show | grep -q "fwmark $mark_hex lookup 110" || ip rule add fwmark " $TPROXY_MARK " table 110
2026-02-14 19:52:31 +04:00
if [ " $ROUTE_LOCAL " = "1" ] ; then
2026-02-18 01:36:35 +04:00
nft add chain ip " $TABLE " output { type route hook output priority mangle \; policy accept \; } 2>/dev/null
nft flush chain ip " $TABLE " output
nft add rule ip " $TABLE " output fib daddr type local accept
else
nft delete chain ip " $TABLE " output 2>/dev/null || true
2026-02-14 19:52:31 +04:00
fi
}
_nft_apply_data() {
local sname = " $1 " file = " $2 " exc = " $3 " dir = " $4 " type = " $5 " changed = " $6 "
[ -z " $TABLE " ] && return 1
2026-02-18 01:36:35 +04:00
local has_output = 0
nft list chain ip " $TABLE " output >/dev/null 2>& 1 && has_output = 1
2026-02-14 19:52:31 +04:00
local flags = "interval"
[ " $type " = "dom" ] || [ " $type " = "udom" ] && flags = "interval,timeout"
nft add set ip " $TABLE " " $sname " { type ipv4_addr \; flags $flags \; auto-merge \; } 2>/dev/null
if [ " $changed " = "1" ] && [ -s " $file " ] && { [ " $type " = "ip" ] || [ " $type " = "uip" ] ; } ; then
_log "Set ' $sname ' data refreshed in NFT"
nft flush set ip " $TABLE " " $sname "
awk '{print "add element ip ' $TABLE ' ' $sname ' { " $1 " }"}' " $file " | nft -f -
fi
if [ " $exc " = "1" ] ; then
2026-02-18 01:36:35 +04:00
local selector = "ip daddr" ; [ " $dir " = "src" ] && selector = "ip saddr"
2026-02-14 19:52:31 +04:00
nft insert rule ip " $TABLE " prerouting $selector "@ $sname " accept
2026-02-18 01:36:35 +04:00
[ " $has_output " = "1" ] && nft insert rule ip " $TABLE " output $selector "@ $sname " accept
2026-02-14 19:52:31 +04:00
elif [ " $dir " = "src" ] ; then
nft add rule ip " $TABLE " prerouting ip saddr "@ $sname " ct mark set 0x66
2026-02-18 01:36:35 +04:00
[ " $has_output " = "1" ] && nft add rule ip " $TABLE " output ip saddr "@ $sname " ct mark set 0x66
2026-02-14 19:52:31 +04:00
else
for proto in tcp udp; do
nft add rule ip " $TABLE " prerouting ct mark 0x66 ip daddr "@ $sname " meta l4proto $proto tproxy to 127.0.0.1:" $TPROXY_PORT " meta mark set " $TPROXY_MARK " accept
2026-02-18 01:36:35 +04:00
[ " $has_output " = "1" ] && nft add rule ip " $TABLE " output ip daddr "@ $sname " meta l4proto $proto ct mark set 0x66 meta mark set " $TPROXY_MARK " accept
2026-02-14 19:52:31 +04:00
done
fi
}
_process_item() {
2026-02-18 01:36:35 +04:00
local id = " $1 " val = " $2 " type = " $3 " dir = " $4 " exc = " $5 " fullpath hfile changed = 0
2026-02-14 19:52:31 +04:00
[ -z " $DNSMASQ_DIR " ] || [ -z " $IPNET_DIR " ] && return 1
[ " $type " = "dom" ] || [ " $type " = "udom" ] && fullpath = " $DNSMASQ_DIR / $id .lst" || fullpath = " $IPNET_DIR / $id .lst"
hfile = " $CACHE_DIR / $id .hash"
echo " $fullpath " >> " $ACTIVE_LIST "
echo " $hfile " >> " $ACTIVE_LIST "
local new_hash = $( _get_md5 " $val " )
local old_hash = $( cat " $hfile " 2>/dev/null)
if [ " $SKIP_URL " = "1" ] && { [ " $type " = "uip" ] || [ " $type " = "udom" ] ; } ; then
2026-02-18 01:36:35 +04:00
[ " $new_hash " != " $old_hash " ] && _log " $id : Configuration changed. Run 'update' to sync!" "warn" "1"
[ -f " $fullpath " ] && { _nft_apply_data " $id " " $fullpath " " $exc " " $dir " " $type " "0" ; return ; }
_log " $id : File not found. Run 'update' to download." "warn" "1" ; return
2026-02-14 19:52:31 +04:00
fi
if [ " $new_hash " != " $old_hash " ] || [ ! -f " $fullpath " ] ; then
changed = 1
case " $type " in
2026-02-18 01:36:35 +04:00
ip) echo " $val " | tr ' ' '\n' | sed 's/\r//g; /^#/d; /^[[:space:]]*$/d; /\//! s|$|/32|' | sort -u > " $fullpath " ;;
2026-02-14 19:52:31 +04:00
dom)
echo " $val " | tr ' ' '\n' | tr '[:upper:]' '[:lower:]' | grep -oE '([a-z0-9-]+\.)+[a-z]{2,}' | sort -u | \
sed "s|^|nftset=/|; s|\$|/4#ip# $TABLE # $id |" > " $fullpath "
DNS_CHANGES = 1 ;;
uip| udom)
2026-02-18 01:36:35 +04:00
_log "Processing $id ..." "info"
local tmp_all = "/tmp/dl_all_ $id " ; : > " $tmp_all "
2026-02-14 19:52:31 +04:00
for url in $val ; do
local tmp_part = "/tmp/dl_part"
2026-02-18 01:36:35 +04:00
curl -sSfL --connect-timeout 10 " $url " -o " $tmp_part " && { cat " $tmp_part " >> " $tmp_all " ; echo "" >> " $tmp_all " ; rm -f " $tmp_part " ; }
2026-02-14 19:52:31 +04:00
done
if [ -s " $tmp_all " ] ; then
local out = "/tmp/out_ $id "
2026-02-18 01:36:35 +04:00
[ " $type " = "uip" ] && sed 's/\r//g; /^#/d; /^[[:space:]]*$/d; /\//! s|$|/32|' " $tmp_all " | sort -u > " $out " || \
{ tr '[:upper:]' '[:lower:]' < " $tmp_all " | grep -oE '([a-z0-9-]+\.)+[a-z]{2,}' | sort -u | sed "s|^|nftset=/|; s|\$|/4#ip# $TABLE # $id |" > " $out " ; DNS_CHANGES = 1; }
mv " $out " " $fullpath " ; rm -f " $tmp_all " ; changed = 1
fi ;;
2026-02-14 19:52:31 +04:00
esac
echo " $new_hash " > " $hfile "
fi
_nft_apply_data " $id " " $fullpath " " $exc " " $dir " " $type " " $changed "
}
_process_section() {
2026-02-18 01:36:35 +04:00
local s = " $1 " name dir exc ips doms uips udoms
config_get name " $s " name; config_get dir " $s " direction "dst" ; config_get exc " $s " exclude "0"
2026-02-14 19:52:31 +04:00
[ -z " $name " ] && return
_append_list() { CURRENT_VAL = " ${ CURRENT_VAL }${ CURRENT_VAL :+ } $1 " ; }
CURRENT_VAL = "" ; config_list_foreach " $s " ip _append_list; ips = " $CURRENT_VAL "
CURRENT_VAL = "" ; config_list_foreach " $s " domain _append_list; doms = " $CURRENT_VAL "
CURRENT_VAL = "" ; config_list_foreach " $s " url_ip _append_list; uips = " $CURRENT_VAL "
CURRENT_VAL = "" ; config_list_foreach " $s " url_domain _append_list; udoms = " $CURRENT_VAL "
[ -n " $( echo " $ips " | xargs) " ] && _process_item " ${ name } _ip" " $ips " "ip" " $dir " " $exc "
[ -n " $( echo " $doms " | xargs) " ] && _process_item " ${ name } _dom" " $doms " "dom" " $dir " " $exc "
[ -n " $( echo " $uips " | xargs) " ] && _process_item " ${ name } _uip" " $uips " "uip" " $dir " " $exc "
[ -n " $( echo " $udoms " | xargs) " ] && _process_item " ${ name } _udom" " $udoms " "udom" " $dir " " $exc "
}
_cleanup_orphans() {
[ -z " $IPNET_DIR " ] || [ -z " $DNSMASQ_DIR " ] && return
for f in " $IPNET_DIR " /* " $DNSMASQ_DIR " /*; do
[ -e " $f " ] || continue
if ! grep -Fxq " $f " " $ACTIVE_LIST " 2>/dev/null; then
local base = " ${ f ##*/ } "
[ " $( dirname " $f " ) " = " $DNSMASQ_DIR " ] && DNS_CHANGES = 1
2026-02-18 01:36:35 +04:00
rm -f " $f " " $CACHE_DIR / ${ base %.* } .hash"
2026-02-14 19:52:31 +04:00
fi
done
rm -f " $ACTIVE_LIST "
}
_load_config() {
config_load xray-manager
TABLE = $( uci -q get xray-manager.@xray-manager[ 0] .table_name)
TPROXY_PORT = $( uci -q get xray-manager.@xray-manager[ 0] .tproxy_port)
TPROXY_MARK = $( uci -q get xray-manager.@xray-manager[ 0] .tproxy_mark)
IPNET_DIR = $( uci -q get xray-manager.@xray-manager[ 0] .ipnet_dir)
DNSMASQ_DIR = $( uci -q get xray-manager.@xray-manager[ 0] .dnsmasq_dir)
2026-02-18 01:36:35 +04:00
CACHE_DIR = "/tmp/xray-manager.cache" ; ACTIVE_LIST = "/tmp/xray_active_files"
2026-02-14 19:52:31 +04:00
ROUTE_LOCAL = $( uci -q get xray-manager.@xray-manager[ 0] .route_local)
mkdir -p " $IPNET_DIR " " $DNSMASQ_DIR " " $CACHE_DIR "
if ! uci show dhcp.@dnsmasq[ 0] .confdir 2>/dev/null | grep -q "' $DNSMASQ_DIR '" ; then
2026-02-18 01:36:35 +04:00
uci add_list dhcp.@dnsmasq[ 0] .confdir= " $DNSMASQ_DIR " ; uci commit dhcp; DNS_CHANGES = 1
2026-02-14 19:52:31 +04:00
fi
}
_run() {
2026-02-18 01:36:35 +04:00
DNS_CHANGES = 0; _load_config || return 1; : > " $ACTIVE_LIST " ; _nft_init || return 1
2026-02-14 19:52:31 +04:00
local lnet_val = $( uci -q get xray-manager.@lnet[ 0] .ip)
[ -n " $lnet_val " ] && _process_item "lnet" " $lnet_val " "ip" "dst" "1"
config_foreach _process_section "xray-list"
_cleanup_orphans
2026-02-18 01:36:35 +04:00
[ " $DNS_CHANGES " = "1" ] && /etc/init.d/dnsmasq restart
2026-02-14 19:52:31 +04:00
}
_stop() {
_load_config || exit 1
nft delete table ip " $TABLE " 2>/dev/null
ip rule del fwmark " $TPROXY_MARK " table 110 2>/dev/null
ip route del local default dev lo table 110 2>/dev/null
rm -rf " $IPNET_DIR " /* " $DNSMASQ_DIR " /* " $CACHE_DIR " /*
/etc/init.d/dnsmasq restart
}
case " $1 " in
start| reload) export SKIP_URL = 1; _run ;;
update) export SKIP_URL = 0; _run ;;
stop) _stop ;;
restart) _stop; export SKIP_URL = 1; _run ;;
*) echo "Usage: $0 {start|reload|update|stop|restart}" ;;
esac