#!/bin/sh # Copyright (C) 2022-2025 xiaorouji # Copyright (C) 2026 Openwrt-Passwall Organization . /lib/functions.sh . /lib/functions/service.sh . /usr/share/libubox/jshn.sh . /usr/share/passwall2/utils.sh GLOBAL_ACL_PATH=${TMP_ACL_PATH}/default LUA_UTIL_PATH=/usr/lib/lua/luci/passwall2 UTIL_SINGBOX=$LUA_UTIL_PATH/util_sing-box.lua UTIL_SS=$LUA_UTIL_PATH/util_shadowsocks.lua UTIL_XRAY=$LUA_UTIL_PATH/util_xray.lua UTIL_NAIVE=$LUA_UTIL_PATH/util_naiveproxy.lua UTIL_HYSTERIA2=$LUA_UTIL_PATH/util_hysteria2.lua UTIL_TUIC=$LUA_UTIL_PATH/util_tuic.lua SINGBOX_BIN=$(first_type $(config_t_get global_app sing_box_file) sing-box) XRAY_BIN=$(first_type $(config_t_get global_app xray_file) xray) check_run_environment() { local prefer_nft=$(config_t_get global_forwarding prefer_nft 1) local dnsmasq_info=$(dnsmasq -v 2>/dev/null) local dnsmasq_ver=$(echo "$dnsmasq_info" | sed -n '1s/.*version \([0-9.]*\).*/\1/p') # local dnsmasq_opts=$(echo "$dnsmasq_info" | grep -i "Compile time options") local dnsmasq_ipset=0; echo "$dnsmasq_info" | grep -qw "ipset" && dnsmasq_ipset=1 local dnsmasq_nftset=0; echo "$dnsmasq_info" | grep -qw "nftset" && dnsmasq_nftset=1 local has_ipt=0; { command -v iptables-legacy || command -v iptables; } >/dev/null && has_ipt=1 local has_ipset=$(command -v ipset >/dev/null && echo 1 || echo 0) local has_fw4=$(command -v fw4 >/dev/null && echo 1 || echo 0) if [ "$prefer_nft" = "1" ]; then if [ "$dnsmasq_nftset" -eq 1 ] && [ "$has_fw4" -eq 1 ]; then USE_TABLES="nftables" elif [ "$has_ipset" -eq 1 ] && [ "$has_ipt" -eq 1 ] && [ "$dnsmasq_ipset" -eq 1 ]; then log_i18n 0 "Warning: The %s application environment is incomplete. Switch to %s. (%s)" "nftables (fw4)" "iptables" "has_fw4:$has_fw4/dnsmasq_nftset:$dnsmasq_nftset" USE_TABLES="iptables" fi else if [ "$has_ipset" -eq 1 ] && [ "$has_ipt" -eq 1 ] && [ "$dnsmasq_ipset" -eq 1 ]; then USE_TABLES="iptables" elif [ "$dnsmasq_nftset" -eq 1 ] && [ "$has_fw4" -eq 1 ]; then log_i18n 0 "Warning: The %s application environment is incomplete. Switch to %s. (%s)" "iptables (fw3)" "nftables" "has_ipt:$has_ipt/has_ipset:$has_ipset/dnsmasq_ipset:$dnsmasq_ipset" USE_TABLES="nftables" fi fi if [ -n "$USE_TABLES" ]; then local dep_list local file_path="/usr/lib/opkg/info" local file_ext=".control" [ -d "/lib/apk/packages" ] && { file_path="/lib/apk/packages"; file_ext=".list"; } if [ "$USE_TABLES" = "iptables" ]; then dep_list="iptables-mod-tproxy iptables-mod-socket iptables-mod-iprange iptables-mod-conntrack-extra kmod-ipt-nat" else dep_list="kmod-nft-socket kmod-nft-tproxy kmod-nft-nat" nftflag=1 local v_num=$(echo "$dnsmasq_ver" | tr -cd '0-9') if [ "${v_num:-0}" -lt 290 ]; then log_i18n 0 "Note: Dnsmasq (%s) is below 2.90. Upgrading is recommended to improve stability." "${dnsmasq_ver}" fi fi local pkg for pkg in $dep_list; do if [ ! -s "${file_path}/${pkg}${file_ext}" ]; then log_i18n 0 "Warning: %s transparent proxy is missing basic dependency %s!" "${USE_TABLES}" "${pkg}" fi done else log 0 "$(i18n "Warning: Not compatible with any transparent proxy system environment.") (has_fw4:$has_fw4/has_ipt:$has_ipt/has_ipset:$has_ipset/dnsmasq_nftset:$dnsmasq_nftset/dnsmasq_ipset:$dnsmasq_ipset)" fi } run_xray() { local flag node redir_port tcp_proxy_way socks_address socks_port socks_username socks_password http_address http_port http_username http_password local dns_listen_port direct_dns_query_strategy remote_dns_protocol remote_dns_udp_server remote_dns_tcp_server remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy dns_cache local loglevel log_file config_file eval_set_val $@ node_protocol=$(config_n_get $node protocol) [ -n "$log_file" ] || local log_file="/dev/null" [ -z "$loglevel" ] && local loglevel=$(config_t_get global loglevel "warning") json_init json_add_string "loglevel" "${loglevel}" [ -n "$flag" ] && { pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1 json_add_string "flag" "${flag}" } [ -n "$socks_address" ] && [ -n "$socks_port" ] && { json_add_string "local_socks_address" "${socks_address}" json_add_string "local_socks_port" "${socks_port}" [ -n "$socks_username" ] && [ -n "$socks_password" ] && { json_add_string "local_socks_username" "${socks_username}" json_add_string "local_socks_password" "${socks_password}" } } [ -n "$http_address" ] && [ -n "$http_port" ] && { json_add_string "local_http_address" "${http_address}" json_add_string "local_http_port" "${http_port}" [ -n "$http_username" ] && [ -n "$http_password" ] && { json_add_string "local_http_username" "${http_username}" json_add_string "local_http_password" "${http_password}" } } [ -n "$dns_listen_port" ] && { json_add_string "dns_listen_port" "${dns_listen_port}" [ -n "$dns_cache" ] && json_add_string "dns_cache" "${dns_cache}" [ "${node_protocol}" = "_shunt" ] && local write_ipset_direct=$(config_n_get $node write_ipset_direct 0) [ "${write_ipset_direct}" = "1" ] && { direct_dnsmasq_listen_port=$(get_new_port $(expr $dns_listen_port + 1) udp) local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf [ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf if [ "${nftflag}" = "1" ]; then local direct_nftset4="passwall2_${node}_white" local direct_nftset6="passwall2_${node}_white6" local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}" else local direct_ipset4="passwall2_${node}_white" local direct_ipset6="passwall2_${node}_white6" local direct_ipset="${direct_ipset4},${direct_ipset6}" fi run_ipset_dns_server listen_port=${direct_dnsmasq_listen_port} server_dns=${AUTO_DNS} ipset="${direct_ipset}" nftset="${direct_nftset}" config_file=${direct_ipset_conf} DIRECT_DNS_UDP_PORT=${direct_dnsmasq_listen_port} DIRECT_DNS_UDP_SERVER="127.0.0.1" [ -n "${direct_ipset}" ] && { json_add_string "direct_ipset" "${direct_ipset}" set_cache_var "node_${node}_direct_ipset4" "${direct_ipset4}" set_cache_var "node_${node}_direct_ipset6" "${direct_ipset6}" } [ -n "${direct_nftset}" ] && { json_add_string "direct_nftset" "${direct_nftset}" set_cache_var "node_${node}_direct_nftset4" "${direct_nftset4}" set_cache_var "node_${node}_direct_nftset6" "${direct_nftset6}" } } [ "$remote_fakedns" = "1" ] && { json_add_string "remote_dns_fake" "1" json_add_string "remote_dns_fake_strategy" "${remote_dns_query_strategy}" } case "$remote_dns_protocol" in udp) local _dns=$(get_first_dns remote_dns_udp_server 53 | sed 's/#/:/g') local _dns_address=$(echo ${_dns} | awk -F ':' '{print $1}') local _dns_port=$(echo ${_dns} | awk -F ':' '{print $2}') json_add_string "remote_dns_udp_port" "${_dns_port}" json_add_string "remote_dns_udp_server" "${_dns_address}" ;; tcp) local _dns=$(get_first_dns remote_dns_tcp_server 53 | sed 's/#/:/g') local _dns_address=$(echo ${_dns} | awk -F ':' '{print $1}') local _dns_port=$(echo ${_dns} | awk -F ':' '{print $2}') json_add_string "remote_dns_tcp_port" "${_dns_port}" json_add_string "remote_dns_tcp_server" "${_dns_address}" ;; doh) local _doh_url=$(echo $remote_dns_doh | awk -F ',' '{print $1}') local _doh_host_port=$(lua_api "get_domain_from_url(\"${_doh_url}\")") #local _doh_host_port=$(echo $_doh_url | sed "s/https:\/\///g" | awk -F '/' '{print $1}') local _doh_host=$(echo $_doh_host_port | awk -F ':' '{print $1}') local is_ip=$(lua_api "is_ip(\"${_doh_host}\")") local _doh_port=$(echo $_doh_host_port | awk -F ':' '{print $2}') [ -z "${_doh_port}" ] && _doh_port=443 local _doh_bootstrap=$(echo $remote_dns_doh | cut -d ',' -sf 2-) [ "${is_ip}" = "true" ] && _doh_bootstrap=${_doh_host} json_add_string "remote_dns_doh_port" "${_doh_port}" json_add_string "remote_dns_doh_url" "${_doh_url}" json_add_string "remote_dns_doh_host" "${_doh_host}" [ -n "$_doh_bootstrap" ] && json_add_string "remote_dns_doh_ip" "${_doh_bootstrap}" ;; esac [ -n "$remote_dns_detour" ] && json_add_string "remote_dns_detour" "${remote_dns_detour}" [ -n "$remote_dns_query_strategy" ] && json_add_string "remote_dns_query_strategy" "${remote_dns_query_strategy}" [ -n "$remote_dns_client_ip" ] && json_add_string "remote_dns_client_ip" "${remote_dns_client_ip}" } json_add_string "direct_dns_udp_port" "${DIRECT_DNS_UDP_PORT}" json_add_string "direct_dns_udp_server" "${DIRECT_DNS_UDP_SERVER}" json_add_string "direct_dns_query_strategy" "${direct_dns_query_strategy}" [ -n "${redir_port}" ] && { json_add_string "redir_port" "${redir_port}" set_cache_var "node_${node}_redir_port" "${redir_port}" [ -n "${tcp_proxy_way}" ] && json_add_string "tcp_proxy_way" "${tcp_proxy_way}" } json_add_string "node" "${node}" local _json_arg="$(json_dump)" lua $UTIL_XRAY gen_config "${_json_arg}" > $config_file test_log_file=$log_file [ "$test_log_file" = "/dev/null" ] && test_log_file="${TMP_PATH}/test.log" $XRAY_BIN run -test -c "$config_file" > $test_log_file; local status=$? if [ "${status}" == 0 ]; then ln_run ${QUEUE_RUN} "$XRAY_BIN" xray $log_file run -c "$config_file" else _error_log_file=$test_log_file return ${status} fi } run_singbox() { local flag node redir_port tcp_proxy_way socks_address socks_port socks_username socks_password http_address http_port http_username http_password local dns_listen_port direct_dns_query_strategy remote_dns_protocol remote_dns_udp_server remote_dns_tcp_server remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy dns_cache local loglevel log_file config_file eval_set_val $@ local type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z') [ -z "$type" ] && return 1 node_protocol=$(config_n_get $node protocol) [ -n "$log_file" ] || local log_file="/dev/null" [ -z "$loglevel" ] && local loglevel=$(config_t_get global loglevel "warn") [ "$loglevel" = "warning" ] && loglevel="warn" local singbox_tag=$($SINGBOX_BIN version | grep 'Tags:' | awk '{print $2}') json_init json_add_string "tags" "${singbox_tag}" if [ "$log_file" = "/dev/null" ]; then json_add_string "log" "0" else json_add_string "log" "1" json_add_string "logfile" "${log_file}" fi json_add_string "loglevel" "${loglevel}" [ -n "$flag" ] && { pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1{print $1}' | xargs kill -9 >/dev/null 2>&1 json_add_string "flag" "${flag}" } [ -n "$socks_address" ] && [ -n "$socks_port" ] && { json_add_string "local_socks_address" "${socks_address}" json_add_string "local_socks_port" "${socks_port}" [ -n "$socks_username" ] && [ -n "$socks_password" ] && { json_add_string "local_socks_username" "${socks_username}" json_add_string "local_socks_password" "${socks_password}" } } [ -n "$http_address" ] && [ -n "$http_port" ] && { json_add_string "local_http_address" "${http_address}" json_add_string "local_http_port" "${http_port}" [ -n "$http_username" ] && [ -n "$http_password" ] && { json_add_string "local_http_username" "${http_username}" json_add_string "local_http_password" "${http_password}" } } [ -n "$dns_listen_port" ] && { [ "${node_protocol}" = "_shunt" ] && local write_ipset_direct=$(config_n_get $node write_ipset_direct 0) [ "${write_ipset_direct}" = "1" ] && { direct_dnsmasq_listen_port=$(get_new_port $(expr $dns_listen_port + 1) udp) local direct_ipset_conf=${GLOBAL_ACL_PATH}/dns_${flag}_direct.conf [ -n "$(echo ${flag} | grep '^acl')" ] && direct_ipset_conf=${TMP_ACL_PATH}/${sid}/dns_${flag}_direct.conf if [ "${nftflag}" = "1" ]; then local direct_nftset4="passwall2_${node}_white" local direct_nftset6="passwall2_${node}_white6" local direct_nftset="4#inet#passwall2#${direct_nftset4},6#inet#passwall2#${direct_nftset6}" else local direct_ipset4="passwall2_${node}_white" local direct_ipset6="passwall2_${node}_white6" local direct_ipset="${direct_ipset4},${direct_ipset6}" fi run_ipset_dns_server listen_port=${direct_dnsmasq_listen_port} server_dns=${AUTO_DNS} ipset="${direct_ipset}" nftset="${direct_nftset}" config_file=${direct_ipset_conf} DIRECT_DNS_UDP_PORT=${direct_dnsmasq_listen_port} DIRECT_DNS_UDP_SERVER="127.0.0.1" [ -n "${direct_ipset}" ] && { json_add_string "direct_ipset" "${direct_ipset}" set_cache_var "node_${node}_direct_ipset4" "${direct_ipset4}" set_cache_var "node_${node}_direct_ipset6" "${direct_ipset6}" } [ -n "${direct_nftset}" ] && { json_add_string "direct_nftset" "${direct_nftset}" set_cache_var "node_${node}_direct_nftset4" "${direct_nftset4}" set_cache_var "node_${node}_direct_nftset6" "${direct_nftset6}" } } case "$remote_dns_protocol" in udp|\ quic) local _dns=$(get_first_dns remote_dns_udp_server 53 | sed 's/#/:/g') local _dns_address=$(echo ${_dns} | awk -F ':' '{print $1}') local _dns_port=$(echo ${_dns} | awk -F ':' '{print $2}') json_add_string "remote_dns_udp_port" "${_dns_port}" json_add_string "remote_dns_udp_server" "${_dns_address}" [ "$remote_dns_protocol" == "quic" ] && json_add_string "remote_dns_quic" "1" ;; tcp|\ tls) local _dns=$(get_first_dns remote_dns_tcp_server 53 | sed 's/#/:/g') local _dns_address=$(echo ${_dns} | awk -F ':' '{print $1}') local _dns_port=$(echo ${_dns} | awk -F ':' '{print $2}') json_add_string "remote_dns_tcp_port" "${_dns_port}" json_add_string "remote_dns_tcp_server" "${_dns_address}" [ "$remote_dns_protocol" == "tls" ] && json_add_string "remote_dns_tls" "1" ;; doh|\ http3) local _doh_url=$(echo $remote_dns_doh | awk -F ',' '{print $1}') local _doh_host_port=$(lua_api "get_domain_from_url(\"${_doh_url}\")") #local _doh_host_port=$(echo $_doh_url | sed "s/https:\/\///g" | awk -F '/' '{print $1}') local _doh_host=$(echo $_doh_host_port | awk -F ':' '{print $1}') local is_ip=$(lua_api "is_ip(\"${_doh_host}\")") local _doh_port=$(echo $_doh_host_port | awk -F ':' '{print $2}') [ -z "${_doh_port}" ] && _doh_port=443 local _doh_bootstrap=$(echo $remote_dns_doh | cut -d ',' -sf 2-) [ "${is_ip}" = "true" ] && _doh_bootstrap=${_doh_host} [ -n "$_doh_bootstrap" ] && json_add_string "remote_dns_doh_ip" "${_doh_bootstrap}" json_add_string "remote_dns_doh_port" "${_doh_port}" json_add_string "remote_dns_doh_url" "${_doh_url}" json_add_string "remote_dns_doh_host" "${_doh_host}" [ "$remote_dns_protocol" == "http3" ] && json_add_string "remote_dns_http3" "1" ;; esac [ -n "$remote_dns_detour" ] && json_add_string "remote_dns_detour" "${remote_dns_detour}" [ -n "$remote_dns_query_strategy" ] && json_add_string "remote_dns_query_strategy" "${remote_dns_query_strategy}" [ -n "$remote_dns_client_ip" ] && json_add_string "remote_dns_client_ip" "${remote_dns_client_ip}" [ -n "$dns_listen_port" ] && json_add_string "dns_listen_port" "${dns_listen_port}" [ -n "$dns_cache" ] && json_add_string "dns_cache" "${dns_cache}" [ "$remote_fakedns" = "1" ] && json_add_string "remote_dns_fake" "1" } json_add_string "direct_dns_udp_port" "${DIRECT_DNS_UDP_PORT}" json_add_string "direct_dns_udp_server" "${DIRECT_DNS_UDP_SERVER}" json_add_string "direct_dns_query_strategy" "${direct_dns_query_strategy}" [ -n "${redir_port}" ] && { json_add_string "redir_port" "${redir_port}" set_cache_var "node_${node}_redir_port" "${redir_port}" [ -n "${tcp_proxy_way}" ] && json_add_string "tcp_proxy_way" "${tcp_proxy_way}" } json_add_string "node" "${node}" local _json_arg="$(json_dump)" lua $UTIL_SINGBOX gen_config "${_json_arg}" > $config_file test_log_file=$log_file [ "$test_log_file" = "/dev/null" ] && test_log_file="${TMP_PATH}/test.log" $SINGBOX_BIN check -c "$config_file" > $test_log_file 2>&1; local status=$? if [ "${status}" == 0 ]; then ln_run ${QUEUE_RUN} "$SINGBOX_BIN" "sing-box" "${log_file}" run -c "$config_file" else _error_log_file=$test_log_file return ${status} fi } run_socks() { local flag node bind socks_port config_file http_port http_config_file relay_port log_file no_run eval_set_val $@ [ -n "$config_file" ] && [ -z "$(echo ${config_file} | grep $TMP_PATH)" ] && config_file=$TMP_PATH/$config_file [ -n "$http_port" ] || http_port=0 [ -n "$http_config_file" ] && [ -z "$(echo ${http_config_file} | grep $TMP_PATH)" ] && http_config_file=$TMP_PATH/$http_config_file if [ -n "$log_file" ] && [ -z "$(echo ${log_file} | grep $TMP_PATH)" ]; then log_file=$TMP_PATH/$log_file else log_file="/dev/null" fi 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 server_port=$(config_n_get $node port) [ -n "$relay_port" ] && { server_host="127.0.0.1" server_port=$relay_port } local error_msg tmp if [ -n "$server_host" ] && [ -n "$server_port" ]; then check_host $server_host [ $? != 0 ] && { log 1 "$(i18n "Socks node: [%s]%s is an invalid server address and cannot be started!" "${$remarks}" "${server_host}")" return 1 } tmp="${server_host}:${server_port}" else error_msg="$(i18n "For some reason, the configuration for this Socks service has been lost, and its startup has been aborted!")" fi if [ "$type" == "sing-box" ] || [ "$type" == "xray" ]; then local protocol=$(config_n_get $node protocol) if [ "$protocol" == "_balancing" ] || [ "$protocol" == "_shunt" ] || [ "$protocol" == "_iface" ] || [ "$protocol" == "_urltest" ]; then unset error_msg fi fi [ -n "${error_msg}" ] && { [ "$bind" != "127.0.0.1" ] && log 1 "$(i18n "Socks node: [%s]%s, start failed %s:%s %s" "${remarks}" "${tmp}" "${bind}" "${socks_port}" "${error_msg}")" return 1 } [ "$bind" != "127.0.0.1" ] && log 1 "$(i18n "Socks node: [%s]%s, starting %s:%s" "${remarks}" "${tmp}" "${bind}" "${socks_port}")" json_init json_add_string "node" "${node}" json_add_string "server_host" "${server_host}" json_add_string "server_port" "${server_port}" case "$type" in sing-box) [ "$http_port" != "0" ] && { http_flag=1 config_file="${config_file//SOCKS/HTTP_SOCKS}" json_add_string "local_http_address" "${bind}" json_add_string "local_http_port" "${http_port}" } [ -z "$relay_port" ] && { json_add_null "server_host" json_add_null "server_port" } [ "${log_file}" != "/dev/null" ] && { local loglevel=$(config_t_get global loglevel "warn") [ "$loglevel" = "warning" ] && loglevel="warn" json_add_string "log" "1" json_add_string "loglevel" "${loglevel}" json_add_string "logfile" "${log_file}" } [ -n "$no_run" ] && json_add_string "no_run" "1" json_add_string "flag" "SOCKS_${flag}" json_add_string "local_socks_address" "${bind}" json_add_string "local_socks_port" "${socks_port}" json_add_string "direct_dns_udp_port" "${DIRECT_DNS_UDP_PORT}" json_add_string "direct_dns_udp_server" "${DIRECT_DNS_UDP_SERVER}" json_add_string "direct_dns_query_strategy" "${DIRECT_DNS_QUERY_STRATEGY}" local _json_arg="$(json_dump)" lua $UTIL_SINGBOX gen_config "${_json_arg}" > $config_file [ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$SINGBOX_BIN" "sing-box" /dev/null run -c "$config_file" ;; xray) [ "$http_port" != "0" ] && { http_flag=1 config_file="${config_file//SOCKS/HTTP_SOCKS}" json_add_string "local_http_address" "${bind}" json_add_string "local_http_port" "${http_port}" } [ -z "$relay_port" ] && { json_add_null "server_host" json_add_null "server_port" } [ -n "$no_run" ] && json_add_string "no_run" "1" json_add_string "flag" "SOCKS_${flag}" json_add_string "local_socks_address" "${bind}" json_add_string "local_socks_port" "${socks_port}" json_add_string "direct_dns_udp_port" "${DIRECT_DNS_UDP_PORT}" json_add_string "direct_dns_udp_server" "${DIRECT_DNS_UDP_SERVER}" json_add_string "direct_dns_query_strategy" "${DIRECT_DNS_QUERY_STRATEGY}" local _json_arg="$(json_dump)" lua $UTIL_XRAY gen_config "${_json_arg}" > $config_file [ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$XRAY_BIN" "xray" $log_file run -c "$config_file" ;; naiveproxy) json_add_string "local_addr" "${bind}" json_add_string "local_port" "${socks_port}" json_add_string "run_type" "socks" local _json_arg="$(json_dump)" lua $UTIL_NAIVE gen_config "${_json_arg}" > $config_file [ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$(first_type naive)" naive $log_file "$config_file" ;; ssr) json_add_string "local_addr" "${bind}" json_add_string "local_port" "${socks_port}" local _json_arg="$(json_dump)" lua $UTIL_SS gen_config "${_json_arg}" > $config_file [ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$(first_type ssr-local)" "ssr-local" $log_file -c "$config_file" -v -u ;; ss) json_add_string "local_addr" "${bind}" json_add_string "local_port" "${socks_port}" json_add_string "mode" "tcp_and_udp" [ -z "$no_run" ] && { local plugin_sh="${config_file%.json}_plugin.sh" json_add_string "plugin_sh" "${plugin_sh}" } local _json_arg="$(json_dump)" lua $UTIL_SS gen_config "${_json_arg}" > $config_file [ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$(first_type ss-local)" "ss-local" $log_file -c "$config_file" -v ;; ss-rust) json_add_string "local_socks_address" "${bind}" json_add_string "local_socks_port" "${socks_port}" [ "$http_port" != "0" ] && { http_flag=1 config_file="${config_file//SOCKS/HTTP_SOCKS}" json_add_string "local_http_address" "${bind}" json_add_string "local_http_port" "${http_port}" } [ -z "$no_run" ] && { local plugin_sh="${config_file%.json}_plugin.sh" json_add_string "plugin_sh" "${plugin_sh}" } local _json_arg="$(json_dump)" lua $UTIL_SS gen_config "${_json_arg}" > $config_file [ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$(first_type sslocal)" "sslocal" $log_file -c "$config_file" -v ;; hysteria2) json_add_string "local_socks_address" "${bind}" json_add_string "local_socks_port" "${socks_port}" [ "$http_port" != "0" ] && { http_flag=1 config_file="${config_file//SOCKS/HTTP_SOCKS}" json_add_string "local_http_address" "${bind}" json_add_string "local_http_port" "${http_port}" } local _json_arg="$(json_dump)" lua $UTIL_HYSTERIA2 gen_config "${_json_arg}" > $config_file [ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$(first_type $(config_t_get global_app hysteria_file))" "hysteria" $log_file -c "$config_file" client ;; tuic) json_add_string "local_addr" "${bind}" json_add_string "local_port" "${socks_port}" local _json_arg="$(json_dump)" lua $UTIL_TUIC gen_config "${_json_arg}" > $config_file [ -z "$no_run" ] && ln_run ${QUEUE_RUN} "$(first_type tuic-client)" "tuic-client" $log_file -c "$config_file" ;; esac # http to socks [ -z "$http_flag" ] && [ "$http_port" != "0" ] && [ -n "$http_config_file" ] && [ "$type" != "sing-box" ] && [ "$type" != "xray" ] && [ "$type" != "socks" ] && { json_init json_add_string "local_http_port" "${http_port}" json_add_string "server_proto" "socks" json_add_string "server_address" "127.0.0.1" json_add_string "server_port" "${socks_port}" json_add_string "server_username" "${_username}" json_add_string "server_password" "${_password}" local _json_arg="$(json_dump)" if [ -n "${SINGBOX_BIN}" ]; then type="sing-box" local bin="${SINGBOX_BIN}" local util="${UTIL_SINGBOX}" elif [ -n "${XRAY_BIN}" ]; then type="xray" local bin="${XRAY_BIN}" local util="${UTIL_XRAY}" fi [ -n "${bin}" ] && [ -n "${util}" ] && { lua ${util} gen_proto_config "${_json_arg}" > ${http_config_file} [ -z "$no_run" ] && ln_run ${QUEUE_RUN} "${bin}" ${type} /dev/null run -c ${http_config_file} } unset bin util } unset http_flag [ -z "$no_run" ] && [ "${server_host}" != "127.0.0.1" ] && [ "$type" != "sing-box" ] && [ "$type" != "xray" ] && echo "${node}" >> $TMP_PATH/direct_node_list } socks_node_switch() { local flag new_node eval_set_val $@ [ -n "$flag" ] && [ -n "$new_node" ] && { local prefix pf filename # Kill the SS plugin process for prefix in "" "HTTP_"; do pf="$TMP_PATH/${prefix}SOCKS_${flag}_plugin.pid" [ -s "$pf" ] && kill -9 "$(head -n1 "$pf")" >/dev/null 2>&1 done pgrep -af "$TMP_BIN_PATH" | awk -v P1="${flag}" 'BEGIN{IGNORECASE=1}$0~P1 && !/acl\/|acl_/{print $1}' | xargs kill -9 >/dev/null 2>&1 for prefix in "" "HTTP_" "HTTP2"; do rm -rf "$TMP_PATH/${prefix}SOCKS_${flag}"* done for filename in $(ls ${TMP_SCRIPT_FUNC_PATH}); do cmd=$(cat ${TMP_SCRIPT_FUNC_PATH}/${filename}) [ -n "$(echo $cmd | grep "${flag}")" ] && rm -f ${TMP_SCRIPT_FUNC_PATH}/${filename} done local bind_local=$(config_n_get $flag bind_local 0) local bind="0.0.0.0" [ "$bind_local" = "1" ] && bind="127.0.0.1" local port=$(config_n_get $flag port) local config_file="SOCKS_${flag}.json" local log_file="SOCKS_${flag}.log" local log=$(config_n_get $flag log 1) [ "$log" == "0" ] && log_file="" local http_port=$(config_n_get $flag http_port 0) local http_config_file="HTTP2SOCKS_${flag}.json" LOG_FILE="/dev/null" run_socks flag=$flag node=$new_node bind=$bind socks_port=$port config_file=$config_file http_port=$http_port http_config_file=$http_config_file log_file=$log_file set_cache_var "socks_${flag}" "$new_node" local USE_TABLES=$(get_cache_var "USE_TABLES") [ -n "$USE_TABLES" ] && source $APP_PATH/${USE_TABLES}.sh filter_direct_node_list } } run_global() { [ -z "$NODE" ] && return 1 TYPE=$(echo $(config_n_get $NODE type) | tr 'A-Z' 'a-z') [ -z "$TYPE" ] && return 1 if [ $PROXY_IPV6 == "1" ]; then log_i18n 0 "To enable experimental IPv6 transparent proxy (TProxy), please ensure your node and type support IPv6!" fi TUN_DNS_PORT=15353 TUN_DNS="127.0.0.1#${TUN_DNS_PORT}" V2RAY_ARGS="flag=global node=$NODE redir_port=$REDIR_PORT tcp_proxy_way=${TCP_PROXY_WAY}" V2RAY_ARGS="${V2RAY_ARGS} dns_listen_port=${TUN_DNS_PORT} direct_dns_query_strategy=${DIRECT_DNS_QUERY_STRATEGY} remote_dns_query_strategy=${REMOTE_DNS_QUERY_STRATEGY} dns_cache=${DNS_CACHE}" local dns_msg="DNS: ${TUN_DNS} ($(i18n "Direct DNS: %s" "${AUTO_DNS}")" [ -n "$REMOTE_DNS_PROTOCOL" ] && { V2RAY_ARGS="${V2RAY_ARGS} remote_dns_protocol=${REMOTE_DNS_PROTOCOL} remote_dns_detour=${REMOTE_DNS_DETOUR}" case "$REMOTE_DNS_PROTOCOL" in udp|\ quic) V2RAY_ARGS="${V2RAY_ARGS} remote_dns_udp_server=${REMOTE_DNS}" dns_msg="${dns_msg} $(i18n "Remote DNS: %s" "${REMOTE_DNS}")" ;; tcp|\ tls) V2RAY_ARGS="${V2RAY_ARGS} remote_dns_tcp_server=${REMOTE_DNS}" dns_msg="${dns_msg} $(i18n "Remote DNS: %s" "${REMOTE_DNS}")" ;; doh|\ http3) REMOTE_DNS_DOH=$(config_t_get global remote_dns_doh "https://1.1.1.1/dns-query") V2RAY_ARGS="${V2RAY_ARGS} remote_dns_doh=${REMOTE_DNS_DOH}" dns_msg="${dns_msg} $(i18n "Remote DNS: %s" "${REMOTE_DNS_DOH}")" ;; esac [ "$REMOTE_FAKEDNS" = "1" ] && { V2RAY_ARGS="${V2RAY_ARGS} remote_fakedns=1" dns_msg="${dns_msg} + FakeDNS " } local _remote_dns_client_ip=$(config_t_get global remote_dns_client_ip) [ -n "${_remote_dns_client_ip}" ] && V2RAY_ARGS="${V2RAY_ARGS} remote_dns_client_ip=${_remote_dns_client_ip}" } dns_msg="${dns_msg})" V2RAY_CONFIG=${GLOBAL_ACL_PATH}/global.json V2RAY_LOG=${GLOBAL_ACL_PATH}/global.log [ "$(config_t_get global log_node 1)" != "1" ] && V2RAY_LOG="/dev/null" V2RAY_ARGS="${V2RAY_ARGS} log_file=${V2RAY_LOG} config_file=${V2RAY_CONFIG}" node_socks_port=$(config_t_get global node_socks_port 1070) node_socks_bind_local=$(config_t_get global node_socks_bind_local 1) node_socks_bind="127.0.0.1" [ "${node_socks_bind_local}" != "1" ] && node_socks_bind="0.0.0.0" V2RAY_ARGS="${V2RAY_ARGS} socks_address=${node_socks_bind} socks_port=${node_socks_port}" set_cache_var "GLOBAL_SOCKS_server" "127.0.0.1:$node_socks_port" node_http_port=$(config_t_get global node_http_port 0) [ "$node_http_port" != "0" ] && V2RAY_ARGS="${V2RAY_ARGS} http_port=${node_http_port}" local run_func [ -n "${XRAY_BIN}" ] && run_func="run_xray" [ -n "${SINGBOX_BIN}" ] && run_func="run_singbox" if [ "${TYPE}" = "xray" ] && [ -n "${XRAY_BIN}" ]; then run_func="run_xray" elif [ "${TYPE}" = "sing-box" ] && [ -n "${SINGBOX_BIN}" ]; then run_func="run_singbox" fi ${run_func} ${V2RAY_ARGS}; local status=$? if [ "$status" == 0 ]; then log 0 ${dns_msg} else log_i18n 0 "[%s] process %s error, skip this transparent proxy!" $(i18n "Global") "${V2RAY_CONFIG}" cat ${_error_log_file} >> ${LOG_FILE} unset _error_log_file ENABLED_DEFAULT_ACL=0 return 1 fi set_cache_var "ACL_GLOBAL_node" "$NODE" set_cache_var "ACL_GLOBAL_redir_port" "$REDIR_PORT" } run_front_dns() { local switch=0 direct_dns_shunt=$(config_t_get global direct_dns_shunt) direct_dns_shunt=$(echo "${direct_dns_shunt}" | grep -v "^#") [ -n "${direct_dns_shunt}" ] && switch=1 [ "${switch}" == "1" ] && { local config_file="${TMP_PATH}/direct_dns.json" local log_file="${TMP_PATH}/direct_dns.log" log_file="/dev/null" local listen_port=$(get_new_port 10553) json_init json_add_string "dns_listen_port" "${listen_port}" json_add_string "direct_dns_udp_server" "${DIRECT_DNS_UDP_SERVER}" json_add_string "direct_dns_udp_port" "${DIRECT_DNS_UDP_PORT}" json_add_string "direct_dns_query_strategy" "${DIRECT_DNS_QUERY_STRATEGY}" [ -n "${ACL_GLOBAL_node}" ] && [ -n "${TUN_DNS_PORT}" ] && { json_add_string "default_dns_udp_server" "127.0.0.1" json_add_string "default_dns_udp_port" "${TUN_DNS_PORT}" } local _json_arg="$(json_dump)" local prefer_core="" [ -n "${XRAY_BIN}" ] && prefer_core="xray" [ -n "${SINGBOX_BIN}" ] && prefer_core="sing-box" if [ "${prefer_core}" = "xray" ]; then lua $UTIL_XRAY gen_front_dns_config "${_json_arg}" > $config_file ln_run 0 "$XRAY_BIN" "xray" "${log_file}" run -c "$config_file" elif [ "${prefer_core}" = "sing-box" ]; then lua $UTIL_SINGBOX gen_front_dns_config "${_json_arg}" > $config_file ln_run 0 "$SINGBOX_BIN" "sing-box" "${log_file}" run -c "$config_file" else return 1 fi FRONT_DNS_SERVER="127.0.0.1" FRONT_DNS_PORT="${listen_port}" } } run_global_dnsmasq() { [ -z "${ACL_GLOBAL_node}" ] && [ -z "${FRONT_DNS_PORT}" ] && return local RUN_NEW_DNSMASQ=1 RUN_NEW_DNSMASQ=${DNS_REDIRECT} DNSMASQ_DEFAULT_DNS="${AUTO_DNS}" DNSMASQ_LOCAL_DNS="${LOCAL_DNS:-${AUTO_DNS}}" DNSMASQ_TUN_DNS="${TUN_DNS}" [ -n "${FRONT_DNS_PORT}" ] && { DNSMASQ_DEFAULT_DNS="${FRONT_DNS_SERVER}#${FRONT_DNS_PORT}" DNSMASQ_LOCAL_DNS="${DNSMASQ_DEFAULT_DNS}" DNSMASQ_TUN_DNS="${DNSMASQ_DEFAULT_DNS}" } if [ "${RUN_NEW_DNSMASQ}" == "0" ]; then #The old logic will be removed in the future. #Run a copy dnsmasq instance, DNS hijack that don't need a proxy devices. [ "1" = "0" ] && { DIRECT_DNSMASQ_PORT=$(get_new_port 11400) DIRECT_DNSMASQ_CONF=${GLOBAL_ACL_PATH}/direct_dnsmasq.conf DIRECT_DNSMASQ_CONF_PATH=${GLOBAL_ACL_PATH}/direct_dnsmasq.d mkdir -p ${DIRECT_DNSMASQ_CONF_PATH} json_init json_add_string "LISTEN_PORT" "${DIRECT_DNSMASQ_PORT}" json_add_string "DNSMASQ_CONF" "${DIRECT_DNSMASQ_CONF}" json_add_string "TMP_DNSMASQ_PATH" "${DIRECT_DNSMASQ_CONF_PATH}" lua $APP_PATH/helper_dnsmasq.lua copy_instance "$(json_dump)" ln_run 0 "$(first_type dnsmasq)" "dnsmasq_direct" "/dev/null" -C ${DIRECT_DNSMASQ_CONF} -x ${GLOBAL_ACL_PATH}/direct_dnsmasq.pid set_cache_var "DIRECT_DNSMASQ_PORT" "${DIRECT_DNSMASQ_PORT}" } #Rewrite the default DNS service configuration #Modify the default dnsmasq service lua $APP_PATH/helper_dnsmasq.lua stretch json_init json_add_string "FLAG" "default" json_add_string "TMP_DNSMASQ_PATH" "${GLOBAL_DNSMASQ_CONF_PATH}" json_add_string "DNSMASQ_CONF_FILE" "${GLOBAL_DNSMASQ_CONF}" json_add_string "DEFAULT_DNS" "${DNSMASQ_DEFAULT_DNS}" json_add_string "LOCAL_DNS" "${DNSMASQ_LOCAL_DNS}" json_add_string "TUN_DNS" "${DNSMASQ_TUN_DNS}" json_add_string "NFTFLAG" "${nftflag:-0}" json_add_string "NO_LOGIC_LOG" "${NO_LOGIC_LOG:-0}" lua $APP_PATH/helper_dnsmasq.lua add_rule "$(json_dump)" uci -q add_list dhcp.@dnsmasq[0].addnmount=${GLOBAL_DNSMASQ_CONF_PATH} uci -q commit dhcp json_init json_add_string "LOG" "1" lua $APP_PATH/helper_dnsmasq.lua logic_restart "$(json_dump)" else #Run a copy dnsmasq instance, DNS hijack for that need proxy devices. GLOBAL_DNSMASQ_PORT=$(get_new_port 11400) run_copy_dnsmasq flag="default" listen_port=$GLOBAL_DNSMASQ_PORT local_dns="${DNSMASQ_LOCAL_DNS}" tun_dns="${DNSMASQ_TUN_DNS}" default_dns="${DNSMASQ_DEFAULT_DNS}" DNS_REDIRECT_PORT=${GLOBAL_DNSMASQ_PORT} #dhcp.leases to hosts $APP_PATH/lease2hosts.sh > /dev/null 2>&1 & fi } start_socks() { [ "$SOCKS_ENABLED" = "1" ] && { local ids=$(uci show $CONFIG | grep "=socks" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}') [ -n "$ids" ] && { log_i18n 0 "Analyzing the node configuration of the Socks service..." for id in $ids; do local enabled=$(config_n_get $id enabled 0) [ "$enabled" == "0" ] && continue local node=$(config_n_get $id node) [ -z "$node" ] && continue local bind_local=$(config_n_get $id bind_local 0) local bind="0.0.0.0" [ "$bind_local" = "1" ] && bind="127.0.0.1" local port=$(config_n_get $id port) local config_file="SOCKS_${id}.json" local log_file="SOCKS_${id}.log" local log=$(config_n_get $id log 1) [ "$log" == "0" ] && log_file="" local http_port=$(config_n_get $id http_port 0) local http_config_file="HTTP2SOCKS_${id}.json" run_socks flag=$id node=$node bind=$bind socks_port=$port config_file=$config_file http_port=$http_port http_config_file=$http_config_file log_file=$log_file set_cache_var "socks_${id}" "$node" # Auto switch logic local enable_autoswitch=$(config_n_get $id enable_autoswitch 0) [ "$enable_autoswitch" = "1" ] && $APP_PATH/socks_auto_switch.sh ${id} > /dev/null 2>&1 & done } } } clean_crontab() { [ -f "/tmp/lock/${CONFIG}_cron.lock" ] && return 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 pgrep -af "${CONFIG}/" | awk '/tasks\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 rm -rf /tmp/lock/${CONFIG}_tasks.lock } start_crontab() { if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then start_daemon=$(config_t_get global_delay start_daemon 0) [ "$start_daemon" = "1" ] && $APP_PATH/monitor.sh > /dev/null 2>&1 & fi [ -f "/tmp/lock/${CONFIG}_cron.lock" ] && { rm -rf "/tmp/lock/${CONFIG}_cron.lock" log_i18n 0 "The task is currently running automatically as a scheduled task; no reconfiguration of the scheduled task is required." return } clean_crontab [ "$ENABLED" != 1 ] && { /etc/init.d/cron restart return } stop_week_mode=$(config_t_get global_delay stop_week_mode) stop_time_mode=$(config_t_get global_delay stop_time_mode) if [ -n "$stop_week_mode" ]; then local t="0 $stop_time_mode * * $stop_week_mode" [ "$stop_week_mode" = "7" ] && t="0 $stop_time_mode * * *" if [ "$stop_week_mode" = "8" ]; then update_loop=1 else echo "$t /etc/init.d/$CONFIG stop > /dev/null 2>&1 &" >>/etc/crontabs/root fi log_i18n 0 "Scheduled tasks: Auto stop service." fi start_week_mode=$(config_t_get global_delay start_week_mode) start_time_mode=$(config_t_get global_delay start_time_mode) if [ -n "$start_week_mode" ]; then local t="0 $start_time_mode * * $start_week_mode" [ "$start_week_mode" = "7" ] && t="0 $start_time_mode * * *" if [ "$start_week_mode" = "8" ]; then update_loop=1 else echo "$t /etc/init.d/$CONFIG start > /dev/null 2>&1 &" >>/etc/crontabs/root fi log_i18n 0 "Scheduled tasks: Auto start service." fi restart_week_mode=$(config_t_get global_delay restart_week_mode) restart_time_mode=$(config_t_get global_delay restart_time_mode) if [ -n "$restart_week_mode" ]; then local t="0 $restart_time_mode * * $restart_week_mode" [ "$restart_week_mode" = "7" ] && t="0 $restart_time_mode * * *" if [ "$restart_week_mode" = "8" ]; then update_loop=1 else echo "$t /etc/init.d/$CONFIG restart > /dev/null 2>&1 &" >>/etc/crontabs/root fi log_i18n 0 "Scheduled tasks: Auto restart service." 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 * * *" if [ "$weekupdate" = "8" ]; then update_loop=1 else echo "$t lua $APP_PATH/rule_update.lua log all cron > /dev/null 2>&1 &" >>/etc/crontabs/root fi log_i18n 0 "Scheduled tasks: Auto update rules." 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} log_i18n 0 "Scheduled tasks: Auto update [%s] subscription." "${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}') cfgids=$(echo -n $(cat ${TMP_SUB_PATH}/${name}) | sed 's# #,#g') local t="0 $time_update * * $week_update" [ "$week_update" = "7" ] && t="0 $time_update * * *" if [ "$week_update" = "8" ]; then update_loop=1 else echo "$t lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 &" >>/etc/crontabs/root fi done rm -rf $TMP_SUB_PATH } if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then [ "$update_loop" = "1" ] && { $APP_PATH/tasks.sh > /dev/null 2>&1 & log_i18n 0 "Auto updates: Starts a cyclical update process." } else log_i18n 0 "Running in no proxy mode, it only allows scheduled tasks for starting and stopping services." fi /etc/init.d/cron restart } stop_crontab() { [ -f "/tmp/lock/${CONFIG}_cron.lock" ] && return clean_crontab /etc/init.d/cron restart #log_i18n 0 "Clear scheduled commands." } start_haproxy() { [ "$(config_t_get global_haproxy balancing_enable 0)" != "1" ] && return haproxy_path=$TMP_PATH/haproxy haproxy_conf="config.cfg" lua $APP_PATH/haproxy.lua -path ${haproxy_path} -conf ${haproxy_conf} ln_run 0 "$(first_type haproxy)" haproxy "/dev/null" -f "${haproxy_path}/${haproxy_conf}" } run_copy_dnsmasq() { local flag listen_port local_dns tun_dns default_dns eval_set_val $@ local dnsmasq_conf=$TMP_ACL_PATH/$flag/dnsmasq.conf local dnsmasq_conf_path=$TMP_ACL_PATH/$flag/dnsmasq.d mkdir -p $dnsmasq_conf_path json_init json_add_string "LISTEN_PORT" "${listen_port}" json_add_string "DNSMASQ_CONF" "${dnsmasq_conf}" lua $APP_PATH/helper_dnsmasq.lua copy_instance "$(json_dump)" json_init json_add_string "FLAG" "${flag}" json_add_string "TMP_DNSMASQ_PATH" "${dnsmasq_conf_path}" json_add_string "DNSMASQ_CONF_FILE" "${dnsmasq_conf}" json_add_string "DEFAULT_DNS" "${default_dns}" json_add_string "LOCAL_DNS" "${local_dns}" json_add_string "TUN_DNS" "${tun_dns}" json_add_string "NFTFLAG" "${nftflag:-0}" json_add_string "NO_LOGIC_LOG" "${NO_LOGIC_LOG:-0}" lua $APP_PATH/helper_dnsmasq.lua add_rule "$(json_dump)" ln_run 0 "$(first_type dnsmasq)" "dnsmasq_${flag}" "/dev/null" -C $dnsmasq_conf -x $TMP_ACL_PATH/$flag/dnsmasq.pid set_cache_var "ACL_${flag}_dns_port" "${listen_port}" } run_ipset_dns_server() { if [ -n "$(first_type chinadns-ng)" ]; then run_ipset_chinadns_ng $@ else run_ipset_dnsmasq $@ fi } run_ipset_chinadns_ng() { local listen_port server_dns ipset nftset config_file eval_set_val $@ [ ! -s "$TMP_ACL_PATH/vpslist" ] && { node_servers=$(uci show "${CONFIG}" | grep -E "(.address=|.download_address=)" | cut -d "'" -f 2) hosts_foreach "node_servers" host_from_url | grep '[a-zA-Z]$' | sort -u | grep -v "engage.cloudflareclient.com" > $TMP_ACL_PATH/vpslist } [ -n "${ipset}" ] && { set_names=$ipset vps_set_names="passwall2_vps,passwall2_vps6" } [ -n "${nftset}" ] && { set_names=$(echo ${nftset} | awk -F, '{printf "%s,%s", substr($1,3), substr($2,3)}' | sed 's/#/@/g') vps_set_names="inet@passwall2@passwall2_vps,inet@passwall2@passwall2_vps6" } cat <<-EOF > $config_file bind-addr 127.0.0.1 bind-port ${listen_port} china-dns ${server_dns} trust-dns ${server_dns} filter-qtype 65 add-tagchn-ip ${set_names} default-tag chn group vpslist group-dnl $TMP_ACL_PATH/vpslist group-upstream ${server_dns} group-ipset ${vps_set_names} EOF ln_run 0 "$(first_type chinadns-ng)" "chinadns-ng" "/dev/null" -C $config_file -v } run_ipset_dnsmasq() { local listen_port server_dns ipset nftset cache_size dns_forward_max config_file eval_set_val $@ cat <<-EOF > $config_file port=${listen_port} no-poll no-resolv strict-order cache-size=${cache_size:-0} dns-forward-max=${dns_forward_max:-1000} EOF for i in $(echo ${server_dns} | sed "s#,# #g"); do echo "server=${i}" >> $config_file done [ -n "${ipset}" ] && echo "ipset=${ipset}" >> $config_file [ -n "${nftset}" ] && echo "nftset=${nftset}" >> $config_file ln_run 0 "$(first_type dnsmasq)" "dnsmasq" "/dev/null" -C $config_file } acl_app() { local items=$(uci show ${CONFIG} | grep "=acl_rule" | cut -d '.' -sf 2 | cut -d '=' -sf 1) [ -n "$items" ] && { local index=0 local item local redir_port dns_port dnsmasq_port local ipt_tmp msg msg2 redir_port=11200 dns_port=11300 dnsmasq_port=${GLOBAL_DNSMASQ_PORT:-11400} for item in $items; do index=$(expr $index + 1) local enabled sid remarks sources interface tcp_no_redir_ports udp_no_redir_ports node direct_dns_query_strategy remote_dns_protocol remote_dns remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy local _ip _mac _iprange _ipset _ip_or_mac source_list config_file local sid=$(uci -q show "${CONFIG}.${item}" | grep "=acl_rule" | awk -F '=' '{print $1}' | awk -F '.' '{print $2}') [ "$(config_n_get $sid enabled)" = "1" ] || continue eval $(uci -q show "${CONFIG}.${item}" | cut -d'.' -sf 3-) if [ -n "${sources}" ]; then for s in $sources; do local s2 is_iprange=$(lua_api "iprange(\"${s}\")") if [ "${is_iprange}" = "true" ]; then s2="iprange:${s}" elif [ -n "$(echo ${s} | grep '^ipset:')" ]; then s2="ipset:${s}" else _ip_or_mac=$(lua_api "ip_or_mac(\"${s}\")") if [ "${_ip_or_mac}" = "ip" ]; then s2="ip:${s}" elif [ "${_ip_or_mac}" = "mac" ]; then s2="mac:${s}" fi fi [ -n "${s2}" ] && source_list="${source_list}\n${s2}" unset s2 done else source_list="any" fi local acl_path=${TMP_ACL_PATH}/$sid mkdir -p ${acl_path} [ -n "${source_list}" ] && echo -e "${source_list}" | sed '/^$/d' > ${acl_path}/source_list node=${node:-default} tcp_no_redir_ports=${tcp_no_redir_ports:-default} udp_no_redir_ports=${udp_no_redir_ports:-default} [ "$tcp_no_redir_ports" = "default" ] && tcp_no_redir_ports=$TCP_NO_REDIR_PORTS [ "$udp_no_redir_ports" = "default" ] && udp_no_redir_ports=$UDP_NO_REDIR_PORTS if has_1_65535 "$tcp_no_redir_ports" && has_1_65535 "$udp_no_redir_ports"; then unset node fi [ -n "$node" ] && { tcp_proxy_mode="global" udp_proxy_mode="global" direct_dns_query_strategy=${direct_dns_query_strategy:-UseIP} remote_dns_protocol=${remote_dns_protocol:-tcp} remote_dns=${remote_dns:-1.1.1.1} case "$remote_dns_protocol" in doh|\ http3) remote_dns=${remote_dns_doh:-https://1.1.1.1/dns-query} ;; esac remote_dns_detour=${remote_dns_detour:-remote} remote_fakedns=${remote_fakedns:-0} remote_dns_query_strategy=${remote_dns_query_strategy:-UseIPv4} local GLOBAL_node=$(get_cache_var "ACL_GLOBAL_node") [ -n "${GLOBAL_node}" ] && GLOBAL_redir_port=$(get_cache_var "ACL_GLOBAL_redir_port") if [ "$node" = "default" ]; then if [ -n "${GLOBAL_node}" ]; then set_cache_var "ACL_${sid}_node" "${GLOBAL_node}" set_cache_var "ACL_${sid}_redir_port" "${GLOBAL_redir_port}" set_cache_var "ACL_${sid}_dns_port" "${GLOBAL_DNSMASQ_PORT}" set_cache_var "ACL_${sid}_default" "1" else log 1 "$(i18n "Global nodes are not enabled, skip [%s]." "${remarks}")" fi else [ "$(config_get_type $node)" = "nodes" ] && { if [ -n "${GLOBAL_node}" ] && [ "$node" = "${GLOBAL_node}" ]; then set_cache_var "ACL_${sid}_node" "${GLOBAL_node}" set_cache_var "ACL_${sid}_redir_port" "${GLOBAL_redir_port}" set_cache_var "ACL_${sid}_dns_port" "${GLOBAL_DNSMASQ_PORT}" set_cache_var "ACL_${sid}_default" "1" else redir_port=$(get_new_port $(expr $redir_port + 1)) local type=$(echo $(config_n_get $node type) | tr 'A-Z' 'a-z') if [ -n "${type}" ]; then config_file=$TMP_ACL_PATH/${node}_TCP_UDP_DNS_${redir_port}.json dns_port=$(get_new_port $(expr $dns_port + 1)) local acl_socks_port=$(get_new_port $(expr $redir_port + $index)) local run_func [ -n "${XRAY_BIN}" ] && run_func="run_xray" [ -n "${SINGBOX_BIN}" ] && run_func="run_singbox" if [ "${type}" = "xray" ] && [ -n "${XRAY_BIN}" ]; then run_func="run_xray" elif [ "${type}" = "sing-box" ] && [ -n "${SINGBOX_BIN}" ]; then run_func="run_singbox" fi ${run_func} flag=acl_$sid node=$node redir_port=$redir_port tcp_proxy_way=${TCP_PROXY_WAY} \ socks_address=127.0.0.1 socks_port=$acl_socks_port \ dns_listen_port=${dns_port} \ direct_dns_query_strategy=${direct_dns_query_strategy} \ remote_dns_protocol=${remote_dns_protocol} remote_dns_tcp_server=${remote_dns} remote_dns_udp_server=${remote_dns} remote_dns_doh="${remote_dns}" \ remote_dns_client_ip=${remote_dns_client_ip} remote_dns_detour=${remote_dns_detour} remote_fakedns=${remote_fakedns} remote_dns_query_strategy=${remote_dns_query_strategy} \ config_file=${config_file} local status=$? if [ "$status" != 0 ]; then log_i18n 2 "[%s] process %s error, skip this transparent proxy!" "${remarks}" "${config_file}" cat ${_error_log_file} >> ${LOG_FILE} unset _error_log_file continue fi fi dnsmasq_port=$(get_new_port $(expr $dnsmasq_port + 1)) run_copy_dnsmasq flag="$sid" listen_port=$dnsmasq_port local_dns="${LOCAL_DNS:-${AUTO_DNS}}" tun_dns="127.0.0.1#${dns_port}" default_dns="${AUTO_DNS}" #dhcp.leases to hostsMore actions $APP_PATH/lease2hosts.sh > /dev/null 2>&1 & set_cache_var "ACL_${sid}_node" "$node" set_cache_var "ACL_${sid}_redir_port" "$redir_port" fi } fi } unset enabled sid remarks sources interface tcp_no_redir_ports udp_no_redir_ports node direct_dns_query_strategy remote_dns_protocol remote_dns remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy unset _ip _mac _iprange _ipset _ip_or_mac source_list config_file done unset redir_port dns_port dnsmasq_port } } start() { pgrep -f /tmp/etc/passwall2/bin > /dev/null 2>&1 && { #log_i18n 0 "The program has started. Please stop it and then restart it!" stop } mkdir -p /tmp/etc /tmp/log $TMP_PATH $TMP_BIN_PATH $TMP_SCRIPT_FUNC_PATH $TMP_ROUTE_PATH $TMP_ACL_PATH $TMP_PATH2 get_config export V2RAY_LOCATION_ASSET=$(config_t_get global_rules v2ray_location_asset "/usr/share/v2ray/") export XRAY_LOCATION_ASSET=$V2RAY_LOCATION_ASSET export ENABLE_DEPRECATED_GEOSITE=true export ENABLE_DEPRECATED_GEOIP=true export SS_SYSTEM_DNS_RESOLVER_FORCE_BUILTIN=1 ulimit -n 65535 start_haproxy start_socks nftflag=0 USE_TABLES="" check_run_environment if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then [ "$(uci -q get dhcp.@dnsmasq[0].dns_redirect)" == "1" ] && { uci -q set ${CONFIG}.@global[0].dnsmasq_dns_redirect='1' uci -q commit ${CONFIG} uci -q set dhcp.@dnsmasq[0].dns_redirect='0' uci -q commit dhcp json_init json_add_string "LOG" "0" lua $APP_PATH/helper_dnsmasq.lua restart "$(json_dump)" } fi mkdir -p ${GLOBAL_ACL_PATH} [ "$ENABLED_DEFAULT_ACL" == 1 ] && run_global run_front_dns run_global_dnsmasq [ -n "$USE_TABLES" ] && source $APP_PATH/${USE_TABLES}.sh start set_cache_var "USE_TABLES" "$USE_TABLES" if [ "$ENABLED_DEFAULT_ACL" == 1 ] || [ "$ENABLED_ACLS" == 1 ]; then bridge_nf_ipt=$(sysctl -e -n net.bridge.bridge-nf-call-iptables) set_cache_var "bak_bridge_nf_ipt" "$bridge_nf_ipt" sysctl -w net.bridge.bridge-nf-call-iptables=0 >/dev/null 2>&1 [ "$PROXY_IPV6" == "1" ] && { bridge_nf_ip6t=$(sysctl -e -n net.bridge.bridge-nf-call-ip6tables) set_cache_var "bak_bridge_nf_ip6t" "$bridge_nf_ip6t" sysctl -w net.bridge.bridge-nf-call-ip6tables=0 >/dev/null 2>&1 } fi run_process_queue start_crontab log_i18n 0 "Running complete!" echolog "\n" [ "$ENABLED" = 1 ] && [ "$1" = "boot" ] && { local cfgids item for item in $(uci show ${CONFIG} | grep "=subscribe_list" | cut -d '.' -sf 2 | cut -d '=' -sf 1); do if [ "$(config_n_get "$item" boot_update 0)" = "1" ]; then local cfgid=$(uci show ${CONFIG}.$item | head -n 1 | cut -d '.' -sf 2 | cut -d '=' -sf 1) cfgids="${cfgids:+$cfgids,}$cfgid" fi done [ -n "$cfgids" ] && { sleep 5 lua $APP_PATH/subscribe.lua start $cfgids cron > /dev/null 2>&1 & } } } stop() { clean_log eval_cache_var [ -n "$USE_TABLES" ] && source $APP_PATH/${USE_TABLES}.sh stop delete_ip2route # Kill the SS plugin process # kill_all xray-plugin v2ray-plugin obfs-local shadow-tls local pid_file pid find "$TMP_PATH" -type f -name '*_plugin.pid' 2>/dev/null | while read -r pid_file; do read -r pid < "$pid_file" if [ -n "$pid" ]; then kill -9 "$pid" >/dev/null 2>&1 fi done pgrep -f "sleep.*(6s|9s|58s)" | xargs kill -9 >/dev/null 2>&1 pgrep -af "${CONFIG}/" | awk '! /app\.sh|subscribe\.lua|rule_update\.lua|tasks\.sh|server_app\.lua|ujail/{print $1}' | xargs kill -9 >/dev/null 2>&1 unset V2RAY_LOCATION_ASSET unset XRAY_LOCATION_ASSET unset SS_SYSTEM_DNS_RESOLVER_FORCE_BUILTIN stop_crontab rm -rf $GLOBAL_DNSMASQ_CONF rm -rf $GLOBAL_DNSMASQ_CONF_PATH [ "1" = "1" ] && { #restore logic bak_dnsmasq_dns_redirect=$(config_t_get global dnsmasq_dns_redirect) [ -n "${bak_dnsmasq_dns_redirect}" ] && { uci -q set dhcp.@dnsmasq[0].dns_redirect="${bak_dnsmasq_dns_redirect}" uci -q commit dhcp uci -q delete ${CONFIG}.@global[0].dnsmasq_dns_redirect uci -q commit ${CONFIG} } if [ -z "${ACL_default_dns_port}" ] || [ -n "${bak_dnsmasq_dns_redirect}" ]; then uci -q del_list dhcp.@dnsmasq[0].addnmount="${GLOBAL_DNSMASQ_CONF_PATH}" uci -q commit dhcp json_init json_add_string "LOG" "0" lua $APP_PATH/helper_dnsmasq.lua restart "$(json_dump)" fi [ -n "${bak_bridge_nf_ipt}" ] && sysctl -w net.bridge.bridge-nf-call-iptables=${bak_bridge_nf_ipt} >/dev/null 2>&1 [ -n "${bak_bridge_nf_ip6t}" ] && sysctl -w net.bridge.bridge-nf-call-ip6tables=${bak_bridge_nf_ip6t} >/dev/null 2>&1 } rm -rf $TMP_PATH rm -rf /tmp/lock/${CONFIG}_socks_auto_switch* rm -rf /tmp/lock/${CONFIG}_lease2hosts* log_i18n 0 "Clearing and closing related programs and cache complete." exit 0 } get_direct_dns() { RESOLVFILE=/tmp/resolv.conf.d/resolv.conf.auto [ -f "${RESOLVFILE}" ] && [ -s "${RESOLVFILE}" ] || RESOLVFILE=/tmp/resolv.conf.auto ISP_DNS=$(cat $RESOLVFILE 2>/dev/null | grep -E -o "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | grep -v -E '^(0\.0\.0\.0|127\.0\.0\.1)$' | awk '!seen[$0]++') ISP_DNS6=$(cat $RESOLVFILE 2>/dev/null | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | awk -F % '{print $1}' | awk -F " " '{print $2}' | grep -v -Fx ::1 | grep -v -Fx :: | awk '!seen[$0]++') DNSMASQ_UPSTREAM_DNS=$(uci show dhcp.@dnsmasq[0] | grep "\.server=" | awk -F '=' '{print $2}' | sed "s/'//g" | tr ' ' '\n' | grep -v "\/" | awk '{if($1 ~ /#/) {sub(/#/, "#", $1); print $1} else {print $1"#53"}}' | head -2 | sed ':label;N;s/\n/,/;b label') DEFAULT_DNS="${DNSMASQ_UPSTREAM_DNS}" [ -z "${DEFAULT_DNS}" ] && DEFAULT_DNS=$(echo -n $ISP_DNS | tr ' ' '\n' | head -2 | tr '\n' ',' | sed 's/,$//') AUTO_DNS=${DEFAULT_DNS:-119.29.29.29} local AUTO_DNS_1=$(echo ${AUTO_DNS} | awk -F ',' '{print $1}') local AUTO_DNS_2=$(echo ${AUTO_DNS} | awk -F ',' '{print $2}') local AUTO_DNS_ADDRESS=$(echo ${AUTO_DNS_1} | awk -F '#' '{print $1}') local AUTO_DNS_PORT=$(echo ${AUTO_DNS_1} | awk -F '#' '{print $2}') DIRECT_DNS_UDP_SERVER=${AUTO_DNS_ADDRESS} DIRECT_DNS_UDP_PORT=${AUTO_DNS_PORT} } get_config() { ENABLED_DEFAULT_ACL=0 ENABLED=$(config_t_get global enabled 0) NODE=$(config_t_get global node) [ "$ENABLED" == 1 ] && { [ -n "$NODE" ] && [ "$(config_get_type $NODE)" == "nodes" ] && ENABLED_DEFAULT_ACL=1 } ENABLED_ACLS=$(config_t_get global acl_enable 0) [ "$ENABLED_ACLS" == 1 ] && { [ "$(uci show ${CONFIG} | grep "@acl_rule" | grep "enabled='1'" | wc -l)" == 0 ] && ENABLED_ACLS=0 } SOCKS_ENABLED=$(config_t_get global socks_enabled 0) REDIR_PORT=$(echo $(get_new_port 1041 tcp,udp)) TCP_PROXY_WAY=$(config_t_get global_forwarding tcp_proxy_way redirect) 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_REDIR_PORTS=$(config_t_get global_forwarding tcp_redir_ports '22,25,53,143,465,587,853,993,995,80,443') UDP_REDIR_PORTS=$(config_t_get global_forwarding udp_redir_ports '1:65535') PROXY_IPV6=$(config_t_get global_forwarding ipv6_tproxy 0) TCP_PROXY_MODE="global" UDP_PROXY_MODE="global" LOCALHOST_PROXY=$(config_t_get global localhost_proxy '1') CLIENT_PROXY=$(config_t_get global client_proxy '1') DIRECT_DNS_QUERY_STRATEGY=$(config_t_get global direct_dns_query_strategy UseIP) REMOTE_DNS_PROTOCOL=$(config_t_get global remote_dns_protocol tcp) REMOTE_DNS_DETOUR=$(config_t_get global remote_dns_detour remote) REMOTE_DNS=$(config_t_get global remote_dns 1.1.1.1:53 | sed 's/#/:/g' | sed -E 's/\:([^:]+)$/#\1/g') REMOTE_FAKEDNS=$(config_t_get global remote_fakedns '0') REMOTE_DNS_QUERY_STRATEGY=$(config_t_get global remote_dns_query_strategy UseIPv4) DNS_CACHE=$(config_t_get global dns_cache 1) DNS_REDIRECT=$(config_t_get global dns_redirect 1) get_direct_dns DEFAULT_DNSMASQ_CONF_DIR=/tmp/dnsmasq.d DNSMASQ_CONF_DIR=${DEFAULT_DNSMASQ_CONF_DIR} DEFAULT_DNSMASQ_CFGID="$(uci -q show "dhcp.@dnsmasq[0]" | awk 'NR==1 {split($0, conf, /[.=]/); print conf[2]}')" if [ -f "/tmp/etc/dnsmasq.conf.$DEFAULT_DNSMASQ_CFGID" ]; then DNSMASQ_CONF_DIR="$(awk -F '=' '/^conf-dir=/ {print $2}' "/tmp/etc/dnsmasq.conf.$DEFAULT_DNSMASQ_CFGID")" if [ -n "$DNSMASQ_CONF_DIR" ]; then DNSMASQ_CONF_DIR=${DNSMASQ_CONF_DIR%*/} else DNSMASQ_CONF_DIR=${DEFAULT_DNSMASQ_CONF_DIR} fi fi set_cache_var GLOBAL_DNSMASQ_CONF ${DNSMASQ_CONF_DIR}/dnsmasq-${CONFIG}.conf set_cache_var GLOBAL_DNSMASQ_CONF_PATH ${GLOBAL_ACL_PATH}/dnsmasq.d QUEUE_RUN=1 } arg1=$1 shift case $arg1 in run_socks) get_direct_dns QUEUE_RUN=0 run_socks $@ ;; socks_node_switch) get_direct_dns QUEUE_RUN=0 socks_node_switch $@ ;; start) start $@ ;; stop) stop ;; esac