#!/bin/sh CONFIG=passwall2 LOG_FILE=/tmp/log/$CONFIG.log LOCK_FILE_DIR=/tmp/lock LOCK_FILE=${LOCK_FILE_DIR}/${CONFIG}_script.lock echolog() { local d="$(date "+%Y-%m-%d %H:%M:%S")" #echo -e "$d: $1" echo -e "$d: $1" >> $LOG_FILE } config_n_get() { local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null) echo "${ret:=$3}" } config_t_get() { local index=0 [ -n "$4" ] && index=$4 local ret=$(uci -q get $CONFIG.@$1[$index].$2 2>/dev/null) echo ${ret:=$3} } test_url() { local url=$1 local try=1 [ -n "$2" ] && try=$2 local timeout=2 [ -n "$3" ] && timeout=$3 local extra_params=$4 curl --help all | grep "\-\-retry-all-errors" > /dev/null [ $? == 0 ] && extra_params="--retry-all-errors ${extra_params}" status=$(/usr/bin/curl -I -o /dev/null -skL $extra_params --connect-timeout ${timeout} --retry ${try} -w %{http_code} "$url") case "$status" in 204|\ 200) status=200 ;; esac echo $status } test_proxy() { result=0 status=$(test_url "https://www.google.com/generate_204" ${retry_num} ${connect_timeout}) if [ "$status" = "200" ]; then result=0 else status2=$(test_url "https://www.baidu.com" ${retry_num} ${connect_timeout}) if [ "$status2" = "200" ]; then result=1 else result=2 ping -c 3 -W 1 223.5.5.5 > /dev/null 2>&1 [ $? -eq 0 ] && { result=1 } fi fi echo $result } url_test_node() { result=0 local node_id=$1 local _type=$(echo $(config_n_get ${node_id} type nil) | tr 'A-Z' 'a-z') [ "${_type}" != "nil" ] && { local _tmp_port=$(/usr/share/${CONFIG}/app.sh get_new_port 61080 tcp,udp) /usr/share/${CONFIG}/app.sh run_socks flag="url_test_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=url_test_${node_id}.json local curlx="socks5h://127.0.0.1:${_tmp_port}" sleep 1s result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "https://www.google.com/generate_204") pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 rm -rf "/tmp/etc/${CONFIG}/url_test_${node_id}.json" } echo $result } test_node() { local node_id=$1 local _type=$(echo $(config_n_get ${node_id} type nil) | tr 'A-Z' 'a-z') [ "${_type}" != "nil" ] && { local _tmp_port=$(/usr/share/${CONFIG}/app.sh get_new_port 61080 tcp,udp) /usr/share/${CONFIG}/app.sh run_socks flag="test_node_${node_id}" node=${node_id} bind=127.0.0.1 socks_port=${_tmp_port} config_file=test_node_${node_id}.json local curlx="socks5h://127.0.0.1:${_tmp_port}" sleep 1s _proxy_status=$(test_url "https://www.google.com/generate_204" ${retry_num} ${connect_timeout} "-x $curlx") pgrep -af "test_node_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 rm -rf "/tmp/etc/${CONFIG}/test_node_${node_id}.json" if [ "${_proxy_status}" -eq 200 ]; then return 0 fi } return 1 } flag=0 main_node=$(config_t_get global node nil) test_auto_switch() { flag=$(expr $flag + 1) local TYPE=$1 local b_nodes=$2 local now_node=$3 [ -z "$now_node" ] && { local f="/tmp/etc/$CONFIG/id/global" if [ -f "${f}" ]; then now_node=$(cat ${f}) if [ "$(config_n_get $now_node protocol nil)" = "_shunt" ]; then if [ "$shunt_logic" == "1" ] && [ -f "${f}_default" ]; then now_node=$(cat ${f}_default) elif [ "$shunt_logic" == "2" ] && [ -f "${f}_main" ]; then now_node=$(cat ${f}_main) else shunt_logic=0 fi else shunt_logic=0 fi else #echolog "自动切换检测:未知错误" return 1 fi } [ $flag -le 1 ] && { main_node=$now_node } status=$(test_proxy) if [ "$status" == 2 ]; then echolog "自动切换检测:无法连接到网络,请检查网络是否正常!" return 2 fi #检测主节点是否能使用 if [ "$restore_switch" == "1" ] && [ "$main_node" != "nil" ] && [ "$now_node" != "$main_node" ]; then test_node ${main_node} [ $? -eq 0 ] && { #主节点正常,切换到主节点 echolog "自动切换检测:${TYPE}主节点【$(config_n_get $main_node type):[$(config_n_get $main_node remarks)]】正常,切换到主节点!" /usr/share/${CONFIG}/app.sh node_switch flag=global new_node=${main_node} shunt_logic=${shunt_logic} [ $? -eq 0 ] && { echolog "自动切换检测:${TYPE}节点切换完毕!" [ "$shunt_logic" != "0" ] && { local node=$(config_t_get global node nil) [ "$(config_n_get $node protocol nil)" = "_shunt" ] && { if [ "$shunt_logic" == "1" ]; then uci set $CONFIG.$node.default_node="$main_node" elif [ "$shunt_logic" == "2" ]; then uci set $CONFIG.$node.main_node="$main_node" fi uci commit $CONFIG } } } return 0 } fi if [ "$status" == 0 ]; then #echolog "自动切换检测:${TYPE}节点【$(config_n_get $now_node type):[$(config_n_get $now_node remarks)]】正常。" return 0 elif [ "$status" == 1 ]; then echolog "自动切换检测:${TYPE}节点【$(config_n_get $now_node type):[$(config_n_get $now_node remarks)]】异常,切换到下一个备用节点检测!" local new_node in_backup_nodes=$(echo $b_nodes | grep $now_node) # 判断当前节点是否存在于备用节点列表里 if [ -z "$in_backup_nodes" ]; then # 如果不存在,设置第一个节点为新的节点 new_node=$(echo $b_nodes | awk -F ' ' '{print $1}') else # 如果存在,设置下一个备用节点为新的节点 #local count=$(expr $(echo $b_nodes | grep -o ' ' | wc -l) + 1) local next_node=$(echo $b_nodes | awk -F "$now_node" '{print $2}' | awk -F " " '{print $1}') if [ -z "$next_node" ]; then new_node=$(echo $b_nodes | awk -F ' ' '{print $1}') else new_node=$next_node fi fi test_node ${new_node} if [ $? -eq 0 ]; then [ "$restore_switch" == "0" ] && { [ "$shunt_logic" == "0" ] && uci set $CONFIG.@global[0].node=$new_node [ -z "$(echo $b_nodes | grep $main_node)" ] && uci add_list $CONFIG.@auto_switch[0].node=$main_node uci commit $CONFIG } echolog "自动切换检测:${TYPE}节点【$(config_n_get $new_node type):[$(config_n_get $new_node remarks)]】正常,切换到此节点!" /usr/share/${CONFIG}/app.sh node_switch flag=global new_node=${new_node} shunt_logic=${shunt_logic} [ $? -eq 0 ] && { [ "$restore_switch" == "1" ] && [ "$shunt_logic" != "0" ] && { local node=$(config_t_get global node nil) [ "$(config_n_get $node protocol nil)" = "_shunt" ] && { if [ "$shunt_logic" == "1" ]; then uci set $CONFIG.$node.default_node="$main_node" elif [ "$shunt_logic" == "2" ]; then uci set $CONFIG.$node.main_node="$main_node" fi uci commit $CONFIG } } echolog "自动切换检测:${TYPE}节点切换完毕!" } return 0 else test_auto_switch ${TYPE} "${b_nodes}" ${new_node} fi fi } start() { ENABLED=$(config_t_get global enabled 0) [ "$ENABLED" != 1 ] && return 1 ENABLED=$(config_t_get auto_switch enable 0) [ "$ENABLED" != 1 ] && return 1 delay=$(config_t_get auto_switch testing_time 1) #sleep 9s connect_timeout=$(config_t_get auto_switch connect_timeout 3) retry_num=$(config_t_get auto_switch retry_num 3) restore_switch=$(config_t_get auto_switch restore_switch 0) shunt_logic=$(config_t_get auto_switch shunt_logic 0) while [ "$ENABLED" -eq 1 ]; do [ -f "$LOCK_FILE" ] && { sleep 6s continue } touch $LOCK_FILE NODE=$(config_t_get auto_switch node nil) [ -n "$NODE" -a "$NODE" != "nil" ] && { NODE=$(echo $NODE | tr -s ' ' '\n' | uniq | tr -s '\n' ' ') test_auto_switch TCP "$NODE" } rm -f $LOCK_FILE sleep ${delay}m done } arg1=$1 shift case $arg1 in test_url) test_url $@ ;; url_test_node) url_test_node $@ ;; *) start ;; esac