2608 lines
91 KiB
Bash
2608 lines
91 KiB
Bash
#!/usr/bin/env bash
|
||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||
export PATH
|
||
|
||
#=================================================
|
||
# System Required: CentOS/Debian/Ubuntu
|
||
# Description: Snell Server 管理脚本
|
||
# Author: 翠花
|
||
# WebSite: https://aapls.com
|
||
#=================================================
|
||
|
||
sh_ver="1.9.6"
|
||
snell_v2_version="2.0.6"
|
||
snell_v3_version="3.0.1"
|
||
snell_v4_version="4.1.1"
|
||
snell_v5_version="5.0.1"
|
||
snell_v6_version="6.0.0b4"
|
||
script_dir=$(cd "$(dirname "$0")"; pwd)
|
||
script_path=$(echo -e "${script_dir}"|awk -F "$0" '{print $1}')
|
||
snell_dir="/etc/snell/"
|
||
snell_bin="/usr/local/bin/snell-server"
|
||
snell_conf="/etc/snell/config.conf"
|
||
snell_version_file="/etc/snell/ver.txt"
|
||
sysctl_conf="/etc/sysctl.d/99-snell.conf"
|
||
|
||
Green_font_prefix="\033[32m" && Red_font_prefix="\033[31m" && Green_background_prefix="\033[42;37m" && Red_background_prefix="\033[41;37m" && Font_color_suffix="\033[0m" && Yellow_font_prefix="\033[0;33m"
|
||
Info="${Green_font_prefix}[信息]${Font_color_suffix}"
|
||
Error="${Red_font_prefix}[警告]${Font_color_suffix}"
|
||
Tip="${Yellow_font_prefix}[提示]${Font_color_suffix}"
|
||
|
||
# 检查是否为 Root 用户
|
||
checkRoot(){
|
||
[[ $EUID != 0 ]] && echo -e "${Error} 当前非ROOT账号(或没有ROOT权限),无法继续操作,请更换ROOT账号或使用 ${Green_background_prefix}sudo su${Font_color_suffix} 命令获取临时ROOT权限(执行后可能会提示输入当前账号的密码)。" && exit 1
|
||
}
|
||
|
||
# 检查系统类型
|
||
checkSys(){
|
||
if [[ -f /etc/redhat-release ]]; then
|
||
release="centos"
|
||
elif cat /etc/issue | grep -q -E -i "debian"; then
|
||
release="debian"
|
||
elif cat /etc/issue | grep -q -E -i "ubuntu"; then
|
||
release="ubuntu"
|
||
elif cat /etc/issue | grep -q -E -i "centos|red hat|redhat"; then
|
||
release="centos"
|
||
elif cat /proc/version | grep -q -E -i "debian"; then
|
||
release="debian"
|
||
elif cat /proc/version | grep -q -E -i "ubuntu"; then
|
||
release="ubuntu"
|
||
elif cat /proc/version | grep -q -E -i "centos|red hat|redhat"; then
|
||
release="centos"
|
||
fi
|
||
}
|
||
|
||
# 检查依赖
|
||
checkDependencies(){
|
||
local deps=("wget" "unzip" "ss")
|
||
for cmd in "${deps[@]}"; do
|
||
if ! command -v "$cmd" &> /dev/null; then
|
||
echo -e "${Error} 缺少依赖: $cmd,正在尝试安装..."
|
||
if [[ -f /etc/debian_version ]]; then
|
||
apt-get update && apt-get install -y "$cmd"
|
||
elif [[ -f /etc/redhat-release ]]; then
|
||
yum install -y "$cmd"
|
||
else
|
||
echo -e "${Error} 不支持的系统,无法自动安装 $cmd"
|
||
exit 1
|
||
fi
|
||
fi
|
||
done
|
||
echo -e "${Info} 依赖检查完成"
|
||
}
|
||
|
||
# 安装依赖
|
||
installDependencies(){
|
||
if [[ ${release} == "centos" ]]; then
|
||
yum update
|
||
yum install gzip wget curl unzip -y
|
||
else
|
||
apt-get update
|
||
apt-get install gzip wget curl unzip -y
|
||
fi
|
||
sysctl -w net.core.rmem_max=26214400
|
||
sysctl -w net.core.rmem_default=26214400
|
||
\cp -f /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||
echo -e "${Info} 依赖安装完成"
|
||
}
|
||
|
||
# 检查系统架构
|
||
sysArch() {
|
||
uname=$(uname -m)
|
||
if [[ "$uname" == "i686" ]] || [[ "$uname" == "i386" ]]; then
|
||
arch="i386"
|
||
elif [[ "$uname" == *"armv7"* ]] || [[ "$uname" == "armv6l" ]]; then
|
||
arch="armv7l"
|
||
elif [[ "$uname" == *"armv8"* ]] || [[ "$uname" == "aarch64" ]]; then
|
||
arch="aarch64"
|
||
else
|
||
arch="amd64"
|
||
fi
|
||
}
|
||
|
||
# 开启 TCP Fast Open
|
||
enableTCPFastOpen() {
|
||
kernel=$(uname -r | awk -F . '{print $1}')
|
||
if [ "$kernel" -ge 3 ]; then
|
||
echo 3 >/proc/sys/net/ipv4/tcp_fastopen
|
||
# 创建或覆盖 Snell 专用的 sysctl 配置文件
|
||
cat > "$sysctl_conf" << EOF
|
||
# Snell Server 网络优化配置
|
||
# 由 Snell 管理脚本自动生成
|
||
|
||
fs.file-max = 51200
|
||
net.core.rmem_max = 67108864
|
||
net.core.wmem_max = 67108864
|
||
net.core.rmem_default = 65536
|
||
net.core.wmem_default = 65536
|
||
net.core.netdev_max_backlog = 4096
|
||
net.core.somaxconn = 4096
|
||
|
||
net.ipv4.tcp_syncookies = 1
|
||
net.ipv4.tcp_tw_reuse = 1
|
||
net.ipv4.tcp_tw_recycle = 0
|
||
net.ipv4.tcp_fin_timeout = 30
|
||
net.ipv4.tcp_keepalive_time = 1200
|
||
net.ipv4.ip_local_port_range = 10000 65000
|
||
net.ipv4.tcp_max_syn_backlog = 4096
|
||
net.ipv4.tcp_max_tw_buckets = 5000
|
||
net.ipv4.tcp_fastopen = 3
|
||
net.ipv4.tcp_rmem = 4096 87380 67108864
|
||
net.ipv4.tcp_wmem = 4096 65536 67108864
|
||
net.ipv4.tcp_mtu_probing = 1
|
||
net.ipv4.tcp_ecn = 1
|
||
|
||
# BBR 拥塞控制
|
||
net.core.default_qdisc = fq
|
||
net.ipv4.tcp_congestion_control = bbr
|
||
EOF
|
||
# 应用配置
|
||
sysctl --system >/dev/null 2>&1
|
||
echo -e "${Info} TCP Fast Open 和网络优化配置已启用!"
|
||
else
|
||
echo -e "${Error} 系统内核版本过低,无法支持 TCP Fast Open!"
|
||
fi
|
||
}
|
||
|
||
# 检查 Snell 是否安装
|
||
checkInstalledStatus(){
|
||
[[ ! -e ${snell_bin} ]] && echo -e "${Error} Snell Server 没有安装,请检查!" && exit 1
|
||
}
|
||
|
||
# 检查 Snell 运行状态
|
||
checkStatus(){
|
||
if systemctl is-active snell-server.service &> /dev/null; then
|
||
status="running"
|
||
else
|
||
status="stopped"
|
||
fi
|
||
}
|
||
|
||
# 版本号比较函数(优先正式版)
|
||
compareVersions(){
|
||
local version1="$1"
|
||
local version2="$2"
|
||
|
||
# 移除版本号前缀 v
|
||
version1=$(echo "$version1" | sed 's/^v//')
|
||
version2=$(echo "$version2" | sed 's/^v//')
|
||
|
||
# 如果版本号完全相同
|
||
if [[ "$version1" == "$version2" ]]; then
|
||
return 1 # 相等
|
||
fi
|
||
|
||
# 提取基础版本号(去除测试版后缀)
|
||
local base_version1=$(echo "$version1" | sed 's/[a-z].*//')
|
||
local base_version2=$(echo "$version2" | sed 's/[a-z].*//')
|
||
|
||
# 检查是否为测试版
|
||
local is_beta1=false
|
||
local is_beta2=false
|
||
[[ "$version1" =~ [a-z] ]] && is_beta1=true
|
||
[[ "$version2" =~ [a-z] ]] && is_beta2=true
|
||
|
||
# 如果基础版本号相同
|
||
if [[ "$base_version1" == "$base_version2" ]]; then
|
||
# 优先选择正式版
|
||
if [[ "$is_beta1" == true && "$is_beta2" == false ]]; then
|
||
return 2 # version1 < version2(正式版优先)
|
||
elif [[ "$is_beta1" == false && "$is_beta2" == true ]]; then
|
||
return 0 # version1 > version2(正式版优先)
|
||
fi
|
||
# 如果都是测试版或都是正式版,使用字母序比较
|
||
if [[ "$version1" < "$version2" ]]; then
|
||
return 2
|
||
else
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
# 基础版本号不同时,使用 sort -V 进行版本号比较
|
||
if printf '%s\n' "$base_version1" "$base_version2" | sort -V | head -1 | grep -q "^$base_version1$"; then
|
||
return 2 # version1 < version2
|
||
else
|
||
return 0 # version1 > version2
|
||
fi
|
||
}
|
||
|
||
# 验证版本 URL 是否有效
|
||
validateVersionUrl(){
|
||
local version="$1"
|
||
getSnellDownloadUrl "$version"
|
||
|
||
# 使用 HEAD 请求检查 URL 是否有效
|
||
if curl -I -s --max-time 10 "$snell_url" | head -1 | grep -q "200 OK"; then
|
||
return 0 # URL 有效
|
||
else
|
||
return 1 # URL 无效
|
||
fi
|
||
}
|
||
|
||
# 检查版本更新
|
||
checkVersionUpdate(){
|
||
local show_info=${1:-false} # 是否显示详细信息,默认为静默
|
||
update_available=false
|
||
current_installed_version=""
|
||
latest_available_version=""
|
||
best_version=""
|
||
|
||
if [[ -e ${snell_bin} && -e ${snell_conf} ]]; then
|
||
current_ver=$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}')
|
||
|
||
# 除 Snell v6 版外的旧版本已停更,不再检查在线更新
|
||
if [[ "$current_ver" != "6" ]]; then
|
||
update_available=false
|
||
return 0
|
||
fi
|
||
|
||
if [[ -e ${snell_version_file} ]]; then
|
||
installed_version=$(cat ${snell_version_file} | sed 's/^v//')
|
||
|
||
# 尝试从二进制程序获取真实版本 (容错处理)
|
||
if command -v timeout >/dev/null 2>&1 && [[ -x ${snell_bin} ]]; then
|
||
bin_version=$(timeout 1 ${snell_bin} --version 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+[a-zA-Z0-9]*' | head -1)
|
||
[[ -z "$bin_version" ]] && bin_version=$(timeout 1 ${snell_bin} -v 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+[a-zA-Z0-9]*' | head -1)
|
||
[[ -z "$bin_version" ]] && bin_version=$(timeout 1 ${snell_bin} --help 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+[a-zA-Z0-9]*' | head -1)
|
||
|
||
if [[ -n "$bin_version" && "$bin_version" != "$installed_version" ]]; then
|
||
installed_version="$bin_version"
|
||
echo "v${installed_version}" > "${snell_version_file}" # 修复不同步的问题
|
||
fi
|
||
fi
|
||
|
||
current_installed_version="$installed_version"
|
||
|
||
# 根据当前版本确定对应的脚本版本和网页版本
|
||
case "$current_ver" in
|
||
"4")
|
||
script_version=${snell_v4_version}
|
||
web_version=$(getLatestVersionFromWeb "v4")
|
||
;;
|
||
"5")
|
||
script_version=${snell_v5_version}
|
||
web_version=$(getLatestVersionFromWeb "v5")
|
||
;;
|
||
"6")
|
||
script_version=${snell_v6_version}
|
||
web_version=$(getLatestVersionFromWeb "v6")
|
||
;;
|
||
*)
|
||
script_version=""
|
||
web_version=""
|
||
;;
|
||
esac
|
||
|
||
# 优先使用脚本内置版本,除非网页版本更新
|
||
best_version="$installed_version"
|
||
version_source="已安装"
|
||
|
||
# 首先比较脚本内置版本
|
||
if [[ -n "$script_version" ]]; then
|
||
compareVersions "$best_version" "$script_version"
|
||
case $? in
|
||
2) # best_version < script_version
|
||
# 验证脚本内置版本的 URL 是否有效
|
||
if validateVersionUrl "$script_version"; then
|
||
best_version="$script_version"
|
||
version_source="脚本内置"
|
||
else
|
||
[[ "$show_info" == true ]] && echo -e "${Tip} 脚本内置版本 v${script_version} 的下载链接无效,跳过"
|
||
fi
|
||
;;
|
||
esac
|
||
fi
|
||
|
||
# 然后比较网页版本,只有当网页版本比当前最佳版本更新时才采用
|
||
if [[ -n "$web_version" ]]; then
|
||
compareVersions "$best_version" "$web_version"
|
||
case $? in
|
||
2) # best_version < web_version
|
||
# 验证网页版本的 URL 是否有效
|
||
if validateVersionUrl "$web_version"; then
|
||
best_version="$web_version"
|
||
version_source="官方网页"
|
||
else
|
||
[[ "$show_info" == true ]] && echo -e "${Tip} 网页版本 Snell v${web_version} 的下载链接无效,使用脚本内置版本"
|
||
fi
|
||
;;
|
||
1|0) # best_version >= web_version
|
||
# 脚本内置版本优先,无需显示
|
||
;;
|
||
esac
|
||
fi
|
||
|
||
latest_available_version="$best_version"
|
||
|
||
# 如果最佳版本与当前安装版本不同,则有更新可用
|
||
compareVersions "$installed_version" "$best_version"
|
||
if [[ $? -eq 2 ]]; then
|
||
update_available=true
|
||
if [[ "$show_info" == true ]]; then
|
||
echo -e "${Info} 发现更新:当前版本 Snell v${installed_version} -> 最新版本 Snell v${best_version}"
|
||
echo -e "${Info} 更新版本来源:${version_source}"
|
||
fi
|
||
fi
|
||
fi
|
||
fi
|
||
}
|
||
|
||
|
||
# 获取 Snell 下载链接
|
||
getSnellDownloadUrl(){
|
||
sysArch
|
||
local version=$1
|
||
snell_url="https://dl.nssurge.com/snell/snell-server-v${version}-linux-${arch}.zip"
|
||
}
|
||
|
||
|
||
|
||
# 下载并安装 Snell v2
|
||
downloadSnellV2() {
|
||
downloadSnellFromGitHub "${snell_v2_version}" "Snell v2 GitHub 备份源版" || downloadSnellFromGitee "${snell_v2_version}" "Snell v2 Gitee 备份源版"
|
||
}
|
||
|
||
# 下载并安装 Snell v3
|
||
downloadSnellV3() {
|
||
downloadSnellFromGitHub "${snell_v3_version}" "Snell v3 GitHub 备份源版" || downloadSnellFromGitee "${snell_v3_version}" "Snell v3 Gitee 备份源版"
|
||
}
|
||
|
||
# 通用下载并安装 Snell 函数(第三方备份源)
|
||
downloadSnellFromBackup(){
|
||
local version=$1
|
||
local version_type=$2
|
||
local backup_url=$3
|
||
|
||
echo -e "${Info} 试图请求 ${Yellow_font_prefix}${version_type}${Font_color_suffix} Snell Server ……"
|
||
|
||
wget --no-check-certificate -N "${backup_url}"
|
||
if [[ ! -e "snell-server-v${version}-linux-${arch}.zip" ]]; then
|
||
echo -e "${Error} Snell Server ${Yellow_font_prefix}${version_type}${Font_color_suffix} 下载失败!"
|
||
return 1
|
||
fi
|
||
|
||
unzip -o "snell-server-v${version}-linux-${arch}.zip"
|
||
if [[ ! -e "snell-server" ]]; then
|
||
echo -e "${Error} Snell Server ${Yellow_font_prefix}${version_type}${Font_color_suffix} 解压失败!"
|
||
return 1
|
||
fi
|
||
|
||
rm -rf "snell-server-v${version}-linux-${arch}.zip"
|
||
chmod +x snell-server
|
||
mv -f snell-server "${snell_bin}"
|
||
echo "v${version}" > "${snell_version_file}"
|
||
echo -e "${Info} Snell Server 主程序下载安装完毕!"
|
||
return 0
|
||
}
|
||
|
||
downloadSnellFromGitHub(){
|
||
local version=$1
|
||
local version_type=$2
|
||
local backup_url="https://raw.githubusercontent.com/xOS/Others/master/snell/v${version}/snell-server-v${version}-linux-${arch}.zip"
|
||
downloadSnellFromBackup "$version" "$version_type" "$backup_url"
|
||
}
|
||
|
||
downloadSnellFromGitee(){
|
||
local version=$1
|
||
local version_type=$2
|
||
local backup_url="https://gitee.com/ten/Others/raw/master/snell/v${version}/snell-server-v${version}-linux-${arch}.zip"
|
||
downloadSnellFromBackup "$version" "$version_type" "$backup_url"
|
||
}
|
||
|
||
# 下载并安装 Snell v4
|
||
downloadSnellV4(){
|
||
downloadSnell "${snell_v4_version}" "Snell v4 官网源版" || downloadSnellFromGitHub "${snell_v4_version}" "Snell v4 GitHub 备份源版" || downloadSnellFromGitee "${snell_v4_version}" "Snell v4 Gitee 备份源版"
|
||
}
|
||
|
||
# 下载并安装 Snell v5
|
||
downloadSnellV5(){
|
||
downloadSnell "${snell_v5_version}" "Snell v5 官网源版" || downloadSnellFromGitHub "${snell_v5_version}" "Snell v5 GitHub 备份源版" || downloadSnellFromGitee "${snell_v5_version}" "Snell v5 Gitee 备份源版"
|
||
}
|
||
|
||
# 通用下载并安装 Snell 函数(带回退机制)
|
||
downloadSnell(){
|
||
local version=$1
|
||
local version_type=$2
|
||
local allow_fallback=${3:-false}
|
||
local fallback_version=$4
|
||
|
||
echo -e "${Info} 试图请求 ${Yellow_font_prefix}${version_type}${Font_color_suffix} Snell Server ……"
|
||
getSnellDownloadUrl "${version}"
|
||
|
||
# 首先检查 URL 是否有效
|
||
if ! curl -I -s --max-time 10 "$snell_url" | head -1 | grep -q "200 OK"; then
|
||
echo -e "${Error} Snell Server ${Yellow_font_prefix}${version_type}${Font_color_suffix} 下载链接无效 (404)!"
|
||
|
||
# 如果允许回退且提供了回退版本
|
||
if [[ "$allow_fallback" == true && -n "$fallback_version" ]]; then
|
||
echo -e "${Info} 尝试回退到已安装版本 v${fallback_version}..."
|
||
getSnellDownloadUrl "${fallback_version}"
|
||
if curl -I -s --max-time 10 "$snell_url" | head -1 | grep -q "200 OK"; then
|
||
version="$fallback_version"
|
||
echo -e "${Info} 回退成功,使用版本 v${version}"
|
||
else
|
||
echo -e "${Error} 回退版本也无法下载!"
|
||
return 1
|
||
fi
|
||
else
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
wget --no-check-certificate -N "${snell_url}"
|
||
if [[ ! -e "snell-server-v${version}-linux-${arch}.zip" ]]; then
|
||
echo -e "${Error} Snell Server ${Yellow_font_prefix}${version_type}${Font_color_suffix} 下载失败!"
|
||
return 1 && exit 1
|
||
else
|
||
unzip -o "snell-server-v${version}-linux-${arch}.zip"
|
||
fi
|
||
if [[ ! -e "snell-server" ]]; then
|
||
echo -e "${Error} Snell Server ${Yellow_font_prefix}${version_type}${Font_color_suffix} 解压失败!"
|
||
return 1 && exit 1
|
||
else
|
||
rm -rf "snell-server-v${version}-linux-${arch}.zip"
|
||
chmod +x snell-server
|
||
mv -f snell-server "${snell_bin}"
|
||
echo "v${version}" > ${snell_version_file}
|
||
echo -e "${Info} Snell Server 主程序下载安装完毕!"
|
||
return 0
|
||
fi
|
||
}
|
||
|
||
# 安装 Snell
|
||
installSnell() {
|
||
if [[ ! -e "${snell_dir}" ]]; then
|
||
mkdir "${snell_dir}"
|
||
else
|
||
[[ -e "${snell_bin}" ]] && rm -rf "${snell_bin}"
|
||
fi
|
||
echo -e "选择安装版本${Yellow_font_prefix}[2-6]${Font_color_suffix}
|
||
==================================
|
||
${Green_font_prefix} 2.${Font_color_suffix} v2 ${Green_font_prefix} 3.${Font_color_suffix} v3 ${Green_font_prefix} 4.${Font_color_suffix} v4 ${Green_font_prefix} 5.${Font_color_suffix} v5 ${Green_font_prefix} 6.${Font_color_suffix} v6
|
||
=================================="
|
||
read -e -p "(默认:4.v4):" ver
|
||
[[ -z "${ver}" ]] && ver="4"
|
||
if [[ ${ver} == "2" ]]; then
|
||
installSnellV2
|
||
elif [[ ${ver} == "3" ]]; then
|
||
installSnellV3
|
||
elif [[ ${ver} == "4" ]]; then
|
||
installSnellV4
|
||
elif [[ ${ver} == "5" ]]; then
|
||
installSnellV5
|
||
elif [[ ${ver} == "6" ]]; then
|
||
installSnellV6
|
||
else
|
||
installSnellV4
|
||
fi
|
||
}
|
||
|
||
# 配置服务
|
||
setupService(){
|
||
echo '
|
||
[Unit]
|
||
Description=Snell Service
|
||
After=network.target
|
||
[Service]
|
||
LimitNOFILE=32767
|
||
Type=simple
|
||
User=root
|
||
Restart=on-failure
|
||
RestartSec=5s
|
||
ExecStart=/usr/local/bin/snell-server -c /etc/snell/config.conf
|
||
[Install]
|
||
WantedBy=multi-user.target' > /etc/systemd/system/snell-server.service
|
||
systemctl daemon-reload
|
||
systemctl enable snell-server
|
||
echo -e "${Info} Snell Server 服务配置完成!"
|
||
}
|
||
|
||
|
||
# 针对 Snell v6 检查密钥长度
|
||
checkPskForV6(){
|
||
if [[ ${#psk} -lt 16 ]] || [[ ${#psk} -gt 255 ]]; then
|
||
echo -e "${Error} 检测到当前密钥 (${psk}) 长度不符合 16-255 位要求,Snell v6 要求升级密钥!"
|
||
echo -e "请选择处理方式:"
|
||
echo -e " 1. 自动生成安全的随机长密钥(推荐)"
|
||
echo -e " 2. 手动输入新的长密钥"
|
||
read -e -p "(默认: 1):" psk_choice
|
||
[[ -z "${psk_choice}" ]] && psk_choice="1"
|
||
|
||
if [[ "${psk_choice}" == "2" ]]; then
|
||
while true; do
|
||
read -e -p "请输入新的密钥(16-255位): " new_psk
|
||
if [[ ${#new_psk} -ge 16 ]] && [[ ${#new_psk} -le 255 ]]; then
|
||
psk=$new_psk
|
||
break
|
||
else
|
||
echo -e "${Error} 密钥长度必须在 16 到 255 位之间!"
|
||
fi
|
||
done
|
||
else
|
||
psk=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 20)
|
||
echo -e "${Info} 已自动生成新密钥: ${Green_font_prefix}${psk}${Font_color_suffix}"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# 写入配置文件
|
||
writeConfig(){
|
||
local config_content="[snell-server]
|
||
listen = ${listen_val}
|
||
$(if [[ ${ver} != "6" ]]; then echo "ipv6 = ${ipv6}"; else echo "# ipv6 = ${ipv6}"; fi)
|
||
psk = ${psk}
|
||
$(if [[ ${ver} != "6" ]]; then echo "obfs = ${obfs}"; else echo "# obfs = ${obfs}"; fi)"
|
||
|
||
if [[ ${ver} != "6" && ${obfs} != "off" ]]; then
|
||
config_content+=$'\n'"obfs-host = ${host}"
|
||
elif [[ ${ver} == "6" && ${obfs} != "off" ]]; then
|
||
config_content+=$'\n'"# obfs-host = ${host}"
|
||
fi
|
||
|
||
config_content+=$'\n'"tfo = ${tfo}
|
||
dns = ${dns}"
|
||
|
||
if [[ ${ver} == "6" ]]; then
|
||
if [[ -n "${dns_ip_pref}" ]]; then
|
||
config_content+=$'\n'"dns-ip-preference = ${dns_ip_pref}"
|
||
else
|
||
config_content+=$'\n'"dns-ip-preference = default"
|
||
fi
|
||
if [[ -n "${mode}" ]]; then
|
||
config_content+=$'\n'"mode = ${mode}"
|
||
else
|
||
config_content+=$'\n'"mode = default"
|
||
fi
|
||
else
|
||
if [[ -n "${dns_ip_pref}" ]]; then
|
||
config_content+=$'\n'"# dns-ip-preference = ${dns_ip_pref}"
|
||
fi
|
||
if [[ -n "${mode}" ]]; then
|
||
config_content+=$'\n'"# mode = ${mode}"
|
||
fi
|
||
fi
|
||
|
||
if [[ -n "${egress_interface}" ]]; then
|
||
config_content+=$'\n'"egress-interface = ${egress_interface}"
|
||
fi
|
||
|
||
config_content+=$'\n'"version = ${ver}"
|
||
|
||
# 保留未知的自定义配置项
|
||
if [[ -f "${snell_conf}" ]]; then
|
||
local custom_configs=$(awk -F '=' '{
|
||
if ($1 !~ /^[[:space:]]*(listen|ipv6|psk|obfs|obfs-host|tfo|dns|dns-ip-preference|mode|version|egress-interface|\[snell-server\]|#)[[:space:]]*/) {
|
||
if (NF > 0 && $0 !~ /^[[:space:]]*$/) {
|
||
print $0
|
||
}
|
||
}
|
||
}' "${snell_conf}")
|
||
|
||
if [[ -n "${custom_configs}" ]]; then
|
||
config_content+=$'\n\n# Custom Configs\n'"${custom_configs}"
|
||
fi
|
||
fi
|
||
|
||
echo "${config_content}" > "${snell_conf}"
|
||
}
|
||
|
||
|
||
|
||
|
||
# 读取配置文件
|
||
readConfig(){
|
||
[[ ! -e ${snell_conf} ]] && echo -e "${Error} Snell Server 配置文件不存在!" && exit 1
|
||
listen_val=$(grep -E '^listen\s*=' ${snell_conf} | sed -E 's/^listen\s*=\s*//' | xargs)
|
||
port=$(echo "$listen_val" | awk -F',' '{print $1}' | awk -F':' '{print $NF}' | xargs)
|
||
ipv6=$(cat ${snell_conf}|grep 'ipv6 = '|awk -F 'ipv6 = ' '{print $NF}')
|
||
psk=$(cat ${snell_conf}|grep 'psk = '|awk -F 'psk = ' '{print $NF}')
|
||
obfs=$(cat ${snell_conf}|grep 'obfs = '|awk -F 'obfs = ' '{print $NF}')
|
||
host=$(cat ${snell_conf}|grep 'obfs-host = '|awk -F 'obfs-host = ' '{print $NF}')
|
||
tfo=$(cat ${snell_conf}|grep 'tfo = '|awk -F 'tfo = ' '{print $NF}')
|
||
dns=$(cat ${snell_conf}|grep 'dns = '|awk -F 'dns = ' '{print $NF}')
|
||
ver=$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}')
|
||
dns_ip_pref=$(cat ${snell_conf}|grep 'dns-ip-preference = '|awk -F 'dns-ip-preference = ' '{print $NF}')
|
||
[[ -z "$dns_ip_pref" && "$ver" == "6" ]] && dns_ip_pref="default"
|
||
mode=$(cat ${snell_conf}|grep 'mode = '|awk -F 'mode = ' '{print $NF}')
|
||
[[ -z "$mode" && "$ver" == "6" ]] && mode="default"
|
||
egress_interface=$(cat ${snell_conf}|grep 'egress-interface = '|awk -F 'egress-interface = ' '{print $NF}')
|
||
}
|
||
|
||
# 设置端口
|
||
setPort(){
|
||
local orig_port="$port"
|
||
while true; do
|
||
echo -e "${Tip} 本步骤不涉及系统防火墙端口操作,请手动放行相应端口!"
|
||
echo -e "请输入 Snell Server 端口${Yellow_font_prefix}[1-65535]${Font_color_suffix}"
|
||
local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}: 2345):"
|
||
[[ -n "$port" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${port} | ${Green_font_prefix}默认${Font_color_suffix}: 2345):"
|
||
echo -e -n "${p_prompt}"
|
||
read -e input_port
|
||
[[ -z "${input_port}" ]] && input_port="2345"
|
||
port=$input_port
|
||
if [[ "${ver}" == "6" || "$current_installed_ver" == "6" ]]; then
|
||
listen_val="0.0.0.0:${port},[::]:${port}"
|
||
else
|
||
listen_val="::0:${port}"
|
||
fi
|
||
if [[ $port =~ ^[0-9]+$ ]] && [[ $port -ge 1 && $port -le 65535 ]]; then
|
||
if [[ "$port" != "$orig_port" ]] && ss -tuln | grep -q ":$port "; then
|
||
echo -e "${Error} 端口 $port 已被占用,请选择其他端口。"
|
||
else
|
||
echo && echo "=============================="
|
||
echo -e "端口 : ${Red_background_prefix} ${port} ${Font_color_suffix}"
|
||
echo "==============================" && echo
|
||
break
|
||
fi
|
||
else
|
||
echo "输入错误, 请输入正确的端口号。"
|
||
sleep 2s
|
||
setPort
|
||
fi
|
||
done
|
||
}
|
||
|
||
|
||
# 设置 IPv6
|
||
setIpv6(){
|
||
echo -e "是否开启 IPv6 解析?
|
||
==================================
|
||
${Green_font_prefix} 1.${Font_color_suffix} 开启 ${Green_font_prefix} 2.${Font_color_suffix} 关闭
|
||
=================================="
|
||
local current_opt="2"
|
||
[[ "$ipv6" == "true" ]] && current_opt="1"
|
||
local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}:2.关闭):"
|
||
[[ -n "$ipv6" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${current_opt}.${ipv6} | ${Green_font_prefix}默认${Font_color_suffix}:2.关闭):"
|
||
echo -e -n "${p_prompt}"
|
||
read -e input_ipv6
|
||
[[ -z "${input_ipv6}" ]] && input_ipv6="2"
|
||
if [[ ${input_ipv6} == "1" ]]; then
|
||
ipv6=true
|
||
else
|
||
ipv6=false
|
||
fi
|
||
echo && echo "=================================="
|
||
echo -e "IPv6 解析 开启状态:${Red_background_prefix} ${ipv6} ${Font_color_suffix}"
|
||
echo "==================================" && echo
|
||
}
|
||
|
||
# 设置密钥
|
||
setPSK(){
|
||
echo "请输入 Snell Server 密钥 [0-9][a-z][A-Z] "
|
||
|
||
local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}: 随机生成):"
|
||
[[ -n "$psk" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${psk} | ${Green_font_prefix}默认${Font_color_suffix}: 随机生成):"
|
||
|
||
if [[ "$ver" == "6" || "$current_installed_ver" == "6" ]]; then
|
||
echo -e "${Tip} 当前目标协议为 Snell v6,密钥长度要求在 16-255 位之间"
|
||
while true; do
|
||
echo -e -n "${p_prompt}"
|
||
read -e input_psk
|
||
if [[ -z "${input_psk}" ]]; then
|
||
psk=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 20)
|
||
break
|
||
else
|
||
if [[ ${#input_psk} -ge 16 ]] && [[ ${#input_psk} -le 255 ]]; then
|
||
psk=$input_psk
|
||
break
|
||
else
|
||
echo -e "${Error} Snell v6 密钥长度必须在 16 到 255 位之间,请重新输入!"
|
||
fi
|
||
fi
|
||
done
|
||
else
|
||
echo -e -n "${p_prompt}"
|
||
read -e input_psk
|
||
if [[ -z "${input_psk}" ]]; then
|
||
psk=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 16)
|
||
else
|
||
psk=$input_psk
|
||
fi
|
||
fi
|
||
|
||
echo && echo "=============================="
|
||
echo -e "密钥 : ${Red_background_prefix} ${psk} ${Font_color_suffix}"
|
||
echo "==============================" && echo
|
||
}
|
||
|
||
# 设置 OBFS
|
||
setObfs(){
|
||
echo -e "配置 OBFS,${Tip} 无特殊作用不建议启用该项。
|
||
==================================
|
||
${Green_font_prefix} 1.${Font_color_suffix} TLS ${Green_font_prefix} 2.${Font_color_suffix} HTTP ${Green_font_prefix} 3.${Font_color_suffix} 关闭
|
||
=================================="
|
||
local current_opt="3"
|
||
if [[ "$obfs" == "tls" ]]; then current_opt="1"; elif [[ "$obfs" == "http" ]]; then current_opt="2"; fi
|
||
local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}:3.关闭):"
|
||
[[ -n "$obfs" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${current_opt}.${obfs} | ${Green_font_prefix}默认${Font_color_suffix}:3.关闭):"
|
||
echo -e -n "${p_prompt}"
|
||
read -e input_obfs
|
||
[[ -z "${input_obfs}" ]] && input_obfs="3"
|
||
obfs=$input_obfs
|
||
if [[ ${obfs} == "1" ]]; then
|
||
obfs="tls"
|
||
setHost # 强制设置 OBFS 域名
|
||
elif [[ ${obfs} == "2" ]]; then
|
||
obfs="http"
|
||
setHost # 强制设置 OBFS 域名
|
||
elif [[ ${obfs} == "3" ]]; then
|
||
obfs="off"
|
||
host="" # 清空 host
|
||
else
|
||
obfs="off"
|
||
host="" # 清空 host
|
||
fi
|
||
echo && echo "=================================="
|
||
echo -e "OBFS 状态:${Red_background_prefix} ${obfs} ${Font_color_suffix}"
|
||
if [[ ${obfs} != "off" ]]; then
|
||
echo -e "OBFS 域名:${Red_background_prefix} ${host} ${Font_color_suffix}"
|
||
fi
|
||
echo "==================================" && echo
|
||
}
|
||
|
||
|
||
# 设置协议版本
|
||
setVer(){
|
||
echo -e "配置 Snell Server 协议版本${Yellow_font_prefix}[2-6]${Font_color_suffix}
|
||
==================================
|
||
${Green_font_prefix} 2.${Font_color_suffix} v2 ${Green_font_prefix} 3.${Font_color_suffix} v3 ${Green_font_prefix} 4.${Font_color_suffix} v4 ${Green_font_prefix} 5.${Font_color_suffix} v5 ${Green_font_prefix} 6.${Font_color_suffix} v6
|
||
=================================="
|
||
local current_ver_display=""
|
||
[[ -n "$ver" ]] && current_ver_display="${ver}.v${ver}"
|
||
local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}:4.v4):"
|
||
[[ -n "$ver" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${current_ver_display} | ${Green_font_prefix}默认${Font_color_suffix}:4.v4):"
|
||
echo -e -n "${p_prompt}"
|
||
read -e input_ver
|
||
[[ -z "${input_ver}" ]] && input_ver="4"
|
||
ver=$input_ver
|
||
if [[ ${ver} == "2" ]]; then
|
||
ver=2
|
||
elif [[ ${ver} == "3" ]]; then
|
||
ver=3
|
||
elif [[ ${ver} == "4" ]]; then
|
||
ver=4
|
||
elif [[ ${ver} == "5" ]]; then
|
||
ver=5
|
||
elif [[ ${ver} == "6" ]]; then
|
||
ver=6
|
||
else
|
||
ver=4
|
||
fi
|
||
echo && echo "=================================="
|
||
echo -e "Snell Server 协议版本:${Red_background_prefix} ${ver} ${Font_color_suffix}"
|
||
echo "==================================" && echo
|
||
}
|
||
|
||
# 设置 OBFS 域名
|
||
setHost(){
|
||
echo "请输入 Snell Server 域名,Snell v4 版本及以上如无特别需求可忽略。"
|
||
local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}: www.wechat.com):"
|
||
[[ -n "$host" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${host} | ${Green_font_prefix}默认${Font_color_suffix}: www.wechat.com):"
|
||
echo -e -n "${p_prompt}"
|
||
read -e input_host
|
||
[[ -z "${input_host}" ]] && input_host="www.wechat.com"
|
||
host=$input_host
|
||
echo && echo "=============================="
|
||
echo -e "域名 : ${Red_background_prefix} ${host} ${Font_color_suffix}"
|
||
echo "==============================" && echo
|
||
}
|
||
|
||
# 设置 TCP Fast Open
|
||
setTFO(){
|
||
echo -e "是否开启 TCP Fast Open?
|
||
==================================
|
||
${Green_font_prefix} 1.${Font_color_suffix} 开启 ${Green_font_prefix} 2.${Font_color_suffix} 关闭
|
||
=================================="
|
||
local current_opt="1"
|
||
if [[ "$tfo" == "false" ]]; then current_opt="2"; fi
|
||
local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}:1.开启):"
|
||
[[ -n "$tfo" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${current_opt}.${tfo} | ${Green_font_prefix}默认${Font_color_suffix}:1.开启):"
|
||
echo -e -n "${p_prompt}"
|
||
read -e input_tfo
|
||
[[ -z "${input_tfo}" ]] && input_tfo="1"
|
||
tfo=$input_tfo
|
||
if [[ ${tfo} == "1" ]]; then
|
||
tfo=true
|
||
enableTCPFastOpen
|
||
else
|
||
tfo=false
|
||
fi
|
||
echo && echo "=================================="
|
||
echo -e "TCP Fast Open 开启状态:${Red_background_prefix} ${tfo} ${Font_color_suffix}"
|
||
echo "==================================" && echo
|
||
}
|
||
|
||
# 设置 DNS
|
||
setDNS(){
|
||
echo -e "${Tip} 请输入正确格式的 DNS,多条记录以英文逗号隔开,仅支持Snell v4.1.0b1 版本及以上。"
|
||
local p_prompt="(${Green_font_prefix}默认值${Font_color_suffix}:1.1.1.1, 8.8.8.8, 2001:4860:4860::8888):"
|
||
[[ -n "$dns" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${dns} | ${Green_font_prefix}默认值${Font_color_suffix}:1.1.1.1, 8.8.8.8, 2001:4860:4860::8888):"
|
||
echo -e -n "${p_prompt}"
|
||
read -e input_dns
|
||
[[ -z "${input_dns}" ]] && input_dns="1.1.1.1, 8.8.8.8, 2001:4860:4860::8888"
|
||
dns=$input_dns
|
||
echo && echo "=================================="
|
||
echo -e "当前 DNS 为:${Red_background_prefix} ${dns} ${Font_color_suffix}"
|
||
echo "==================================" && echo
|
||
}
|
||
|
||
|
||
# 设置 DNS IP 偏好 (Snell v6 专属)
|
||
setDNSIPPref(){
|
||
echo -e "配置 DNS IP 偏好 (Snell v6 专属)
|
||
==================================
|
||
${Green_font_prefix} 1.${Font_color_suffix} default ${Green_font_prefix} 2.${Font_color_suffix} prefer-ipv4 ${Green_font_prefix} 3.${Font_color_suffix} prefer-ipv6 ${Green_font_prefix} 4.${Font_color_suffix} ipv4-only ${Green_font_prefix} 5.${Font_color_suffix} ipv6-only
|
||
=================================="
|
||
local current_opt="1"
|
||
if [[ "$dns_ip_pref" == "prefer-ipv4" ]]; then current_opt="2"; elif [[ "$dns_ip_pref" == "prefer-ipv6" ]]; then current_opt="3"; elif [[ "$dns_ip_pref" == "ipv4-only" ]]; then current_opt="4"; elif [[ "$dns_ip_pref" == "ipv6-only" ]]; then current_opt="5"; fi
|
||
local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}:1.default):"
|
||
[[ -n "$dns_ip_pref" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${current_opt}.${dns_ip_pref} | ${Green_font_prefix}默认${Font_color_suffix}:1.default):"
|
||
echo -e -n "${p_prompt}"
|
||
read -e input_pref
|
||
[[ -z "${input_pref}" && -n "$dns_ip_pref" ]] && input_pref=$current_opt
|
||
[[ -z "${input_pref}" && -z "$dns_ip_pref" ]] && input_pref="1"
|
||
dns_ip_pref_opt=$input_pref
|
||
if [[ ${dns_ip_pref_opt} == "2" ]]; then
|
||
dns_ip_pref="prefer-ipv4"
|
||
elif [[ ${dns_ip_pref_opt} == "3" ]]; then
|
||
dns_ip_pref="prefer-ipv6"
|
||
elif [[ ${dns_ip_pref_opt} == "4" ]]; then
|
||
dns_ip_pref="ipv4-only"
|
||
elif [[ ${dns_ip_pref_opt} == "5" ]]; then
|
||
dns_ip_pref="ipv6-only"
|
||
else
|
||
dns_ip_pref="default"
|
||
fi
|
||
echo && echo "=================================="
|
||
echo -e "DNS IP 偏好 状态:${Red_background_prefix} ${dns_ip_pref} ${Font_color_suffix}"
|
||
echo "==================================" && echo
|
||
}
|
||
|
||
# 设置 模式 (Snell v6 专属)
|
||
setMode(){
|
||
echo -e "配置 混淆模式
|
||
==================================
|
||
${Green_font_prefix} 1.${Font_color_suffix} default ${Green_font_prefix} 2.${Font_color_suffix} unshaped ${Green_font_prefix} 3.${Font_color_suffix} unsafe-raw
|
||
=================================="
|
||
local current_opt="1"
|
||
if [[ "$mode" == "unshaped" ]]; then current_opt="2"; elif [[ "$mode" == "unsafe-raw" ]]; then current_opt="3"; fi
|
||
local p_prompt="(${Green_font_prefix}默认${Font_color_suffix}:1.default):"
|
||
[[ -n "$mode" ]] && p_prompt="(${Yellow_font_prefix}当前${Font_color_suffix}: ${current_opt}.${mode} | ${Green_font_prefix}默认${Font_color_suffix}:1.default):"
|
||
echo -e -n "${p_prompt}"
|
||
read -e input_pref
|
||
[[ -z "${input_pref}" && -n "$mode" ]] && input_pref=$current_opt
|
||
[[ -z "${input_pref}" && -z "$mode" ]] && input_pref="1"
|
||
mode_opt=$input_pref
|
||
if [[ ${mode_opt} == "2" ]]; then
|
||
mode="unshaped"
|
||
elif [[ ${mode_opt} == "3" ]]; then
|
||
mode="unsafe-raw"
|
||
else
|
||
mode="default"
|
||
fi
|
||
echo && echo "=================================="
|
||
echo -e "混淆模式:${Red_background_prefix} ${mode} ${Font_color_suffix}"
|
||
echo "==================================" && echo
|
||
}
|
||
|
||
# 切换二进制版本
|
||
switchBinary(){
|
||
local old_ver=$1
|
||
local new_ver=$2
|
||
|
||
echo -e "${Info} 开始下载 Snell v${new_ver} 版本..."
|
||
systemctl stop snell-server
|
||
|
||
if [[ -e "${snell_bin}" ]]; then
|
||
echo -e "${Info} 备份当前程序文件..."
|
||
cp "${snell_bin}" "${snell_bin}.v${old_ver}.backup.$(date +%Y%m%d_%H%M%S)"
|
||
fi
|
||
|
||
local target_full_version=""
|
||
local web_version=$(getLatestVersionFromWeb "v${new_ver}")
|
||
local script_version=""
|
||
|
||
case "$new_ver" in
|
||
"2") script_version=${snell_v2_version} ;;
|
||
"3") script_version=${snell_v3_version} ;;
|
||
"4") script_version=${snell_v4_version} ;;
|
||
"5") script_version=${snell_v5_version} ;;
|
||
"6") script_version=${snell_v6_version} ;;
|
||
esac
|
||
|
||
if [[ -n "$web_version" ]]; then
|
||
if validateVersionUrl "$web_version"; then
|
||
target_full_version="$web_version"
|
||
echo -e "${Info} 使用网页获取的 Snell v${new_ver} 版本: v${target_full_version}"
|
||
fi
|
||
fi
|
||
|
||
if [[ -z "$target_full_version" && -n "$script_version" ]]; then
|
||
if validateVersionUrl "$script_version"; then
|
||
target_full_version="$script_version"
|
||
echo -e "${Info} 使用脚本内置的 Snell v${new_ver} 版本: v${target_full_version}"
|
||
fi
|
||
fi
|
||
|
||
if [[ -z "$target_full_version" ]]; then
|
||
target_full_version="$script_version"
|
||
fi
|
||
|
||
if [[ -n "$target_full_version" ]]; then
|
||
downloadSnell "${target_full_version}" "Snell v${new_ver} 版本" true "$old_ver"
|
||
if [[ $? -eq 0 ]]; then
|
||
echo -e "${Info} Snell v${new_ver} 核心切换成功!"
|
||
return 0
|
||
else
|
||
echo -e "${Error} Snell v${new_ver} 下载失败,正在回滚..."
|
||
fi
|
||
else
|
||
echo -e "${Error} 无法找到 Snell v${new_ver} 的下载链接,切换失败!"
|
||
fi
|
||
|
||
backup_file=$(ls -t "${snell_bin}".v${old_ver}.backup.* 2>/dev/null | head -1)
|
||
if [[ -n "$backup_file" && -e "$backup_file" ]]; then
|
||
cp "$backup_file" "${snell_bin}"
|
||
echo "v${old_ver}" > ${snell_version_file}
|
||
echo -e "${Info} 已回滚到 Snell v${old_ver} 版本"
|
||
fi
|
||
systemctl start snell-server
|
||
return 1
|
||
}
|
||
|
||
# 修改配置
|
||
setConfig(){
|
||
checkInstalledStatus
|
||
local current_installed_ver=$(cat ${snell_version_file} | sed 's/^v//' | awk -F. '{print $1}')
|
||
[[ -z "$current_installed_ver" ]] && current_installed_ver=$ver
|
||
|
||
echo && echo -e "请输入要操作配置项的序号,然后回车
|
||
=============================="
|
||
echo -e " ${Green_font_prefix}1.${Font_color_suffix} 修改 端口"
|
||
echo -e " ${Green_font_prefix}2.${Font_color_suffix} 修改 密钥"
|
||
if [[ "$current_installed_ver" != "6" ]]; then
|
||
echo -e " ${Green_font_prefix}3.${Font_color_suffix} 配置 OBFS"
|
||
echo -e " ${Green_font_prefix}4.${Font_color_suffix} 配置 OBFS 域名"
|
||
echo -e " ${Green_font_prefix}5.${Font_color_suffix} 开关 IPv6 解析"
|
||
fi
|
||
echo -e " ${Green_font_prefix}6.${Font_color_suffix} 开关 TCP Fast Open"
|
||
echo -e " ${Green_font_prefix}7.${Font_color_suffix} 配置 DNS"
|
||
echo -e " ${Green_font_prefix}8.${Font_color_suffix} 配置 Snell 协议版本"
|
||
if [[ "$current_installed_ver" == "6" ]]; then
|
||
echo -e " ${Green_font_prefix}9.${Font_color_suffix} 配置 DNS IP 偏好"
|
||
echo -e "${Green_font_prefix}10.${Font_color_suffix} 配置 混淆模式"
|
||
fi
|
||
echo -e "=============================="
|
||
echo -e "${Green_font_prefix}11.${Font_color_suffix} 修改 全部配置" && echo
|
||
read -e -p "(默认: 取消):" modify
|
||
[[ -z "${modify}" ]] && echo "已取消..." && exit 1
|
||
if [[ "${modify}" == "1" ]]; then
|
||
readConfig
|
||
setPort
|
||
writeConfig
|
||
restartSnell
|
||
elif [[ "${modify}" == "2" ]]; then
|
||
readConfig
|
||
setPSK
|
||
writeConfig
|
||
restartSnell
|
||
elif [[ "${modify}" == "3" ]]; then
|
||
readConfig
|
||
setObfs # 在 setObfs 中已处理 host
|
||
writeConfig
|
||
restartSnell
|
||
elif [[ "${modify}" == "4" ]]; then
|
||
readConfig
|
||
if [[ ${obfs} == "off" ]]; then
|
||
echo -e "${Error} OBFS 当前为 off,无法修改 OBFS 域名。"
|
||
else
|
||
setHost
|
||
writeConfig
|
||
restartSnell
|
||
fi
|
||
elif [[ "${modify}" == "5" ]]; then
|
||
readConfig
|
||
setIpv6
|
||
writeConfig
|
||
restartSnell
|
||
elif [[ "${modify}" == "6" ]]; then
|
||
readConfig
|
||
setTFO
|
||
writeConfig
|
||
restartSnell
|
||
elif [[ "${modify}" == "7" ]]; then
|
||
readConfig
|
||
setDNS
|
||
writeConfig
|
||
restartSnell
|
||
elif [[ "${modify}" == "8" ]]; then
|
||
readConfig
|
||
local current_installed_ver=$(cat ${snell_version_file} | sed 's/^v//' | awk -F. '{print $1}')
|
||
[[ -z "$current_installed_ver" ]] && current_installed_ver=$ver
|
||
|
||
setVer
|
||
|
||
if [[ "$ver" != "$current_installed_ver" ]]; then
|
||
if [[ "$ver" -gt "$current_installed_ver" ]]; then
|
||
echo -e "${Info} 协议版本将从 Snell v${current_installed_ver} 升级到 Snell v${ver}"
|
||
echo -e "确定要升级吗?(y/N)"
|
||
else
|
||
echo -e "${Info} 协议版本将从 Snell v${current_installed_ver} 降级到 Snell v${ver}"
|
||
echo -e "确定要降级吗?(y/N)"
|
||
fi
|
||
read -e -p "(默认: n):" confirm
|
||
[[ -z "${confirm}" ]] && confirm="n"
|
||
|
||
if [[ ${confirm} == [Yy] ]]; then
|
||
if [[ "$ver" == "6" ]]; then
|
||
setDNSIPPref
|
||
setMode
|
||
checkPskForV6
|
||
else
|
||
setObfs
|
||
fi
|
||
|
||
switchBinary "$current_installed_ver" "$ver"
|
||
if [[ $? -ne 0 ]]; then
|
||
ver=$current_installed_ver
|
||
else
|
||
writeConfig
|
||
systemctl restart snell-server
|
||
for ((i=0; i<10; i++)); do
|
||
sleep 1
|
||
checkStatus
|
||
if [[ "$status" == "running" ]]; then
|
||
break
|
||
fi
|
||
done
|
||
if [[ "$status" != "running" ]]; then
|
||
echo -e "${Error} 协议切换后服务启动失败,以下为错误日志:"
|
||
journalctl -u snell-server -n 20 --no-pager
|
||
echo -e "${Error} 正在回滚到原版本..."
|
||
backup_file=$(ls -t "${snell_bin}".v${current_installed_ver}.backup.* 2>/dev/null | head -1)
|
||
if [[ -n "$backup_file" && -e "$backup_file" ]]; then
|
||
cp "$backup_file" "${snell_bin}"
|
||
echo "v${current_installed_ver}" > ${snell_version_file}
|
||
ver=$current_installed_ver
|
||
writeConfig
|
||
systemctl start snell-server
|
||
echo -e "${Info} 已回滚到 Snell v${current_installed_ver} 版本并恢复配置"
|
||
fi
|
||
else
|
||
echo -e "${Info} Snell Server 重启完毕!"
|
||
fi
|
||
sleep 3s
|
||
startMenu
|
||
return
|
||
fi
|
||
else
|
||
echo -e "${Info} 已取消切换,保持原配置"
|
||
ver=$current_installed_ver
|
||
fi
|
||
else
|
||
echo -e "${Info} 版本未发生改变"
|
||
fi
|
||
|
||
writeConfig
|
||
restartSnell
|
||
elif [[ "${modify}" == "9" ]]; then
|
||
readConfig
|
||
if [[ "$current_installed_ver" != "6" ]]; then
|
||
echo -e "${Error} 当前版本不是 Snell v6,不支持 DNS IP 偏好配置!"
|
||
sleep 2s; setConfig; return
|
||
fi
|
||
setDNSIPPref
|
||
writeConfig
|
||
restartSnell
|
||
elif [[ "${modify}" == "10" ]]; then
|
||
readConfig
|
||
if [[ "$current_installed_ver" != "6" ]]; then
|
||
echo -e "${Error} 当前版本不是 Snell v6,不支持 混淆模式配置!"
|
||
sleep 2s; setConfig; return
|
||
fi
|
||
setMode
|
||
writeConfig
|
||
restartSnell
|
||
elif [[ "${modify}" == "11" ]]; then
|
||
readConfig
|
||
local current_installed_ver=$(cat ${snell_version_file} | sed 's/^v//' | awk -F. '{print $1}')
|
||
[[ -z "$current_installed_ver" ]] && current_installed_ver=$ver
|
||
|
||
setVer
|
||
|
||
if [[ "$ver" != "$current_installed_ver" ]]; then
|
||
if [[ "$ver" -gt "$current_installed_ver" ]]; then
|
||
echo -e "${Info} 协议版本将从 Snell v${current_installed_ver} 升级到 Snell v${ver}"
|
||
echo -e "确定要升级吗?(y/N)"
|
||
else
|
||
echo -e "${Info} 协议版本将从 Snell v${current_installed_ver} 降级到 Snell v${ver}"
|
||
echo -e "确定要降级吗?(y/N)"
|
||
fi
|
||
read -e -p "(默认: n):" confirm
|
||
[[ -z "${confirm}" ]] && confirm="n"
|
||
|
||
if [[ ${confirm} != [Yy] ]]; then
|
||
echo -e "${Info} 已取消切换,保持原版本"
|
||
ver=$current_installed_ver
|
||
fi
|
||
fi
|
||
|
||
setPort
|
||
setPSK
|
||
if [[ "$ver" != "6" ]]; then
|
||
setIpv6
|
||
fi
|
||
setTFO
|
||
setDNS
|
||
|
||
if [[ "$ver" == "6" ]]; then
|
||
setDNSIPPref
|
||
setMode
|
||
checkPskForV6
|
||
else
|
||
setObfs
|
||
fi
|
||
|
||
if [[ "$ver" != "$current_installed_ver" ]]; then
|
||
switchBinary "$current_installed_ver" "$ver"
|
||
if [[ $? -ne 0 ]]; then
|
||
ver=$current_installed_ver
|
||
writeConfig
|
||
restartSnell
|
||
else
|
||
writeConfig
|
||
systemctl restart snell-server
|
||
for ((i=0; i<10; i++)); do
|
||
sleep 1
|
||
checkStatus
|
||
if [[ "$status" == "running" ]]; then
|
||
break
|
||
fi
|
||
done
|
||
if [[ "$status" != "running" ]]; then
|
||
echo -e "${Error} 协议切换后服务启动失败,以下为错误日志:"
|
||
journalctl -u snell-server -n 20 --no-pager
|
||
echo -e "${Error} 正在回滚到原版本..."
|
||
backup_file=$(ls -t "${snell_bin}".v${current_installed_ver}.backup.* 2>/dev/null | head -1)
|
||
if [[ -n "$backup_file" && -e "$backup_file" ]]; then
|
||
cp "$backup_file" "${snell_bin}"
|
||
echo "v${current_installed_ver}" > ${snell_version_file}
|
||
ver=$current_installed_ver
|
||
writeConfig
|
||
systemctl start snell-server
|
||
echo -e "${Info} 已回滚到 v${current_installed_ver} 版本并恢复配置"
|
||
fi
|
||
else
|
||
echo -e "${Info} Snell Server 重启完毕!"
|
||
fi
|
||
sleep 3s
|
||
startMenu
|
||
return
|
||
fi
|
||
else
|
||
writeConfig
|
||
restartSnell
|
||
fi
|
||
else
|
||
echo -e "${Error} 请输入正确数字${Yellow_font_prefix}[1-11]${Font_color_suffix}"
|
||
sleep 2s
|
||
setConfig
|
||
fi
|
||
sleep 3s
|
||
startMenu
|
||
}
|
||
|
||
|
||
# 安装 Snell v2
|
||
installSnellV2(){
|
||
checkRoot
|
||
[[ -e ${snell_bin} ]] && echo -e "${Error} 检测到 Snell Server 已安装!" && exit 1
|
||
echo -e "${Info} 开始设置 配置..."
|
||
setPort
|
||
setPSK
|
||
setObfs
|
||
setIpv6
|
||
setTFO
|
||
echo -e "${Info} 开始安装/配置 依赖..."
|
||
checkDependencies
|
||
installDependencies
|
||
echo -e "${Info} 开始下载/安装..."
|
||
downloadSnellV2
|
||
echo -e "${Info} 开始安装 服务脚本..."
|
||
setupService
|
||
echo -e "${Info} 开始写入 配置文件..."
|
||
writeConfig
|
||
echo -e "${Info} 所有步骤 安装完毕,开始启动..."
|
||
startSnell
|
||
echo -e "${Info} 启动完成,查看配置..."
|
||
viewConfig
|
||
}
|
||
|
||
# 安装 Snell v3
|
||
installSnellV3(){
|
||
checkRoot
|
||
[[ -e ${snell_bin} ]] && echo -e "${Error} 检测到 Snell Server 已安装!" && exit 1
|
||
echo -e "${Info} 开始设置 配置..."
|
||
setPort
|
||
setPSK
|
||
setObfs
|
||
setIpv6
|
||
setTFO
|
||
echo -e "${Info} 开始安装/配置 依赖..."
|
||
checkDependencies
|
||
installDependencies
|
||
echo -e "${Info} 开始下载/安装..."
|
||
downloadSnellV3
|
||
echo -e "${Info} 开始安装 服务脚本..."
|
||
setupService
|
||
echo -e "${Info} 开始写入 配置文件..."
|
||
writeConfig
|
||
echo -e "${Info} 所有步骤 安装完毕,开始启动..."
|
||
startSnell
|
||
echo -e "${Info} 启动完成,查看配置..."
|
||
viewConfig
|
||
}
|
||
|
||
# 安装 Snell v4
|
||
installSnellV4(){
|
||
checkRoot
|
||
[[ -e ${snell_bin} ]] && echo -e "${Error} 检测到 Snell Server 已安装,请先卸载旧版再安装新版!" && exit 1
|
||
echo -e "${Info} 开始设置 配置..."
|
||
setPort
|
||
setPSK
|
||
setObfs
|
||
setIpv6
|
||
setTFO
|
||
setDNS
|
||
echo -e "${Info} 开始安装/配置 依赖..."
|
||
checkDependencies
|
||
installDependencies
|
||
echo -e "${Info} 开始下载/安装..."
|
||
downloadSnellV4
|
||
echo -e "${Info} 开始安装 服务脚本..."
|
||
setupService
|
||
echo -e "${Info} 开始写入 配置文件..."
|
||
writeConfig
|
||
echo -e "${Info} 所有步骤 安装完毕,开始启动..."
|
||
startSnell
|
||
echo -e "${Info} 启动完成,查看配置..."
|
||
viewConfig
|
||
}
|
||
|
||
# 安装 Snell v5
|
||
installSnellV5(){
|
||
checkRoot
|
||
[[ -e ${snell_bin} ]] && echo -e "${Error} 检测到 Snell Server 已安装,请先卸载旧版再安装新版!" && exit 1
|
||
echo -e "${Info} 开始设置 配置..."
|
||
setPort
|
||
setPSK
|
||
setObfs
|
||
setIpv6
|
||
setTFO
|
||
setDNS
|
||
echo -e "${Info} 开始安装/配置 依赖..."
|
||
checkDependencies
|
||
installDependencies
|
||
echo -e "${Info} 开始下载/安装..."
|
||
downloadSnellV5
|
||
echo -e "${Info} 开始安装 服务脚本..."
|
||
setupService
|
||
echo -e "${Info} 开始写入 配置文件..."
|
||
writeConfig
|
||
echo -e "${Info} 所有步骤 安装完毕,开始启动..."
|
||
startSnell
|
||
echo -e "${Info} 启动完成,查看配置..."
|
||
viewConfig
|
||
}
|
||
|
||
|
||
# 下载并安装 Snell v6(官方源)
|
||
downloadSnellV6(){
|
||
downloadSnell "${snell_v6_version}" "Snell v6 官网源版"
|
||
}
|
||
|
||
# 安装 Snell v6
|
||
installSnellV6(){
|
||
checkRoot
|
||
[[ -e ${snell_bin} ]] && echo -e "${Error} 检测到 Snell Server 已安装,请先卸载旧版再安装新版!" && exit 1
|
||
echo -e "${Info} 开始设置 配置..."
|
||
setPort
|
||
setPSK
|
||
setTFO
|
||
setDNS
|
||
setDNSIPPref
|
||
setMode
|
||
echo -e "${Info} 开始安装/配置 依赖..."
|
||
checkDependencies
|
||
installDependencies
|
||
echo -e "${Info} 开始下载/安装..."
|
||
downloadSnellV6
|
||
echo -e "${Info} 开始安装 服务脚本..."
|
||
setupService
|
||
echo -e "${Info} 开始写入 配置文件..."
|
||
writeConfig
|
||
echo -e "${Info} 所有步骤 安装完毕,开始启动..."
|
||
startSnell
|
||
echo -e "${Info} 启动完成,查看配置..."
|
||
viewConfig
|
||
}
|
||
# 启动 Snell
|
||
startSnell(){
|
||
checkInstalledStatus
|
||
checkStatus
|
||
if [[ "$status" == "running" ]]; then
|
||
echo -e "${Info} Snell Server 已在运行!"
|
||
else
|
||
echo -e "${Info} 正在启动 Snell Server..."
|
||
systemctl start snell-server
|
||
|
||
# 等待启动完成,最多等待10秒
|
||
local timeout=10
|
||
local elapsed=0
|
||
while [[ $elapsed -lt $timeout ]]; do
|
||
sleep 1
|
||
checkStatus
|
||
if [[ "$status" == "running" ]]; then
|
||
echo -e "${Info} Snell Server 启动成功!"
|
||
return 0
|
||
fi
|
||
((elapsed++))
|
||
done
|
||
|
||
# 如果5秒后仍未启动,检查状态并报错
|
||
checkStatus
|
||
if [[ "$status" == "running" ]]; then
|
||
echo -e "${Info} Snell Server 启动成功!"
|
||
else
|
||
echo -e "${Error} Snell Server 启动失败!"
|
||
echo -e "${Error} 请使用 'systemctl status snell-server' 查看详细错误信息"
|
||
journalctl -u snell-server -n 20 --no-pager
|
||
exit 1
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# 停止 Snell
|
||
stopSnell(){
|
||
checkInstalledStatus
|
||
checkStatus
|
||
[[ !"$status" == "running" ]] && echo -e "${Error} Snell Server 没有运行,请检查!" && exit 1
|
||
systemctl stop snell-server
|
||
echo -e "${Info} Snell Server 停止成功!"
|
||
sleep 3s
|
||
startMenu
|
||
}
|
||
|
||
# 重启 Snell
|
||
restartSnell(){
|
||
checkInstalledStatus
|
||
systemctl restart snell-server
|
||
echo -e "${Info} Snell Server 重启完毕!"
|
||
sleep 3s
|
||
startMenu
|
||
}
|
||
|
||
# 更新 Snell(占位,待实现)
|
||
updateSnell(){
|
||
checkInstalledStatus
|
||
echo -e "${Info} Snell Server 更新完毕!"
|
||
sleep 3s
|
||
startMenu
|
||
}
|
||
|
||
# Snell v2 更新到 Snell v3
|
||
updateV2toV3(){
|
||
checkInstalledStatus
|
||
readConfig
|
||
|
||
# 检查当前版本是否为 Snell v2
|
||
if [[ "$ver" != "2" ]]; then
|
||
echo -e "${Error} 当前版本不是 Snell v2,无法使用此功能!当前版本:Snell v${ver}"
|
||
sleep 3s
|
||
startMenu
|
||
return 1
|
||
fi
|
||
|
||
echo -e "${Info} 即将将 Snell Server 从 Snell v2 更新到 Snell v3 版本"
|
||
echo -e "确定要更新吗?(y/N)"
|
||
read -e -p "(默认: n):" confirm
|
||
[[ -z "${confirm}" ]] && confirm="n"
|
||
|
||
if [[ ${confirm} != [Yy] ]]; then
|
||
echo -e "${Info} 已取消更新"
|
||
sleep 2s
|
||
startMenu
|
||
return 0
|
||
fi
|
||
|
||
echo -e "${Info} 开始更新 Snell v2 到 Snell v3..."
|
||
|
||
# 停止服务
|
||
echo -e "${Info} 停止 Snell Server 服务..."
|
||
systemctl stop snell-server
|
||
|
||
# 备份当前二进制文件
|
||
if [[ -e "${snell_bin}" ]]; then
|
||
echo -e "${Info} 备份当前程序文件..."
|
||
cp "${snell_bin}" "${snell_bin}.v2.backup.$(date +%Y%m%d_%H%M%S)"
|
||
fi
|
||
|
||
echo -e "${Info} 开始下载 Snell v3 版本..."
|
||
downloadSnellV3
|
||
|
||
if [[ $? -eq 0 ]]; then
|
||
# 更新配置文件中的版本号
|
||
echo -e "${Info} 更新配置文件版本号..."
|
||
sed -i "s/version = 2/version = 3/g" "${snell_conf}"
|
||
|
||
# 重新加载 systemd 并启动服务
|
||
echo -e "${Info} 重启 Snell Server 服务..."
|
||
systemctl daemon-reload
|
||
systemctl start snell-server
|
||
|
||
# 检查服务状态
|
||
sleep 2
|
||
checkStatus
|
||
if [[ "$status" == "running" ]]; then
|
||
echo -e "${Info} Snell v2 到 Snell v3 更新成功!"
|
||
echo -e "${Info} 当前版本:Snell v3"
|
||
else
|
||
echo -e "${Error} 服务启动失败,正在回滚..."
|
||
# 回滚到 Snell v2
|
||
backup_file=$(ls -t "${snell_bin}".v2.backup.* 2>/dev/null | head -1)
|
||
if [[ -n "$backup_file" && -e "$backup_file" ]]; then
|
||
cp "$backup_file" "${snell_bin}"
|
||
sed -i "s/version = 3/version = 2/g" "${snell_conf}"
|
||
systemctl start snell-server
|
||
echo -e "${Info} 已回滚到 Snell v2 版本"
|
||
fi
|
||
fi
|
||
else
|
||
echo -e "${Error} Snell v3 下载失败,保持 Snell v2 版本"
|
||
systemctl start snell-server
|
||
fi
|
||
|
||
sleep 3s
|
||
startMenu
|
||
}
|
||
|
||
# Snell v3 更新到 Snell v4
|
||
updateV3toV4(){
|
||
checkInstalledStatus
|
||
readConfig
|
||
|
||
# 检查当前版本是否为 Snell v3
|
||
if [[ "$ver" != "3" ]]; then
|
||
echo -e "${Error} 当前版本不是 Snell v3,无法使用此功能!当前版本:Snell v${ver}"
|
||
sleep 3s
|
||
startMenu
|
||
return 1
|
||
fi
|
||
|
||
echo -e "${Info} 即将将 Snell Server 从 Snell v3 更新到 Snell v4 版本"
|
||
echo -e "确定要更新吗?(y/N)"
|
||
read -e -p "(默认: n):" confirm
|
||
[[ -z "${confirm}" ]] && confirm="n"
|
||
|
||
if [[ ${confirm} != [Yy] ]]; then
|
||
echo -e "${Info} 已取消更新"
|
||
sleep 2s
|
||
startMenu
|
||
return 0
|
||
fi
|
||
|
||
echo -e "${Info} 开始更新 Snell v3 到 Snell v4..."
|
||
|
||
# 停止服务
|
||
echo -e "${Info} 停止 Snell Server 服务..."
|
||
systemctl stop snell-server
|
||
|
||
# 备份当前二进制文件
|
||
if [[ -e "${snell_bin}" ]]; then
|
||
echo -e "${Info} 备份当前程序文件..."
|
||
cp "${snell_bin}" "${snell_bin}.v3.backup.$(date +%Y%m%d_%H%M%S)"
|
||
fi
|
||
|
||
# 获取最新的 Snell v4 版本号
|
||
web_v4_version=$(getLatestVersionFromWeb "v4")
|
||
script_v4_version="${snell_v4_version}"
|
||
|
||
target_v4_version=""
|
||
if [[ -n "$web_v4_version" ]] && validateVersionUrl "$web_v4_version"; then
|
||
target_v4_version="$web_v4_version"
|
||
echo -e "${Info} 使用网页获取的 Snell v4 版本: v${target_v4_version}"
|
||
elif [[ -n "$script_v4_version" ]] && validateVersionUrl "$script_v4_version"; then
|
||
target_v4_version="$script_v4_version"
|
||
echo -e "${Info} 使用脚本内置的 Snell v4 版本: v${target_v4_version}"
|
||
fi
|
||
|
||
if [[ -z "$target_v4_version" ]]; then
|
||
echo -e "${Error} 无法找到有效的 Snell v4 版本进行更新"
|
||
systemctl start snell-server
|
||
sleep 3s
|
||
startMenu
|
||
return 1
|
||
fi
|
||
|
||
echo -e "${Info} 开始下载 Snell v4 版本..."
|
||
downloadSnell "${target_v4_version}" "Snell v4 版本" true "3.0.1"
|
||
|
||
if [[ $? -eq 0 ]]; then
|
||
actual_version=$(cat ${snell_version_file} | sed 's/^v//')
|
||
if [[ "$actual_version" =~ ^4\. ]]; then
|
||
echo -e "${Info} 更新配置文件版本号..."
|
||
sed -i "s/version = 3/version = 4/g" "${snell_conf}"
|
||
else
|
||
echo -e "${Tip} 注意:由于 Snell v4 下载链接问题,已回退重新安装 Snell v${actual_version} 版本"
|
||
fi
|
||
|
||
# 重新加载 systemd 并启动服务
|
||
echo -e "${Info} 重启 Snell Server 服务..."
|
||
systemctl daemon-reload
|
||
systemctl start snell-server
|
||
|
||
# 检查服务状态
|
||
sleep 2
|
||
checkStatus
|
||
if [[ "$status" == "running" ]]; then
|
||
if [[ "$actual_version" =~ ^4\. ]]; then
|
||
echo -e "${Info} Snell v3 到 Snell v4 更新成功!"
|
||
echo -e "${Info} 当前版本:Snell v${actual_version}"
|
||
fi
|
||
else
|
||
echo -e "${Error} 服务启动失败,正在回滚..."
|
||
# 回滚到 Snell v3
|
||
backup_file=$(ls -t "${snell_bin}".v3.backup.* 2>/dev/null | head -1)
|
||
if [[ -n "$backup_file" && -e "$backup_file" ]]; then
|
||
cp "$backup_file" "${snell_bin}"
|
||
echo "v3.0.1" > ${snell_version_file}
|
||
sed -i "s/version = 4/version = 3/g" "${snell_conf}"
|
||
systemctl start snell-server
|
||
echo -e "${Info} 已回滚到 Snell v3 版本"
|
||
fi
|
||
fi
|
||
else
|
||
echo -e "${Error} Snell v4 下载失败,保持 Snell v3 版本"
|
||
systemctl start snell-server
|
||
fi
|
||
|
||
sleep 3s
|
||
startMenu
|
||
}
|
||
|
||
# Snell v4 更新到 Snell v5
|
||
updateV4toV5(){
|
||
checkInstalledStatus
|
||
readConfig
|
||
|
||
# 检查当前版本是否为 Snell v4
|
||
if [[ "$ver" != "4" ]]; then
|
||
echo -e "${Error} 当前版本不是 Snell v4,无法使用此功能!当前版本:Snell v${ver}"
|
||
sleep 3s
|
||
startMenu
|
||
return 1
|
||
fi
|
||
|
||
echo -e "${Info} 即将将 Snell Server 从 Snell v4 更新到 Snell v5 版本"
|
||
echo -e "确定要更新吗?(y/N)"
|
||
read -e -p "(默认: n):" confirm
|
||
[[ -z "${confirm}" ]] && confirm="n"
|
||
|
||
if [[ ${confirm} != [Yy] ]]; then
|
||
echo -e "${Info} 已取消更新"
|
||
sleep 2s
|
||
startMenu
|
||
return 0
|
||
fi
|
||
|
||
echo -e "${Info} 开始更新 Snell v4 到 Snell v5..."
|
||
|
||
# 停止服务
|
||
echo -e "${Info} 停止 Snell Server 服务..."
|
||
systemctl stop snell-server
|
||
|
||
# 备份当前二进制文件
|
||
if [[ -e "${snell_bin}" ]]; then
|
||
echo -e "${Info} 备份当前程序文件..."
|
||
cp "${snell_bin}" "${snell_bin}.v4.backup.$(date +%Y%m%d_%H%M%S)"
|
||
fi
|
||
|
||
# 获取当前安装的 Snell v4 版本作为回退版本
|
||
current_v4_version=$(cat ${snell_version_file} | sed 's/^v//')
|
||
|
||
# 获取最新的 Snell v5 版本号(优先使用网页版本,然后是脚本内置版本)
|
||
web_v5_version=$(getLatestVersionFromWeb "v5")
|
||
script_v5_version="${snell_v5_version}"
|
||
|
||
# 选择最新的 Snell v5 版本
|
||
target_v5_version=""
|
||
if [[ -n "$web_v5_version" ]]; then
|
||
if validateVersionUrl "$web_v5_version"; then
|
||
target_v5_version="$web_v5_version"
|
||
echo -e "${Info} 使用网页获取的 Snell v5 版本: v${target_v5_version}"
|
||
fi
|
||
fi
|
||
|
||
# 如果网页版本无效,尝试脚本内置版本
|
||
if [[ -z "$target_v5_version" && -n "$script_v5_version" ]]; then
|
||
if validateVersionUrl "$script_v5_version"; then
|
||
target_v5_version="$script_v5_version"
|
||
echo -e "${Info} 使用脚本内置的 Snell v5 版本: v${target_v5_version}"
|
||
fi
|
||
fi
|
||
|
||
# 如果都无效,取消更新
|
||
if [[ -z "$target_v5_version" ]]; then
|
||
echo -e "${Error} 无法找到有效的 Snell v5 版本进行更新"
|
||
systemctl start snell-server
|
||
sleep 3s
|
||
startMenu
|
||
return 1
|
||
fi
|
||
|
||
# 下载并安装 Snell v5,启用回退机制
|
||
echo -e "${Info} 开始下载 Snell v5 版本..."
|
||
downloadSnell "${target_v5_version}" "Snell v5 版本" true "${current_v4_version}"
|
||
|
||
if [[ $? -eq 0 ]]; then
|
||
actual_version=$(cat ${snell_version_file} | sed 's/^v//')
|
||
|
||
if [[ "$actual_version" =~ ^5\. ]]; then
|
||
# 更新配置文件中的版本号
|
||
echo -e "${Info} 更新配置文件版本号..."
|
||
sed -i "s/version = 4/version = 5/g" "${snell_conf}"
|
||
else
|
||
echo -e "${Tip} 注意:由于 Snell v5 下载链接问题,已回退重新安装 Snell v${actual_version} 版本"
|
||
fi
|
||
|
||
# 重新加载 systemd 并启动服务
|
||
echo -e "${Info} 重启 Snell Server 服务..."
|
||
systemctl daemon-reload
|
||
systemctl start snell-server
|
||
|
||
# 检查服务状态
|
||
sleep 2
|
||
checkStatus
|
||
if [[ "$status" == "running" ]]; then
|
||
if [[ "$actual_version" =~ ^5\. ]]; then
|
||
echo -e "${Info} Snell v4 到 Snell v5 更新成功!"
|
||
echo -e "${Info} 当前版本:Snell v${actual_version}"
|
||
fi
|
||
else
|
||
echo -e "${Error} 服务启动失败,正在回滚..."
|
||
# 回滚到 Snell v4
|
||
backup_file=$(ls -t "${snell_bin}".v4.backup.* 2>/dev/null | head -1)
|
||
if [[ -n "$backup_file" && -e "$backup_file" ]]; then
|
||
cp "$backup_file" "${snell_bin}"
|
||
echo "v${current_v4_version}" > ${snell_version_file}
|
||
sed -i "s/version = 5/version = 4/g" "${snell_conf}"
|
||
systemctl start snell-server
|
||
echo -e "${Info} 已回滚到 Snell v4 版本"
|
||
fi
|
||
fi
|
||
else
|
||
echo -e "${Error} Snell v5 下载失败,保持 Snell v4 版本"
|
||
systemctl start snell-server
|
||
fi
|
||
|
||
sleep 3s
|
||
startMenu
|
||
}
|
||
|
||
|
||
# Snell v5 更新到 Snell v6
|
||
updateV5toV6(){
|
||
checkInstalledStatus
|
||
readConfig
|
||
|
||
if [[ "$ver" != "5" ]]; then
|
||
echo -e "${Error} 当前版本不是 Snell v5,无法使用此功能!当前版本:Snell v${ver}"
|
||
sleep 3s
|
||
startMenu
|
||
return 1
|
||
fi
|
||
|
||
echo -e "${Info} 即将将 Snell Server 从 Snell v5 更新到 Snell v6 版本"
|
||
echo -e "确定要更新吗?(y/N)"
|
||
read -e -p "(默认: n):" confirm
|
||
[[ -z "${confirm}" ]] && confirm="n"
|
||
|
||
if [[ ${confirm} != [Yy] ]]; then
|
||
echo -e "${Info} 已取消更新"
|
||
sleep 2s
|
||
startMenu
|
||
return 0
|
||
fi
|
||
|
||
echo -e "${Info} 开始更新 Snell v5 到 Snell v6..."
|
||
|
||
systemctl stop snell-server
|
||
|
||
if [[ -e "${snell_bin}" ]]; then
|
||
echo -e "${Info} 备份当前程序文件..."
|
||
cp "${snell_bin}" "${snell_bin}.v5.backup.$(date +%Y%m%d_%H%M%S)"
|
||
fi
|
||
|
||
current_v5_version=$(cat ${snell_version_file} | sed 's/^v//')
|
||
web_v6_version=$(getLatestVersionFromWeb "v6")
|
||
script_v6_version="${snell_v6_version}"
|
||
|
||
target_v6_version=""
|
||
if [[ -n "$web_v6_version" ]]; then
|
||
if validateVersionUrl "$web_v6_version"; then
|
||
target_v6_version="$web_v6_version"
|
||
echo -e "${Info} 使用网页获取的 Snell v6 版本: v${target_v6_version}"
|
||
fi
|
||
fi
|
||
|
||
if [[ -z "$target_v6_version" && -n "$script_v6_version" ]]; then
|
||
if validateVersionUrl "$script_v6_version"; then
|
||
target_v6_version="$script_v6_version"
|
||
echo -e "${Info} 使用脚本内置的 Snell v6 版本: v${target_v6_version}"
|
||
fi
|
||
fi
|
||
|
||
if [[ -z "$target_v6_version" ]]; then
|
||
echo -e "${Error} 无法找到有效的 Snell v6 版本进行更新"
|
||
systemctl start snell-server
|
||
sleep 3s
|
||
startMenu
|
||
return 1
|
||
fi
|
||
|
||
echo -e "${Info} 开始下载 Snell v6 版本..."
|
||
downloadSnell "${target_v6_version}" "Snell v6 版本" true "${current_v5_version}"
|
||
|
||
if [[ $? -eq 0 ]]; then
|
||
actual_version=$(cat ${snell_version_file} | sed 's/^v//')
|
||
|
||
if [[ "$actual_version" =~ ^6\. ]]; then
|
||
echo -e "${Info} 更新配置文件版本号..."
|
||
ver=6
|
||
setDNSIPPref
|
||
setMode
|
||
checkPskForV6
|
||
writeConfig
|
||
else
|
||
echo -e "${Tip} 注意:由于 Snell v6 下载链接问题,已回退重新安装 Snell v${actual_version} 版本"
|
||
fi
|
||
|
||
systemctl daemon-reload
|
||
systemctl start snell-server
|
||
|
||
for ((i=0; i<10; i++)); do
|
||
sleep 1
|
||
checkStatus
|
||
if [[ "$status" == "running" ]]; then
|
||
break
|
||
fi
|
||
done
|
||
if [[ "$status" == "running" ]]; then
|
||
if [[ "$actual_version" =~ ^6\. ]]; then
|
||
echo -e "${Info} Snell v5 到 Snell v6 更新成功!"
|
||
echo -e "${Info} 当前版本:Snell v${actual_version}"
|
||
fi
|
||
else
|
||
echo -e "${Error} 升级后服务启动失败,以下为错误日志:"
|
||
journalctl -u snell-server -n 20 --no-pager
|
||
echo -e "${Error} 正在回滚..."
|
||
backup_file=$(ls -t "${snell_bin}".v5.backup.* 2>/dev/null | head -1)
|
||
if [[ -n "$backup_file" && -e "$backup_file" ]]; then
|
||
cp "$backup_file" "${snell_bin}"
|
||
echo "v${current_v5_version}" > ${snell_version_file}
|
||
ver=5
|
||
writeConfig
|
||
systemctl start snell-server
|
||
echo -e "${Info} 已回滚到 Snell v5 版本"
|
||
fi
|
||
fi
|
||
else
|
||
echo -e "${Error} Snell v6 下载失败,保持 Snell v5 版本"
|
||
systemctl start snell-server
|
||
fi
|
||
|
||
sleep 3s
|
||
startMenu
|
||
}
|
||
# 更新 Snell Server 到最新版本
|
||
updateSnellServer(){
|
||
checkInstalledStatus
|
||
readConfig
|
||
|
||
echo -e "${Info} 准备更新 Snell Server..."
|
||
|
||
# 显示详细的版本检查信息
|
||
echo -e "${Info} 正在检查版本信息..."
|
||
updateBuiltinVersions true
|
||
checkVersionUpdate true
|
||
|
||
# 检查是否有更新可用
|
||
force_checked=false
|
||
if [[ "$update_available" != true ]]; then
|
||
echo -e "${Info} 当前已是最新版本,无需更新!"
|
||
echo -e "${Info} 当前版本: ${Green_font_prefix}v${current_installed_version}${Font_color_suffix}"
|
||
echo
|
||
echo -e "${Tip} 是否要强制重新检查最新版本?(y/N)"
|
||
read -e -p "(默认: n):" force_check
|
||
[[ -z "${force_check}" ]] && force_check="n"
|
||
|
||
if [[ ${force_check} == [Yy] ]]; then
|
||
echo -e "${Info} 强制重新检查最新版本..."
|
||
# 清除缓存并重新检查
|
||
rm -f /tmp/snell_version_cache
|
||
updateBuiltinVersions true
|
||
checkVersionUpdate true
|
||
force_checked=true
|
||
|
||
# 重新检查后如果有更新,继续更新流程
|
||
if [[ "$update_available" == true ]]; then
|
||
echo -e "${Info} 检测到新版本,继续更新流程..."
|
||
else
|
||
echo -e "${Info} 重新检查后仍为最新版本"
|
||
sleep 3s
|
||
startMenu
|
||
return 0
|
||
fi
|
||
else
|
||
sleep 3s
|
||
startMenu
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
# 显示版本信息
|
||
echo -e "${Info} 当前版本: ${Yellow_font_prefix}v${current_installed_version}${Font_color_suffix}"
|
||
echo -e "${Info} 最新版本: ${Green_font_prefix}v${latest_available_version}${Font_color_suffix}"
|
||
echo -e "确定要更新吗?(Y/n)"
|
||
read -e -p "(默认: y):" confirm
|
||
[[ -z "${confirm}" ]] && confirm="y"
|
||
|
||
if [[ ${confirm} == [Nn] ]]; then
|
||
echo -e "${Info} 已取消更新"
|
||
sleep 2s
|
||
startMenu
|
||
return 0
|
||
fi
|
||
|
||
echo -e "${Info} 开始更新 Snell Server 到最新版本..."
|
||
|
||
# 停止服务
|
||
echo -e "${Info} 停止 Snell Server 服务..."
|
||
systemctl stop snell-server
|
||
|
||
# 备份当前二进制文件
|
||
if [[ -e "${snell_bin}" ]]; then
|
||
echo -e "${Info} 备份当前程序文件..."
|
||
cp "${snell_bin}" "${snell_bin}.backup.$(date +%Y%m%d_%H%M%S)"
|
||
fi
|
||
|
||
# 根据版本选择下载函数,启用回退机制
|
||
echo -e "${Info} 开始下载最新版本..."
|
||
case "$ver" in
|
||
"6")
|
||
downloadSnell "${latest_available_version}" "Snell v6 最新版" true "${current_installed_version}"
|
||
;;
|
||
*)
|
||
echo -e "${Error} 不支持的版本: Snell v${ver}"
|
||
systemctl start snell-server
|
||
sleep 3s
|
||
startMenu
|
||
return 1
|
||
;;
|
||
esac
|
||
|
||
if [[ $? -eq 0 ]]; then
|
||
# 重新加载 systemd 并启动服务
|
||
echo -e "${Info} 重启 Snell Server 服务..."
|
||
systemctl daemon-reload
|
||
systemctl start snell-server
|
||
|
||
# 检查服务状态
|
||
sleep 2
|
||
checkStatus
|
||
if [[ "$status" == "running" ]]; then
|
||
actual_version=$(cat ${snell_version_file} | sed 's/^v//')
|
||
echo -e "${Info} Snell Server 更新成功!"
|
||
echo -e "${Info} 当前版本:v${actual_version}"
|
||
|
||
# 如果实际更新版本与预期不同,给出提示
|
||
if [[ "$actual_version" != "$latest_available_version" ]]; then
|
||
echo -e "${Tip} 注意:由于下载链接问题,已回退到 v${actual_version} 版本"
|
||
fi
|
||
else
|
||
echo -e "${Error} 服务启动失败,正在回滚..."
|
||
# 回滚到备份版本
|
||
backup_file=$(ls -t "${snell_bin}".backup.* 2>/dev/null | head -1)
|
||
if [[ -n "$backup_file" && -e "$backup_file" ]]; then
|
||
cp "$backup_file" "${snell_bin}"
|
||
echo "v${current_installed_version}" > ${snell_version_file}
|
||
systemctl start snell-server
|
||
echo -e "${Info} 已回滚到备份版本 v${current_installed_version}"
|
||
fi
|
||
fi
|
||
else
|
||
echo -e "${Error} 下载失败,启动原版本"
|
||
systemctl start snell-server
|
||
fi
|
||
|
||
sleep 3s
|
||
startMenu
|
||
}
|
||
|
||
# 自动获取 Snell 最新版本号
|
||
getLatestVersionFromWeb(){
|
||
local version_type=$1
|
||
local release_page="https://kb.nssurge.com/surge-knowledge-base/zh/release-notes/snell"
|
||
|
||
page_content=$(curl -s -L --max-time 10 "$release_page" 2>/dev/null)
|
||
|
||
if [[ -z "$page_content" ]]; then
|
||
return 1
|
||
fi
|
||
|
||
if [[ "$version_type" == "v4" ]]; then
|
||
latest_v4=$(echo "$page_content" | grep -oE "snell-server-v4\.[0-9]+\.[0-9]+-linux" | head -1 | sed 's/snell-server-v//g' | sed 's/-linux//g')
|
||
if [[ -n "$latest_v4" ]]; then
|
||
echo "$latest_v4"
|
||
return 0
|
||
fi
|
||
elif [[ "$version_type" == "v5" ]]; then
|
||
latest_v5=$(echo "$page_content" | grep -oE "snell-server-v5\.[0-9]+\.[0-9]+[a-z]*[0-9]*-linux" | head -1 | sed 's/snell-server-v//g' | sed 's/-linux//g')
|
||
if [[ -n "$latest_v5" ]]; then
|
||
echo "$latest_v5"
|
||
return 0
|
||
fi
|
||
elif [[ "$version_type" == "v6" ]]; then
|
||
latest_v6=$(echo "$page_content" | grep -oE "snell-server-v6\.[0-9]+\.[0-9]+[a-z]*[0-9]*-linux" | head -1 | sed 's/snell-server-v//g' | sed 's/-linux//g')
|
||
if [[ -n "$latest_v6" ]]; then
|
||
echo "$latest_v6"
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
return 1
|
||
}
|
||
|
||
# 更新脚本内置版本号(带缓存机制,但保持脚本版本优先级)
|
||
updateBuiltinVersions(){
|
||
local show_info=${1:-false} # 是否显示详细信息,默认为静默
|
||
local cache_file="/tmp/snell_version_cache"
|
||
local cache_time=3600
|
||
local current_time=$(date +%s)
|
||
|
||
# Snell v2 和 Snell v3 始终使用固定版本,无需检查
|
||
web_v2_newer=false
|
||
web_v3_newer=false
|
||
|
||
# 检查缓存是否存在且有效
|
||
if [[ -f "$cache_file" ]]; then
|
||
local cache_timestamp=$(head -1 "$cache_file" 2>/dev/null)
|
||
if [[ -n "$cache_timestamp" && $((current_time - cache_timestamp)) -lt $cache_time ]]; then
|
||
local cached_v4=$(sed -n '2p' "$cache_file" 2>/dev/null)
|
||
local cached_v5=$(sed -n '3p' "$cache_file" 2>/dev/null)
|
||
local cached_v6=$(sed -n '4p' "$cache_file" 2>/dev/null)
|
||
if [[ -n "$cached_v4" && -n "$cached_v5" && -n "$cached_v6" ]]; then
|
||
# 检查网页版本是否比脚本内置版本更新
|
||
compareVersions "${snell_v4_version}" "$cached_v4"
|
||
if [[ $? -eq 2 ]]; then
|
||
web_v4_newer=true
|
||
else
|
||
web_v4_newer=false
|
||
fi
|
||
|
||
compareVersions "${snell_v5_version}" "$cached_v5"
|
||
if [[ $? -eq 2 ]]; then
|
||
web_v5_newer=true
|
||
else
|
||
web_v5_newer=false
|
||
fi
|
||
|
||
compareVersions "${snell_v6_version}" "$cached_v6"
|
||
if [[ $? -eq 2 ]]; then
|
||
web_v6_newer=true
|
||
else
|
||
web_v6_newer=false
|
||
fi
|
||
|
||
return 0
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
[[ "$show_info" == true ]] && echo -e "${Info} 正在检查官方最新版本..."
|
||
|
||
# 获取最新的 Snell v4 版本
|
||
local latest_v4_web
|
||
latest_v4_web=$(getLatestVersionFromWeb "v4")
|
||
if [[ $? -eq 0 && -n "$latest_v4_web" ]]; then
|
||
# 比较网页版本和脚本内置版本
|
||
compareVersions "${snell_v4_version}" "$latest_v4_web"
|
||
if [[ $? -eq 2 ]]; then
|
||
web_v4_newer=true
|
||
else
|
||
web_v4_newer=false
|
||
fi
|
||
else
|
||
web_v4_newer=false
|
||
latest_v4_web="${snell_v4_version}"
|
||
fi
|
||
|
||
# 获取最新的 Snell v5 版本
|
||
local latest_v5_web
|
||
latest_v5_web=$(getLatestVersionFromWeb "v5")
|
||
if [[ $? -eq 0 && -n "$latest_v5_web" ]]; then
|
||
# 比较网页版本和脚本内置版本
|
||
compareVersions "${snell_v5_version}" "$latest_v5_web"
|
||
if [[ $? -eq 2 ]]; then
|
||
web_v5_newer=true
|
||
else
|
||
web_v5_newer=false
|
||
fi
|
||
else
|
||
web_v5_newer=false
|
||
latest_v5_web="${snell_v5_version}"
|
||
fi
|
||
|
||
# 获取最新的 Snell v6 版本
|
||
local latest_v6_web
|
||
latest_v6_web=$(getLatestVersionFromWeb "v6")
|
||
if [[ $? -eq 0 && -n "$latest_v6_web" ]]; then
|
||
# 比较网页版本和脚本内置版本
|
||
compareVersions "${snell_v6_version}" "$latest_v6_web"
|
||
if [[ $? -eq 2 ]]; then
|
||
web_v6_newer=true
|
||
else
|
||
web_v6_newer=false
|
||
fi
|
||
else
|
||
web_v6_newer=false
|
||
latest_v6_web="${snell_v6_version}"
|
||
fi
|
||
|
||
# 更新缓存(缓存 Snell v4, Snell v5, Snell v6)
|
||
echo "$current_time" > "$cache_file"
|
||
echo "$latest_v4_web" >> "$cache_file"
|
||
echo "$latest_v5_web" >> "$cache_file"
|
||
echo "$latest_v6_web" >> "$cache_file"
|
||
}
|
||
|
||
# 强制检查最新版本(清除缓存)
|
||
forceCheckVersions(){
|
||
echo -e "${Info} 强制检查 Snell 最新版本..."
|
||
|
||
rm -f "/tmp/snell_version_cache"
|
||
updateBuiltinVersions true
|
||
|
||
echo -e "${Info} 版本检查完成!"
|
||
echo -e "${Info} 脚本内置 Snell v4 版本: ${Green_font_prefix}${snell_v4_version}${Font_color_suffix}"
|
||
echo -e "${Info} 脚本内置 Snell v5 版本: ${Green_font_prefix}${snell_v5_version}${Font_color_suffix}"
|
||
echo -e "${Info} 脚本内置 Snell v6 版本: ${Green_font_prefix}${snell_v6_version}${Font_color_suffix}"
|
||
|
||
# 获取网页版本进行对比
|
||
web_v4=$(getLatestVersionFromWeb "v4")
|
||
web_v5=$(getLatestVersionFromWeb "v5")
|
||
web_v6=$(getLatestVersionFromWeb "v6")
|
||
|
||
if [[ -n "$web_v4" ]]; then
|
||
echo -e "${Info} 网页获取 Snell v4 版本: ${Yellow_font_prefix}${web_v4}${Font_color_suffix}"
|
||
compareVersions "${snell_v4_version}" "$web_v4"
|
||
case $? in
|
||
1) echo -e "${Info} Snell v4 版本状态: 脚本内置版本与网页版本相同" ;;
|
||
0) echo -e "${Info} Snell v4 版本状态: 脚本内置版本比网页版本更新" ;;
|
||
2) echo -e "${Tip} Snell v4 版本状态: 网页版本比脚本内置版本更新" ;;
|
||
esac
|
||
fi
|
||
|
||
if [[ -n "$web_v5" ]]; then
|
||
echo -e "${Info} 网页获取 Snell v5 版本: ${Yellow_font_prefix}${web_v5}${Font_color_suffix}"
|
||
compareVersions "${snell_v5_version}" "$web_v5"
|
||
case $? in
|
||
1) echo -e "${Info} Snell v5 版本状态: 脚本内置版本与网页版本相同" ;;
|
||
0) echo -e "${Info} Snell v5 版本状态: 脚本内置版本比网页版本更新" ;;
|
||
2) echo -e "${Tip} Snell v5 版本状态: 网页版本比脚本内置版本更新" ;;
|
||
esac
|
||
fi
|
||
|
||
if [[ -n "$web_v6" ]]; then
|
||
echo -e "${Info} 网页获取 Snell v6 版本: ${Yellow_font_prefix}${web_v6}${Font_color_suffix}"
|
||
compareVersions "${snell_v6_version}" "$web_v6"
|
||
case $? in
|
||
1) echo -e "${Info} Snell v6 版本状态: 脚本内置版本与网页版本相同" ;;
|
||
0) echo -e "${Info} Snell v6 版本状态: 脚本内置版本比网页版本更新" ;;
|
||
2) echo -e "${Tip} Snell v6 版本状态: 网页版本比脚本内置版本更新" ;;
|
||
esac
|
||
fi
|
||
|
||
sleep 3s
|
||
startMenu
|
||
}
|
||
|
||
# 卸载 Snell
|
||
uninstallSnell(){
|
||
checkInstalledStatus
|
||
echo "确定要卸载 Snell Server ? (y/N)"
|
||
echo
|
||
read -e -p "(默认: n):" unyn
|
||
[[ -z ${unyn} ]] && unyn="n"
|
||
if [[ ${unyn} == [Yy] ]]; then
|
||
echo -e "${Info} 停止并禁用服务..."
|
||
systemctl stop snell-server
|
||
systemctl disable snell-server
|
||
|
||
echo -e "${Info} 移除主程序..."
|
||
rm -rf "${snell_bin}"
|
||
|
||
echo -e "${Info} 移除 systemd 服务文件..."
|
||
rm -f /etc/systemd/system/snell-server.service
|
||
systemctl daemon-reload
|
||
|
||
if [[ -f "${sysctl_conf}" ]]; then
|
||
echo -e "${Tip} 由于网络优化配置可能被其他程序共用,卸载过程未移除网络优化配置,如需彻底移除可手动删除:${sysctl_conf}"
|
||
fi
|
||
|
||
echo -e "${Info} 移除配置文件及版本记录..."
|
||
rm -rf /etc/snell
|
||
echo && echo "Snell Server 卸载完成!" && echo
|
||
else
|
||
echo && echo "卸载已取消..." && echo
|
||
fi
|
||
sleep 3s
|
||
startMenu
|
||
}
|
||
|
||
# 获取 IPv4 地址
|
||
getIpv4(){
|
||
ipv4=$(wget -qO- -4 -t1 -T2 ipinfo.io/ip)
|
||
if [[ -z "${ipv4}" ]]; then
|
||
ipv4=$(wget -qO- -4 -t1 -T2 api.ip.sb/ip)
|
||
if [[ -z "${ipv4}" ]]; then
|
||
ipv4=$(wget -qO- -4 -t1 -T2 members.3322.org/dyndns/getip)
|
||
if [[ -z "${ipv4}" ]]; then
|
||
ipv4="IPv4_Error"
|
||
fi
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# 获取 IPv6 地址
|
||
getIpv6(){
|
||
ip6=$(wget -qO- -6 -t1 -T2 ifconfig.co)
|
||
if [[ -z "${ip6}" ]]; then
|
||
ip6="IPv6_Error"
|
||
fi
|
||
}
|
||
|
||
# 查看配置信息
|
||
viewConfig(){
|
||
checkInstalledStatus
|
||
readConfig
|
||
getIpv4
|
||
getIpv6
|
||
clear && echo
|
||
echo -e "Snell Server 配置信息:"
|
||
echo -e "——————————————————————————————————————————————————"
|
||
if [[ "${ipv4}" != "IPv4_Error" ]]; then
|
||
echo -e " IPv4 地址\t: ${Green_font_prefix}${ipv4}${Font_color_suffix}"
|
||
fi
|
||
if [[ "${ip6}" != "IPv6_Error" ]]; then
|
||
echo -e " IPv6 地址\t: ${Green_font_prefix}${ip6}${Font_color_suffix}"
|
||
fi
|
||
echo -e " 端口\t\t: ${Green_font_prefix}${port}${Font_color_suffix}"
|
||
echo -e " 密钥\t\t: ${Green_font_prefix}${psk}${Font_color_suffix}"
|
||
|
||
if [[ "$ver" != "6" ]]; then
|
||
echo -e " OBFS\t\t: ${Green_font_prefix}${obfs}${Font_color_suffix}"
|
||
if [[ "$obfs" != "off" && -n "$host" ]]; then
|
||
echo -e " 域名\t\t: ${Green_font_prefix}${host}${Font_color_suffix}"
|
||
fi
|
||
echo -e " IPv6\t\t: ${Green_font_prefix}${ipv6}${Font_color_suffix}"
|
||
fi
|
||
|
||
echo -e " TFO\t\t: ${Green_font_prefix}${tfo}${Font_color_suffix}"
|
||
echo -e " DNS\t\t: ${Green_font_prefix}${dns}${Font_color_suffix}"
|
||
|
||
if [[ -n "${egress_interface}" ]]; then
|
||
echo -e " 出口网卡\t: ${Green_font_prefix}${egress_interface}${Font_color_suffix}"
|
||
fi
|
||
|
||
if [[ "$ver" == "6" && -n "$dns_ip_pref" ]]; then
|
||
echo -e " DNS IP 偏好\t: ${Green_font_prefix}${dns_ip_pref}${Font_color_suffix}"
|
||
fi
|
||
|
||
if [[ "$ver" == "6" && -n "$mode" ]]; then
|
||
echo -e " 混淆模式\t: ${Green_font_prefix}${mode}${Font_color_suffix}"
|
||
fi
|
||
|
||
echo -e " 版本\t\t: ${Green_font_prefix}${ver}${Font_color_suffix}"
|
||
local node_name_default="$(uname -n)"
|
||
local node_name=""
|
||
echo -e -n "请输入节点名称(${Green_font_prefix}默认${Font_color_suffix}: ${node_name_default}):"
|
||
read -e node_name
|
||
[[ -z "${node_name}" ]] && node_name="${node_name_default}"
|
||
echo -e " 节点名称 : ${Green_font_prefix}${node_name}${Font_color_suffix}"
|
||
echo -e "——————————————————————————————————————————————————"
|
||
if [[ "$ver" == "6" ]]; then
|
||
echo -e "${Tip} 如有监听多 IP 多端口需求,请手动编辑配置文件。"
|
||
echo -e "——————————————————————————————————————————————————"
|
||
fi
|
||
echo -e "${Info} Surge 配置:"
|
||
if [[ "${ipv4}" != "IPv4_Error" ]]; then
|
||
if [[ "${obfs}" == "off" || "${ver}" == "6" ]]; then
|
||
if [[ "${ver}" == "6" && -n "${mode}" ]]; then
|
||
echo -e "${node_name} = snell, ${ipv4}, ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, mode=${mode}, reuse=true, ecn=true"
|
||
else
|
||
echo -e "${node_name} = snell, ${ipv4}, ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, reuse=true, ecn=true"
|
||
fi
|
||
else
|
||
echo -e "${node_name} = snell, ${ipv4}, ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, obfs=${obfs}, obfs-host=${host}, reuse=true, ecn=true"
|
||
fi
|
||
elif [[ "${ip6}" != "IPv6_Error" ]]; then
|
||
if [[ "${obfs}" == "off" || "${ver}" == "6" ]]; then
|
||
if [[ "${ver}" == "6" && -n "${mode}" ]]; then
|
||
echo -e "${node_name} = snell, [${ip6}], ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, mode=${mode}, reuse=true, ecn=true"
|
||
else
|
||
echo -e "${node_name} = snell, [${ip6}], ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, reuse=true, ecn=true"
|
||
fi
|
||
else
|
||
echo -e "${node_name} = snell, [${ip6}], ${port}, psk=${psk}, version=${ver}, tfo=${tfo}, obfs=${obfs}, obfs-host=${host}, reuse=true, ecn=true"
|
||
fi
|
||
else
|
||
echo -e "${Error} 无法获取 IP 地址!"
|
||
fi
|
||
echo -e "——————————————————————————————————————————————————"
|
||
echo -e "${Info} Mihomo / Nikki 配置:"
|
||
local mihomo_server=""
|
||
if [[ "${ipv4}" != "IPv4_Error" ]]; then
|
||
mihomo_server="${ipv4}"
|
||
elif [[ "${ip6}" != "IPv6_Error" ]]; then
|
||
mihomo_server="${ip6}"
|
||
fi
|
||
|
||
if [[ -z "${mihomo_server}" ]]; then
|
||
echo -e "${Error} 无法获取 IP 地址!"
|
||
elif [[ "${ver}" == "6" ]]; then
|
||
echo -e "${Tip} mihomo/Nikki 当前文档仅支持 Snell v1/2/3/4/5,暂不输出 v6,v6 请用上面的 Surge 配置。"
|
||
else
|
||
local mihomo_json="{\"name\":\"${node_name}\",\"server\":\"${mihomo_server}\",\"port\":${port},\"psk\":\"${psk}\",\"version\":${ver},\"tfo\":${tfo},\"type\":\"snell\""
|
||
if [[ "${ver}" == "3" || "${ver}" == "4" || "${ver}" == "5" ]]; then
|
||
mihomo_json+=",\"udp\":true"
|
||
fi
|
||
if [[ "${ver}" == "4" || "${ver}" == "5" ]]; then
|
||
mihomo_json+=",\"reuse\":true"
|
||
fi
|
||
if [[ "${obfs}" != "off" && -n "${host}" ]]; then
|
||
mihomo_json+=",\"obfs-opts\":{\"mode\":\"${obfs}\",\"host\":\"${host}\"}"
|
||
fi
|
||
mihomo_json+="}"
|
||
echo -e "${mihomo_json}"
|
||
fi
|
||
echo -e "——————————————————————————————————————————————————"
|
||
beforeStartMenu
|
||
}
|
||
|
||
|
||
# 查看运行状态
|
||
viewStatus(){
|
||
echo -e "${Info} 获取 Snell Server 活动日志 ……"
|
||
echo -e "${Tip} 返回主菜单请按 q !"
|
||
systemctl status snell-server
|
||
startMenu
|
||
}
|
||
|
||
# 检查地理位置(用于更新脚本源选择)
|
||
geo_check() {
|
||
api_list="https://blog.cloudflare.com/cdn-cgi/trace https://dash.cloudflare.com/cdn-cgi/trace https://cf-ns.com/cdn-cgi/trace"
|
||
ua="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
|
||
set -- $api_list
|
||
for url in $api_list; do
|
||
text="$(curl -A "$ua" -m 10 -s $url)"
|
||
endpoint="$(echo $text | sed -n 's/.*h=\([^ ]*\).*/\1/p')"
|
||
if echo $text | grep -qw 'CN'; then
|
||
isCN=true
|
||
break
|
||
elif echo $url | grep -q $endpoint; then
|
||
break
|
||
fi
|
||
done
|
||
}
|
||
|
||
# 更新脚本
|
||
updateShell(){
|
||
geo_check
|
||
if [ ! -z "$isCN" ]; then
|
||
shell_url="${SNELL_SCRIPT_CN_URL:-https://mjjtop.com/snell}"
|
||
else
|
||
shell_url="${SNELL_SCRIPT_URL:-https://mjjtop.com/snell}"
|
||
fi
|
||
|
||
echo -e "当前版本为 [ ${sh_ver} ],开始检测最新版本..."
|
||
sh_new_ver=$(wget --no-check-certificate -qO- "$shell_url"|grep 'sh_ver="'|awk -F "=" '{print $NF}'|sed 's/\"//g'|head -1)
|
||
[[ -z ${sh_new_ver} ]] && echo -e "${Error} 检测最新版本失败!" && startMenu
|
||
if [[ ${sh_new_ver} != ${sh_ver} ]]; then
|
||
echo -e "发现新版本[ ${sh_new_ver} ],是否更新?[Y/n]"
|
||
read -p "(默认: y):" yn
|
||
[[ -z "${yn}" ]] && yn="y"
|
||
if [[ ${yn} == [Yy] ]]; then
|
||
wget -O snell.sh --no-check-certificate "$shell_url" && chmod +x snell.sh
|
||
echo -e "脚本已更新为最新版本[ ${sh_new_ver} ]!"
|
||
echo -e "3s后执行新脚本"
|
||
sleep 3s
|
||
exec bash snell.sh
|
||
else
|
||
echo && echo " 已取消..." && echo
|
||
sleep 3s
|
||
startMenu
|
||
fi
|
||
else
|
||
echo -e "当前已是最新版本[ ${sh_new_ver} ]!"
|
||
sleep 3s
|
||
startMenu
|
||
fi
|
||
}
|
||
|
||
|
||
# 返回主菜单前提示
|
||
beforeStartMenu() {
|
||
echo && echo -n -e "${Yellow_font_prefix}* 按回车返回主菜单 *${Font_color_suffix}" && read temp
|
||
startMenu
|
||
}
|
||
|
||
# 显示版本检查进度
|
||
showVersionCheckProgress(){
|
||
local check_duration=${1:-3} # 检查预期耗时,默认3秒
|
||
echo -e "${Info} 正在检查 Snell 版本信息..."
|
||
echo -n "检查进度: "
|
||
|
||
# 根据检查时间动态调整进度条
|
||
local steps=30
|
||
local step_time=$(echo "scale=2; $check_duration / $steps" | bc 2>/dev/null || echo "0.1")
|
||
|
||
for i in $(seq 1 $steps); do
|
||
echo -n -e "${Green_font_prefix}█${Font_color_suffix}"
|
||
sleep $step_time
|
||
done
|
||
echo -e " ${Green_font_prefix}完成${Font_color_suffix}"
|
||
echo -e "${Info} 版本检查完成,正在加载主菜单..."
|
||
sleep 0.3
|
||
}
|
||
|
||
# 检查是否需要进行版本检查
|
||
shouldCheckVersion(){
|
||
local check_interval=3600 # 1小时 = 3600秒
|
||
local last_check_file="/tmp/snell_last_check"
|
||
local current_time=$(date +%s)
|
||
|
||
# 如果没有安装 Snell,不需要检查
|
||
if [[ ! -e ${snell_bin} || ! -e ${snell_conf} ]]; then
|
||
return 1 # 不需要检查
|
||
fi
|
||
|
||
# 如果版本不是 Snell v6,旧版本已停更,不需要检查
|
||
local current_ver=$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}')
|
||
if [[ "$current_ver" != "6" ]]; then
|
||
return 1 # 不需要检查
|
||
fi
|
||
|
||
# 如果检查记录文件不存在,说明是第一次检查
|
||
if [[ ! -f "$last_check_file" ]]; then
|
||
echo "$current_time" > "$last_check_file"
|
||
return 0 # 需要检查
|
||
fi
|
||
|
||
# 读取上次检查时间
|
||
local last_check_time
|
||
last_check_time=$(cat "$last_check_file" 2>/dev/null)
|
||
|
||
# 如果文件内容无效,重新记录并检查
|
||
if [[ ! "$last_check_time" =~ ^[0-9]+$ ]]; then
|
||
echo "$current_time" > "$last_check_file"
|
||
return 0 # 需要检查
|
||
fi
|
||
|
||
# 计算时间差
|
||
local time_diff=$((current_time - last_check_time))
|
||
|
||
# 如果超过1小时,需要检查
|
||
if [[ $time_diff -ge $check_interval ]]; then
|
||
echo "$current_time" > "$last_check_file"
|
||
return 0 # 需要检查
|
||
fi
|
||
|
||
return 1 # 不需要检查
|
||
}
|
||
|
||
# 检查版本更新(带进度显示)
|
||
checkVersionUpdateWithProgress(){
|
||
# 先检查是否需要进行版本检查
|
||
if shouldCheckVersion; then
|
||
echo -e "${Info} 正在检查 Snell 版本信息..."
|
||
|
||
# 显示绿色进度条
|
||
(
|
||
echo -n "检查进度: "
|
||
for i in {1..30}; do
|
||
echo -n -e "${Green_font_prefix}█${Font_color_suffix}"
|
||
sleep 0.1
|
||
done
|
||
echo -e " ${Green_font_prefix}完成${Font_color_suffix}"
|
||
) &
|
||
progress_pid=$!
|
||
|
||
# 在后台进行版本检查
|
||
updateBuiltinVersions false >/dev/null 2>&1
|
||
checkVersionUpdate false >/dev/null 2>&1
|
||
|
||
# 等待进度条完成
|
||
wait $progress_pid
|
||
|
||
echo -e "${Info} 版本检查完成,正在加载主菜单..."
|
||
sleep 0.5
|
||
clear
|
||
else
|
||
# 静默进行版本检查(使用缓存)
|
||
if [[ -e ${snell_bin} && -e ${snell_conf} ]]; then
|
||
updateBuiltinVersions false >/dev/null 2>&1
|
||
checkVersionUpdate false >/dev/null 2>&1
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# 主菜单
|
||
startMenu(){
|
||
clear
|
||
checkRoot
|
||
checkSys
|
||
sysArch
|
||
action=$1
|
||
|
||
# 检查版本更新(在显示菜单前)
|
||
checkVersionUpdateWithProgress
|
||
|
||
# 检查是否安装了 Snell,需要显示更新或升级选项
|
||
show_update_option=false
|
||
major_upgrade_text=""
|
||
major_upgrade_func=""
|
||
|
||
if [[ -e ${snell_bin} && -e ${snell_conf} ]]; then
|
||
current_ver=$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}')
|
||
if [[ "$current_ver" == "2" ]]; then
|
||
major_upgrade_text="Snell v2 更新到 Snell v3"
|
||
major_upgrade_func="updateV2toV3"
|
||
elif [[ "$current_ver" == "3" ]]; then
|
||
major_upgrade_text="Snell v3 更新到 Snell v4"
|
||
major_upgrade_func="updateV3toV4"
|
||
elif [[ "$current_ver" == "4" ]]; then
|
||
major_upgrade_text="Snell v4 更新到 Snell v5"
|
||
major_upgrade_func="updateV4toV5"
|
||
elif [[ "$current_ver" == "5" ]]; then
|
||
major_upgrade_text="Snell v5 更新到 Snell v6"
|
||
major_upgrade_func="updateV5toV6"
|
||
elif [[ "$current_ver" == "6" ]]; then
|
||
show_update_option=true
|
||
fi
|
||
fi
|
||
|
||
echo && echo -e "
|
||
==============================
|
||
Snell Server 管理脚本 ${Red_font_prefix}[v${sh_ver}]${Font_color_suffix}
|
||
==============================
|
||
${Green_font_prefix} 0.${Font_color_suffix} 更新脚本
|
||
——————————————————————————————
|
||
${Green_font_prefix} 1.${Font_color_suffix} 安装 Snell Server
|
||
${Green_font_prefix} 2.${Font_color_suffix} 卸载 Snell Server"
|
||
|
||
# 根据不同情况显示更新选项
|
||
if [[ "$show_update_option" == true ]]; then
|
||
if [[ "$update_available" == true ]]; then
|
||
echo -e " ${Green_font_prefix} 3.${Font_color_suffix} 更新 Snell Server ${Yellow_font_prefix}(可更新)${Font_color_suffix}"
|
||
else
|
||
echo -e " ${Green_font_prefix} 3.${Font_color_suffix} 更新 Snell Server"
|
||
fi
|
||
echo -e "——————————————————————————————
|
||
${Green_font_prefix} 4.${Font_color_suffix} 启动 Snell Server
|
||
${Green_font_prefix} 5.${Font_color_suffix} 停止 Snell Server
|
||
${Green_font_prefix} 6.${Font_color_suffix} 重启 Snell Server
|
||
——————————————————————————————
|
||
${Green_font_prefix} 7.${Font_color_suffix} 设置 配置信息
|
||
${Green_font_prefix} 8.${Font_color_suffix} 查看 配置信息
|
||
${Green_font_prefix} 9.${Font_color_suffix} 查看 运行状态
|
||
——————————————————————————————
|
||
${Green_font_prefix}00.${Font_color_suffix} 退出脚本"
|
||
menu_max=9
|
||
elif [[ -n "$major_upgrade_text" ]]; then
|
||
echo -e " ${Green_font_prefix} 3.${Font_color_suffix} ${major_upgrade_text}"
|
||
echo -e "——————————————————————————————
|
||
${Green_font_prefix} 4.${Font_color_suffix} 启动 Snell Server
|
||
${Green_font_prefix} 5.${Font_color_suffix} 停止 Snell Server
|
||
${Green_font_prefix} 6.${Font_color_suffix} 重启 Snell Server
|
||
——————————————————————————————
|
||
${Green_font_prefix} 7.${Font_color_suffix} 设置 配置信息
|
||
${Green_font_prefix} 8.${Font_color_suffix} 查看 配置信息
|
||
${Green_font_prefix} 9.${Font_color_suffix} 查看 运行状态
|
||
——————————————————————————————
|
||
${Green_font_prefix}00.${Font_color_suffix} 退出脚本"
|
||
menu_max=9
|
||
else
|
||
echo -e "——————————————————————————————
|
||
${Green_font_prefix} 3.${Font_color_suffix} 启动 Snell Server
|
||
${Green_font_prefix} 4.${Font_color_suffix} 停止 Snell Server
|
||
${Green_font_prefix} 5.${Font_color_suffix} 重启 Snell Server
|
||
——————————————————————————————
|
||
${Green_font_prefix} 6.${Font_color_suffix} 设置 配置信息
|
||
${Green_font_prefix} 7.${Font_color_suffix} 查看 配置信息
|
||
${Green_font_prefix} 8.${Font_color_suffix} 查看 运行状态
|
||
——————————————————————————————
|
||
${Green_font_prefix}00.${Font_color_suffix} 退出脚本"
|
||
menu_max=8
|
||
fi
|
||
|
||
echo "==============================" && echo
|
||
if [[ -e ${snell_bin} ]]; then
|
||
checkStatus
|
||
if [[ "$status" == "running" ]]; then
|
||
echo -e " 当前状态: ${Green_font_prefix}已安装${Yellow_font_prefix}[v$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}')]${Font_color_suffix}且${Green_font_prefix}已启动${Font_color_suffix}"
|
||
else
|
||
echo -e " 当前状态: ${Green_font_prefix}已安装${Yellow_font_prefix}[v$(cat ${snell_conf}|grep 'version = '|awk -F 'version = ' '{print $NF}')]${Font_color_suffix}但${Red_font_prefix}未启动${Font_color_suffix}"
|
||
fi
|
||
else
|
||
echo -e " 当前状态: ${Red_font_prefix}未安装${Font_color_suffix}"
|
||
fi
|
||
echo
|
||
|
||
if [[ "$show_update_option" == true || -n "$major_upgrade_text" ]]; then
|
||
read -e -p " 请输入数字[0-9]:" num
|
||
else
|
||
read -e -p " 请输入数字[0-8]:" num
|
||
fi
|
||
|
||
# 根据不同菜单模式处理用户输入
|
||
if [[ "$show_update_option" == true ]]; then
|
||
case "$num" in
|
||
0) updateShell ;;
|
||
1) installSnell ;;
|
||
2) uninstallSnell ;;
|
||
3) updateSnellServer ;;
|
||
4) startSnell ;;
|
||
5) stopSnell ;;
|
||
6) restartSnell ;;
|
||
7) setConfig ;;
|
||
8) viewConfig ;;
|
||
9) viewStatus ;;
|
||
00) exit 1 ;;
|
||
*) echo -e "请输入正确数字${Yellow_font_prefix}[0-9]${Font_color_suffix}"; sleep 2s; startMenu ;;
|
||
esac
|
||
elif [[ -n "$major_upgrade_func" ]]; then
|
||
case "$num" in
|
||
0) updateShell ;;
|
||
1) installSnell ;;
|
||
2) uninstallSnell ;;
|
||
3) ${major_upgrade_func} ;;
|
||
4) startSnell ;;
|
||
5) stopSnell ;;
|
||
6) restartSnell ;;
|
||
7) setConfig ;;
|
||
8) viewConfig ;;
|
||
9) viewStatus ;;
|
||
00) exit 1 ;;
|
||
*) echo -e "请输入正确数字${Yellow_font_prefix}[0-9]${Font_color_suffix}"; sleep 2s; startMenu ;;
|
||
esac
|
||
else
|
||
case "$num" in
|
||
0) updateShell ;;
|
||
1) installSnell ;;
|
||
2) uninstallSnell ;;
|
||
3) startSnell ;;
|
||
4) stopSnell ;;
|
||
5) restartSnell ;;
|
||
6) setConfig ;;
|
||
7) viewConfig ;;
|
||
8) viewStatus ;;
|
||
00) exit 1 ;;
|
||
*) echo -e "请输入正确数字${Yellow_font_prefix}[0-8]${Font_color_suffix}"; sleep 2s; startMenu ;;
|
||
esac
|
||
fi
|
||
|
||
}
|
||
|
||
startMenu
|