#!/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 permission,Unable 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 }