op-packages/luci-app-xclient/root/etc/init.d/xclient
github-actions[bot] 32600acc60 🌴 Sync 2026-03-05 23:51:42
2026-03-05 23:51:42 +08:00

474 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh /etc/rc.common
START=95
STOP=15
SERVICE_DAEMONIZE=1
NAME=xclient
LOCK_FILE=/var/lock/$NAME.lock
LOG_FILE=/var/log/$NAME.log
XRAY_LOG_FILE=/usr/share/$NAME/$NAME.txt
REAL_LOG=$XRAY_LOG_FILE
TMP_PATH=/var/etc/$NAME
TMP_BIN_PATH=$TMP_PATH/bin
TMP_DNSMASQ_PATH=/tmp/dnsmasq.d/dnsmasq-$NAME.d
XBIN=/usr/bin/xray
tcp_config_file=
enable_socks=0
udp_config_file=
local_config_file=
ARG_UDP=
dns_port="5335" #dns port
server_count=0
redir_tcp=0
redir_udp=0
local_enable=0
pdnsd_enable_flag=0
switch_server=$1
CRON_FILE=/etc/crontabs/root
EXTRA_COMMANDS='reset'
EXTRA_HELP=" reset Reset to default settings"
#extra_command "reset" "Reset to default settings"
PS="/bin/busybox ps"
uci_get_by_name() {
local ret=$(uci get $NAME.$1.$2 2>/dev/null)
echo ${ret:=$3}
}
uci_get_by_type() {
local ret=$(uci get $NAME.@$1[0].$2 2>/dev/null)
echo ${ret:=$3}
}
uci_set_by_name() {
uci set $NAME.$1.$2=$3 2>/dev/null
uci commit $NAME
}
uci_set_by_type() {
uci set $NAME.@$1[0].$2=$3 2>/dev/null
uci commit $NAME
}
uci_get_by_cfgid() {
local ret=$(uci show $NAME.@$1[0].$2 | awk -F '.' '{print $2}' 2>/dev/null)
echo ${ret:=$3}
}
get_host_ip() {
local host=$(uci_get_by_name $1 server)
local ip=$host
if [ -z "$(echo $host | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}")" ]; then
if [ "$host" == "${host#*:[0-9a-fA-F]}" ]; then
ip=$(resolveip -4 -t 3 $host | awk 'NR==1{print}')
[ -z "$ip" ] && ip=$(uclient-fetch -q -O- http://119.29.29.29/d?dn=$host | awk -F ';' '{print $1}')
fi
fi
[ -z "$ip" ] || uci_set_by_name $1 ip $ip
echo ${ip:="$(uci_get_by_name $1 ip "ERROR")"}
}
clean_log() {
local logsnum=$(cat $LOG_FILE 2>/dev/null | wc -l)
[ "$logsnum" -gt 1000 ] && {
echo "$(date "+%Y-%m-%d %H:%M:%S") The log file is too long, clear it" >$LOG_FILE
}
}
echolog() {
local d="$(date "+%Y-%m-%d %H:%M:%S")"
echo -e "$d: $*" >>$LOG_FILE
}
add_cron() {
touch $CRON_FILE
sed -i '/xclient.log/d' $CRON_FILE
[ $(uci get xclient.config.auto_update_servers) > 0 ] && echo "0 */$(uci get xclient.config.auto_update_servers) * * * /usr/bin/lua /usr/share/xclient/subscribe.lua >$LOG_FILE" >>$CRON_FILE
crontab $CRON_FILE
}
del_cron() {
touch $CRON_FILE
sed -i '/xclient.log/d' $CRON_FILE
crontab $CRON_FILE
clean_log
}
set_lock() {
exec 1000>"$LOCK_FILE"
flock -xn 1000
}
unset_lock() {
flock -u 1000
rm -rf "$LOCK_FILE"
}
unlock() {
failcount=1
while [ "$failcount" -le 10 ]; do
if [ -f "$LOCK_FILE" ]; then
let "failcount++"
sleep 1s
[ "$failcount" -ge 10 ] && unset_lock
else
break
fi
done
}
_exit() {
local rc=$1
unset_lock
exit ${rc}
}
first_type() {
type -t -p "/bin/${1}" -p "${TMP_BIN_PATH}/${1}" -p "${1}" "$@" | head -n1
}
ln_start_bin() {
local file_func=${1}
local ln_name=${2}
shift 2
if [ "${file_func%%/*}" != "${file_func}" ]; then
[ ! -L "${file_func}" ] && {
ln -s "${file_func}" "${TMP_BIN_PATH}/${ln_name}" >/dev/null 2>&1
file_func="${TMP_BIN_PATH}/${ln_name}"
}
[ -x "${file_func}" ] || echolog "$(readlink ${file_func}) no execute permissionUnable to start${file_func} $*"
fi
#echo "${file_func} $*" >&2
[ -x "${file_func}" ] || {
echolog "Can't find ${file_func}Unable to start..."
echolog "-----------end------------"
_exit 2
}
${file_func:-echolog " - ${ln_name}"} "$@" >/dev/null 2>&1 &
}
start_dns() {
local xclient_dns="$(uci_get_by_type global pdnsd_enable 0)"
local dnsstr="$(uci_get_by_type global tunnel_forward 8.8.4.4:53)"
local dnsserver=$(echo "$dnsstr" | awk -F ':' '{print $1}')
local dnsport=$(echo "$dnsstr" | awk -F ':' '{print $2}')
start_pdnsd() {
local usr_dns="$1"
local usr_port="$2"
if [ ! -f "$TMP_PATH/pdnsd/pdnsd.cache" ]; then
mkdir -p $TMP_PATH/pdnsd
touch $TMP_PATH/pdnsd/pdnsd.cache
chown -R nobody:nogroup $TMP_PATH/pdnsd
fi
cat <<-EOF >$TMP_PATH/pdnsd.conf
global{
perm_cache=1024;
cache_dir="$TMP_PATH/pdnsd";
pid_file="/var/run/pdnsd.pid";
run_as="nobody";
server_ip=127.0.0.1;
server_port=$dns_port;
status_ctl=on;
query_method=tcp_only;
min_ttl=1h;
max_ttl=1w;
timeout=10;
neg_domain_pol=on;
proc_limit=2;
procq_limit=8;
par_queries=1;
}
server{
label="xclient-usrdns";
ip=$usr_dns;
port=$usr_port;
timeout=6;
uptest=none;
interval=10m;
purge_cache=off;
}
EOF
ln_start_bin $(first_type pdnsd) pdnsd -c $TMP_PATH/pdnsd.conf
}
if [ "$xclient_dns" != "0" ]; then
local dns2socks_port=$(uci_get_by_type global dns2socks_port)
ipset add xclient_spec_wan_ac $dnsserver nomatch 2>/dev/null
case "$xclient_dns" in
1)
start_pdnsd $dnsserver $dnsport
pdnsd_enable_flag=1
;;
2)
ln_start_bin $(first_type microsocks) microsocks -i 127.0.0.1 -p $dns2socks_port xclient-dns
ln_start_bin $(first_type dns2socks) dns2socks 127.0.0.1:$dns2socks_port $dnsserver:$dnsport 127.0.0.1:$dns_port -q
pdnsd_enable_flag=2
;;
esac
fi
}
gen_config_file() {
case "$2" in
1)
local protocol="redir"
config_file=$tcp_config_file
;;
2)
local protocol="redir"
config_file=$udp_config_file
;;
4)
local protocol="socks"
config_file=$local_config_file
;;
esac
lua /usr/share/xclient/gen_config.lua $1 $mode $3 $4 $protocol $5 $s5>$config_file
sed -i 's/\\//g' $TMP_PATH/*-xclient-*.json
}
start_udp() {
redir_udp=1
local type=$(uci_get_by_name $UDP_RELAY_SERVER protocol)
local udp_port=$(uci_get_by_type global udp_port)
case "$type" in
shadowsocks-plugin)
gen_config_file $UDP_RELAY_SERVER 2 $udp_port 0 $type
ss_program="$(first_type sslocal ss-redir)"
ln_start_bin $ss_program ss-redir -c $udp_config_file > $XRAY_LOG_FILE 2>&1 &
echolog "UDP TPROXY Relay:$(get_name ss) Started!"
;;
shadowsocks|vless|vmess|trojan)
gen_config_file $UDP_RELAY_SERVER 2 $udp_port 0 $type
sed -i "2i\ \"stats\": {}," $udp_config_file 2>/dev/null
$XBIN -config $udp_config_file > $XRAY_LOG_FILE 2>&1 &
echolog "UDP TPROXY Relay:$($(first_type "xray" "xray") -version | head -1) Started!"
;;
esac
}
start_local() {
[ "$LOCAL_SERVER" = "nil" ] && return 1
local socks_port=$(uci_get_by_type global socks5_port)
local type=$(uci_get_by_name $LOCAL_SERVER protocol)
case "$type" in
shadowsocks-plugin)
gen_config_file $LOCAL_SERVER 4 $socks_port 0 $type
ss_program="$(first_type sslocal ss-local)"
ln_start_bin $ss_program ss-local -c $local_config_file > $XRAY_LOG_FILE 2>&1 &
echolog "Global_Socks5:$(get_name ss) Started!"
;;
shadowsocks|vless|vmess|trojan)
if [ "$_local" == "2" ]; then
gen_config_file $LOCAL_SERVER 4 0 $socks_port 1 $type
sed -i "2i\ \"stats\": {}," $local_config_file 2>/dev/null
$XBIN -config $local_config_file > $XRAY_LOG_FILE 2>&1 &
echolog "Global_Socks5:$($(first_type "xray" "xray") -version | head -1) Started!"
fi
;;
esac
local_enable=1
return 0
}
Start_Run() {
if [ "$_local" == "1" ]; then
local socks_port=$(uci_get_by_type global socks5_port)
tcp_config_file=$TMP_PATH/local-xclient-retcp.json
[ "$mode" == "tcp,udp" ] && tcp_config_file=$TMP_PATH/local-udp-xclient-retcp.json
fi
local type=$(uci_get_by_name $GLOBAL_SERVER protocol)
local tcp_port=$(uci_get_by_type global tcp_port)
s5=""
case "$type" in
shadowsocks-plugin)
gen_config_file $GLOBAL_SERVER 1 $tcp_port $socks_port 0 $type
ss_program="$(first_type sslocal ss-redir)"
ln_start_bin "$ss_program" ss-redir -c $tcp_config_file > $XRAY_LOG_FILE 2>&1 &
echolog "Main node:$(get_name $type) $threads Threads Started!"
;;
shadowsocks|vless|vmess|trojan)
gen_config_file $GLOBAL_SERVER 1 $tcp_port $socks_port 0 $type
sed -i "2i\ \"stats\": {}," $tcp_config_file 2>/dev/null
$XBIN -config $tcp_config_file > $XRAY_LOG_FILE 2>&1 &
echolog "Main node:$($(first_type xray xray) -version | head -1) Started!"
;;
esac
redir_tcp=1
return 0
}
load_config() {
if [ -z "$switch_server" ]; then
GLOBAL_SERVER=$(uci_get_by_type global global_server nil)
else
GLOBAL_SERVER=$switch_server
fi
LOCAL_SERVER=$(uci_get_by_type global socks5_server nil)
if [ "$GLOBAL_SERVER" == "nil" ]; then
mode="tcp,udp"
_local="2"
local_config_file=$TMP_PATH/tcp-udp-xclient-local.json
start_local
return 1
fi
UDP_RELAY_SERVER=$(uci_get_by_type global udp_relay_server nil)
tcp_config_file=$TMP_PATH/tcp-only-xclient-retcp.json
case "$UDP_RELAY_SERVER" in
nil)
mode="tcp"
;;
$GLOBAL_SERVER | same)
mode="tcp,udp"
tcp_config_file=$TMP_PATH/tcp-udp-xclient-retcp.json
ARG_UDP="-u"
UDP_RELAY_SERVER=$GLOBAL_SERVER
;;
*)
mode="udp"
udp_config_file=$TMP_PATH/udp-only-xclient-reudp.json
ARG_UDP="-U"
start_udp
mode="tcp"
;;
esac
case "$LOCAL_SERVER" in
nil)
_local="0"
;;
$GLOBAL_SERVER | same)
_local="1"
LOCAL_SERVER=$GLOBAL_SERVER
local_config_file=$TMP_PATH/tcp-udp-xclient-local.json
start_local
local_enable=0
;;
*)
_local="2"
s5="none"
local_config_file=$TMP_PATH/tcp-udp-xclient-local.json
start_local
;;
esac
return 0
}
check_server() {
ENABLE_SERVER=$(uci_get_by_type global global_server nil)
if [ "$ENABLE_SERVER" == "nil" ]; then
return 1
else
local STYPE=$(uci_get_by_name $ENABLE_SERVER type nil)
if [ "$STYPE" == "nil" ]; then
local CFGID=$(uci_get_by_cfgid servers type nil)
if [ "$CFGID" == "nil" ]; then
uci_set_by_type global global_server 'nil'
else
uci_set_by_type global global_server $CFGID
fi
/etc/init.d/xclient restart
fi
fi
}
start_monitor() {
if [ $(uci_get_by_type global monitor_enable 1) == "1" ]; then
let total_count=redir_tcp+redir_udp+pdnsd_enable_flag
if [ $total_count -gt 0 ]; then
service_start /usr/share/xclient/monitor.sh $redir_tcp $redir_udp $pdnsd_enable_flag
fi
fi
}
start_rules() {
local server=$(get_host_ip $GLOBAL_SERVER)
local tcp_port=$(uci_get_by_type global tcp_port)
if [ "$redir_udp" == "1" ]; then
local udp_server=$(get_host_ip $UDP_RELAY_SERVER)
local udp_port=udp_port=$(uci_get_by_type global udp_port)
fi
/usr/share/xclient/iptables.sh \
-s "$server" \
-l "$tcp_port" \
-S "$udp_server" \
-L "$udp_port" \
-b "$(uci_get_by_type global wan_bp_ips)" \
-B "$(uci_get_by_type global lan_bp_ips)" \
-p "$(uci_get_by_type global lan_fp_ips)" \
-m "$(uci_get_by_type global Interface)" \
$ARG_UDP
return $?
}
start() {
echo "Starting Client" > $REAL_LOG
set_lock
echolog "----------starting------------"
mkdir -p /var/run /var/lock /var/log /tmp/dnsmasq.d $TMP_BIN_PATH $TMP_DNSMASQ_PATH
echo "conf-dir=${TMP_DNSMASQ_PATH}" >"/tmp/dnsmasq.d/dnsmasq-xclient.conf"
if load_config; then
echo "Generating Client Config" > $REAL_LOG
Start_Run
echo "Setting up iptables rules" > $REAL_LOG
start_rules
echo "Enabling Dns" > $REAL_LOG
start_dns
add_cron
fi
echo "Restarting Dnsmasq " > $REAL_LOG
/etc/init.d/dnsmasq restart >/dev/null 2>&1
check_server
start_monitor
clean_log
echolog "-----------Running------------"
unset_lock
echo "XClient Started Successfully" >$REAL_LOG
sleep 1
echo "XClient is Running" >$REAL_LOG
}
boot() {
ENABLE_=$(uci_get_by_type global enable 0)
if [ "$ENABLE_" == "1" ]; then
mkdir -p /var/run /var/lock /var/log /tmp/dnsmasq.d $TMP_BIN_PATH $TMP_DNSMASQ_PATH
ulimit -n 65535
start
else
if pidof xclient >/dev/null; then
stop
fi
fi
}
stop() {
echo "Stopping Client" > $REAL_LOG
unlock
set_lock
/usr/share/xclient/iptables.sh -f
$PS -w | grep -v "grep" | grep monitor | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
$PS -w | grep -v "grep" | grep "$TMP_PATH" | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 &
killall -q -9 xray-plugin
rm -f /var/lock/monitor.lock
if [ -f "/tmp/dnsmasq.d/dnsmasq-xclient.conf" ]; then
rm -rf /tmp/dnsmasq.d/dnsmasq-xclient.conf $TMP_DNSMASQ_PATH $TMP_PATH/*-xclient-*.json
/etc/init.d/dnsmasq restart >/dev/null 2>&1
fi
del_cron
unset_lock
echo "0" > /usr/share/$NAME/logstatus_check >/dev/null 2>&1
echo "" > $LOG_FILE >/dev/null 2>&1
echo "XClient Stopped Successfully" > $REAL_LOG
}
reload(){
stop >/dev/null 2>&1
sleep 1
boot >/dev/null 2>&1
}