diff --git a/README.md b/README.md index 4053573..45dc3c5 100644 --- a/README.md +++ b/README.md @@ -24,16 +24,18 @@ * * * ## 1.更新信息 +2026.03.14 v1.3.5 Performance: Optimize concurrent process execution to significantly accelerate script installation. 性能优化:优化并发进程执行,大幅提升脚本安装速度 + 2026.02.08 v1.3.4 Chore: upgrade SS encryption method to SS-2022 spec; 新装的 Shadowsocks 协议加密方式从 aes-128-gcm 改为 2022-blake3-aes-128-gcm -2026.01.20 v1.3.3 1. Security: In v2rayN, add pinnedPeerCertSha256 for Hysteria2/Trojan to prevent MITM (replaces AllowInsecure); 2. Compatibility: Refactor SFM/SFI/SFA configs for sing-box v1.13.0+; 1. 安全增强:v2rayN 的 Hysteria2/Trojan 支持 pinnedPeerCertSha256 替代 跳过证书验证,防御 MITM 攻击; 2. 适配更新:重构 SFM/SFI/SFA 配置,支持 sing-box v1.13.0+ - -2025.12.11 v1.3.2 Argo tunnel creation via API. Suitable for users with large-scale deployments, one Token for all. Automatically completed: Create tunnel > DNS configuration > Origin settings. Thanks to [zmlu] for providing the method: https://raw.githubusercontent.com/zmlu/sba/main/tunnel.sh; Argo 隧道新增通过 API 创建,适合大量部署的用户,一个 Token 走天下。自动完成:创建隧道 > DNS 配置 > 回源设置。感谢热心网友 [zmlu] 提供的方法: https://raw.githubusercontent.com/zmlu/sba/main/tunnel.sh -
历史更新 history(点击即可展开或收起)
+>2026.01.20 v1.3.3 1. Security: In v2rayN, add pinnedPeerCertSha256 for Hysteria2/Trojan to prevent MITM (replaces AllowInsecure); 2. Compatibility: Refactor SFM/SFI/SFA configs for sing-box v1.13.0+; 1. 安全增强:v2rayN 的 Hysteria2/Trojan 支持 pinnedPeerCertSha256 替代 跳过证书验证,防御 MITM 攻击; 2. 适配更新:重构 SFM/SFI/SFA 配置,支持 sing-box v1.13.0+ +> +>2025.12.11 v1.3.2 Argo tunnel creation via API. Suitable for users with large-scale deployments, one Token for all. Automatically completed: Create tunnel > DNS configuration > Origin settings. Thanks to [zmlu] for providing the method: https://raw.githubusercontent.com/zmlu/sba/main/tunnel.sh; Argo 隧道新增通过 API 创建,适合大量部署的用户,一个 Token 走天下。自动完成:创建隧道 > DNS 配置 > 回源设置。感谢热心网友 [zmlu] 提供的方法: https://raw.githubusercontent.com/zmlu/sba/main/tunnel.sh +> >2025.11.12 v1.3.1 1. Reality Configuration Update: In Reality configurations, the original multiplexing (multiplex) has been replaced with xtls-rprx-vision flow control, improving transmission efficiency, reducing latency, and enhancing security. The original configuration conversion script command remains fully compatible and unchanged — `bash <(curl -sSL https://raw.githubusercontent.com/fscarmen/tools/main/vision.sh)`; 2. Quick Install Mode: Added a one-click installation feature that auto-fills all parameters, simplifying the deployment process. Chinese users can use -l or -L; English users can use -k or -K. Case-insensitive support makes operations more flexible; 3. Custom Reality Key Support: In response to user feedback, you can now specify a custom Reality private key via --REALITY_PRIVATE=. The script will automatically compute the corresponding public key using the integrated API. If left blank, it generates a random private-public key pair in real-time; 4. Enhanced HTTP + Reality Support in Clash Clients: Added full compatibility for HTTP + Reality transport in Clash clients, improving connection stability and performance; 1. Reality 配置变更:在 Reality 配置中,将原来的多路复用(multiplex)替换为 xtls-rprx-vision 流控,提升传输效率、降低延迟并增强安全性。原配置转换脚本命令—— `bash <(curl -sSL https://raw.githubusercontent.com/fscarmen/tools/main/vision.sh)` ; 2. 极速安装模式:新增一键安装功能,所有参数自动填充,简化部署流程。中文用户使用 -l 或 -L,英文用户使用 -k 或 -K,大小写均支持,操作更灵; 3. 自定义 Reality 密钥支持:响应用户反馈,现支持通过 --REALITY_PRIVATE= 指定自定义 Reality 私钥,脚本将调用相关 API 自动计算对应公钥。若留空,则实时生成随机公私钥; 4. HTTP + Reality 在 Clash 客户端的增强支持:补充了对 Clash 客户端中 HTTP + Reality 传输方式的完整兼容,提升了连接稳定性和性能 > >2025.11.10 v1.3.0 Replace multiplex with xtls-rprx-vision flow control in reality configuration. The original configuration conversion script: bash <(curl -sSL https://raw.githubusercontent.com/fscarmen/tools/main/vision.sh); 在 reality 配置中将多路复用 multiplex 替换为 xtls-rprx-vision 流控。原来的配置转换脚本: bash <(curl -sSL https://raw.githubusercontent.com/fscarmen/tools/main/vision.sh) @@ -695,6 +697,7 @@ SharonNetworks 为您的业务起飞保驾护航! * 坚如磐石的数据中心。高枕无忧 + ## 15.免责声明 * 本程序仅供学习了解, 非盈利目的,请于下载后 24 小时内删除, 不得用作任何商业用途, 文字、数据及图片均有所属版权, 如转载须注明来源。 * 使用本程序必循遵守部署免责声明。使用本程序必循遵守部署服务器所在地、所在国家和用户所在国家的法律法规, 程序作者不对使用者任何不当行为负责。 @@ -703,4 +706,4 @@ SharonNetworks 为您的业务起飞保驾护航! ## 16.开源证书 * 本项目严格遵守 GNU GPL v3 许可证 [LICENSE](LICENSE)。 * 任何形式的复制、分发、修改或衍生使用,必须完整保留原版权声明、许可证文本,并以相同许可证开源发布。违反此条款(如闭源使用、商业独占或未开源修改版)将被视为抄袭,作者保留追究法律责任的权利。 -* 鼓励社区贡献,但请通过 Pull Request 提交。 +* 鼓励社区贡献,但请通过 Pull Request 提交。 \ No newline at end of file diff --git a/sing-box.sh b/sing-box.sh index 64b4c95..387e162 100644 --- a/sing-box.sh +++ b/sing-box.sh @@ -1,10 +1,10 @@ #!/usr/bin/env bash # 当前脚本版本号 -VERSION='v1.3.4 (2026.02.08)' +VERSION='v1.3.5 (2026.03.14)' -# Github 反代加速代理,第一个为空相当于直连 -GITHUB_PROXY=('' 'https://v6.gh-proxy.org/' 'https://gh-proxy.com/' 'https://hub.glowp.xyz/' 'https://proxy.vvvv.ee/' 'https://ghproxy.lvedong.eu.org/') +# Github 反代加速代理 +GITHUB_PROXY=('https://v6.gh-proxy.org/' 'https://gh-proxy.com/' 'https://hub.glowp.xyz/' 'https://proxy.vvvv.ee/' 'https://ghproxy.lvedong.eu.org/') # 各变量默认值 TEMP_DIR='/tmp/sing-box' @@ -24,14 +24,14 @@ DEFAULT_NEWEST_VERSION='1.13.0-rc.4' export DEBIAN_FRONTEND=noninteractive -trap "rm -rf $TEMP_DIR >/dev/null 2>&1 ; echo -e '\n' ;exit" INT QUIT TERM EXIT +trap "wait 2>/dev/null; rm -rf $TEMP_DIR >/dev/null 2>&1; echo -e '\n'; exit" INT QUIT TERM EXIT mkdir -p $TEMP_DIR E[0]="Language:\n 1. English (default) \n 2. 简体中文" C[0]="${E[0]}" -E[1]="1. Chore: upgrade SS encryption method to SS-2022 spec 2. Security: Add pinnedPeerCertSha256 for Hysteria2/Trojan in v2rayN to prevent MITM (replaces AllowInsecure); 3. Compatibility: Refactor SFM/SFI/SFA configs for sing-box v1.13.0+." -C[1]="1. 新装的 Shadowsocks 协议加密方式从 aes-128-gcm 改为 2022-blake3-aes-128-gcm; 2. 安全增强:v2rayN 的 Hysteria2/Trojan 支持 pinnedPeerCertSha256 替代 跳过证书验证,防御 MITM 攻击; 3. 适配更新:重构 SFM/SFI/SFA 配置,支持 sing-box v1.13.0+" +E[1]="1. Chore: upgrade SS encryption method to SS-2022 spec; 2. Security: Add pinnedPeerCertSha256 for Hysteria2/Trojan in v2rayN to prevent MITM (replaces AllowInsecure); 3. Compatibility: Refactor SFM/SFI/SFA configs for sing-box v1.13.0+; 4. Performance: Optimize concurrent process execution to significantly accelerate script installation." +C[1]="1. 新装的 Shadowsocks 协议加密方式从 aes-128-gcm 改为 2022-blake3-aes-128-gcm; 2. 安全增强:v2rayN 的 Hysteria2/Trojan 支持 pinnedPeerCertSha256 替代 跳过证书验证,防御 MITM 攻击; 3. 适配更新:重构 SFM/SFI/SFA 配置,支持 sing-box v1.13.0+; 4. 性能优化:优化并发进程执行,大幅提升脚本安装速度。" E[2]="Downloading Sing-box. Please wait a seconds ..." C[2]="下载 Sing-box 中,请稍等 ..." E[3]="Input errors up to 5 times.The script is aborted." @@ -293,11 +293,25 @@ text() { grep -q '\$' <<< "${E[$*]}" && eval echo "\$(eval echo "\${${L}[$*]}")" # 检测是否需要启用 Github CDN,如能直接连通,则不使用 check_cdn() { - # GITHUB_PROXY 数组第一个元素为空,相当于直连 - for PROXY_URL in "${GITHUB_PROXY[@]}"; do - local PROXY_STATUS_CODE=$(wget --server-response --spider --quiet --timeout=3 --tries=1 ${PROXY_URL}https://api.github.com/repos/SagerNet/sing-box/releases 2>&1 | awk '/HTTP\//{last_field = $2} END {print last_field}') - [ "$PROXY_STATUS_CODE" = "200" ] && GH_PROXY="$PROXY_URL" && break + local PROXY + + for PROXY in "" "${GITHUB_PROXY[@]}"; do + { + local CODE=$(wget -qT3 --spider --server-response "${PROXY}https://raw.githubusercontent.com/" 2>&1 | awk '/HTTP\//{code=$2} END{print code}') + [ "$CODE" = "200" ] && [ ! -s "${TEMP_DIR}/cdn_proxy" ] && echo "$PROXY" > "${TEMP_DIR}/cdn_proxy" + } & done + + # 等第一个成功 + while [ ! -s "${TEMP_DIR}/cdn_proxy" ]; do + sleep 0.05 + done + + GH_PROXY=$(cat "${TEMP_DIR}/cdn_proxy") + + # 清理后台任务和临时文件 + rm -rf "${TEMP_DIR}/cdn_proxy" + wait 2>/dev/null } # 检测是否解锁 chatGPT,以决定是否使用 warp 链式代理或者是 direct out,此处判断改编自 https://github.com/lmc999/RegionRestrictionCheck @@ -332,7 +346,7 @@ check_chatgpt() { } # 脚本当天及累计运行次数统计 -statistics_of_run-times() { +statistics_of_run_times() { local UPDATE_OR_GET=$1 local SCRIPT=$2 if grep -q 'update' <<< "$UPDATE_OR_GET"; then @@ -358,7 +372,7 @@ select_language() { # 字母与数字的 ASCII 码值转换 asc() { if [[ "$1" = [a-z] ]]; then - [ "$2" = '++' ] && printf "\\$(printf '%03o' "$[ $(printf "%d" "'$1'") + 1 ]")" || printf "%d" "'$1'" + [ "$2" = '++' ] && printf "\\$(printf '%03o' "$(( $(printf "%d" "'$1'") + 1 ))")" || printf "%d" "'$1'" else [[ "$1" =~ ^[0-9]+$ ]] && printf "\\$(printf '%03o' "$1")" fi @@ -368,7 +382,7 @@ asc() { input_cdn() { echo "" for c in "${!CDN_DOMAIN[@]}"; do - hint " $[c+1]. ${CDN_DOMAIN[c]} " + hint " $(( c+1 )). ${CDN_DOMAIN[c]} " done reading "\n $(text 53) " CUSTOM_CDN @@ -395,7 +409,7 @@ change_cdn() { # 提示当前使用的 CDN 并让用户选择或输入新的 CDN hint "\n $(text 109) \n" for ((c=0; c<${#CDN_DOMAIN[@]}; c++)); do - hint " $[c+1]. ${CDN_DOMAIN[c]} " + hint " $(( c+1 )). ${CDN_DOMAIN[c]} " done reading "\n $(text 111) " CDN_CHOOSE @@ -647,9 +661,9 @@ input_hopping_port() { PORT_HOPPING_RANGE=${PORT_HOPPING_RANGE//-/:} PORT_HOPPING_START=${PORT_HOPPING_RANGE%:*} PORT_HOPPING_END=${PORT_HOPPING_RANGE#*:} - [[ "$PORT_HOPPING_START" < "$PORT_HOPPING_END" && "$PORT_HOPPING_START" -ge "$MIN_HOPPING_PORT" && "$PORT_HOPPING_END" -le "$MAX_HOPPING_PORT" ]] && IS_HOPPING=is_hopping || warning "\n $(text 36) " + [[ "$PORT_HOPPING_START" -lt "$PORT_HOPPING_END" && "$PORT_HOPPING_START" -ge "$MIN_HOPPING_PORT" && "$PORT_HOPPING_END" -le "$MAX_HOPPING_PORT" ]] && IS_HOPPING=is_hopping || warning "\n $(text 36) " elif [[ -z "$PORT_HOPPING_RANGE" || "${PORT_HOPPING_RANGE,,}" =~ ^(n|no)$ ]]; then - IS_HOPPING=no_hoppinng + IS_HOPPING=no_hopping else warning "\n $(text 36) " fi @@ -691,7 +705,7 @@ input_argo_auth() { ARGO_DOMAIN=$(sed 's/[ ]*//g; s/:[ ]*//' <<< "$ARGO_DOMAIN") fi - if [[ -z "$ARGO_DOMAIN" && ( "$ARGO_DOMAIN" =~ trycloudflare\.com$ || "$IS_CHANGE_ARGO" = 'is_add_protocols' || "$IS_CHANGE_ARGO" = 'is_install' || "$NONINTERACTIVE_INSTALL" = 'noninteractive_install' ) ]]; then + if [[ ( -z "$ARGO_DOMAIN" || "$ARGO_DOMAIN" =~ trycloudflare\.com$ ) && ( "$IS_CHANGE_ARGO" = 'is_add_protocols' || "$IS_CHANGE_ARGO" = 'is_install' || "$NONINTERACTIVE_INSTALL" = 'noninteractive_install' ) ]]; then ARGO_RUNS="${WORK_DIR}/cloudflared tunnel --edge-ip-version auto --no-autoupdate --url http://localhost:$PORT_NGINX" elif [ -n "${ARGO_DOMAIN}" ]; then if [ -z "${ARGO_AUTH}" ]; then @@ -859,7 +873,7 @@ check_install() { if [ -s ${SINGBOX_DAEMON_FILE} ]; then SYSTEMD_EXECSTART=$(grep '^ExecStart=' ${SINGBOX_DAEMON_FILE}) case "$SYSTEMD_EXECSTART" in - 'ExecStart=/etc/sing-box/sing-box run -C /etc/sing-box/conf/' | 'ExecStart=/etc/sing-box/sing-box run -C /etc/sing-box/conf' ) + "ExecStart=${WORK_DIR}/sing-box run -C ${WORK_DIR}/conf/" | "ExecStart=${WORK_DIR}/sing-box run -C ${WORK_DIR}/conf" ) [ "$(systemctl is-active sing-box)" = 'active' ] && STATUS[0]=$(text 28) || STATUS[0]=$(text 27) ;; 'ExecStart=/etc/v2ray-agent/sing-box/sing-box run -c /etc/v2ray-agent/sing-box/conf/config.json' ) @@ -876,7 +890,7 @@ check_install() { ;; * ) # 检查是否是自己的脚本安装的,但路径略有不同 - if [[ "$SYSTEMD_EXECSTART" =~ "ExecStart=/etc/sing-box/sing-box run" ]]; then + if [[ "$SYSTEMD_EXECSTART" =~ "ExecStart=${WORK_DIR}/sing-box run" ]]; then [ "$(systemctl is-active sing-box)" = 'active' ] && STATUS[0]=$(text 28) || STATUS[0]=$(text 27) else SING_BOX_SCRIPT='Unknown or customized sing-box' && error "\n $(text 99) \n" @@ -890,7 +904,7 @@ check_install() { ;; * ) # 检查是否是自己的脚本安装的,但路径略有不同 - if [[ "$SYSTEMD_EXECSTART" =~ "ExecStart=/etc/sing-box/sing-box run" ]]; then + if [[ "$SYSTEMD_EXECSTART" =~ "ExecStart=${WORK_DIR}/sing-box run" ]]; then [ "$(systemctl is-active sing-box)" = 'active' ] && STATUS[0]=$(text 28) || STATUS[0]=$(text 27) else SING_BOX_SCRIPT='Unknown or customized sing-box' && error "\n $(text 99) \n" @@ -901,16 +915,41 @@ check_install() { fi fi + # 并发下载订阅模板 (clash, clash2, sing-box),在新安装和更换协议时会用到 + { + wget --no-check-certificate --continue -qO $TEMP_DIR/clash ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/clash 2>/dev/null & + wget --no-check-certificate --continue -qO $TEMP_DIR/clash2 ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/clash2 2>/dev/null & + wget --no-check-certificate --continue -qO $TEMP_DIR/sing-box ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/sing-box 2>/dev/null & + wait + } & + # 如果有需要,后台静默下载 sing-box if [ "${STATUS[0]}" = "$(text 26)" ] && [ ! -s ${WORK_DIR}/sing-box ]; then + # 任务1: 下载 sing-box { - # 获取需要下载的 sing-box 版本 local ONLINE=$(get_sing_box_version) - wget --no-check-certificate --continue ${GH_PROXY}https://github.com/SagerNet/sing-box/releases/download/v$ONLINE/sing-box-$ONLINE-linux-$SING_BOX_ARCH.tar.gz -qO- | tar xz -C $TEMP_DIR sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box >/dev/null 2>&1 - [ -s $TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box ] && mv $TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box $TEMP_DIR - wget --no-check-certificate --continue -qO $TEMP_DIR/jq ${GH_PROXY}https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$JQ_ARCH >/dev/null 2>&1 && chmod +x $TEMP_DIR/jq >/dev/null 2>&1 - wget --no-check-certificate --continue -qO $TEMP_DIR/qrencode ${GH_PROXY}https://github.com/fscarmen/client_template/raw/main/qrencode-go/qrencode-go-linux-$QRENCODE_ARCH >/dev/null 2>&1 && chmod +x $TEMP_DIR/qrencode >/dev/null 2>&1 - }& + local SB_DIR="$TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH" + local SB_BIN="$SB_DIR/sing-box" + wget --no-check-certificate --continue \ + ${GH_PROXY}https://github.com/SagerNet/sing-box/releases/download/v$ONLINE/sing-box-$ONLINE-linux-$SING_BOX_ARCH.tar.gz \ + -qO- | tar xz -C $TEMP_DIR 2>/dev/null + [ -s "$SB_BIN" ] && [ -x "$SB_BIN" ] && mv "$SB_BIN" "$TEMP_DIR/sing-box" + } & + + # 任务2: 下载 jq + { + wget --no-check-certificate --continue -qO $TEMP_DIR/jq \ + ${GH_PROXY}https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$JQ_ARCH 2>/dev/null \ + && chmod +x $TEMP_DIR/jq + } & + + # 任务3: 下载 qrencode + { + wget --no-check-certificate --continue -qO $TEMP_DIR/qrencode \ + ${GH_PROXY}https://github.com/fscarmen/client_template/raw/main/qrencode-go/qrencode-go-linux-$QRENCODE_ARCH 2>/dev/null \ + && chmod +x $TEMP_DIR/qrencode + } & + elif [ "${STATUS[0]}" != "$(text 26)" ]; then # 查 sing-box 进程号,运行时长和内存占用,占用的端口 SING_BOX_VERSION="Version: $(${WORK_DIR}/sing-box version | awk '/version/{print $NF}')" @@ -1208,17 +1247,34 @@ check_system_ip() { [ -n "$DEFAULT_LOCAL_IP6" ] && local BIND_ADDRESS6="--bind-address=$DEFAULT_LOCAL_IP6" fi - local IP4_JSON=$(wget $BIND_ADDRESS4 -4 -qO- --no-check-certificate --tries=2 --timeout=2 https://ip.cloudflare.now.cc${IS_CHINESE}) && + # 并行检测 IPv4 和 IPv6 信息 + { + local CHECK_IP4=$(wget $BIND_ADDRESS4 -4 -qO- --no-check-certificate --tries=2 --timeout=2 https://ip.cloudflare.now.cc${IS_CHINESE}) + grep -q '.' <<< "$CHECK_IP4" && echo "$CHECK_IP4" > $TEMP_DIR/ip4.json + }& + + { + local CHECK_IP6=$(wget $BIND_ADDRESS6 -6 -qO- --no-check-certificate --tries=2 --timeout=2 https://ip.cloudflare.now.cc${IS_CHINESE}) + grep -q '.' <<< "$CHECK_IP6" && echo "$CHECK_IP6" > $TEMP_DIR/ip6.json + }& + + wait + + [ -s $TEMP_DIR/ip4.json ] && + local IP4_JSON=$(cat $TEMP_DIR/ip4.json) && WAN4=$(awk -F '"' '/"ip"/{print $4}' <<< "$IP4_JSON") && COUNTRY4=$(awk -F '"' '/"country"/{print $4}' <<< "$IP4_JSON") && EMOJI4=$(awk -F '"' '/"emoji"/{print $4}' <<< "$IP4_JSON") && - ASNORG4=$(awk -F '"' '/"isp"/{print $4}' <<< "$IP4_JSON") + ASNORG4=$(awk -F '"' '/"isp"/{print $4}' <<< "$IP4_JSON") && + rm -f $TEMP_DIR/ip4.json - local IP6_JSON=$(wget $BIND_ADDRESS6 -6 -qO- --no-check-certificate --tries=2 --timeout=2 https://ip.cloudflare.now.cc${IS_CHINESE}) && + [ -s $TEMP_DIR/ip6.json ] && + local IP6_JSON=$(cat $TEMP_DIR/ip6.json) && WAN6=$(awk -F '"' '/"ip"/{print $4}' <<< "$IP6_JSON") && COUNTRY6=$(awk -F '"' '/"country"/{print $4}' <<< "$IP6_JSON") && EMOJI6=$(awk -F '"' '/"emoji"/{print $4}' <<< "$IP6_JSON") && - ASNORG6=$(awk -F '"' '/"isp"/{print $4}' <<< "$IP6_JSON") + ASNORG6=$(awk -F '"' '/"isp"/{print $4}' <<< "$IP6_JSON") && + rm -f $TEMP_DIR/ip6.json } # 输入起始 port 函数 @@ -1265,11 +1321,11 @@ sing-box_variables() { fi # 选择安装的协议,由于选项 a 为全部协议,所以选项数不是从 a 开始,而是从 b 开始,处理输入:把大写全部变为小写,把不符合的选项去掉,把重复的选项合并 - MAX_CHOOSE_PROTOCOLS=$(asc $[CONSECUTIVE_PORTS+96+1]) + MAX_CHOOSE_PROTOCOLS=$(asc $(( CONSECUTIVE_PORTS+96+1 ))) if [ -z "$CHOOSE_PROTOCOLS" ]; then hint "\n (1/6) $(text 49) " for e in "${!PROTOCOL_LIST[@]}"; do - [[ "$e" =~ '6'|'7' ]] && hint " $(asc $[e+98]). ${PROTOCOL_LIST[e]} " || hint " $(asc $[e+98]). ${PROTOCOL_LIST[e]} " + hint " $(asc $(( e+98 ))). ${PROTOCOL_LIST[e]} " done reading "\n $(text 24) " CHOOSE_PROTOCOLS fi @@ -1281,7 +1337,7 @@ sing-box_variables() { if [ -z "$START_PORT" ]; then hint "\n $(text 60) " for w in "${!INSTALL_PROTOCOLS[@]}"; do - [ "$w" -ge 9 ] && hint " $[w+1]. ${PROTOCOL_LIST[$(($(asc ${INSTALL_PROTOCOLS[w]}) - 98))]} " || hint " $[w+1] . ${PROTOCOL_LIST[$(($(asc ${INSTALL_PROTOCOLS[w]}) - 98))]} " + [ "$w" -ge 9 ] && hint " $(( w+1 )). ${PROTOCOL_LIST[$(($(asc ${INSTALL_PROTOCOLS[w]}) - 98))]} " || hint " $(( w+1 )) . ${PROTOCOL_LIST[$(($(asc ${INSTALL_PROTOCOLS[w]}) - 98))]} " done input_start_port ${#INSTALL_PROTOCOLS[@]} fi @@ -1881,7 +1937,7 @@ EOF # 第1个协议为 b (a为全部),生成 XTLS + Reality 配置 CHECK_PROTOCOLS=b if [[ "${INSTALL_PROTOCOLS[@]}" =~ "$CHECK_PROTOCOLS" ]]; then - [ -z "$PORT_XTLS_REALITY" ] && PORT_XTLS_REALITY=$[START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}")] + [ -z "$PORT_XTLS_REALITY" ] && PORT_XTLS_REALITY=$(( START_PORT+$(awk -v target=$CHECK_PROTOCOLS '{ for(i=1; i<=NF; i++) if($i == target) { print i-1; break } }' <<< "${INSTALL_PROTOCOLS[*]}") )) NODE_NAME[11]=${NODE_NAME[11]:-"$NODE_NAME_CONFIRM"} && UUID[11]=${UUID[11]:-"$UUID_CONFIRM"} && TLS_SERVER[11]=${TLS_SERVER[11]:-"$TLS_SERVER"} && REALITY_PRIVATE[11]=${REALITY_PRIVATE[11]:-"$REALITY_PRIVATE"} && REALITY_PUBLIC[11]=${REALITY_PUBLIC[11]:-"$REALITY_PUBLIC"} && cat > ${WORK_DIR}/conf/11_${NODE_TAG[0]}_inbounds.json << EOF // "public_key":"${REALITY_PUBLIC[11]}" @@ -2567,8 +2623,13 @@ fetch_quicktunnel_domain() { local CLOUDFLARED_PID=$(ps -eo pid,args | awk -v work_dir="$WORK_DIR" '$0~(work_dir"/cloudflared"){print $1;exit}') [[ -z "$METRICS_ADDRESS" && "$CLOUDFLARED_PID" =~ ^[0-9]+$ ]] && local METRICS_ADDRESS=$(ss -nltp | grep "pid=$CLOUDFLARED_PID" | awk '{print $4}') [ -n "$METRICS_ADDRESS" ] && ARGO_DOMAIN=$(wget -qO- http://$METRICS_ADDRESS/quicktunnel | awk -F '"' '{print $4}') - [[ ! "$ARGO_DOMAIN" =~ trycloudflare\.com$ ]] && (( QUICKTUNNEL_ERROR_TIME-- )) && sleep 2 || break - [ "$QUICKTUNNEL_ERROR_TIME" = '0' ] && error " $(text 93) " + if [[ ! "$ARGO_DOMAIN" =~ trycloudflare\.com$ ]]; then + (( QUICKTUNNEL_ERROR_TIME-- )) || true + [ "$QUICKTUNNEL_ERROR_TIME" = '0' ] && error " $(text 93) " + sleep 2 + else + break + fi done # 把临时隧道写到 Sing-box 相应的 ws inbounds 文件 @@ -2771,18 +2832,20 @@ export_list() { # 后台生成 clash 订阅配置文件 { # 模板1: 使用 proxy providers - wget --no-check-certificate -qO- --tries=3 --timeout=2 ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/clash | sed "s#NODE_NAME#${NODE_NAME_CONFIRM}#g; s#PROXY_PROVIDERS_URL#$SUBSCRIBE_ADDRESS/${UUID_CONFIRM}/proxies#" > ${WORK_DIR}/subscribe/clash + cat ${TEMP_DIR}/clash | sed "s#NODE_NAME#${NODE_NAME_CONFIRM}#g; s#PROXY_PROVIDERS_URL#$SUBSCRIBE_ADDRESS/${UUID_CONFIRM}/proxies#" > ${WORK_DIR}/subscribe/clash # 模板2: 不使用 proxy providers CLASH2_PORT=("$PORT_XTLS_REALITY" "$PORT_HYSTERIA2" "$PORT_TUIC" "$PORT_SHADOWTLS" "$PORT_SHADOWSOCKS" "$PORT_TROJAN" "$PORT_VMESS_WS" "$PORT_VLESS_WS" "$PORT_GRPC_REALITY" "$PORT_ANYTLS") CLASH2_PROXY_INSERT=("$CLASH_XTLS_REALITY" "$CLASH_HYSTERIA2" "$CLASH_TUIC" "$CLASH_SHADOWTLS" "$CLASH_SHADOWSOCKS" "$CLASH_TROJAN" "$CLASH_VMESS_WS" "$CLASH_VLESS_WS" "$CLASH_GRPC_REALITY" "$CLASH_ANYTLS") CLASH2_PROXY_GROUPS_INSERT=("- ${NODE_NAME[11]} ${NODE_TAG[0]}" "- ${NODE_NAME[12]} ${NODE_TAG[1]}" "- ${NODE_NAME[13]} ${NODE_TAG[2]}" "- ${NODE_NAME[14]} ${NODE_TAG[3]}" "- ${NODE_NAME[15]} ${NODE_TAG[4]}" "- ${NODE_NAME[16]} ${NODE_TAG[5]}" "- ${NODE_NAME[17]} ${NODE_TAG[6]}" "- ${NODE_NAME[18]} ${NODE_TAG[7]}" "- ${NODE_NAME[20]} ${NODE_TAG[9]}" "- ${NODE_NAME[21]} ${NODE_TAG[10]}") - CLASH2_YAML=$(wget --no-check-certificate -qO- --tries=3 --timeout=2 ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/clash2) + CLASH2_YAML=$(cat ${TEMP_DIR}/clash2) for x in ${!CLASH2_PORT[@]}; do [[ ${CLASH2_PORT[x]} =~ [0-9]+ ]] && { CLASH2_YAML=$(sed "/proxy-groups:/i\ ${CLASH2_PROXY_INSERT[x]}" <<< "$CLASH2_YAML"); CLASH2_YAML=$(sed -E "/- name: (♻️ 自动选择|📲 电报消息|💬 OpenAi|📹 油管视频|🎥 奈飞视频|📺 巴哈姆特|📺 哔哩哔哩|🌍 国外媒体|🌏 国内媒体|📢 谷歌FCM|Ⓜ️ 微软Bing|Ⓜ️ 微软云盘|Ⓜ️ 微软服务|🍎 苹果服务|🎮 游戏平台|🎶 网易音乐|🎯 全球直连)|^rules:$/i\ ${CLASH2_PROXY_GROUPS_INSERT[x]}" <<< "$CLASH2_YAML"); } done echo "$CLASH2_YAML" > ${WORK_DIR}/subscribe/clash2 + + rm -f ${TEMP_DIR}/clash{,2} } &>/dev/null # 生成 ShadowRocket 订阅配置文件 @@ -2933,7 +2996,7 @@ trojan://$TROJAN_PASSWORD@${SERVER_IP_1}:$PORT_TROJAN?security=tls&insecure=1&al if [[ "${STATUS[1]}" =~ $(text 27)|$(text 28) ]] || [[ "$IS_ARGO" = 'is_argo' && "$NONINTERACTIVE_INSTALL" = 'noninteractive_install' ]]; then local V2RAYN_SUBSCRIBE+=" ---------------------------- -vmess://$(echo -n "{ \"v\": \"2\", \"ps\": \"${NODE_NAME[17]} ${NODE_TAG[6]}\", \"add\": \"${CDN[18]}\", \"port\": \"80\", \"id\": \"${UUID[18]}\", \"aid\": \"0\", \"scy\": \"none\", \"net\": \"ws\", \"type\": \"auto\", \"host\": \"$ARGO_DOMAIN\", \"path\": \"/$VMESS_WS_PATH\", \"tls\": \"\", \"sni\": \"\", \"alpn\": \"\" }" | base64 -w0)" +vmess://$(echo -n "{ \"v\": \"2\", \"ps\": \"${NODE_NAME[17]} ${NODE_TAG[6]}\", \"add\": \"${CDN[17]}\", \"port\": \"80\", \"id\": \"${UUID[17]}\", \"aid\": \"0\", \"scy\": \"none\", \"net\": \"ws\", \"type\": \"auto\", \"host\": \"$ARGO_DOMAIN\", \"path\": \"/$VMESS_WS_PATH\", \"tls\": \"\", \"sni\": \"\", \"alpn\": \"\" }" | base64 -w0)" [ "$ARGO_TYPE" = 'is_token_argo' ] && V2RAYN_SUBSCRIBE+=" # $(text 94) @@ -2941,7 +3004,7 @@ vmess://$(echo -n "{ \"v\": \"2\", \"ps\": \"${NODE_NAME[17]} ${NODE_TAG[6]}\", else WS_SERVER_IP_SHOW=${WS_SERVER_IP[17]} && TYPE_HOST_DOMAIN=$VMESS_HOST_DOMAIN && TYPE_PORT_WS=$PORT_VMESS_WS && local V2RAYN_SUBSCRIBE+=" ---------------------------- -vmess://$(echo -n "{ \"v\": \"2\", \"ps\": \"${NODE_NAME[17]} ${NODE_TAG[6]}\", \"add\": \"${CDN[18]}\", \"port\": \"80\", \"id\": \"${UUID[18]}\", \"aid\": \"0\", \"scy\": \"none\", \"net\": \"ws\", \"type\": \"auto\", \"host\": \"$VMESS_HOST_DOMAIN\", \"path\": \"/$VMESS_WS_PATH\", \"tls\": \"\", \"sni\": \"\", \"alpn\": \"\" }" | base64 -w0) +vmess://$(echo -n "{ \"v\": \"2\", \"ps\": \"${NODE_NAME[17]} ${NODE_TAG[6]}\", \"add\": \"${CDN[17]}\", \"port\": \"80\", \"id\": \"${UUID[17]}\", \"aid\": \"0\", \"scy\": \"none\", \"net\": \"ws\", \"type\": \"auto\", \"host\": \"$VMESS_HOST_DOMAIN\", \"path\": \"/$VMESS_WS_PATH\", \"tls\": \"\", \"sni\": \"\", \"alpn\": \"\" }" | base64 -w0) # $(text 52)" fi @@ -3135,9 +3198,9 @@ anytls://${UUID[21]}@${SERVER_IP_1}:${PORT_ANYTLS}?security=tls&sni=${TLS_SERVER local NODE_REPLACE+="\"${NODE_NAME[21]} ${NODE_TAG[10]}\"," { - # sing-box SFM SFA 模板 - local SING_BOX_JSON=$(wget --no-check-certificate -qO- --tries=3 --timeout=2 ${GH_PROXY}${SUBSCRIBE_TEMPLATE}/sing-box) - echo $SING_BOX_JSON | sed "s#\"\",#$OUTBOUND_REPLACE#; s#\"\"#${NODE_REPLACE%,}#g" | ${WORK_DIR}/jq > ${WORK_DIR}/subscribe/sing-box + # 生成 sing-box SFM SFA SFI 订阅文件 + cat $TEMP_DIR/sing-box | sed "s#\"\",#$OUTBOUND_REPLACE#; s#\"\"#${NODE_REPLACE%,}#g" | ${WORK_DIR}/jq > ${WORK_DIR}/subscribe/sing-box + rm -f $TEMP_DIR/sing-box } &>/dev/null # 生成二维码 url 文件 @@ -3268,7 +3331,7 @@ $(${WORK_DIR}/qrencode $SUBSCRIBE_ADDRESS/${UUID_CONFIRM}/auto2) cat ${WORK_DIR}/list # 显示脚本使用情况数据 - statistics_of_run-times get + statistics_of_run_times get } # 创建快捷方式 @@ -3317,16 +3380,16 @@ change_protocols() { # 列出已安装协议 hint "\n $(text 63) (${#EXISTED_PROTOCOLS[@]})" for h in "${!EXISTED_PROTOCOLS[@]}"; do - hint " $(asc $[h+97]). ${EXISTED_PROTOCOLS[h]} " + hint " $(asc $(( h+97 ))). ${EXISTED_PROTOCOLS[h]} " done # 从已安装的协议中选择需要删除的协议名,并存放在 REMOVE_PROTOCOLS,把保存的协议的协议存放在 KEEP_PROTOCOLS reading "\n $(text 64) " REMOVE_SELECT # 统一为小写,去掉重复选项,处理不在可选列表里的选项,把特殊符号处理 - REMOVE_SELECT=$(sed "s/[^a-$(asc $[${#EXISTED_PROTOCOLS[@]} + 96])]//g" <<< "${REMOVE_SELECT,,}" | awk 'BEGIN{RS=""; FS=""}{delete seen; output=""; for(i=1; i<=NF; i++){ if(!seen[$i]++){ output=output $i } } print output}') + REMOVE_SELECT=$(sed "s/[^a-$(asc $(( ${#EXISTED_PROTOCOLS[@]} + 96 )))]//g" <<< "${REMOVE_SELECT,,}" | awk 'BEGIN{RS=""; FS=""}{delete seen; output=""; for(i=1; i<=NF; i++){ if(!seen[$i]++){ output=output $i } } print output}') for ((j=0; j<${#REMOVE_SELECT}; j++)); do - REMOVE_PROTOCOLS+=("${EXISTED_PROTOCOLS[$[$(asc "$(awk "NR==$[j+1] {print}" <<< "$(grep -o . <<< "$REMOVE_SELECT")")") - 97]]}") + REMOVE_PROTOCOLS+=("${EXISTED_PROTOCOLS[$(( $(asc "$(awk "NR==$[j+1] {print}" <<< "$(grep -o . <<< "$REMOVE_SELECT")")") - 97 ))]}") done for k in "${EXISTED_PROTOCOLS[@]}"; do @@ -3337,14 +3400,14 @@ change_protocols() { if [ "${#NOT_EXISTED_PROTOCOLS[@]}" -gt 0 ]; then hint "\n $(text 65) (${#NOT_EXISTED_PROTOCOLS[@]}) " for i in "${!NOT_EXISTED_PROTOCOLS[@]}"; do - hint " $(asc $[i+97]). ${NOT_EXISTED_PROTOCOLS[i]} " + hint " $(asc $(( i+97 ))). ${NOT_EXISTED_PROTOCOLS[i]} " done reading "\n $(text 66) " ADD_SELECT # 统一为小写,去掉重复选项,处理不在可选列表里的选项,把特殊符号处理 - ADD_SELECT=$(sed "s/[^a-$(asc $[${#NOT_EXISTED_PROTOCOLS[@]} + 96])]//g" <<< "${ADD_SELECT,,}" | awk 'BEGIN{RS=""; FS=""}{delete seen; output=""; for(i=1; i<=NF; i++){ if(!seen[$i]++){ output=output $i } } print output}') + ADD_SELECT=$(sed "s/[^a-$(asc $(( ${#NOT_EXISTED_PROTOCOLS[@]} + 96 )))]//g" <<< "${ADD_SELECT,,}" | awk 'BEGIN{RS=""; FS=""}{delete seen; output=""; for(i=1; i<=NF; i++){ if(!seen[$i]++){ output=output $i } } print output}') for ((l=0; l<${#ADD_SELECT}; l++)); do - ADD_PROTOCOLS+=("${NOT_EXISTED_PROTOCOLS[$[$(asc "$(awk "NR==$[l+1] {print}" <<< "$(grep -o . <<< "$ADD_SELECT")")") - 97]]}") + ADD_PROTOCOLS+=("${NOT_EXISTED_PROTOCOLS[$(( $(asc "$(awk "NR==$[l+1] {print}" <<< "$(grep -o . <<< "$ADD_SELECT")")") - 97 ))]}") done fi @@ -3754,7 +3817,7 @@ menu_setting() { OPTION[6]="6 . $(text 31)" OPTION[7]="7 . $(text 32)" OPTION[8]="8 . $(text 62)" - OPTION[9]="9. $(text 121)" + OPTION[9]="9 . $(text 121)" OPTION[10]="10. $(text 33)" OPTION[11]="11. $(text 59)" OPTION[12]="12. $(text 69)" @@ -3841,7 +3904,7 @@ menu() { } check_cdn -statistics_of_run-times update sing-box.sh 2>/dev/null +statistics_of_run_times update sing-box.sh 2>/dev/null # 传参 [[ "${*^^}" =~ '-E'|'-K' ]] && L=E