#!/bin/sh # Copyright (C) 2018-2020 L-WRT Team # Copyright (C) 2021 xiaorouji . $IPKG_INSTROOT/lib/functions.sh . $IPKG_INSTROOT/lib/functions/service.sh CONFIG=passwall TMP_PATH=/tmp/etc/$CONFIG TMP_BIN_PATH=$TMP_PATH/bin TMP_ID_PATH=$TMP_PATH/id TMP_PORT_PATH=$TMP_PATH/port TMP_ROUTE_PATH=$TMP_PATH/route TMP_ACL_PATH=$TMP_PATH/acl TMP_PATH2=/tmp/etc/${CONFIG}_tmp DNSMASQ_PATH=/etc/dnsmasq.d TMP_DNSMASQ_PATH=/tmp/dnsmasq.d/passwall LOG_FILE=/tmp/log/$CONFIG.log APP_PATH=/usr/share/$CONFIG RULES_PATH=/usr/share/${CONFIG}/rules DNS_N=dnsmasq DNS_PORT=7913 TUN_DNS="127.0.0.1#${DNS_PORT}" LOCAL_DNS=119.29.29.29 DEFAULT_DNS= NO_PROXY=0 PROXY_IPV6=0 resolve_dns=0 use_tcp_node_resolve_dns=0 use_udp_node_resolve_dns=0 LUA_API_PATH=/usr/lib/lua/luci/model/cbi/$CONFIG/api API_GEN_SS=$LUA_API_PATH/gen_shadowsocks.lua API_GEN_V2RAY=$LUA_API_PATH/gen_v2ray.lua API_GEN_V2RAY_PROTO=$LUA_API_PATH/gen_v2ray_proto.lua API_GEN_TROJAN=$LUA_API_PATH/gen_trojan.lua API_GEN_NAIVE=$LUA_API_PATH/gen_naiveproxy.lua API_GEN_HYSTERIA=$LUA_API_PATH/gen_hysteria.lua echolog() { local d="$(date "+%Y-%m-%d %H:%M:%S")" echo -e "$d: $*" >>$LOG_FILE } config_get_type() { local ret=$(uci -q get "${CONFIG}.${1}" 2>/dev/null) echo "${ret:=$2}" } config_n_get() { local ret=$(uci -q get "${CONFIG}.${1}.${2}" 2>/dev/null) echo "${ret:=$3}" } config_t_get() { local index=${4:-0} local ret=$(uci -q get "${CONFIG}.@${1}[${index}].${2}" 2>/dev/null) echo "${ret:=${3}}" } get_enabled_anonymous_secs() { uci -q show "${CONFIG}" | grep "${1}\[.*\.enabled='1'" | cut -d '.' -sf2 } convert_ip_port_format() { local ip=$1 local ip_port= local result=$(echo $ip | grep "#") if [ -z "$result" ]; then ip_port=$(echo $ip | sed -E 's/\:([^:]+)$/#\1/g') else ip_port=$result fi echo $ip_port } get_host_ip() { local host=$2 local count=$3 [ -z "$count" ] && count=3 local isip="" local ip=$host if [ "$1" == "ipv6" ]; then isip=$(echo $host | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}") if [ -n "$isip" ]; then isip=$(echo $host | cut -d '[' -f2 | cut -d ']' -f1) else isip=$(echo $host | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}") fi else isip=$(echo $host | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}") fi [ -z "$isip" ] && { local t=4 [ "$1" == "ipv6" ] && t=6 local vpsrip=$(resolveip -$t -t $count $host | awk 'NR==1{print}') ip=$vpsrip } echo $ip } get_node_host_ip() { local ip local address=$(config_n_get $1 address) [ -n "$address" ] && { local use_ipv6=$(config_n_get $1 use_ipv6) local network_type="ipv4" [ "$use_ipv6" == "1" ] && network_type="ipv6" ip=$(get_host_ip $network_type $address) } echo $ip } get_ip_port_from() { local __host=${1}; shift 1 local __ipv=${1}; shift 1 local __portv=${1}; shift 1 local __ucipriority=${1}; shift 1 local val1 val2 if [ -n "${__ucipriority}" ]; then val2=$(config_n_get ${__host} port $(echo $__host | sed -n 's/^.*[:#]\([0-9]*\)$/\1/p')) val1=$(config_n_get ${__host} address "${__host%%${val2:+[:#]${val2}*}}") else val2=$(echo $__host | sed -n 's/^.*[:#]\([0-9]*\)$/\1/p') val1="${__host%%${val2:+[:#]${val2}*}}" fi eval "${__ipv}=\"$val1\"; ${__portv}=\"$val2\"" } host_from_url(){ local f=${1} ## Remove protocol part of url ## f="${f##http://}" f="${f##https://}" f="${f##ftp://}" f="${f##sftp://}" ## Remove username and/or username:password part of URL ## f="${f##*:*@}" f="${f##*@}" ## Remove rest of urls ## f="${f%%/*}" echo "${f%%:*}" } hosts_foreach() { local __hosts eval "__hosts=\$${1}"; shift 1 local __func=${1}; shift 1 local __default_port=${1}; shift 1 local __ret=1 [ -z "${__hosts}" ] && return 0 local __ip __port for __host in $(echo $__hosts | sed 's/[ ,]/\n/g'); do get_ip_port_from "$__host" "__ip" "__port" eval "$__func \"${__host}\" \"\${__ip}\" \"\${__port:-${__default_port}}\" \"$@\"" __ret=$? [ ${__ret} -ge ${ERROR_NO_CATCH:-1} ] && return ${__ret} done } check_host() { local f=${1} a=$(echo $f | grep "\/") [ -n "$a" ] && return 1 # 判断是否包含汉字~ local tmp=$(echo -n $f | awk '{print gensub(/[!-~]/,"","g",$0)}') [ -n "$tmp" ] && return 1 return 0 } get_first_dns() { local __hosts_val=${1}; shift 1 __first() { [ -z "${2}" ] && return 0 echo "${2}#${3}" return 1 } eval "hosts_foreach \"${__hosts_val}\" __first \"$@\"" } get_last_dns() { local __hosts_val=${1}; shift 1 local __first __last __every() { [ -z "${2}" ] && return 0 __last="${2}#${3}" __first=${__first:-${__last}} } eval "hosts_foreach \"${__hosts_val}\" __every \"$@\"" [ "${__first}" == "${__last}" ] || echo "${__last}" } check_port_exists() { port=$1 protocol=$2 [ -n "$protocol" ] || protocol="tcp,udp" result= if [ "$protocol" = "tcp" ]; then result=$(netstat -tln | grep -c ":$port ") elif [ "$protocol" = "udp" ]; then result=$(netstat -uln | grep -c ":$port ") elif [ "$protocol" = "tcp,udp" ]; then result=$(netstat -tuln | grep -c ":$port ") fi echo "${result}" } get_new_port() { port=$1 [ "$port" == "auto" ] && port=2082 protocol=$(echo $2 | tr 'A-Z' 'a-z') result=$(check_port_exists $port $protocol) if [ "$result" != 0 ]; then temp= if [ "$port" -lt 65535 ]; then temp=$(expr $port + 1) elif [ "$port" -gt 1 ]; then temp=$(expr $port - 1) fi get_new_port $temp $protocol else echo $port fi } first_type() { local path_name=${1} type -t -p "/bin/${path_name}" -p "${TMP_BIN_PATH}/${path_name}" -p "${path_name}" "$@" | head -n1 } eval_set_val() { for i in $@; do for j in $i; do eval $j done done } eval_unset_val() { for i in $@; do for j in $i; do eval unset j done done } ln_start_bin() { local file_func=${1} local ln_name=${2} local output=${3} shift 3; 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}) 没有执行权限,无法启动:${file_func} $*" fi #echo "${file_func} $*" >&2 [ -n "${file_func}" ] || echolog " - 找不到 ${ln_name},无法启动..." ${file_func:-echolog " - ${ln_name}"} "$@" >${output} 2>&1 & } lua_api() { local func=${1} [ -z "${func}" ] && { echo "nil" return } echo $(lua -e "local api = require 'luci.model.cbi.passwall.api.api' print(api.${func})") } # 必用变量 ENABLED=$(config_t_get global enabled 0) SOCKS_ENABLED=$(config_t_get global socks_enabled 0) TCP_REDIR_PORT=1041 TCP_NODE=$(config_t_get global tcp_node nil) UDP_REDIR_PORT=1051 UDP_NODE=$(config_t_get global udp_node nil) TCP_UDP=0 [ "$UDP_NODE" == "tcp" ] && { UDP_NODE=$TCP_NODE TCP_UDP=1 } [ "$ENABLED" != 1 ] && NO_PROXY=1 [ "$TCP_NODE" == "nil" -a "$UDP_NODE" == "nil" ] && NO_PROXY=1 [ "$(config_get_type $TCP_NODE nil)" == "nil" -a "$(config_get_type $UDP_NODE nil)" == "nil" ] && NO_PROXY=1 tcp_proxy_way=$(config_t_get global_forwarding tcp_proxy_way redirect) REDIRECT_LIST="socks ss ss-rust ssr v2ray xray trojan-plus trojan-go naiveproxy" TPROXY_LIST="socks ss ss-rust ssr v2ray xray trojan-plus brook trojan-go hysteria" KCPTUN_REDIR_PORT=$(config_t_get global_forwarding kcptun_port 12948) RESOLVFILE=/tmp/resolv.conf.d/resolv.conf.auto [ -f "${RESOLVFILE}" ] && [ -s "${RESOLVFILE}" ] || RESOLVFILE=/tmp/resolv.conf.auto load_config() { TCP_REDIR_PORTS=$(config_t_get global_forwarding tcp_redir_ports '80,443') UDP_REDIR_PORTS=$(config_t_get global_forwarding udp_redir_ports '1:65535') TCP_NO_REDIR_PORTS=$(config_t_get global_forwarding tcp_no_redir_ports 'disable') UDP_NO_REDIR_PORTS=$(config_t_get global_forwarding udp_no_redir_ports 'disable') TCP_PROXY_MODE=$(config_t_get global tcp_proxy_mode chnroute) UDP_PROXY_MODE=$(config_t_get global udp_proxy_mode chnroute) LOCALHOST_TCP_PROXY_MODE=$(config_t_get global localhost_tcp_proxy_mode default) LOCALHOST_UDP_PROXY_MODE=$(config_t_get global localhost_udp_proxy_mode default) [ "$LOCALHOST_TCP_PROXY_MODE" == "default" ] && LOCALHOST_TCP_PROXY_MODE=$TCP_PROXY_MODE [ "$LOCALHOST_UDP_PROXY_MODE" == "default" ] && LOCALHOST_UDP_PROXY_MODE=$UDP_PROXY_MODE global=$(echo "${TCP_PROXY_MODE}${LOCALHOST_TCP_PROXY_MODE}${UDP_PROXY_MODE}${LOCALHOST_UDP_PROXY_MODE}" | grep "global") returnhome=$(echo "${TCP_PROXY_MODE}${LOCALHOST_TCP_PROXY_MODE}${UDP_PROXY_MODE}${LOCALHOST_UDP_PROXY_MODE}" | grep "returnhome") chnlist=$(echo "${TCP_PROXY_MODE}${LOCALHOST_TCP_PROXY_MODE}${UDP_PROXY_MODE}${LOCALHOST_UDP_PROXY_MODE}" | grep "chnroute") gfwlist=$(echo "${TCP_PROXY_MODE}${LOCALHOST_TCP_PROXY_MODE}${UDP_PROXY_MODE}${LOCALHOST_UDP_PROXY_MODE}" | grep "gfwlist") DNS_MODE=$(config_t_get global dns_mode pdnsd) DNS_FORWARD=$(convert_ip_port_format $(config_t_get global dns_forward 1.1.1.1:53)) DNS_CACHE=$(config_t_get global dns_cache 0) CHINADNS_NG=$(config_t_get global chinadns_ng 1) dns_listen_port=${DNS_PORT} DEFAULT_DNS=$(uci show dhcp | grep "@dnsmasq" | grep "\.server=" | awk -F '=' '{print $2}' | sed "s/'//g" | tr ' ' '\n' | grep -v "\/" | head -2 | sed ':label;N;s/\n/,/;b label') [ -z "${DEFAULT_DNS}" ] && DEFAULT_DNS=$(echo -n $(sed -n 's/^nameserver[ \t]*\([^ ]*\)$/\1/p' "${RESOLVFILE}" | grep -v -E "0.0.0.0|127.0.0.1|::" | head -2) | tr ' ' ',') LOCAL_DNS="${DEFAULT_DNS:-119.29.29.29}" PROXY_IPV6=$(config_t_get global_forwarding ipv6_tproxy 0) export V2RAY_LOCATION_ASSET=$(config_t_get global_rules v2ray_location_asset "/usr/share/xray/") export XRAY_LOCATION_ASSET=$V2RAY_LOCATION_ASSET mkdir -p /tmp/etc $TMP_PATH $TMP_BIN_PATH $TMP_ID_PATH $TMP_PORT_PATH $TMP_ROUTE_PATH $TMP_ACL_PATH $TMP_PATH2 } run_ipt2socks() { local flag redir_type tcp_tproxy local_port socks_address socks_port socks_username socks_password log_file local _extra_param="" eval_set_val $@ [ -n "$log_file" ] || log_file="/dev/null" socks_address=$(get_host_ip "ipv4" ${socks_address}) [ -n "$socks_username" ] && [ -n "$socks_password" ] && _extra_param="${_extra_param} -a $socks_username -k $socks_password" [ -n "$tcp_tproxy" ] || _extra_param="${_extra_param} -R" case "$redir_type" in UDP) flag="${flag}_UDP" _extra_param="${_extra_param} -U" ;; TCP) flag="${flag}_TCP" _extra_param="${_extra_param} -T" ;; esac _extra_param="${_extra_param} -v" ln_start_bin "$(first_type ipt2socks)" "ipt2socks_${flag}" $log_file -l $local_port -b 0.0.0.0 -s $socks_address -p $socks_port ${_extra_param} } run_v2ray() { local flag node proxy_way redir_type redir_port socks_address socks_port socks_username socks_password http_address http_port http_username http_password log_file config_file local _extra_param="" local proto="tcp,udp" eval_set_val $@ local type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z') if [ "$type" != "v2ray" ] && [ "$type" != "xray" ]; then return 1 fi [ -n "$log_file" ] || log_file="/dev/null" [ -n "$socks_username" ] && [ -n "$socks_password" ] && _extra_param="${_extra_param} -local_socks_username $socks_username -local_socks_password $socks_password" [ -n "$http_username" ] && [ -n "$http_password" ] && _extra_param="${_extra_param} -local_http_username $http_username -local_http_password $http_password" case "$redir_type" in UDP) flag="${flag}_UDP" proto="udp" proxy_way="tproxy" ;; TCP) flag="${flag}_TCP" proto="tcp" proxy_way=$tcp_proxy_way ;; esac lua $API_GEN_V2RAY -node $node -proto $proto -redir_port $redir_port -proxy_way $proxy_way -local_socks_address $socks_address -local_socks_port $socks_port -local_http_address $http_address -local_http_port $http_port ${_extra_param} > $config_file ln_start_bin "$(first_type $(config_t_get global_app ${type}_file) ${type})" ${type} $log_file -config="$config_file" } run_dns2socks() { local flag socks socks_address socks_port socks_username socks_password listen_address listen_port dns cache log_file local _extra_param="" eval_set_val $@ [ -n "$flag" ] && flag="_${flag}" [ -n "$log_file" ] || log_file="/dev/null" dns=$(get_first_dns dns 53 | sed 's/#/:/g') [ -n "$socks" ] && { socks=$(echo $socks | sed "s/#/:/g") socks_address=$(echo $socks | awk -F ':' '{print $1}') socks_port=$(echo $socks | awk -F ':' '{print $2}') } [ -n "$socks_username" ] && [ -n "$socks_password" ] && _extra_param="${_extra_param} /u $socks_username /p $socks_password" [ -z "$cache" ] && cache=1 [ "$cache" = "0" ] && _extra_param="${_extra_param} /d" ln_start_bin "$(first_type dns2socks)" "dns2socks${flag}" $log_file ${_extra_param} "${socks_address}:${socks_port}" "${dns}" "${listen_address}:${listen_port}" } run_v2ray_dns_socks() { local flag type socks_address socks_port socks_username socks_password listen_address listen_port dns_proto dns_tcp_server doh dns_client_ip dns_query_strategy log_file config_file eval_set_val $@ [ -z "$type" ] && { local bin=$(first_type $(config_t_get global_app v2ray_file) v2ray) if [ -n "$bin" ]; then type="v2ray" else bin=$(first_type $(config_t_get global_app xray_file) xray) [ -n "$bin" ] && type="xray" fi } [ -z "$type" ] && return 1 [ -n "$log_file" ] || log_file="/dev/null" if [ "$dns_proto" = "tcp" ]; then local _dns_forward=$(get_first_dns dns_tcp_server 53 | sed 's/#/:/g') local _dns_address=$(echo ${_dns_forward} | awk -F ':' '{print $1}') lua $API_GEN_V2RAY -dns_listen_port "${listen_port}" -dns_server "${_dns_address}" -dns_tcp_server "tcp://${_dns_forward}" -dns_query_strategy "${dns_query_strategy}" -dns_socks_address "${socks_address}" -dns_socks_port "${socks_port}" > $config_file elif [ "$dns_proto" = "doh" ]; then _doh_url=$(echo $doh | awk -F ',' '{print $1}') _doh_host_port=$(echo $_doh_url | sed "s/https:\/\///g" | awk -F '/' '{print $1}') _doh_host=$(echo $_doh_host_port | awk -F ':' '{print $1}') _doh_port=$(echo $_doh_host_port | awk -F ':' '{print $2}') _doh_bootstrap=$(echo $doh | cut -d ',' -sf 2-) lua $API_GEN_V2RAY -dns_listen_port "${listen_port}" -dns_server "${_doh_bootstrap}" -doh_url "${_doh_url}" -doh_host "${_doh_host}" -dns_client_ip "${dns_client_ip}" -dns_query_strategy "${dns_query_strategy}" -dns_socks_address "${socks_address}" -dns_socks_port "${socks_port}" > $config_file fi ln_start_bin "$(first_type $(config_t_get global_app ${type}_file) ${type})" ${type} $log_file -config="$config_file" } run_socks() { local flag node bind socks_port config_file http_port http_config_file relay_port log_file eval_set_val $@ [ -n "$http_port" ] || http_port=0 [ -n "$http_config_file" ] || http_config_file="nil" [ -n "$log_file" ] || log_file="/dev/null" local type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z') local remarks=$(config_n_get $node remarks) local server_host=$(config_n_get $node address) local port=$(config_n_get $node port) [ -n "$relay_port" ] && { server_host="127.0.0.1" port=$relay_port } local error_msg tmp if [ -n "$server_host" ] && [ -n "$port" ]; then check_host $server_host [ $? != 0 ] && { echolog " - Socks节点:[$remarks]${server_host} 是非法的服务器地址,无法启动!" return 1 } tmp="${server_host}:${port}" else error_msg="某种原因,此 Socks 服务的相关配置已失联,启动中止!" fi if ([ "$type" == "v2ray" ] || [ "$type" == "xray" ]) && ([ -n "$(config_n_get $node balancing_node)" ] || [ "$(config_n_get $node default_node)" != "_direct" -a "$(config_n_get $node default_node)" != "_blackhole" ]); then unset error_msg fi [ -n "${error_msg}" ] && { [ "$bind" != "127.0.0.1" ] && echolog " - Socks节点:[$remarks]${tmp},启动中止 ${bind}:${socks_port} ${error_msg}" return 1 } [ "$bind" != "127.0.0.1" ] && echolog " - Socks节点:[$remarks]${tmp},启动 ${bind}:${socks_port}" case "$type" in socks) local bin=$(first_type $(config_t_get global_app v2ray_file) v2ray) if [ -n "$bin" ]; then type="v2ray" else bin=$(first_type $(config_t_get global_app xray_file) xray) [ -n "$bin" ] && type="xray" fi [ -z "$type" ] && return 1 local _socks_address=$(config_n_get $node address) local _socks_port=$(config_n_get $node port) local _socks_username=$(config_n_get $node username) local _socks_password=$(config_n_get $node password) [ "$http_port" != "0" ] && { local _extra_param="-local_http_port $http_port" config_file=$(echo $config_file | sed "s/SOCKS/HTTP_SOCKS/g") } lua $API_GEN_V2RAY_PROTO -local_socks_port $socks_port ${_extra_param} -server_proto socks -server_address ${_socks_address} -server_port ${_socks_port} -server_username ${_socks_username} -server_password ${_socks_password} > $config_file ln_start_bin "$bin" $type $log_file -config="$config_file" ;; v2ray|\ xray) [ "$http_port" != "0" ] && { local _extra_param="-local_http_port $http_port" config_file=$(echo $config_file | sed "s/SOCKS/HTTP_SOCKS/g") } lua $API_GEN_V2RAY -node $node -local_socks_port $socks_port ${_extra_param} > $config_file ln_start_bin "$(first_type $(config_t_get global_app ${type}_file) ${type})" ${type} $log_file -config="$config_file" ;; trojan-go) lua $API_GEN_TROJAN -node $node -run_type client -local_addr $bind -local_port $socks_port -server_host $server_host -server_port $port > $config_file ln_start_bin "$(first_type $(config_t_get global_app trojan_go_file) trojan-go)" trojan-go $log_file -config "$config_file" ;; trojan*) lua $API_GEN_TROJAN -node $node -run_type client -local_addr $bind -local_port $socks_port -server_host $server_host -server_port $port > $config_file ln_start_bin "$(first_type ${type})" "${type}" $log_file -c "$config_file" ;; naiveproxy) lua $API_GEN_NAIVE -node $node -run_type socks -local_addr $bind -local_port $socks_port -server_host $server_host -server_port $port > $config_file ln_start_bin "$(first_type naive)" naive $log_file "$config_file" ;; brook) local protocol=$(config_n_get $node protocol client) local prefix="" [ "$protocol" == "wsclient" ] && { prefix="ws://" local brook_tls=$(config_n_get $node brook_tls 0) [ "$brook_tls" == "1" ] && { prefix="wss://" protocol="wssclient" } local ws_path=$(config_n_get $node ws_path "/ws") } server_host=${prefix}${server_host} ln_start_bin "$(first_type $(config_t_get global_app brook_file) brook)" "brook_SOCKS_${flag}" $log_file "$protocol" --socks5 "$bind:$socks_port" -s "${server_host}:${port}${ws_path}" -p "$(config_n_get $node password)" ;; ssr) lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $socks_port -server_host $server_host -server_port $port > $config_file ln_start_bin "$(first_type ssr-local)" "ssr-local" $log_file -c "$config_file" -v -u ;; ss) lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $socks_port -server_host $server_host -server_port $port -mode tcp_and_udp > $config_file ln_start_bin "$(first_type ss-local)" "ss-local" $log_file -c "$config_file" -v ;; ss-rust) lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $socks_port -server_host $server_host -server_port $port -protocol socks -mode tcp_and_udp > $config_file ln_start_bin "$(first_type sslocal)" "sslocal" $log_file -c "$config_file" -v ;; hysteria) lua $API_GEN_HYSTERIA -node $node -local_socks_port $socks_port > $config_file ln_start_bin "$(first_type $(config_t_get global_app hysteria_file))" "hysteria" $log_file -c "$config_file" client ;; esac # http to socks [ "$type" != "v2ray" ] && [ "$type" != "xray" ] && [ "$type" != "socks" ] && [ "$http_port" != "0" ] && [ "$http_config_file" != "nil" ] && { local bin=$(first_type $(config_t_get global_app v2ray_file) v2ray) if [ -n "$bin" ]; then type="v2ray" else bin=$(first_type $(config_t_get global_app xray_file) xray) [ -n "$bin" ] && type="xray" fi [ -z "$type" ] && return 1 lua $API_GEN_V2RAY_PROTO -local_http_port $http_port -server_proto socks -server_address "127.0.0.1" -server_port $socks_port -server_username $_username -server_password $_password > $http_config_file ln_start_bin "$bin" ${type} /dev/null -config="$http_config_file" } } run_redir() { local node bind local_port config_file REDIR_TYPE log_file eval_set_val $@ [ -n "$log_file" ] || log_file="/dev/null" local redir_type=$(echo $REDIR_TYPE | tr 'A-Z' 'a-z') local type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z') local close_log=$(config_t_get global close_log_${redir_type} 1) [ "$close_log" = "1" ] && log_file="/dev/null" local remarks=$(config_n_get $node remarks) local server_host=$(config_n_get $node address) local port=$(config_n_get $node port) [ -n "$server_host" ] && [ -n "$port" ] && { check_host $server_host [ $? != 0 ] && { echolog "${REDIR_TYPE}节点:[$remarks]${server_host} 是非法的服务器地址,无法启动!" return 1 } [ "$bind" != "127.0.0.1" ] && echolog "${REDIR_TYPE}节点:[$remarks]${server_host}:${port},监听端口:$local_port" } eval ${REDIR_TYPE}_NODE_PORT=$port case "$REDIR_TYPE" in UDP) case "$type" in socks) local _socks_address=$(config_n_get $node address) _socks_address=$(get_host_ip "ipv4" ${_socks_address}) local _socks_port=$(config_n_get $node port) local _socks_username=$(config_n_get $node username) local _socks_password=$(config_n_get $node password) [ -n "${_socks_username}" ] && [ -n "${_socks_password}" ] && local _extra_param="-a ${_socks_username} -k ${_socks_password}" ln_start_bin "$(first_type ipt2socks)" "ipt2socks_UDP" $log_file -l $local_port -b 0.0.0.0 -s ${_socks_address} -p ${_socks_port} ${_extra_param} -U -v ;; v2ray|\ xray) local loglevel=$(config_t_get global loglevel "warning") lua $API_GEN_V2RAY -node $node -proto udp -redir_port $local_port -proxy_way tproxy -loglevel $loglevel > $config_file ln_start_bin "$(first_type $(config_t_get global_app ${type}_file) ${type})" ${type} $log_file -config="$config_file" ;; trojan-go) local loglevel=$(config_t_get global trojan_loglevel "2") lua $API_GEN_TROJAN -node $node -run_type nat -local_addr "0.0.0.0" -local_port $local_port -loglevel $loglevel > $config_file ln_start_bin "$(first_type $(config_t_get global_app trojan_go_file) trojan-go)" trojan-go $log_file -config "$config_file" ;; trojan*) local loglevel=$(config_t_get global trojan_loglevel "2") lua $API_GEN_TROJAN -node $node -run_type nat -local_addr "0.0.0.0" -local_port $local_port -loglevel $loglevel > $config_file ln_start_bin "$(first_type ${type})" "${type}" $log_file -c "$config_file" ;; naiveproxy) echolog "Naiveproxy不支持UDP转发!" ;; brook) local protocol=$(config_n_get $node protocol client) if [ "$protocol" == "wsclient" ]; then echolog "Brook的WebSocket不支持UDP转发!" else ln_start_bin "$(first_type $(config_t_get global_app brook_file) brook)" "brook_UDP" $log_file tproxy -l ":$local_port" -s "$server_host:$port" -p "$(config_n_get $node password)" --doNotRunScripts fi ;; ssr) lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $local_port > $config_file ln_start_bin "$(first_type ssr-redir)" "ssr-redir" $log_file -c "$config_file" -v -U ;; ss) lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $local_port -mode udp_only > $config_file ln_start_bin "$(first_type ss-redir)" "ss-redir" $log_file -c "$config_file" -v ;; ss-rust) lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $local_port -protocol redir -mode udp_only > $config_file ln_start_bin "$(first_type sslocal)" "sslocal" $log_file -c "$config_file" -v ;; hysteria) lua $API_GEN_HYSTERIA -node $node -local_udp_redir_port $local_port > $config_file ln_start_bin "$(first_type $(config_t_get global_app hysteria_file))" "hysteria" $log_file -c "$config_file" client ;; esac ;; TCP) if [ $PROXY_IPV6 == "1" ]; then echolog "开启实验性IPv6透明代理(TProxy),请确认您的节点及类型支持IPv6!" fi local kcptun_use=$(config_n_get $node use_kcp 0) if [ "$kcptun_use" == "1" ]; then local kcptun_server_host=$(config_n_get $node kcp_server) local network_type="ipv4" local kcptun_port=$(config_n_get $node kcp_port) local kcptun_config="$(config_n_get $node kcp_opts)" if [ -z "$kcptun_port" -o -z "$kcptun_config" ]; then echolog "Kcptun未配置参数,错误!" return 1 fi if [ -n "$kcptun_port" -a -n "$kcptun_config" ]; then local run_kcptun_ip=$server_host [ -n "$kcptun_server_host" ] && run_kcptun_ip=$(get_host_ip $network_type $kcptun_server_host) KCPTUN_REDIR_PORT=$(get_new_port $KCPTUN_REDIR_PORT tcp) kcptun_params="-l 0.0.0.0:$KCPTUN_REDIR_PORT -r $run_kcptun_ip:$kcptun_port $kcptun_config" ln_start_bin "$(first_type $(config_t_get global_app kcptun_client_file) kcptun-client)" "kcptun_TCP" $log_file $kcptun_params fi fi if [ "$tcp_proxy_way" = "redirect" ]; then can_ipt=$(echo "$REDIRECT_LIST" | grep "$type") elif [ "$tcp_proxy_way" = "tproxy" ]; then can_ipt=$(echo "$TPROXY_LIST" | grep "$type") fi [ -z "$can_ipt" ] && type="socks" case "$type" in socks) _socks_flag=1 _socks_address=$(config_n_get $node address) _socks_address=$(get_host_ip "ipv4" ${_socks_address}) _socks_port=$(config_n_get $node port) _socks_username=$(config_n_get $node username) _socks_password=$(config_n_get $node password) [ -z "$can_ipt" ] && { local _config_file=$config_file _config_file=$(echo ${_config_file} | sed "s/TCP/SOCKS_${node}/g") local _port=$(get_new_port 2080) run_socks flag="TCP" node=$node bind=127.0.0.1 socks_port=${_port} config_file=${_config_file} _socks_address=127.0.0.1 _socks_port=${_port} unset _socks_username unset _socks_password } ;; v2ray|\ xray) local loglevel=$(config_t_get global loglevel "warning") local proto="-proto tcp" local _extra_param="" [ "$tcp_node_socks" = "1" ] && { local socks_param="-local_socks_port $tcp_node_socks_port" _extra_param="${_extra_param} ${socks_param}" config_file=$(echo $config_file | sed "s/TCP/TCP_SOCKS_$tcp_node_socks_id/g") } [ "$tcp_node_http" = "1" ] && { local http_param="-local_http_port $tcp_node_http_port" _extra_param="${_extra_param} ${http_param}" config_file=$(echo $config_file | sed "s/TCP/TCP_HTTP_$tcp_node_http_id/g") } [ "$TCP_UDP" = "1" ] && { config_file=$(echo $config_file | sed "s/TCP/TCP_UDP/g") proto="-proto tcp,udp" UDP_REDIR_PORT=$TCP_REDIR_PORT UDP_NODE="nil" } _extra_param="${_extra_param} ${proto}" [ "${DNS_MODE}" = "v2ray" -o "${DNS_MODE}" = "xray" ] && [ "$(config_t_get global dns_by)" = "tcp" ] && { config_file=$(echo $config_file | sed "s/.json/_DNS.json/g") resolve_dns=1 local v2ray_dns_mode=$(config_t_get global v2ray_dns_mode tcp) case "$v2ray_dns_mode" in tcp) local dns_forward=$(get_first_dns DNS_FORWARD 53 | sed 's/#/:/g') local dns_address=$(echo $dns_forward | awk -F ':' '{print $1}') _extra_param="${_extra_param} -dns_listen_port ${dns_listen_port} -dns_server ${dns_address} -dns_tcp_server tcp://${dns_forward}" echolog " - 域名解析 DNS Over TCP..." ;; doh) up_trust_doh=$(config_t_get global up_trust_doh "https://cloudflare-dns.com/dns-query,1.1.1.1") _doh_url=$(echo $up_trust_doh | awk -F ',' '{print $1}') _doh_host_port=$(echo $_doh_url | sed "s/https:\/\///g" | awk -F '/' '{print $1}') _doh_host=$(echo $_doh_host_port | awk -F ':' '{print $1}') _doh_port=$(echo $_doh_host_port | awk -F ':' '{print $2}') _doh_bootstrap=$(echo $up_trust_doh | cut -d ',' -sf 2-) _dns_client_ip=$(config_t_get global dns_client_ip) _extra_param="${_extra_param} -dns_listen_port ${dns_listen_port} -dns_server ${_doh_bootstrap} -doh_url ${_doh_url} -doh_host ${_doh_host} -dns_client_ip ${_dns_client_ip}" unset _doh_url _doh_port _doh_bootstrap echolog " - 域名解析 DNS Over HTTPS..." ;; esac } lua $API_GEN_V2RAY -node $node -redir_port $local_port -proxy_way $tcp_proxy_way -loglevel $loglevel ${_extra_param} > $config_file ln_start_bin "$(first_type $(config_t_get global_app ${type}_file) ${type})" ${type} $log_file -config="$config_file" ;; trojan-go) [ "$TCP_UDP" = "1" ] && { config_file=$(echo $config_file | sed "s/TCP/TCP_UDP/g") UDP_REDIR_PORT=$TCP_REDIR_PORT UDP_NODE="nil" } local loglevel=$(config_t_get global trojan_loglevel "2") lua $API_GEN_TROJAN -node $node -run_type nat -local_addr "0.0.0.0" -local_port $local_port -loglevel $loglevel > $config_file ln_start_bin "$(first_type $(config_t_get global_app trojan_go_file) trojan-go)" trojan-go $log_file -config "$config_file" ;; trojan*) [ "$tcp_proxy_way" = "tproxy" ] && lua_tproxy_arg="-use_tproxy true" [ "$TCP_UDP" = "1" ] && { config_file=$(echo $config_file | sed "s/TCP/TCP_UDP/g") UDP_REDIR_PORT=$TCP_REDIR_PORT UDP_NODE="nil" } local loglevel=$(config_t_get global trojan_loglevel "2") lua $API_GEN_TROJAN -node $node -run_type nat -local_addr "0.0.0.0" -local_port $local_port -loglevel $loglevel $lua_tproxy_arg > $config_file ln_start_bin "$(first_type ${type})" "${type}" $log_file -c "$config_file" ;; naiveproxy) lua $API_GEN_NAIVE -node $node -run_type redir -local_addr "0.0.0.0" -local_port $local_port > $config_file ln_start_bin "$(first_type naive)" naive $log_file "$config_file" ;; brook) local server_ip=$server_host local protocol=$(config_n_get $node protocol client) local prefix="" [ "$protocol" == "wsclient" ] && { prefix="ws://" local brook_tls=$(config_n_get $node brook_tls 0) [ "$brook_tls" == "1" ] && prefix="wss://" local ws_path=$(config_n_get $node ws_path "/ws") } [ "$kcptun_use" == "1" ] && { server_ip=127.0.0.1 port=$KCPTUN_REDIR_PORT } server_ip=${prefix}${server_ip} ln_start_bin "$(first_type $(config_t_get global_app brook_file) brook)" "brook_TCP" $log_file tproxy -l ":$local_port" -s "${server_ip}:${port}${ws_path}" -p "$(config_n_get $node password)" --doNotRunScripts ;; ssr) [ "$tcp_proxy_way" = "tproxy" ] && lua_tproxy_arg="-tcp_tproxy true" if [ "$kcptun_use" == "1" ]; then lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $local_port -server_host "127.0.0.1" -server_port $KCPTUN_REDIR_PORT $lua_tproxy_arg > $config_file else [ "$TCP_UDP" = "1" ] && { config_file=$(echo $config_file | sed "s/TCP/TCP_UDP/g") UDP_REDIR_PORT=$TCP_REDIR_PORT UDP_NODE="nil" _extra_param="-u" } lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $local_port $lua_tproxy_arg > $config_file fi ln_start_bin "$(first_type ssr-redir)" "ssr-redir" $log_file -c "$config_file" -v ${_extra_param} ;; ss) [ "$tcp_proxy_way" = "tproxy" ] && lua_tproxy_arg="-tcp_tproxy true" lua_mode_arg="-mode tcp_only" if [ "$kcptun_use" == "1" ]; then lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $local_port -server_host "127.0.0.1" -server_port $KCPTUN_REDIR_PORT $lua_mode_arg $lua_tproxy_arg > $config_file else [ "$TCP_UDP" = "1" ] && { config_file=$(echo $config_file | sed "s/TCP/TCP_UDP/g") UDP_REDIR_PORT=$TCP_REDIR_PORT UDP_NODE="nil" lua_mode_arg="-mode tcp_and_udp" } lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $local_port $lua_mode_arg $lua_tproxy_arg > $config_file fi ln_start_bin "$(first_type ss-redir)" "ss-redir" $log_file -c "$config_file" -v ;; ss-rust) [ "$tcp_proxy_way" = "tproxy" ] && lua_tproxy_arg="-tcp_tproxy true" lua_mode_arg="-mode tcp_only" if [ "$kcptun_use" == "1" ]; then lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $local_port -server_host "127.0.0.1" -server_port $KCPTUN_REDIR_PORT -protocol redir $lua_mode_arg $lua_tproxy_arg > $config_file else [ "$TCP_UDP" = "1" ] && { config_file=$(echo $config_file | sed "s/TCP/TCP_UDP/g") UDP_REDIR_PORT=$TCP_REDIR_PORT UDP_NODE="nil" lua_mode_arg="-mode tcp_and_udp" } lua $API_GEN_SS -node $node -local_addr "0.0.0.0" -local_port $local_port -protocol redir $lua_mode_arg $lua_tproxy_arg > $config_file fi ln_start_bin "$(first_type sslocal)" "sslocal" $log_file -c "$config_file" -v ;; hysteria) [ "$TCP_UDP" = "1" ] && { config_file=$(echo $config_file | sed "s/TCP/TCP_UDP/g") UDP_REDIR_PORT=$TCP_REDIR_PORT UDP_NODE="nil" _extra_param="-local_udp_redir_port $local_port" } lua $API_GEN_HYSTERIA -node $node -local_tcp_redir_port $local_port ${_extra_param} > $config_file ln_start_bin "$(first_type $(config_t_get global_app hysteria_file))" "hysteria" $log_file -c "$config_file" client ;; esac if [ -n "${_socks_flag}" ]; then local _flag="TCP" local _extra_param="-T" [ "$TCP_UDP" = "1" ] && { _flag="TCP_UDP" _extra_param="" UDP_REDIR_PORT=$TCP_REDIR_PORT UDP_NODE="nil" } local _socks_tproxy="-R" [ "$tcp_proxy_way" = "tproxy" ] && _socks_tproxy="" _extra_param="${_extra_param} ${_socks_tproxy}" [ -n "${_socks_username}" ] && [ -n "${_socks_password}" ] && _extra_param="-a ${_socks_username} -k ${_socks_password} ${_extra_param}" ln_start_bin "$(first_type ipt2socks)" "ipt2socks_${_flag}" $log_file -l $local_port -b 0.0.0.0 -s ${_socks_address} -p ${_socks_port} ${_extra_param} -v fi ([ "$type" != "v2ray" ] && [ "$type" != "xray" ]) && { [ "$tcp_node_socks" = "1" ] && { local port=$tcp_node_socks_port local config_file=$TMP_PATH/SOCKS_$tcp_node_socks_id.json local log_file=$TMP_PATH/SOCKS_$tcp_node_socks_id.log local http_port=0 local http_config_file=$TMP_PATH/HTTP2SOCKS_$tcp_node_http_id.json [ "$tcp_node_http" = "1" ] && { http_port=$tcp_node_http_port } run_socks flag=$tcp_node_socks_id node=$node bind=0.0.0.0 socks_port=$port config_file=$config_file http_port=$http_port http_config_file=$http_config_file } } ;; esac return 0 } node_switch() { [ -n "$1" -a -n "$2" ] && { [ -n "$4" ] && LOG_FILE="/dev/null" local node=$2 pgrep -af "$TMP_PATH" | awk -v P1="$1" 'BEGIN{IGNORECASE=1}$0~P1 && !/acl\/|acl_/{print $1}' | xargs kill -9 >/dev/null 2>&1 rm -rf $TMP_PATH/${1}* local config_file=$TMP_PATH/${1}.json local log_file=$TMP_PATH/${1}.log local port=$(cat $TMP_PORT_PATH/${1}) [ "$SOCKS_ENABLED" = "1" ] && { local ids=$(uci show $CONFIG | grep "=socks" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}') for id in $ids; do [ "$(config_n_get $id enabled 0)" == "0" ] && continue [ "$(config_n_get $id node nil)" != "tcp" ] && continue local socks_port=$(config_n_get $id port) local http_port=$(config_n_get $id http_port 0) pgrep -af "${TMP_PATH}.*${id}" | awk 'BEGIN{IGNORECASE=1}/SOCKS/{print $1}' | xargs kill -9 >/dev/null 2>&1 tcp_node_socks=1 tcp_node_socks_port=$socks_port tcp_node_socks_id=$id [ "$http_port" != "0" ] && { tcp_node_http=1 tcp_node_http_port=$http_port tcp_node_http_id=$id } break done } [ "$3" != "0" ] && { local tcp_node=$(config_t_get global tcp_node nil) [ "$(config_n_get $tcp_node protocol nil)" = "_shunt" ] && { if [ "$3" == "1" ]; then uci set $CONFIG.$tcp_node.default_node="$node" elif [ "$3" == "2" ]; then uci set $CONFIG.$tcp_node.main_node="$node" fi uci commit $CONFIG } node=$tcp_node } run_redir node=$node bind=0.0.0.0 local_port=$port config_file=$config_file REDIR_TYPE=$1 log_file=$log_file echo $node > $TMP_ID_PATH/${1} [ "$3" != "0" ] && [ "$(config_n_get $node protocol nil)" = "_shunt" ] && { echo $(config_n_get $node default_node nil) > $TMP_ID_PATH/${1}_default echo $(config_n_get $node main_node nil) > $TMP_ID_PATH/${1}_main uci commit $CONFIG } [ "$1" = "TCP" ] && { [ "$(config_t_get global udp_node nil)" = "tcp" ] && [ "$UDP_REDIR_PORT" != "$TCP_REDIR_PORT" ] && { pgrep -af "$TMP_PATH" | awk 'BEGIN{IGNORECASE=1}/UDP/ && !/acl\/|acl_/{print $1}' | xargs kill -9 >/dev/null 2>&1 UDP_NODE=$node start_redir UDP } } #local node_net=$(echo $1 | tr 'A-Z' 'a-z') #uci set $CONFIG.@global[0].${node_net}_node=$node #uci commit $CONFIG source $APP_PATH/helper_${DNS_N}.sh logic_restart no_log=1 } } start_redir() { eval node=\$${1}_NODE if [ "$node" != "nil" ]; then TYPE=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z') local config_file=$TMP_PATH/${1}.json local log_file=$TMP_PATH/${1}.log eval current_port=\$${1}_REDIR_PORT local port=$(echo $(get_new_port $current_port $1)) eval ${1}_REDIR=$port run_redir node=$node bind=0.0.0.0 local_port=$port config_file=$config_file REDIR_TYPE=$1 log_file=$log_file #eval ip=\$${1}_NODE_IP echo $port > $TMP_PORT_PATH/${1} echo $node > $TMP_ID_PATH/${1} [ "$(config_n_get $node protocol nil)" = "_shunt" ] && { local default_node=$(config_n_get $node default_node nil) local main_node=$(config_n_get $node main_node nil) echo $default_node > $TMP_ID_PATH/${1}_default echo $main_node > $TMP_ID_PATH/${1}_main } else [ "$1" = "UDP" ] && [ "$TCP_UDP" = "1" ] && return echolog "${1}节点没有选择或为空,不代理${1}。" fi } start_socks() { [ "$SOCKS_ENABLED" = "1" ] && { local ids=$(uci show $CONFIG | grep "=socks" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}') [ -n "$ids" ] && echolog "分析 Socks 服务的节点配置..." for id in $ids; do local enabled=$(config_n_get $id enabled 0) [ "$enabled" == "0" ] && continue local node=$(config_n_get $id node nil) [ "$node" == "nil" ] && continue local port=$(config_n_get $id port) local config_file=$TMP_PATH/SOCKS_${id}.json local log_file=$TMP_PATH/SOCKS_${id}.log local http_port=$(config_n_get $id http_port 0) local http_config_file=$TMP_PATH/HTTP2SOCKS_${id}.json [ "$node" == "tcp" ] && { tcp_node_socks=1 tcp_node_socks_port=$port tcp_node_socks_id=$id [ "$http_port" != "0" ] && { tcp_node_http=1 tcp_node_http_port=$http_port tcp_node_http_id=$id } continue } run_socks flag=$id node=$node bind=0.0.0.0 socks_port=$port config_file=$config_file http_port=$http_port http_config_file=$http_config_file done } } clean_log() { logsnum=$(cat $LOG_FILE 2>/dev/null | wc -l) [ "$logsnum" -gt 1000 ] && { echo "" > $LOG_FILE echolog "日志文件过长,清空处理!" } } clean_crontab() { touch /etc/crontabs/root #sed -i "/${CONFIG}/d" /etc/crontabs/root >/dev/null 2>&1 sed -i "/$(echo "/etc/init.d/${CONFIG}" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 sed -i "/$(echo "lua ${APP_PATH}/rule_update.lua log" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 sed -i "/$(echo "lua ${APP_PATH}/subscribe.lua start" | sed 's#\/#\\\/#g')/d" /etc/crontabs/root >/dev/null 2>&1 } start_crontab() { clean_crontab [ "$ENABLED" != 1 ] && { /etc/init.d/cron restart return } auto_on=$(config_t_get global_delay auto_on 0) if [ "$auto_on" = "1" ]; then time_off=$(config_t_get global_delay time_off) time_on=$(config_t_get global_delay time_on) time_restart=$(config_t_get global_delay time_restart) [ -z "$time_off" -o "$time_off" != "nil" ] && { echo "0 $time_off * * * /etc/init.d/$CONFIG stop" >>/etc/crontabs/root echolog "配置定时任务:每天 $time_off 点关闭服务。" } [ -z "$time_on" -o "$time_on" != "nil" ] && { echo "0 $time_on * * * /etc/init.d/$CONFIG start" >>/etc/crontabs/root echolog "配置定时任务:每天 $time_on 点开启服务。" } [ -z "$time_restart" -o "$time_restart" != "nil" ] && { echo "0 $time_restart * * * /etc/init.d/$CONFIG restart" >>/etc/crontabs/root echolog "配置定时任务:每天 $time_restart 点重启服务。" } fi autoupdate=$(config_t_get global_rules auto_update) weekupdate=$(config_t_get global_rules week_update) dayupdate=$(config_t_get global_rules time_update) if [ "$autoupdate" = "1" ]; then local t="0 $dayupdate * * $weekupdate" [ "$weekupdate" = "7" ] && t="0 $dayupdate * * *" echo "$t lua $APP_PATH/rule_update.lua log > /dev/null 2>&1 &" >>/etc/crontabs/root echolog "配置定时任务:自动更新规则。" fi TMP_SUB_PATH=$TMP_PATH/sub_crontabs mkdir -p $TMP_SUB_PATH for item in $(uci show ${CONFIG} | grep "=subscribe_list" | cut -d '.' -sf 2 | cut -d '=' -sf 1); do if [ "$(config_n_get $item auto_update 0)" = "1" ]; then cfgid=$(uci show ${CONFIG}.$item | head -n 1 | cut -d '.' -sf 2 | cut -d '=' -sf 1) remark=$(config_n_get $item remark) week_update=$(config_n_get $item week_update) time_update=$(config_n_get $item time_update) echo "$cfgid" >> $TMP_SUB_PATH/${week_update}_${time_update} echolog "配置定时任务:自动更新【$remark】订阅。" fi done [ -d "${TMP_SUB_PATH}" ] && { for name in $(ls ${TMP_SUB_PATH}); do week_update=$(echo $name | awk -F '_' '{print $1}') time_update=$(echo $name | awk -F '_' '{print $2}') local t="0 $time_update * * $week_update" [ "$week_update" = "7" ] && t="0 $time_update * * *" cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g') echo "$t lua $APP_PATH/subscribe.lua start $cfgids > /dev/null 2>&1 &" >>/etc/crontabs/root done rm -rf $TMP_SUB_PATH } if [ "$NO_PROXY" == 0 ]; then start_daemon=$(config_t_get global_delay start_daemon 0) [ "$start_daemon" = "1" ] && $APP_PATH/monitor.sh > /dev/null 2>&1 & AUTO_SWITCH_ENABLE=$(config_t_get auto_switch enable 0) [ "$AUTO_SWITCH_ENABLE" = "1" ] && $APP_PATH/test.sh > /dev/null 2>&1 & else echolog "运行于非代理模式,仅允许服务启停的定时任务。" fi /etc/init.d/cron restart } stop_crontab() { clean_crontab /etc/init.d/cron restart #echolog "清除定时执行命令。" } start_dns() { TUN_DNS="127.0.0.1#${dns_listen_port}" echolog "过滤服务配置:准备接管域名解析..." case "$DNS_MODE" in nonuse) echolog " - 不过滤DNS..." TUN_DNS="" use_chinadns_ng=$(config_t_get global always_use_chinadns_ng 0) [ "$use_chinadns_ng" == "0" ] && return ;; dns2socks) local dns2socks_socks_server=$(echo $(config_t_get global socks_server 127.0.0.1:1080) | sed "s/#/:/g") local dns2socks_forward=$(get_first_dns DNS_FORWARD 53 | sed 's/#/:/g') run_dns2socks socks=$dns2socks_socks_server listen_address=127.0.0.1 listen_port=${dns_listen_port} dns=$dns2socks_forward cache=$DNS_CACHE echolog " - 域名解析:dns2socks(127.0.0.1:${dns_listen_port}),${dns2socks_socks_server} -> ${dns2socks_forward}" ;; v2ray|\ xray) [ "${resolve_dns}" == "0" ] && { [ "${DNS_CACHE}" == "0" ] && local _extra_param="-dns_cache 0" local dns_query_strategy=$(config_t_get global dns_query_strategy UseIPv4) _extra_param="${_extra_param} -dns_query_strategy ${dns_query_strategy}" local dns_by=$(config_t_get global dns_by "tcp") if [ "${dns_by}" = "tcp" ]; then use_tcp_node_resolve_dns=1 elif [ "${dns_by}" = "socks" ]; then use_tcp_node_resolve_dns=0 local socks_server=$(echo $(config_t_get global socks_server 127.0.0.1:1080) | sed "s/#/:/g") local socks_address=$(echo $socks_server | awk -F ':' '{print $1}') local socks_port=$(echo $socks_server | awk -F ':' '{print $2}') _extra_param="${_extra_param} -dns_socks_address ${socks_address} -dns_socks_port ${socks_port}" fi local v2ray_dns_mode=$(config_t_get global v2ray_dns_mode tcp) case "$v2ray_dns_mode" in tcp) local dns_forward=$(get_first_dns DNS_FORWARD 53 | sed 's/#/:/g') local dns_address=$(echo $dns_forward | awk -F ':' '{print $1}') lua $API_GEN_V2RAY -dns_listen_port "${dns_listen_port}" -dns_server "${dns_address}" -dns_tcp_server "tcp://${dns_forward}" ${_extra_param} > $TMP_PATH/DNS.json echolog " - 域名解析 DNS Over TCP..." ;; doh) up_trust_doh=$(config_t_get global up_trust_doh "https://cloudflare-dns.com/dns-query,1.1.1.1") _doh_url=$(echo $up_trust_doh | awk -F ',' '{print $1}') _doh_host_port=$(echo $_doh_url | sed "s/https:\/\///g" | awk -F '/' '{print $1}') _doh_host=$(echo $_doh_host_port | awk -F ':' '{print $1}') _doh_port=$(echo $_doh_host_port | awk -F ':' '{print $2}') _doh_bootstrap=$(echo $up_trust_doh | cut -d ',' -sf 2-) _dns_client_ip=$(config_t_get global dns_client_ip) if [ "${dns_by}" = "tcp" ]; then DNS_FORWARD="" _doh_bootstrap_dns=$(echo $_doh_bootstrap | sed "s/,/ /g") for _dns in $_doh_bootstrap_dns; do _dns=$(echo $_dns | awk -F ':' '{print $1}'):${_doh_port:-443} [ -n "$DNS_FORWARD" ] && DNS_FORWARD=${DNS_FORWARD},${_dns} || DNS_FORWARD=${_dns} done unset _dns _doh_bootstrap_dns fi lua $API_GEN_V2RAY -dns_listen_port "${dns_listen_port}" -dns_server "${_doh_bootstrap}" -doh_url "${_doh_url}" -doh_host "${_doh_host}" -dns_client_ip "${_dns_client_ip}" ${_extra_param} > $TMP_PATH/DNS.json unset _doh_url _doh_port _doh_bootstrap echolog " - 域名解析 DNS Over HTTPS..." ;; esac ln_start_bin "$(first_type $(config_t_get global_app ${DNS_MODE}_file) ${DNS_MODE})" ${DNS_MODE} $TMP_PATH/DNS.log -config="$TMP_PATH/DNS.json" } ;; pdnsd) use_tcp_node_resolve_dns=1 gen_pdnsd_config "${dns_listen_port}" "${DNS_FORWARD}" "${DNS_CACHE}" ln_start_bin "$(first_type pdnsd)" pdnsd "/dev/null" --daemon -c "${TMP_PATH}/pdnsd/pdnsd.conf" -d echolog " - 域名解析:pdnsd + 使用(TCP节点)解析域名..." ;; udp) use_udp_node_resolve_dns=1 TUN_DNS=${DNS_FORWARD} echolog " - 域名解析:直接使用UDP节点请求DNS($TUN_DNS)" ;; custom) custom_dns=$(config_t_get global custom_dns) TUN_DNS="$(convert_ip_port_format $(echo ${custom_dns}))" echolog " - 域名解析:使用UDP协议自定义DNS($TUN_DNS)解析..." ;; esac [ "${use_tcp_node_resolve_dns}" = "1" ] && echolog " * 请确认上游 DNS 支持 TCP 查询,如非直连地址,确保 TCP 代理打开,并且已经正确转发!" [ "${use_udp_node_resolve_dns}" = "1" ] && echolog " * 要求代理 DNS 请求,如上游 DNS 非直连地址,确保 UDP 代理打开,并且已经正确转发!" [ -n "$chnlist" ] && [ "$CHINADNS_NG" = "1" ] && [ -n "$(first_type chinadns-ng)" ] && [ -s "${RULES_PATH}/chnlist" ] && { china_ng_listen_port=$(expr $dns_listen_port + 1) china_ng_listen="127.0.0.1#${china_ng_listen_port}" china_ng_chn=$(echo -n $(echo "${LOCAL_DNS}" | sed "s/,/\n/g" | head -n2) | tr " " ",") china_ng_gfw="${TUN_DNS}" echolog " | - (chinadns-ng) 最高支持4级域名过滤..." local gfwlist_param="${TMP_PATH}/chinadns_gfwlist" [ -s "${RULES_PATH}/gfwlist" ] && cp -a "${RULES_PATH}/gfwlist" "${gfwlist_param}" local chnlist_param="${TMP_PATH}/chinadns_chnlist" [ -s "${RULES_PATH}/chnlist" ] && cp -a "${RULES_PATH}/chnlist" "${chnlist_param}" [ -s "${RULES_PATH}/proxy_host" ] && { cat "${RULES_PATH}/proxy_host" >> "${gfwlist_param}" echolog " | - [$?](chinadns-ng) 代理域名表合并到防火墙域名表" } [ -s "${RULES_PATH}/direct_host" ] && { cat "${RULES_PATH}/direct_host" >> "${chnlist_param}" echolog " | - [$?](chinadns-ng) 域名白名单合并到中国域名表" } chnlist_param=${chnlist_param:+-m "${chnlist_param}" -M} local log_path="${TMP_PATH}/chinadns-ng.log" log_path="/dev/null" ln_start_bin "$(first_type chinadns-ng)" chinadns-ng "$log_path" -v -b 0.0.0.0 -l "${china_ng_listen_port}" ${china_ng_chn:+-c "${china_ng_chn}"} ${chnlist_param} ${china_ng_gfw:+-t "${china_ng_gfw}"} ${gfwlist_param:+-g "${gfwlist_param}"} -f echolog " + 过滤服务:ChinaDNS-NG(:${china_ng_listen_port}):国内DNS:${china_ng_chn},可信DNS:${china_ng_gfw}" } source $APP_PATH/helper_${DNS_N}.sh stretch source $APP_PATH/helper_${DNS_N}.sh add DNS_MODE=$DNS_MODE TMP_DNSMASQ_PATH=$TMP_DNSMASQ_PATH DNSMASQ_CONF_FILE=/tmp/dnsmasq.d/dnsmasq-passwall.conf DEFAULT_DNS=$DEFAULT_DNS LOCAL_DNS=$LOCAL_DNS TUN_DNS=$TUN_DNS CHINADNS_DNS=$china_ng_listen TCP_NODE=$TCP_NODE PROXY_MODE=${TCP_PROXY_MODE}${LOCALHOST_TCP_PROXY_MODE} } gen_pdnsd_config() { local listen_port=${1} local up_dns=${2} local cache=${3} local pdnsd_dir=${TMP_PATH}/pdnsd local perm_cache=2048 local _cache="on" local query_method="tcp_only" local reject_ipv6_dns= [ "${cache}" = "0" ] && _cache="off" && perm_cache=0 mkdir -p "${pdnsd_dir}" touch "${pdnsd_dir}/pdnsd.cache" chown -R root.nogroup "${pdnsd_dir}" if [ $PROXY_IPV6 == "0" ]; then reject_ipv6_dns=$(cat <<- 'EOF' reject = ::/0; reject_policy = negate; EOF ) fi cat > "${pdnsd_dir}/pdnsd.conf" <<-EOF global { perm_cache = $perm_cache; cache_dir = "$pdnsd_dir"; run_as = "root"; server_ip = 127.0.0.1; server_port = ${listen_port}; status_ctl = on; query_method = ${query_method}; min_ttl = 1h; max_ttl = 1w; timeout = 10; par_queries = 2; neg_domain_pol = on; udpbufsize = 1024; proc_limit = 2; procq_limit = 8; } EOF echolog " + [$?]Pdnsd (127.0.0.1:${listen_port})..." append_pdnsd_updns() { [ -z "${2}" ] && echolog " | - 略过错误 : ${1}" && return 0 cat >> $pdnsd_dir/pdnsd.conf <<-EOF server { label = "node-${2}_${3}"; ip = ${2}; edns_query = on; port = ${3}; timeout = 4; interval = 10m; uptest = none; purge_cache = off; proxy_only = on; caching = $_cache;${reject_ipv6_dns} } EOF echolog " | - [$?]上游DNS:${2}:${3}" } hosts_foreach up_dns append_pdnsd_updns 53 } add_ip2route() { local ip=$(get_host_ip "ipv4" $1) [ -z "$ip" ] && { echolog " - 无法解析[${1}],路由表添加失败!" return 1 } local remarks="${1}" [ "$remarks" != "$ip" ] && remarks="${1}(${ip})" . /lib/functions/network.sh local gateway device network_get_gateway gateway "$2" network_get_device device "$2" [ -z "${device}" ] && device="$2" if [ -n "${gateway}" ]; then route add -host ${ip} gw ${gateway} dev ${device} >/dev/null 2>&1 echo "$ip" >> $TMP_ROUTE_PATH/${device} echolog " - [${remarks}]添加到接口[${device}]路由表成功!" else echolog " - [${remarks}]添加到接口[${device}]路由表失功!原因是找不到[${device}]网关。" fi } delete_ip2route() { [ -d "${TMP_ROUTE_PATH}" ] && { for interface in $(ls ${TMP_ROUTE_PATH}); do for ip in $(cat ${TMP_ROUTE_PATH}/${interface}); do route del -host ${ip} dev ${interface} >/dev/null 2>&1 done done } } start_haproxy() { local haproxy_path haproxy_file item items lport sort_items [ "$(config_t_get global_haproxy balancing_enable 0)" != "1" ] && return echolog "HAPROXY 负载均衡..." haproxy_path=${TMP_PATH}/haproxy mkdir -p "${haproxy_path}" haproxy_file=${haproxy_path}/config.cfg cat <<-EOF > "${haproxy_file}" global log 127.0.0.1 local2 chroot ${haproxy_path} maxconn 60000 stats socket ${haproxy_path}/haproxy.sock daemon defaults mode tcp log global option tcplog option dontlognull option http-server-close #option forwardfor except 127.0.0.0/8 option redispatch retries 2 timeout http-request 10s timeout queue 1m timeout connect 10s timeout client 1m timeout server 1m timeout http-keep-alive 10s timeout check 10s maxconn 3000 EOF items=$(uci show ${CONFIG} | grep "=haproxy_config" | cut -d '.' -sf 2 | cut -d '=' -sf 1) for item in $items; do lport=$(config_n_get ${item} haproxy_port 0) [ "${lport}" = "0" ] && echolog " - 丢弃1个明显无效的节点" && continue sort_items="${sort_items}${IFS}${lport} ${item}" done items=$(echo "${sort_items}" | sort -n | cut -d ' ' -sf 2) unset lport local haproxy_port lbss lbweight export backup remark local msg bip bport hasvalid bbackup failcount interface for item in ${items}; do unset haproxy_port bbackup eval $(uci -q show "${CONFIG}.${item}" | cut -d '.' -sf 3-) [ "$enabled" = "1" ] || continue get_ip_port_from "$lbss" bip bport 1 [ -z "$haproxy_port" ] || [ -z "$bip" ] && echolog " - 丢弃1个明显无效的节点" && continue [ "$backup" = "1" ] && bbackup="backup" remark=$(echo $bip | sed "s/\[//g" | sed "s/\]//g") [ "$lport" = "${haproxy_port}" ] || { hasvalid="1" lport=${haproxy_port} echolog " + 入口 0.0.0.0:${lport}..." cat <<-EOF >> "${haproxy_file}" listen $lport mode tcp bind 0.0.0.0:$lport EOF } cat <<-EOF >> "${haproxy_file}" server $remark:$bport $bip:$bport weight $lbweight check inter 1500 rise 1 fall 3 $bbackup EOF if [ "$export" != "0" ]; then add_ip2route ${bip} ${export} > /dev/null 2>&1 & fi haproxy_items="${haproxy_items}${IFS}${bip}:${bport}" echolog " | - 出口节点:${bip}:${bport},权重:${lbweight}" done # 控制台配置 local console_port=$(config_t_get global_haproxy console_port) local console_user=$(config_t_get global_haproxy console_user) local console_password=$(config_t_get global_haproxy console_password) local auth="" [ -n "$console_user" ] && [ -n "$console_password" ] && auth="stats auth $console_user:$console_password" cat <<-EOF >> "${haproxy_file}" listen console bind 0.0.0.0:$console_port mode http stats refresh 30s stats uri / stats admin if TRUE $auth EOF [ "${hasvalid}" != "1" ] && echolog " - 没有发现任何有效节点信息,不启动。" && return 0 ln_start_bin "$(first_type haproxy)" haproxy "/dev/null" -f "${haproxy_file}" echolog " * 控制台端口:${console_port}/,${auth:-公开}" } kill_all() { kill -9 $(pidof "$@") >/dev/null 2>&1 } force_stop() { stop exit 0 } boot() { [ "$ENABLED" == 1 ] && { local delay=$(config_t_get global_delay start_delay 1) if [ "$delay" -gt 0 ]; then echolog "执行启动延时 $delay 秒后再启动!" sleep $delay && start >/dev/null 2>&1 & else start fi } return 0 } start() { ulimit -n 65535 load_config start_haproxy start_socks [ "$NO_PROXY" == 1 ] || { if [ -z "$(command -v iptables)" ] && [ -z "$(command -v ipset)" ]; then echolog "系统未安装iptables或ipset,无法透明代理!" else start_redir TCP start_redir UDP start_dns source $APP_PATH/iptables.sh start source $APP_PATH/helper_${DNS_N}.sh logic_restart fi } start_crontab echolog "运行完成!\n" } stop() { clean_log source $APP_PATH/iptables.sh stop delete_ip2route kill_all v2ray-plugin obfs-local pgrep -f "sleep.*(9s|58s)" | xargs kill -9 >/dev/null 2>&1 pgrep -af "${CONFIG}/" | awk '! /app\.sh|subscribe\.lua|rule_update\.lua/{print $1}' | xargs kill -9 >/dev/null 2>&1 rm -rf $TMP_PATH unset V2RAY_LOCATION_ASSET unset XRAY_LOCATION_ASSET stop_crontab source $APP_PATH/helper_${DNS_N}.sh del source $APP_PATH/helper_${DNS_N}.sh restart no_log=1 echolog "清空并关闭相关程序和缓存完成。" } arg1=$1 shift case $arg1 in get_new_port) get_new_port $@ ;; run_socks) run_socks $@ ;; run_redir) run_redir $@ ;; node_switch) node_switch $@ ;; echolog) echolog $@ ;; stop) [ "$1" = "force" ] && force_stop stop ;; start) start ;; boot) boot ;; esac