From ca016e8fca35e0cb9ce0f9aea03339c7e288bd90 Mon Sep 17 00:00:00 2001 From: sushen339 Date: Fri, 17 Oct 2025 02:14:17 +0800 Subject: [PATCH] clean commit --- README.md | 89 ++++++ curl-cc.sh | 345 ++++++++++++++++++++++ mihomo-install.sh | 581 +++++++++++++++++++++++++++++++++++++ mssh.sh | 726 ++++++++++++++++++++++++++++++++++++++++++++++ tcp.sh | 255 ++++++++++++++++ tproxy.sh | 346 ++++++++++++++++++++++ 6 files changed, 2342 insertions(+) create mode 100644 README.md create mode 100644 curl-cc.sh create mode 100644 mihomo-install.sh create mode 100644 mssh.sh create mode 100644 tcp.sh create mode 100644 tproxy.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..9eb15f7 --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ + + +# 🚀 脚本工具集 + +常用 Linux/路由器/服务器 Bash 工具脚本,适合自动化、性能优化、代理配置等场景。 + +--- + +## 🛠️ 工具与一键命令 + +### 1. tcp.sh —— TCP 网络优化 +优化: + +```bash +bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/tcp.sh") 1 +``` + +代理加速: + +```bash +bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/tcp.sh") 1 +``` + + +--- + +### 2. tproxy.sh —— OpenWrt/路由透明代理自动配置 +一键运行: + +```bash +bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/tproxy.sh") +``` + +代理加速: + +```bash +bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/tproxy.sh") +``` + +--- + +### 3. mihomo-install.sh —— Mihomo 加速核心一键安装 +一键安装: + +```bash +bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/mihomo-install.sh") +``` + +代理加速: + +```bash +bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/mihomo-install.sh") +``` + +--- + +### 4. mssh.sh —— 多主机 SSH 管理与端口转发 +一键运行: + +```bash +bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/mssh.sh") +``` + +代理加速: + +```bash +bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/mssh.sh") +``` + +--- + +### 5. curl-cc.sh —— 模拟浏览器 UA 自动访问签到 +一键运行: + +```bash +bash <(curl -fsSL "https://raw.githubusercontent.com/sushen339/tools/main/curl-cc.sh") +``` + +代理加速: + +```bash +bash <(curl -fsSL "https://gh-proxy.net/https://raw.githubusercontent.com/sushen339/tools/main/curl-cc.sh") +``` + +--- + +> 建议所有脚本以 root 权限运行,详细参数和说明请阅读各脚本头部注释。 + +> 建议所有脚本以 root 权限运行,详细参数和说明请阅读各脚本头部注释。 diff --git a/curl-cc.sh b/curl-cc.sh new file mode 100644 index 0000000..671c18b --- /dev/null +++ b/curl-cc.sh @@ -0,0 +1,345 @@ +#!/usr/bin/env bash +# curl-cc.sh +# 自动访问指定网站,模拟真实浏览器行为 +# 用法: ./curl-cc.sh [-v|--verbose] [-h|--help] + +set -o pipefail + +# 配置选项 +VERBOSE=0 +SCRIPT_NAME=$(basename "$0") + +# 显示帮助信息 +show_help() { + cat << EOF +用法: $SCRIPT_NAME [选项] + +描述: + 自动访问指定网站,模拟真实浏览器行为进行签到或访问 + +选项: + -v, --verbose 显示详细输出信息 + -h, --help 显示此帮助信息 + +示例: + $SCRIPT_NAME # 静默模式运行 + $SCRIPT_NAME -v # 详细模式运行 + +EOF + exit 0 +} + +# 日志函数 +log_info() { + if [ "$VERBOSE" -eq 1 ]; then + echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*" + fi +} + +log_error() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" >&2 +} + +# 解析命令行参数 +while [ $# -gt 0 ]; do + case "$1" in + -v|--verbose) + VERBOSE=1 + shift + ;; + -h|--help) + show_help + ;; + *) + echo "未知选项: $1" + echo "使用 -h 或 --help 查看帮助" + exit 1 + ;; + esac +done + +VISIT_PATH="/short_code/visit_short_code_proc_ajax.php?ref=" +CLICK_PATH="/short_code/click_short_code_proc_ajax.php?c=c" + +# Referer 来源列表(最常用的社交媒体和平台) +REFERERS=( + # 主流社交媒体 + "https://www.facebook.com/" + "https://twitter.com/" + "https://www.instagram.com/" + "https://www.tiktok.com/" + "https://www.reddit.com/" + + # 视频平台 + "https://www.youtube.com/" + + # 搜索引擎 + "https://www.google.com/" + + # 通讯工具 + "https://t.me/" +) + +# User-Agent 列表(2025年最新版本,最常用设备) +UAS=( + # === Android 设备 === + # Samsung Galaxy (市场占有率最高) + "Mozilla/5.0 (Linux; Android 14; SM-S24 Ultra) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36" + "Mozilla/5.0 (Linux; Android 14; SM-S23) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36" + + # Google Pixel + "Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.58 Mobile Safari/537.36" + + # === iOS 设备 === + # iPhone - Safari (最常用) + "Mozilla/5.0 (iPhone; CPU iPhone OS 18_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Mobile/15E148 Safari/604.1" + "Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.7 Mobile/15E148 Safari/604.1" + + # iPhone - Chrome + "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/130.0.6723.90 Mobile/15E148 Safari/604.1" + + # === Windows 桌面 === + # Chrome (最常用) + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36" + + # Edge + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.2849.68" + + # === macOS 桌面 === + # Safari + "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15" + + # Chrome on Mac + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36" +) + +hosts=( "org.cc.cc" "sky.cc.cc" ) + +# 生成随机 ref 参数(30% 概率为空,70% 概率为 6-12 位随机字符串) +rand_ref() { + if [ $((RANDOM % 10)) -lt 3 ]; then + printf "" + else + local len=$((6 + RANDOM % 7)) + # 使用更便携的随机字符串生成方式 + if [ -r /dev/urandom ]; then + head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c "$len" + else + # 备用方案(如果 /dev/urandom 不可用) + < /dev/urandom tr -dc 'a-zA-Z0-9' | head -c "$len" + fi + fi +} + +# 随机选择一个 User-Agent +choose_ua() { + echo "${UAS[$((RANDOM % ${#UAS[@]}))]}" +} + +# 随机选择一个 Referer +choose_referer() { + echo "${REFERERS[$((RANDOM % ${#REFERERS[@]}))]}" +} + +# 根据地理位置获取 Accept-Language +get_accept_language() { + local country_code="" + + # 尝试从 ipinfo.io 获取国家代码(设置超时 3 秒) + if command -v curl &> /dev/null; then + country_code=$(curl -s --max-time 3 https://ipinfo.io/country 2>/dev/null | tr -d '[:space:]') + fi + + # 根据国家代码返回对应的 Accept-Language + case "$country_code" in + CN|HK|TW|MO) # 中国、香港、台湾、澳门 + echo "zh-CN,zh;q=0.9,en;q=0.8" + ;; + JP) # 日本 + echo "ja,en;q=0.9" + ;; + KR) # 韩国 + echo "ko,en;q=0.9" + ;; + ES|MX|AR|CO|CL|PE|VE) # 西班牙语国家 + echo "es,en;q=0.9" + ;; + FR|BE|CH) # 法语国家 + echo "fr,en;q=0.9" + ;; + DE|AT) # 德语国家 + echo "de,en;q=0.9" + ;; + RU|BY|KZ|UA) # 俄语国家 + echo "ru,en;q=0.9" + ;; + PT|BR) # 葡萄牙语国家 + echo "pt,en;q=0.9" + ;; + IT) # 意大利 + echo "it,en;q=0.9" + ;; + TR) # 土耳其 + echo "tr,en;q=0.9" + ;; + SA|AE|EG|IQ|JO) # 阿拉伯语国家 + echo "ar,en;q=0.9" + ;; + IN) # 印度 + echo "en-IN,hi;q=0.9,en;q=0.8" + ;; + *) # 默认英文(包括 US, GB, CA, AU, SG 等) + echo "en-US,en;q=0.9" + ;; + esac +} + +# 执行访问请求 +make_visit_request() { + local host="$1" + local ref_url="$2" # URL 参数中的 ref(referral site URL) + local ua="$3" + local referer="https://${host}/" # HTTP Header 中的 Referer 固定为自身 + + # 构建完整 URL,ref 参数为 referral site 的完整 URL + local url="https://${host}${VISIT_PATH}${ref_url}" + + log_info "正在访问: ${host}" + log_info "URL: ${url}" + log_info "Referer Header: ${referer}" + log_info "User-Agent: ${ua}" + + # 使用 || true 确保即使 curl 失败也不会导致脚本退出 + local curl_exit_code=0 + curl -s -S -X GET "$url" \ + -H "Host: ${host}" \ + -H "Connection: keep-alive" \ + -H "Accept: */*" \ + -H "X-Requested-With: XMLHttpRequest" \ + -H "User-Agent: ${ua}" \ + -H "Content-Type: charset=utf-8" \ + -H "Referer: ${referer}" \ + -H "Accept-Encoding: gzip, deflate" \ + -H "Accept-Language: ${ACCEPT_LANG}" \ + --compressed -o /dev/null --max-time ${MAX_TIME} || curl_exit_code=$? + + if [ $curl_exit_code -eq 0 ]; then + log_info "✓ ${host} 访问成功" + return 0 + else + log_error "✗ ${host} 访问失败 (curl 退出码: ${curl_exit_code})" + return 1 + fi +} + +# 执行电话点击请求 +make_click_request() { + local host="$1" + local ua="$2" + local referer="https://${host}/" # HTTP Header 中的 Referer 固定为自身 + + # 构建完整 URL + local url="https://${host}${CLICK_PATH}" + + log_info " ├─ 电话点击统计" + log_info " ├─ URL: ${url}" + + # 使用 || true 确保即使 curl 失败也不会导致脚本退出 + local curl_exit_code=0 + curl -s -S -X GET "$url" \ + -H "Host: ${host}" \ + -H "Connection: keep-alive" \ + -H "Accept: */*" \ + -H "X-Requested-With: XMLHttpRequest" \ + -H "User-Agent: ${ua}" \ + -H "Content-Type: charset=utf-8" \ + -H "Referer: ${referer}" \ + -H "Accept-Encoding: gzip, deflate" \ + -H "Accept-Language: ${ACCEPT_LANG}" \ + --compressed -o /dev/null --max-time ${MAX_TIME} || curl_exit_code=$? + + if [ $curl_exit_code -eq 0 ]; then + log_info " └─ ✓ 电话点击记录成功" + return 0 + else + log_error " └─ ✗ 电话点击记录失败 (curl 退出码: ${curl_exit_code})" + return 1 + fi +} + +MAX_TIME=15 +SLEEP_MIN=3 +SLEEP_MAX=6 + +# 主执行流程 +main() { + log_info "==================== 开始执行 ====================" + log_info "目标主机数量: ${#hosts[@]}" + + local success_count=0 + local fail_count=0 + + # 获取地理位置对应的语言 + ACCEPT_LANG="$(get_accept_language)" + log_info "Accept-Language: ${ACCEPT_LANG}" + + # 为本次执行生成统一的 UA(所有请求使用同一个 UA) + local ua + ua="$(choose_ua)" + + for host in "${hosts[@]}"; do + local ref_url="" + + # 33% 概率直接访问(unknown),67% 概率从社交媒体跳转 + if [ $((RANDOM % 3)) -eq 0 ]; then + # 直接访问,ref 参数为空 + ref_url="" + log_info "直接访问(referral site: unknown)" + else + # 从随机社交媒体跳转 + ref_url="$(choose_referer)" + log_info "referral site: ${ref_url}" + fi + + # 执行访问请求,不管成功失败都继续 + if make_visit_request "$host" "$ref_url" "$ua"; then + ((success_count++)) + + # 20% 概率点击电话统计 + if [ $((RANDOM % 5)) -eq 0 ]; then + log_info " ↳ 触发电话点击(20% 概率)" + make_click_request "$host" "$ua" || true + fi + else + ((fail_count++)) + fi + + # 在请求之间随机等待(最后一个请求除外) + if [ "$host" != "${hosts[-1]}" ]; then + local sleep_sec=$((SLEEP_MIN + RANDOM % (SLEEP_MAX - SLEEP_MIN + 1))) + local ms=$((RANDOM % 1000)) + local sleep_time + + # 检查 awk 是否可用 + if command -v awk &> /dev/null; then + sleep_time=$(awk "BEGIN{printf \"%.3f\", ${sleep_sec} + ${ms}/1000}") + else + sleep_time="${sleep_sec}" + fi + + log_info "等待 ${sleep_time} 秒..." + sleep "$sleep_time" + fi + done + + log_info "==================== 执行完成 ====================" + log_info "成功: ${success_count}, 失败: ${fail_count}" + + # 如果有失败的请求,返回非零退出码 + if [ "$fail_count" -gt 0 ]; then + exit 1 + fi +} + +# 执行主函数 +main diff --git a/mihomo-install.sh b/mihomo-install.sh new file mode 100644 index 0000000..f914acd --- /dev/null +++ b/mihomo-install.sh @@ -0,0 +1,581 @@ +#!/bin/bash +# mihomo 安装脚本 +# 作者: su +# 版本: v1.0.0 +# 日期: 2024年11月14日 + +set -e -o pipefail + +# 定义颜色 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # 无颜色 + +# 检测系统类型和架构 +detect_system() { + if [ -f /etc/alpine-release ]; then + SYSTEM_TYPE="Alpine" + elif command -v systemctl > /dev/null; then + SYSTEM_TYPE="Debian" + elif [ -f /etc/openwrt_release ]; then + SYSTEM_TYPE="OpenWrt" + elif [ -d /etc/init.d ]; then + SYSTEM_TYPE="Init.d" + else + echo "不支持的初始化系统" + exit 1 + fi + + ARCH_RAW=$(uname -m) + case "${ARCH_RAW}" in + 'x86_64') ARCH='amd64';; + 'x86' | 'i686' | 'i386') ARCH='386';; + 'aarch64' | 'arm64') ARCH='arm64';; + 'armv7l') ARCH='armv7';; + 's390x') ARCH='s390x';; + *) echo "Unsupported architecture: ${ARCH_RAW}"; exit 1;; + esac + + if [ "$SYSTEM_TYPE" == "Debian" ]; then + SYSTEM_VERSION=$(cat /etc/os-release | grep VERSION_ID | cut -d'=' -f2 | tr -d '"') + elif [ "$SYSTEM_TYPE" == "Alpine" ]; then + SYSTEM_VERSION=$(cat /etc/alpine-release) + elif [ "$SYSTEM_TYPE" == "OpenWrt" ]; then + SYSTEM_VERSION=$(cat /etc/openwrt_release | grep DISTRIB_RELEASE | cut -d'=' -f2) + else + SYSTEM_VERSION="Unknown" + fi + echo -e "${GREEN}当前系统为 ${SYSTEM_TYPE}-${SYSTEM_VERSION}_${ARCH_RAW}${NC}" +} + +# 设置时区 +set_timezone() { + if [ "$SYSTEM_TYPE" == "Alpine" ]; then + echo -e "${YELLOW}检测到 Alpine 系统,使用 cp 和 echo 设置时区${NC}" + apk add --no-cache tzdata + cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + echo "Asia/Shanghai" > /etc/timezone + apk del tzdata + else + CURRENT_TIMEZONE=$(timedatectl show --property=Timezone --value) + if [ "$CURRENT_TIMEZONE" != "Asia/Shanghai" ]; then + echo -e "${YELLOW}设置时区为Asia/Shanghai${NC}" + timedatectl set-timezone Asia/Shanghai + fi + fi +} + +# 安装必要软件 +install_software() { + if [ "$SYSTEM_TYPE" == "Alpine" ]; then + echo -e "${YELLOW}正在安装必要软件${NC}" + apk add --no-cache curl nano grep gzip + else + echo -e "${YELLOW}正在安装必要软件${NC}" + if command -v apt-get > /dev/null; then + apt-get update + apt-get install -y curl nano gzip + elif command -v yum > /dev/null; then + yum install -y curl nano gzip + else + echo -e "${RED}不支持的包管理器${NC}" + exit 1 + fi + fi +} + +# 检测并下载 mihomo 版本 +download_mihomo() { + VERSION=$(curl -sL "https://gh.llkk.cc/https://api.github.com/repos/MetaCubeX/mihomo/releases/latest" | grep -oP '"tag_name": "\K(.*)(?=")') + if [ -z "$VERSION" ]; then + echo -e "${RED}无法获取最新版本号${NC}" + exit 1 + fi + echo -e "${GREEN}获取到的版本: ${VERSION}${NC}" + + DOWNLOAD_URL="https://gh.llkk.cc/https://github.com/MetaCubeX/mihomo/releases/download/${VERSION}/mihomo-linux-${ARCH}-${VERSION}.gz" + echo "从 ${DOWNLOAD_URL} 下载 mihomo" + curl -Lo mihomo.gz "${DOWNLOAD_URL}" + echo -e "${YELLOW}Mihomo ${VERSION} 下载完成, 开始安装${NC}" + gzip -d mihomo.gz + chmod +x mihomo + mv mihomo /usr/local/bin/ +} + +# 删除已有服务 +remove_existing_service() { + if [ -f /etc/systemd/system/mihomo.service ] || [ -f /etc/init.d/mihomo ] || [ -f /etc/rc.d/mihomo ]; then + read -p "检测到已有的 mihomo 服务,是否删除?(y/n): " choice + case "$choice" in + y|Y ) + if [ -f /etc/systemd/system/mihomo.service ]; then + systemctl stop mihomo + systemctl disable mihomo + rm -f /etc/systemd/system/mihomo.service + systemctl daemon-reload + fi + + if [ -f /etc/init.d/mihomo ]; then + /etc/init.d/mihomo stop + rm -f /etc/init.d/mihomo + fi + + if [ -f /etc/rc.d/mihomo ]; then + /etc/rc.d/mihomo stop + rm -f /etc/rc.d/mihomo + fi + ;; + n|N ) + echo -e "${YELLOW}保留已有的 mihomo 服务,退出安装。${NC}" + exit 0 + ;; + * ) + echo -e "${RED}无效的选择,退出安装。${NC}" + exit 1 + ;; + esac + fi +} + +# 配置 OpenRC 服务,Alpine 专用 +configure_openrc() { + echo "创建 OpenRC 服务文件" + cat < /etc/init.d/mihomo +#!/sbin/openrc-run +command="/usr/local/bin/mihomo" +command_args="-d /etc/mihomo" +command_background=true +pidfile="/var/run/mihomo.pid" +name="Mihomo Service" + +depend() { + need net +} + +start_pre() { + ebegin "等待 1 秒" + sleep 1 + eend \$? +} +EOF + chmod +x /etc/init.d/mihomo + rc-update add mihomo default + rc-service mihomo start +} + +# 配置 systemd 服务,Debian系 专用 +configure_systemd() { + echo "创建 systemd 服务文件" + cat < /etc/systemd/system/mihomo.service +[Unit] +Description=mihomo Daemon, Another Clash Kernel. +After=network.target NetworkManager.service systemd-networkd.service iwd.service + +[Service] +Type=simple +LimitNPROC=500 +LimitNOFILE=1000000 +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE +AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE +Restart=always +ExecStartPre=/usr/bin/sleep 1s +ExecStart=/usr/local/bin/mihomo -d /etc/mihomo +ExecReload=/bin/kill -HUP \$MAINPID + +[Install] +WantedBy=multi-user.target +EOF + systemctl daemon-reload + systemctl start mihomo + systemctl enable mihomo +} + +# 配置 init.d 服务,OpenWrt 和 Init.d 专用 +configure_initd() { + echo "创建 init.d 服务文件" + cat < /etc/init.d/mihomo +#!/bin/sh +### BEGIN INIT INFO +# Provides: mihomo +# Required-Start: \$network +# Required-Stop: \$network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Mihomo Service +### END INIT INFO + +start() { + echo "Starting mihomo" + /usr/bin/sleep 1 + /usr/local/bin/mihomo -d /etc/mihomo & +} + +stop() { + echo "Stopping mihomo" + pkill -f /usr/local/bin/mihomo +} + +restart() { + stop + start +} + +case "\$1" in + start) + start + ;; + stop) + stop + ;; + restart) + restart + ;; + *) + echo "Usage: /etc/init.d/mihomo {start|stop|restart}" + exit 1 + ;; +esac + +exit 0 +EOF + chmod +x /etc/init.d/mihomo + update-rc.d mihomo defaults + /etc/init.d/mihomo start +} + +# 内置配置 +BUILTIN_CONFIG=$(cat < /etc/mihomo/config.yaml + ;; + n|N ) + echo -e "${YELLOW}不使用内置配置,请上传配置到/etc/mihomo/config.yaml${NC}" + ;; + * ) + echo -e "${RED}无效的选择,退出安装。${NC}" + exit 1 + ;; + esac +} + +# 启用系统转发 +enable_ip_forwarding() { + echo 'net.ipv4.ip_forward = 1' | tee -a /etc/sysctl.conf + echo 'net.ipv6.conf.all.forwarding = 1' | tee -a /etc/sysctl.conf + sysctl -p + if [ "$SYSTEM_TYPE" == "Alpine" ]; then + rc-update add sysctl + fi +} + +# 主函数 +main() { + detect_system + set_timezone + install_software + remove_existing_service + download_mihomo + ask_builtin_config + # enable_ip_forwarding + + case "$SYSTEM_TYPE" in + "Alpine") + configure_openrc + ;; + "Debian") + configure_systemd + ;; + "OpenWrt" | "Init.d") + configure_initd + ;; + esac + + echo -e "${GREEN}脚本执行完毕。${NC}" +} + +main \ No newline at end of file diff --git a/mssh.sh b/mssh.sh new file mode 100644 index 0000000..095e021 --- /dev/null +++ b/mssh.sh @@ -0,0 +1,726 @@ +#!/bin/bash + +# mssh - SSH 管理工具 + +# 配置文件路径 +CONFIG_FILE="$HOME/.mssh.conf" + +# 颜色定义 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +ORANGE='\033[0;33m' +BLUE='\033[0;34m' +PURPLE='\033[0;35m' +CYAN='\033[0;36m' +NC='\033[0m' + +# Emoji 定义 +SERVER_EMOJI="🌐" +KEY_EMOJI="🔑" +PASSWORD_EMOJI="🔒" +CONNECT_EMOJI="🔌" +SUCCESS_EMOJI="✅" +ERROR_EMOJI="❌" +WARNING_EMOJI="⚠️" +INFO_EMOJI="𝒊" +FORWARD_EMOJI="📡" + +# 信号处理 +graceful_exit() { + echo -e "\n${INFO_EMOJI} ${CYAN} 正在退出 mssh...${NC}" + + # 检查是否有活跃的端口转发 + local active_forwards + active_forwards=$(get_section "active_forwards") + + if [ -n "$active_forwards" ]; then + echo -e "${WARNING_EMOJI} ${YELLOW}检测到活跃的端口转发进程${NC}" + echo -e "${INFO_EMOJI} ${CYAN}这些进程将继续在后台运行${NC}" + echo -e "${CYAN}您可以随时重新启动 mssh 来管理它们${NC}" + echo + + # 显示活跃的转发 + local i=1 + while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do + [ -z "$rule_name" ] && continue + printf " ${GREEN}%2d.${NC} ${FORWARD_EMOJI} ${YELLOW}%s${NC} (PID:%s)\n" "$i" "$rule_name" "$pid" + ((i++)) + done <<< "$active_forwards" + fi + + echo -e "\n🔚 脚本已退出" + exit 0 +} + +# 设置信号处理 +trap graceful_exit SIGINT SIGTERM SIGQUIT + +# 密码加密函数 +encrypt_password() { + local password="$1" + [ -z "$password" ] && return + + # 简单的字符位移 + base64 + local encrypted + encrypted=$(echo "$password" | tr 'A-Za-z0-9' 'N-ZA-Mn-za-m5-90-4' | base64 -w 0) + echo "ENC:$encrypted" +} + +# 密码解密函数 +decrypt_password() { + local encrypted="$1" + [ -z "$encrypted" ] && return + + # 解密格式:ENC:base64data + if [[ "$encrypted" =~ ^ENC: ]]; then + local encrypted_data="${encrypted#ENC:}" + echo "$encrypted_data" | base64 -d | tr 'N-ZA-Mn-za-m5-90-4' 'A-Za-z0-9' + fi +} + +# 安全读取函数 +safe_read() { + local prompt="$1" + local var_name="$2" + local response + + if ! read -r -p "$prompt" response; then + graceful_exit + fi + + if [ -n "$var_name" ]; then + eval "$var_name=\"$response\"" + fi +} + +# 检查 sshpass +check_sshpass() { + if ! command -v sshpass >/dev/null 2>&1; then + echo -e "${ERROR_EMOJI} ${RED}未找到 sshpass。某些功能可能不可用。${NC}" + echo -e "${INFO_EMOJI} ${YELLOW}可以运行以下命令安装:${NC}" + echo -e " ${CYAN}Ubuntu/Debian: sudo apt install sshpass${NC}" + echo -e " ${CYAN}CentOS/RHEL: sudo yum install sshpass${NC}" + fi +} + +# 初始化配置文件 +init_config() { + if [ ! -f "$CONFIG_FILE" ]; then + cat > "$CONFIG_FILE" << 'EOF' +# mssh 配置文件 +# 格式说明: +# [servers] 部分:alias|user@host|port|keypath|encrypted_password +# [port_forwards] 部分:server_alias|rule_name|local_port|remote_host|remote_port|user@host|port|keypath|encrypted_password +# [active_forwards] 部分:rule_name|local_port|pid|user@host|port|server_alias|remote_host|remote_port + +[servers] + +[port_forwards] + +[active_forwards] + +EOF + echo -e "${SUCCESS_EMOJI} ${GREEN}配置文件已创建: $CONFIG_FILE${NC}" + fi +} + +# 获取配置文件中的特定部分 +get_section() { + local section="$1" + local config_file="${2:-$CONFIG_FILE}" + + awk -v section="[$section]" ' + $0 == section { in_section = 1; next } + /^\[.*\]$/ { in_section = 0; next } + in_section && NF > 0 && !/^#/ { print } + ' "$config_file" +} + +# 格式化端口转发显示 +format_forward_display() { + local num="$1" + local rule_name="$2" + local local_port="$3" + local server_alias="$4" + local remote_host="$5" + local remote_port="$6" + local pid="$7" + + local pid_text="" + [ -n "$pid" ] && pid_text=" PID:$pid" + + if [ "$remote_host" = "127.0.0.1" ]; then + printf " ${GREEN}%2d.${NC} ${FORWARD_EMOJI} ${YELLOW}%s${NC} (本地:%s -> %s:%s)%s\n" \ + "$num" "$rule_name" "$local_port" "$server_alias" "$remote_port" "$pid_text" + else + printf " ${GREEN}%2d.${NC} ${FORWARD_EMOJI} ${YELLOW}%s${NC} (本地:%s -> %s -> %s:%s)%s\n" \ + "$num" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port" "$pid_text" + fi +} + +# 显示服务器列表 +show_servers() { + echo -e "${CYAN}已保存的服务器:${NC}" + echo -e "${CYAN}---------------${NC}" + + local servers + servers=$(get_section "servers") + if [ -z "$servers" ]; then + echo -e " ${YELLOW}暂无服务器${NC}" + return 0 + fi + + local i=1 + while IFS='|' read -r alias_name user_host port key_path password; do + [ -z "$alias_name" ] && continue + + # 显示密码和密钥状态 + local has_password="" + local has_key="" + [ -n "$password" ] && has_password="${PASSWORD_EMOJI}" + [ -n "$key_path" ] && [ -f "$key_path" ] && has_key="${KEY_EMOJI}" + + printf " ${GREEN}%2d.${NC} ${SERVER_EMOJI} ${YELLOW}%s${NC} (%s:%s) %s%s\n" \ + "$i" "$alias_name" "$user_host" "$port" "$has_password" "$has_key" + ((i++)) + done <<< "$servers" + + return $((i-1)) +} + +# 显示活跃的端口转发 +show_active_forwards() { + local active_forwards + active_forwards=$(get_section "active_forwards") + if [ -n "$active_forwards" ]; then + echo + echo -e "${CYAN}已启用的端口转发:${NC}" + echo -e "${CYAN}------------------${NC}" + + local i=1 + while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do + [ -z "$rule_name" ] && continue + format_forward_display "$i" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port" "$pid" + ((i++)) + done <<< "$active_forwards" + return 0 + else + return 1 + fi +} + +# 检查并清理无效的端口转发进程 +check_and_cleanup_forwards() { + local active_forwards + active_forwards=$(get_section "active_forwards") + + if [ -z "$active_forwards" ]; then + return 0 + fi + + local temp_file + temp_file=$(mktemp) + local cleanup_needed=false + local cleaned_count=0 + + # 复制配置文件到临时文件 + cp "$CONFIG_FILE" "$temp_file" + + while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do + [ -z "$rule_name" ] && continue + + # 检查进程是否存在 + if ! kill -0 "$pid" 2>/dev/null; then + # 进程不存在,从配置中移除 + local rule_line="${rule_name}|${local_port}|${pid}|${user_host}|${port}|${server_alias}|${remote_host}|${remote_port}" + + awk -v target_line="$rule_line" ' + $0 == target_line && in_active { next } + /^\[active_forwards\]$/ { in_active=1 } + /^\[.*\]$/ && !/^\[active_forwards\]$/ { in_active=0 } + { print } + ' "$temp_file" > "${temp_file}.new" && mv "${temp_file}.new" "$temp_file" + + cleanup_needed=true + ((cleaned_count++)) + fi + done <<< "$active_forwards" + + # 如果有清理,更新配置文件并显示信息 + if [ "$cleanup_needed" = true ]; then + mv "$temp_file" "$CONFIG_FILE" + if [ "$cleaned_count" -gt 0 ]; then + echo -e "${INFO_EMOJI} ${YELLOW}已清理 $cleaned_count 个失效的端口转发进程${NC}" + sleep 1 + fi + else + rm -f "$temp_file" + fi +} + +# 主菜单 +main_menu() { + # 在显示菜单前检查并清理无效的端口转发进程 + check_and_cleanup_forwards + + clear + echo -e "${PURPLE}==========================================${NC}" + echo -e "${PURPLE}:: mssh - SSH 管理工具 ::${NC}" + echo -e "${PURPLE}==========================================${NC}" + echo + + show_servers + show_active_forwards + local has_active_forwards=$? + + echo + echo -e "${BLUE}选项分区:${NC}" + echo -e "${BLUE}---------${NC}" + echo -e " ${GREEN}[a] 增加服务器${NC}" + echo -e " ${RED}[d] 删除服务器${NC}" + echo -e " ${BLUE}[p] 端口转发${NC}" + echo -e " ${CYAN}[q] 退出${NC}" + + # 显示端口转发持久化提示 + if [ $has_active_forwards -eq 0 ]; then + echo + echo -e "${INFO_EMOJI} ${YELLOW}提示:端口转发将在退出程序后继续运行${NC}" + fi + + echo + echo -ne "${GREEN}请选择操作: ${NC}" +} + +# 添加服务器 +add_server() { + while true; do + clear + echo -e "${PURPLE}============ 添加服务器 ============${NC}" + echo + echo -e "${INFO_EMOJI} ${CYAN}添加新服务器 (0或回车返回)${NC}" + echo + safe_read "$(echo -e "${CYAN}输入别名: ${NC}")" alias_name; [ "$alias_name" = "0" ] || [ -z "$alias_name" ] && return + safe_read "$(echo -e "${CYAN}输入用户名@主机: ${NC}")" user_host; [ "$user_host" = "0" ] || [ -z "$user_host" ] && return + safe_read "$(echo -e "${CYAN}输入端口 (默认22): ${NC}")" port; [ "$port" = "0" ] && return; port=${port:-22} + echo -ne "${CYAN}输入密码 (可选): ${NC}" + read -r -s password + echo + safe_read "$(echo -e "${CYAN}输入私钥路径 (可选): ${NC}")" key_path; [ "$key_path" = "0" ] && return + + if [ -n "$key_path" ] && [ ! -f "$key_path" ]; then + echo -e "${ERROR_EMOJI} ${RED}密钥文件不存在${NC}" + safe_read "$(echo -e "${YELLOW}是否继续? (y/N): ${NC}")" continue_without_key + if [[ ! "${continue_without_key:-n}" =~ [yY] ]]; then + continue + fi + key_path="" + fi + + # 加密密码 + [ -n "$password" ] && password=$(encrypt_password "$password") + + # 添加到配置文件 + local temp_file + temp_file=$(mktemp) + awk -v new_server="${alias_name}|${user_host}|${port}|${key_path}|${password}" ' + /^\[servers\]$/ { print; in_servers=1; next } + /^\[.*\]$/ { in_servers=0 } + in_servers && /^$/ { print new_server; added=1 } + { print } + END { if (!added && in_servers) print new_server } + ' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE" + + echo -e "${SUCCESS_EMOJI} ${GREEN}服务器已添加: $alias_name${NC}" + echo + safe_read "$(echo -e "${YELLOW}是否继续添加? (y/N): ${NC}")" continue_add + if [[ ! "${continue_add:-n}" =~ [yY] ]]; then + break + fi + done +} + +# 删除服务器 +delete_server() { + while true; do + clear + echo -e "${PURPLE}============ 删除服务器 ============${NC}" + echo + echo -e "${WARNING_EMOJI} ${YELLOW}删除服务器${NC}" + echo + show_servers + echo + safe_read "$(echo -e "${CYAN}选择要删除的服务器编号 (0或回车返回): ${NC}")" choice + [ "$choice" = "0" ] || [ -z "$choice" ] && return + + local servers + servers=$(get_section "servers") + local server_line + server_line=$(echo "$servers" | sed -n "${choice}p") + + if [ -n "$server_line" ]; then + local alias_name + alias_name=$(echo "$server_line" | cut -d'|' -f1) + echo -e "${WARNING_EMOJI} ${YELLOW}确认删除服务器: $alias_name${NC}" + + local temp_file + temp_file=$(mktemp) + awk -v target_line="$server_line" ' + $0 == target_line && in_servers { next } + /^\[servers\]$/ { in_servers=1 } + /^\[.*\]$/ && !/^\[servers\]$/ { in_servers=0 } + { print } + ' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE" + + echo -e "${SUCCESS_EMOJI} ${GREEN}服务器已删除: $alias_name${NC}" + else + echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}" + fi + + echo + safe_read "$(echo -e "${YELLOW}是否继续删除? (y/N): ${NC}")" continue_del + if [[ ! "${continue_del:-n}" =~ [yY] ]]; then + break + fi + done +} + +# 添加端口转发规则 +add_port_forward_rule() { + echo -e "${INFO_EMOJI} ${CYAN}添加新端口转发规则 (0或回车返回):${NC}" + + safe_read "$(echo -e "${CYAN}输入规则名称: ${NC}")" rule_name + if [ "$rule_name" = "0" ] || [ -z "$rule_name" ]; then + return 1 + fi + + safe_read "$(echo -e "${CYAN}输入本地端口: ${NC}")" local_port + if [ "$local_port" = "0" ] || [ -z "$local_port" ]; then + return 1 + fi + + safe_read "$(echo -e "${CYAN}输入远程主机 (默认127.0.0.1): ${NC}")" remote_host + if [ "$remote_host" = "0" ]; then + return 1 + fi + remote_host=${remote_host:-127.0.0.1} + + safe_read "$(echo -e "${CYAN}输入远程端口 (默认${local_port}): ${NC}")" remote_port + if [ "$remote_port" = "0" ]; then + return 1 + fi + remote_port=${remote_port:-$local_port} + + echo -e "\n${CYAN}请选择要使用的服务器:${NC}" + show_servers + echo + safe_read "$(echo -e "${CYAN}选择服务器编号: ${NC}")" server_choice + if [ "$server_choice" = "0" ] || [ -z "$server_choice" ]; then + return 1 + fi + + local servers + servers=$(get_section "servers") + local selected_server + selected_server=$(echo "$servers" | sed -n "${server_choice}p") + if [ -n "$selected_server" ]; then + IFS='|' read -r server_alias user_host port key_path password <<< "$selected_server" + + local temp_file + temp_file=$(mktemp) + awk -v new_rule="${server_alias}|${rule_name}|${local_port}|${remote_host}|${remote_port}|${user_host}|${port}|${key_path}|${password}" ' + /^\[port_forwards\]$/ { print; in_forwards=1; next } + /^\[.*\]$/ { in_forwards=0 } + in_forwards && /^$/ { print new_rule; added=1 } + { print } + END { if (!added && in_forwards) print new_rule } + ' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE" + + echo -e "${SUCCESS_EMOJI} ${GREEN}端口转发规则已添加: $rule_name${NC}" + return 0 + else + echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}" + return 1 + fi +} + +# 删除端口转发规则 +delete_port_forward() { + local rule_number="$1" + + local forwards + forwards=$(get_section "port_forwards") + local rule_line + rule_line=$(echo "$forwards" | sed -n "${rule_number}p") + + if [ -n "$rule_line" ]; then + local rule_name + rule_name=$(echo "$rule_line" | cut -d'|' -f2) + + local temp_file + temp_file=$(mktemp) + awk -v target_line="$rule_line" ' + $0 == target_line && in_forwards { next } + /^\[port_forwards\]$/ { in_forwards=1 } + /^\[.*\]$/ && !/^\[port_forwards\]$/ { in_forwards=0 } + { print } + ' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE" + + echo -e "${SUCCESS_EMOJI} ${GREEN}规则已删除: $rule_name${NC}" + else + echo -e "${ERROR_EMOJI} ${RED}无效的规则编号!${NC}" + fi +} + +# 停止后台端口转发 +stop_background_forward() { + local rule_number="$1" + + local active_forwards + active_forwards=$(get_section "active_forwards") + local active_line + active_line=$(echo "$active_forwards" | sed -n "${rule_number}p") + + if [ -n "$active_line" ]; then + IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port <<< "$active_line" + + if kill "$pid" 2>/dev/null; then + echo -e "${SUCCESS_EMOJI} ${GREEN}端口转发已停止: $rule_name (PID: $pid)${NC}" + else + echo -e "${WARNING_EMOJI} ${YELLOW}进程可能已经停止: $rule_name (PID: $pid)${NC}" + fi + + # 从活跃转发列表中移除 + local temp_file + temp_file=$(mktemp) + awk -v target_line="$active_line" ' + $0 == target_line && in_active { next } + /^\[active_forwards\]$/ { in_active=1 } + /^\[.*\]$/ && !/^\[active_forwards\]$/ { in_active=0 } + { print } + ' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE" + else + echo -e "${ERROR_EMOJI} ${RED}无效的活跃转发编号!${NC}" + fi +} + +# 启动后台端口转发 +start_background_forward() { + local rule_number="$1" + + local forwards + forwards=$(get_section "port_forwards") + local rule_line + rule_line=$(echo "$forwards" | sed -n "${rule_number}p") + + if [ -n "$rule_line" ]; then + IFS='|' read -r server_alias rule_name local_port remote_host remote_port user_host port key_path password <<< "$rule_line" + + # 解密密码 + [ -n "$password" ] && password=$(decrypt_password "$password") + + # 构建 SSH 命令 + local ssh_args=("-p" "$port" "-L" "${local_port}:${remote_host}:${remote_port}" "-N" "$user_host") + [ -n "$key_path" ] && ssh_args+=("-i" "$key_path") + + # 启动后台进程 (使用密码或密钥认证) + local ssh_pid + if [ -n "$password" ] && command -v sshpass >/dev/null 2>&1; then + echo -e "${INFO_EMOJI} ${YELLOW}使用保存的密码启动端口转发...${NC}" + nohup sshpass -p "$password" ssh "${ssh_args[@]}" >/dev/null 2>&1 & + ssh_pid=$! + disown $ssh_pid 2>/dev/null + + # 等待SSH连接建立 + sleep 1 + + # 检查进程是否仍在运行 + if ! kill -0 "$ssh_pid" 2>/dev/null; then + echo -e "${ERROR_EMOJI} ${RED}端口转发启动失败,请检查网络连接和认证信息${NC}" + return 1 + fi + else + echo -e "${INFO_EMOJI} ${YELLOW}启动端口转发 (需要手动输入密码)...${NC}" + nohup ssh "${ssh_args[@]}" /dev/null 2>&1 & + ssh_pid=$! + disown $ssh_pid 2>/dev/null + + # 给用户一些时间输入密码 + echo -e "${INFO_EMOJI} ${CYAN}请在上方输入SSH密码...${NC}" + sleep 3 + + # 检查进程是否仍在运行 + if ! kill -0 "$ssh_pid" 2>/dev/null; then + echo -e "${ERROR_EMOJI} ${RED}端口转发启动失败,请检查网络连接和认证信息${NC}" + return 1 + fi + fi + + local temp_file + temp_file=$(mktemp) + awk -v new_active="${rule_name}|${local_port}|${ssh_pid}|${user_host}|${port}|${server_alias}|${remote_host}|${remote_port}" ' + /^\[active_forwards\]$/ { print; in_active=1; next } + /^\[.*\]$/ { in_active=0 } + in_active && /^$/ { print new_active; added=1 } + { print } + END { if (!added && in_active) print new_active } + ' "$CONFIG_FILE" > "$temp_file" && mv "$temp_file" "$CONFIG_FILE" + + echo -e "${SUCCESS_EMOJI} ${GREEN}端口转发已启动: $rule_name (PID: $ssh_pid)${NC}" + else + echo -e "${ERROR_EMOJI} ${RED}无效的规则编号!${NC}" + fi +} + +# 端口转发管理 +list_port_forwards() { + while true; do + clear + echo -e "${PURPLE}=========== 端口转发管理 ===========${NC}" + echo + + local forwards + forwards=$(get_section "port_forwards") + if [ -z "$forwards" ]; then + echo -e "${CYAN}已配置的端口转发规则:${NC}" + echo -e "${CYAN}------------------------${NC}" + echo -e " ${YELLOW}暂无端口转发规则${NC}" + else + echo -e "${CYAN}已配置的端口转发规则:${NC}" + echo -e "${CYAN}------------------------${NC}" + local i=1 + while IFS='|' read -r server_alias rule_name local_port remote_host remote_port _ _ _ _ || [ -n "$rule_name" ]; do + [ -z "$rule_name" ] && continue + format_forward_display "$i" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port" + ((i++)) + done <<< "$forwards" + fi + + show_active_forwards + + echo + echo -e "${BLUE}操作选项:${NC}" + echo -e "${BLUE}---------${NC}" + echo -e " ${GREEN}[1] 添加端口转发${NC}" + echo -e " ${YELLOW}[2] 启动端口转发${NC}" + echo -e " ${ORANGE}[3] 停止端口转发${NC}" + echo -e " ${RED}[4] 删除转发规则${NC}" + echo + safe_read "$(echo -e "${CYAN}请选择操作 (0或回车返回): ${NC}")" choice + + case $choice in + 0|"") return ;; + 1) + echo + if add_port_forward_rule; then + safe_read "按回车键继续..." pause_key + fi + ;; + 2) + if [ -z "$forwards" ]; then + echo -e "${ERROR_EMOJI} ${RED}暂无端口转发规则可启动!${NC}" + safe_read "按回车键继续..." pause_key + else + safe_read "$(echo -e "${CYAN}输入要启动的规则编号: ${NC}")" start_choice + if [ -n "$start_choice" ]; then + start_background_forward "$start_choice" + fi + safe_read "按回车键继续..." pause_key + fi + ;; + 3) + local active_forwards + active_forwards=$(get_section "active_forwards") + if [ -z "$active_forwards" ]; then + echo -e "${ERROR_EMOJI} ${RED}暂无活跃的端口转发可停止!${NC}" + safe_read "按回车键继续..." pause_key + else + echo + echo -e "${CYAN}当前活跃的端口转发:${NC}" + local i=1 + while IFS='|' read -r rule_name local_port pid user_host port server_alias remote_host remote_port; do + [ -z "$rule_name" ] && continue + format_forward_display "$i" "$rule_name" "$local_port" "$server_alias" "$remote_host" "$remote_port" "$pid" + ((i++)) + done <<< "$active_forwards" + echo + safe_read "$(echo -e "${CYAN}输入要停止的端口转发编号: ${NC}")" stop_choice + if [ -n "$stop_choice" ]; then + stop_background_forward "$stop_choice" + fi + safe_read "按回车键继续..." pause_key + fi + ;; + 4) + if [ -z "$forwards" ]; then + echo -e "${ERROR_EMOJI} ${RED}暂无端口转发规则可删除!${NC}" + safe_read "按回车键继续..." pause_key + else + safe_read "$(echo -e "${CYAN}输入要删除的规则编号: ${NC}")" del_choice + if [ -n "$del_choice" ]; then + delete_port_forward "$del_choice" + fi + safe_read "按回车键继续..." pause_key + fi + ;; + *) echo -e "${ERROR_EMOJI} ${RED}无效选项!${NC}"; sleep 1 ;; + esac + done +} + +# 连接服务器 +connect_server() { + local choice=$1 + + local servers + servers=$(get_section "servers") + local selected_server + selected_server=$(echo "$servers" | sed -n "${choice}p") + + if [ -n "$selected_server" ]; then + IFS='|' read -r alias_name user_host port key_path password <<< "$selected_server" + + # 解密密码 + [ -n "$password" ] && password=$(decrypt_password "$password") + + echo -e "${CONNECT_EMOJI} ${GREEN}连接到: $alias_name ($user_host:$port)${NC}" + + local ssh_args=("-p" "$port" "$user_host") + [ -n "$key_path" ] && ssh_args+=("-i" "$key_path") + + if [ -n "$password" ] && command -v sshpass >/dev/null 2>&1; then + echo -e "${WARNING_EMOJI} ${YELLOW}使用保存的密码连接...${NC}" + sshpass -p "$password" ssh "${ssh_args[@]}" + else + ssh "${ssh_args[@]}" + fi + else + echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}" + sleep 1 + fi +} + +# 主程序 +check_sshpass +init_config + +while true; do + main_menu + safe_read "" choice + + case $choice in + [0-9]*) + if [[ "$choice" =~ ^[0-9]+$ ]]; then + connect_server "$choice" + else + echo -e "${ERROR_EMOJI} ${RED}无效的服务器编号!${NC}" + sleep 1 + fi + ;; + a|A) add_server ;; + d|D) delete_server ;; + p|P) list_port_forwards ;; + q|Q) graceful_exit ;; + *) echo -e "${ERROR_EMOJI} ${RED}无效选项!${NC}"; sleep 1 ;; + esac +done \ No newline at end of file diff --git a/tcp.sh b/tcp.sh new file mode 100644 index 0000000..589a521 --- /dev/null +++ b/tcp.sh @@ -0,0 +1,255 @@ +#!/bin/bash +# tcp调优脚本 v25.10.11 +# 用法: +# ./tcp.sh 1 启用调优 +# ./tcp.sh 0 还原配置 +# ./tcp.sh 2 对比优化前后的参数 + +BACKUP_FILE="/var/backups/tcp_backup_adaptive.conf" +TUNE_FILE="/etc/sysctl.d/tcp_bbr_adaptive.conf" + +# 检查是否以 root 权限运行 +check_root() { + if [ "$EUID" -ne 0 ]; then + echo "✗ 错误: 此脚本需要 root 权限运行" + echo " 请使用: sudo $0" + exit 1 + fi +} + +# 确保备份目录存在 +ensure_backup_dir() { + local backup_dir + backup_dir=$(dirname "$BACKUP_FILE") + if [ ! -d "$backup_dir" ]; then + mkdir -p "$backup_dir" 2>/dev/null || { + echo "✗ 无法创建备份目录: $backup_dir" + exit 1 + } + fi +} + +PARAMS=( + "net.core.default_qdisc" + "net.ipv4.tcp_congestion_control" + "net.core.rmem_max" + "net.core.wmem_max" + "net.ipv4.tcp_rmem" + "net.ipv4.tcp_wmem" +) + +get_sys_info() { + MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}') + MEM_BYTES=$((MEM_KB * 1024)) + echo "内存: $((MEM_BYTES/1024/1024)) MB, CPU: $(nproc) 核" +} + +ask_network_info() { + # 读取并验证带宽输入 + while true; do + read -p "最大带宽 (Mbps): " BW + if [[ "$BW" =~ ^[0-9]+(\.[0-9]+)?$ ]] && (( $(echo "$BW > 0" | bc -l) )); then + break + else + echo "✗ 无效输入,请输入正数 (例如: 100 或 1000)" + fi + done + + # 读取并验证 RTT 输入 + while true; do + read -p "平均延迟 RTT (ms): " RTT + if [[ "$RTT" =~ ^[0-9]+(\.[0-9]+)?$ ]] && (( $(echo "$RTT > 0" | bc -l) )); then + break + else + echo "✗ 无效输入,请输入正数 (例如: 10 或 50)" + fi + done + + BW_BITS=$((BW * 1000000)) + RTT_SEC=$(awk "BEGIN {print $RTT/1000}") + BDP_BITS=$(awk "BEGIN {print $BW_BITS * $RTT_SEC}") + BDP_BYTES=$(awk "BEGIN {print int($BDP_BITS/8)}") + MIN_BUF=$((4*1024*1024)) + MAX_BUF_LIMIT=$((MEM_BYTES/8)) + if [ $BDP_BYTES -lt $MIN_BUF ]; then + BUF=$MIN_BUF + elif [ $BDP_BYTES -gt $MAX_BUF_LIMIT ]; then + BUF=$MAX_BUF_LIMIT + else + BUF=$BDP_BYTES + fi + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "计算结果:" + echo " 带宽延迟积 (BDP): $(numfmt --to=iec-i --suffix=B $BDP_BYTES)" + echo " 缓冲区上限: $(numfmt --to=iec-i --suffix=B $BUF)" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +} + +check_bbr() { + if ! sysctl net.ipv4.tcp_available_congestion_control 2>/dev/null | grep -qw bbr; then + echo "⚠️ 未检测到 BBR 支持,尝试加载 tcp_bbr 模块..." + if modprobe tcp_bbr 2>/dev/null; then + echo "✓ tcp_bbr 模块加载成功" + # 添加到开机自动加载 + if [ ! -f /etc/modules-load.d/tcp_bbr.conf ]; then + echo "tcp_bbr" > /etc/modules-load.d/tcp_bbr.conf + echo "✓ 已设置 BBR 模块开机自动加载" + fi + else + echo "✗ 内核不支持 BBR,无法加载模块" + echo " 请确认内核版本 >= 4.9 或更新内核" + exit 1 + fi + else + echo "✓ BBR 支持已启用" + fi +} + +apply_tune() { + check_root + ensure_backup_dir + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " 系统信息检测" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + get_sys_info + echo "" + check_bbr + echo "" + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " 网络参数配置" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + ask_network_info + echo "" + + if [ ! -f "$BACKUP_FILE" ]; then + echo "📋 备份当前参数到 $BACKUP_FILE" + for p in "${PARAMS[@]}"; do + echo "$p = $(sysctl -n $p)" >> "$BACKUP_FILE" + done + echo "✓ 备份完成" + else + echo "ℹ️ 检测到已存在备份文件,跳过备份" + fi + + echo "" + echo "📝 生成配置文件: $TUNE_FILE" + cat > "$TUNE_FILE" </dev/null 2>&1; then + echo "✓ 配置应用成功" + else + echo "✗ 配置应用失败" + exit 1 + fi + + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " 当前参数" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + for p in "${PARAMS[@]}"; do + printf "%-35s %s\n" "$p" "$(sysctl -n $p)" + done + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +} + +restore_tune() { + check_root + + if [ ! -f "$BACKUP_FILE" ]; then + echo "✗ 未找到备份文件: $BACKUP_FILE" + echo " 无法还原配置" + exit 1 + fi + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " 还原配置" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + [ -f "$TUNE_FILE" ] && rm -f "$TUNE_FILE" && echo "✓ 已删除调优配置文件" + + echo "⚙️ 还原系统参数..." + while IFS= read -r line; do + key=$(echo "$line" | awk -F= '{print $1}' | xargs) + value=$(echo "$line" | awk -F= '{print $2}' | xargs) + if [ -n "$key" ] && [ -n "$value" ]; then + if sysctl -w "$key=$value" >/dev/null 2>&1; then + echo " ✓ $key" + else + echo " ✗ $key (失败)" + fi + fi + done < "$BACKUP_FILE" + + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " 当前参数" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + for p in "${PARAMS[@]}"; do + printf "%-35s %s\n" "$p" "$(sysctl -n $p)" + done + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + read -p "是否删除备份文件? (y/N): " del_backup + if [[ "$del_backup" =~ ^[Yy]$ ]]; then + rm -f "$BACKUP_FILE" + echo "✓ 已删除备份文件" + else + echo "ℹ️ 保留备份文件: $BACKUP_FILE" + fi +} + +compare_params() { + if [ ! -f "$BACKUP_FILE" ]; then + echo "✗ 未找到备份文件: $BACKUP_FILE" + echo " 无法对比参数" + exit 1 + fi + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + printf "%-35s %-25s %-25s\n" "参数" "优化前" "优化后" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + for p in "${PARAMS[@]}"; do + old=$(grep "^$p" "$BACKUP_FILE" | awk -F= '{print $2}' | xargs) + new=$(sysctl -n $p 2>/dev/null || echo "N/A") + printf "%-35s %-25s %-25s\n" "$p" "$old" "$new" + done + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +} + +if [ -z "$1" ]; then + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo " TCP 调优脚本 v25.10.11" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "请选择操作:" + echo " 1 - 启用 BBR 调优" + echo " 0 - 还原默认配置" + echo " 2 - 对比优化前后参数" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + read -p "请输入选项 (1/0/2): " choice + case "$choice" in + 1) apply_tune ;; + 0) restore_tune ;; + 2) compare_params ;; + *) echo "✗ 无效选项" ; exit 1 ;; + esac +else + case "$1" in + 1) apply_tune ;; + 0) restore_tune ;; + 2) compare_params ;; + *) echo "用法: $0 {1|0|2} + 1 = 启用调优 + 0 = 还原配置 + 2 = 对比优化前后的参数" ; exit 1 ;; + esac +fi diff --git a/tproxy.sh b/tproxy.sh new file mode 100644 index 0000000..dcf7fe9 --- /dev/null +++ b/tproxy.sh @@ -0,0 +1,346 @@ +#!/bin/sh + +# TPROXY 透明代理脚本 for OpenWrt with NFTables +# 功能: IPv4/v6, DNS劫持(可选), IP分流, 本机代理 +# 版本: 1.1.2 (2025-06-16) +# 维护: su + +# --- [ 用户配置区: 请根据您的环境修改此区域 ] --- + +# --- 功能开关 --- +# 是否开启DNS劫持功能. "true":开启, "false":关闭 +ENABLE_DNS_HIJACKING="false" + +# --- 核心设置 --- +# TPROXY 代理服务监听的 TCP/UDP 端口 +PROXY_PORT=7893 +# Adguard 或其他 DNS 软件的监听端口 +ADGUARDHOME_DNS_PORT=553 +# 代理软件的 DNS 监听端口 (仅在开启劫持时有效) +PROXY_DNS_PORT=1053 + +# --- 系统集成设置 --- +# LAN 接口名称 +LAN_IF="br-lan" +# 运行代理软件的用户名 (用于豁免代理自身流量,防止死循环) +PROXY_USER="root" +# 存放 .nft 自定义规则文件的目录 +NFT_CUSTOM_PATH="/etc/nikki/nftables" +# 您正在使用的 nftables 表名 +NFT_TABLE="nikki" + +# --- IP 集合名称 (需与 .nft 文件中定义的名称完全一致) --- +NFT_SET_RESERVED_V4="reserved_ip" +NFT_SET_RESERVED_V6="reserved_ip6" +NFT_SET_BYPASS_V4="china_ip" +NFT_SET_BYPASS_V6="china_ip6" + +# --- 高级设置 --- +# 防火墙标记 (fwmark) 和策略路由表 ID +PROXY_MARK=0xff +ROUTE_TABLE=100 + +# 锁文件路径 (防止重复运行) +LOCK_FILE="/var/run/tproxy.lock" + + +# --- [ 核心逻辑区: 通常无需修改 ] --- + +check_root() { + if [ "$(id -u)" -ne 0 ]; then + printf "错误: 此脚本需要以 root 权限运行。\n" >&2 + exit 1 + fi +} + +# 检查是否已有实例运行 +check_lock() { + if [ -f "$LOCK_FILE" ]; then + # 检查服务是否真正在运行(通过防火墙表存在性判断) + if nft list table inet "$NFT_TABLE" >/dev/null 2>&1; then + printf "错误: TPROXY 服务已在运行中\n" >&2 + printf "如需重新启动,请使用: %s restart\n" "$0" >&2 + printf "如需强制启动,请先停止服务: %s stop\n" "$0" >&2 + exit 1 + else + # 清理失效的锁文件 + printf " -> 清理失效的锁文件...\n" + rm -f "$LOCK_FILE" + fi + fi +} + +# 创建锁文件 +create_lock() { + # 确保锁文件目录存在 + local lock_dir=$(dirname "$LOCK_FILE") + [ ! -d "$lock_dir" ] && mkdir -p "$lock_dir" 2>/dev/null + + # 写入启动时间戳而不是PID,因为脚本执行完后进程会结束 + date '+%Y-%m-%d %H:%M:%S' > "$LOCK_FILE" + if [ $? -ne 0 ]; then + printf "警告: 无法创建锁文件 %s\n" "$LOCK_FILE" >&2 + else + printf " -> 锁文件已创建: %s\n" "$LOCK_FILE" + fi +} + +# 清理锁文件 +remove_lock() { + if [ -f "$LOCK_FILE" ]; then + rm -f "$LOCK_FILE" + printf " -> 锁文件已清理: %s\n" "$LOCK_FILE" + fi +} + +load_user_nft_files() { + if [ ! -d "$NFT_CUSTOM_PATH" ]; then + printf "错误: 找不到目录 %s\n" "$NFT_CUSTOM_PATH" >&2 + exit 1 + fi + printf " -> 正在从 %s 加载自定义规则...\n" "$NFT_CUSTOM_PATH" + + # 检查是否存在 .nft 文件 + file_count=$(find "$NFT_CUSTOM_PATH" -name "*.nft" -type f | wc -l) + if [ "$file_count" -eq 0 ]; then + printf "警告: 目录 %s 中没有找到 .nft 文件\n" "$NFT_CUSTOM_PATH" >&2 + return 0 + fi + + for file in "$NFT_CUSTOM_PATH"/*.nft; do + if [ -f "$file" ]; then + printf " - 加载 %s\n" "$(basename "$file")" + if ! nft -f "$file"; then + printf "错误: 加载 %s 时发生错误!\n" "$file" >&2 + exit 1 + fi + fi + done +} + +do_start() { + printf "▶️ 启动透明代理服务...\n" + check_lock + + # 先清理可能存在的残留规则,但不删除锁文件 + printf " -> 清理可能存在的残留规则...\n" + ip -4 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true + ip -6 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true + ip -4 route flush table $ROUTE_TABLE 2>/dev/null + ip -6 route flush table $ROUTE_TABLE 2>/dev/null + nft delete table inet $NFT_TABLE 2>/dev/null || true + + # 创建锁文件 + create_lock + + # 设置异常退出时自动清理锁文件 + trap 'remove_lock; exit' INT TERM + + # 1. 设置策略路由 + printf " -> 正在设置策略路由...\n" + ip -4 rule add fwmark $PROXY_MARK lookup $ROUTE_TABLE + ip -6 rule add fwmark $PROXY_MARK lookup $ROUTE_TABLE + ip -4 route add local default dev lo table $ROUTE_TABLE + ip -6 route add local default dev lo table $ROUTE_TABLE + + # 2. 加载并配置 nftables + printf " -> 正在加载并配置 nftables...\n" + + nft add table inet $NFT_TABLE + if [ $? -ne 0 ]; then + printf "错误: 创建 nftables 表 '%s' 失败。请检查是否已有同名表或nftables服务是否正常。\n" "$NFT_TABLE" >&2 + exit 1 + fi + + # 加载 IP Set 文件 + load_user_nft_files + + nft add chain inet $NFT_TABLE mangle_prerouting { type filter hook prerouting priority -150\; }; nft flush chain inet $NFT_TABLE mangle_prerouting + nft add chain inet $NFT_TABLE mangle_output { type route hook output priority -150\; }; nft flush chain inet $NFT_TABLE mangle_output + for chain in tproxy_lan_v4 tproxy_lan_v6 tproxy_loc_v4 tproxy_loc_v6; do + nft add chain inet $NFT_TABLE "$chain"; nft flush chain inet $NFT_TABLE "$chain" + done + + # 3. 应用规则 + printf " -> 正在应用核心防火墙规则...\n" + # 规则 A: DNS 劫持 (可选) + if [ "$ENABLE_DNS_HIJACKING" = "true" ]; then + printf " - DNS 劫持已开启。\n" + nft add chain inet $NFT_TABLE dns_redirect { type nat hook prerouting priority -100\; }; nft flush chain inet $NFT_TABLE dns_redirect + nft add rule inet $NFT_TABLE dns_redirect iifname $LAN_IF meta l4proto {tcp, udp} th dport 53 redirect to :$PROXY_DNS_PORT + else + printf " - DNS 劫持已关闭。\n" + fi + + # 规则 B: 处理本机发出流量 (在 OUTPUT 链中仅作标记) + nft add rule inet $NFT_TABLE mangle_output meta nfproto ipv4 jump tproxy_loc_v4 + nft add rule inet $NFT_TABLE mangle_output meta nfproto ipv6 jump tproxy_loc_v6 + nft add rule inet $NFT_TABLE tproxy_loc_v4 skuid $PROXY_USER return # 关键: 豁免代理程序自身,防止死循环 + nft add rule inet $NFT_TABLE tproxy_loc_v4 ip daddr @$NFT_SET_RESERVED_V4 return + nft add rule inet $NFT_TABLE tproxy_loc_v4 ip daddr @$NFT_SET_BYPASS_V4 return + # 不处理 DNS 流量 + nft add rule inet $NFT_TABLE tproxy_loc_v4 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return + nft add rule inet $NFT_TABLE tproxy_loc_v4 meta l4proto {tcp, udp} meta mark set $PROXY_MARK + nft add rule inet $NFT_TABLE tproxy_loc_v6 skuid $PROXY_USER return + nft add rule inet $NFT_TABLE tproxy_loc_v6 ip6 daddr @$NFT_SET_RESERVED_V6 return + nft add rule inet $NFT_TABLE tproxy_loc_v6 ip6 daddr @$NFT_SET_BYPASS_V6 return + # 不处理 DNS 流量 + nft add rule inet $NFT_TABLE tproxy_loc_v6 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return + nft add rule inet $NFT_TABLE tproxy_loc_v6 meta l4proto {tcp, udp} meta mark set $PROXY_MARK + + # 规则 C: 处理局域网及本机回环流量 (在 PREROUTING 链中执行 TPROXY) + nft add rule inet $NFT_TABLE mangle_prerouting iifname $LAN_IF meta nfproto ipv4 meta l4proto {tcp, udp} jump tproxy_lan_v4 + nft add rule inet $NFT_TABLE mangle_prerouting iifname $LAN_IF meta nfproto ipv6 meta l4proto {tcp, udp} jump tproxy_lan_v6 + nft add rule inet $NFT_TABLE mangle_prerouting iifname "lo" meta nfproto ipv4 meta l4proto {tcp, udp} meta mark $PROXY_MARK jump tproxy_lan_v4 + nft add rule inet $NFT_TABLE mangle_prerouting iifname "lo" meta nfproto ipv6 meta l4proto {tcp, udp} meta mark $PROXY_MARK jump tproxy_lan_v6 + + nft add rule inet $NFT_TABLE tproxy_lan_v4 ip daddr @$NFT_SET_RESERVED_V4 return + nft add rule inet $NFT_TABLE tproxy_lan_v4 ip daddr @$NFT_SET_BYPASS_V4 return + # 不处理 DNS 流量 + nft add rule inet $NFT_TABLE tproxy_lan_v4 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return + nft add rule inet $NFT_TABLE tproxy_lan_v4 meta l4proto {tcp, udp} meta mark set $PROXY_MARK tproxy to :$PROXY_PORT + nft add rule inet $NFT_TABLE tproxy_lan_v6 ip6 daddr @$NFT_SET_RESERVED_V6 return + nft add rule inet $NFT_TABLE tproxy_lan_v6 ip6 daddr @$NFT_SET_BYPASS_V6 return + # 不处理 DNS 流量 + nft add rule inet $NFT_TABLE tproxy_lan_v6 meta l4proto {tcp, udp} th dport {53, $ADGUARDHOME_DNS_PORT, $PROXY_DNS_PORT} return + nft add rule inet $NFT_TABLE tproxy_lan_v6 meta l4proto {tcp, udp} meta mark set $PROXY_MARK tproxy to :$PROXY_PORT + + printf "✅ 透明代理服务已成功启动。\n" + + # 清除陷阱,正常启动时保持锁文件 + trap - INT TERM +} + +do_stop() { + printf "⏹️ 停止透明代理服务...\n" + + # 清理策略路由 + ip -4 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true + ip -6 rule del fwmark $PROXY_MARK lookup $ROUTE_TABLE 2>/dev/null || true + # 清理自定义路由表内容 + ip -4 route flush table $ROUTE_TABLE 2>/dev/null + ip -6 route flush table $ROUTE_TABLE 2>/dev/null + # 删除 nft 路由表 + printf " -> 正在清理防火墙表 '%s'...\n" "$NFT_TABLE" + nft delete table inet $NFT_TABLE 2>/dev/null || true + + # 清理锁文件 + remove_lock + + printf "🛑 透明代理服务已完全停止。\n" +} + +do_status() { + ICON_OK="✅" + ICON_FAIL="❌" + ICON_INFO="ℹ️" + ICON_OFF="⚪️" + + printf "--- TPROXY 服务状态检查 ---\n" + + # 检查锁文件状态 + if [ -f "$LOCK_FILE" ]; then + local start_time=$(cat "$LOCK_FILE" 2>/dev/null) + if [ -n "$start_time" ]; then + printf "锁文件状态: %s 存在 (启动时间: %s)\n" "$ICON_OK" "$start_time" + else + printf "锁文件状态: %s 存在但无效\n" "$ICON_FAIL" + fi + else + printf "锁文件状态: %s 不存在\n" "$ICON_OFF" + fi + + if ! nft list table inet "$NFT_TABLE" >/dev/null 2>&1; then + printf "总状态: %s 未激活 (找不到防火墙表 '%s')\n" "$ICON_OFF" "$NFT_TABLE" + exit 0 + fi + + if ip rule show | grep -q "fwmark $PROXY_MARK lookup $ROUTE_TABLE"; then + printf "总状态: %s 激活\n" "$ICON_OK" + else + printf "总状态: %s 部分激活 (防火墙规则存在,但策略路由缺失)\n" "$ICON_FAIL" + fi + + printf "\n[+] IP 路由与规则:\n" + if ip rule show | grep -q "fwmark $PROXY_MARK"; then + printf " %s 策略路由:\n" "$ICON_INFO" + ip rule show | grep "fwmark $PROXY_MARK" | sed 's/^/ -> /' + else + printf " %s 策略路由: %s 缺失!\n" "$ICON_FAIL" "$ICON_FAIL" + fi + printf " %s 自定义路由表 (ID: %s):\n" "$ICON_INFO" "$ROUTE_TABLE" + ip route show table "$ROUTE_TABLE" | sed 's/^/ -> /' + + printf "\n[+] NFTables 规则状态 (主表: %s)\n" "$NFT_TABLE" + + if [ "$ENABLE_DNS_HIJACKING" = "true" ]; then + if nft list chain inet "$NFT_TABLE" "dns_redirect" >/dev/null 2>&1; then + printf " %s DNS 劫持: 已激活 (转发至端口 %s)\n" "$ICON_OK" "$PROXY_DNS_PORT" + else + printf " %s DNS 劫持: 激活失败\n" "$ICON_FAIL" + fi + else + printf " %s DNS 劫持: 已禁用\n" "$ICON_OFF" + fi + + if nft list chain inet "$NFT_TABLE" "tproxy_lan_v4" >/dev/null 2>&1; then + printf " %s TPROXY 代理: 已激活 (监听端口 %s)\n" "$ICON_OK" "$PROXY_PORT" + else + printf " %s TPROXY 代理: 激活失败\n" "$ICON_FAIL" + fi + + printf " %s IP 列表加载状态:\n" "$ICON_INFO" + if nft list set inet "$NFT_TABLE" "$NFT_SET_RESERVED_V4" >/dev/null 2>&1; then + printf " - %s: %s\n" "$NFT_SET_RESERVED_V4" "$ICON_OK 本地 IPv4 已直连" + else + printf " - %s: %s\n" "$NFT_SET_RESERVED_V4" "$ICON_FAIL 未加载" + fi + if nft list set inet "$NFT_TABLE" "$NFT_SET_RESERVED_V6" >/dev/null 2>&1; then + printf " - %s: %s\n" "$NFT_SET_RESERVED_V6" "$ICON_OK 本地 IPv6 已直连" + else + printf " - %s: %s\n" "$NFT_SET_RESERVED_V6" "$ICON_FAIL 未加载" + fi + if nft list set inet "$NFT_TABLE" "$NFT_SET_BYPASS_V4" >/dev/null 2>&1; then + printf " - %s: %s\n" "$NFT_SET_BYPASS_V4" "$ICON_OK 国内 IPv4 已直连" + else + printf " - %s: %s\n" "$NFT_SET_BYPASS_V4" "$ICON_FAIL 未加载" + fi + if nft list set inet "$NFT_TABLE" "$NFT_SET_BYPASS_V6" >/dev/null 2>&1; then + printf " - %s: %s\n" "$NFT_SET_BYPASS_V6" "$ICON_OK 国内 IPv6 已直连" + else + printf " - %s: %s\n" "$NFT_SET_BYPASS_V6" "$ICON_FAIL 未加载" + fi + + printf -- "--------------------------------\n" +} + +# --- 脚本主入口 --- +check_root + +case "$1" in + start) + do_start + ;; + stop) + do_stop + ;; + restart) + printf "🔄 重启透明代理服务...\n" + do_stop + sleep 1 + do_start + ;; + status) + do_status + ;; + *) + printf "用法: %s {start|stop|restart|status}\n" "$0" + printf " start - 启动透明代理服务\n" + printf " stop - 停止透明代理服务\n" + printf " restart - 重启透明代理服务\n" + printf " status - 查看服务状态\n" + exit 1 + ;; +esac + +exit 0