Files
sing-box/src/core.sh
2025-04-24 12:52:13 +08:00

1720 lines
55 KiB
Bash

#!/bin/bash
protocol_list=(
TUIC
Trojan
Hysteria2
VMess-WS
VMess-TCP
VMess-HTTP
VMess-QUIC
Shadowsocks
VMess-H2-TLS
VMess-WS-TLS
VLESS-H2-TLS
VLESS-WS-TLS
Trojan-H2-TLS
Trojan-WS-TLS
VMess-HTTPUpgrade-TLS
VLESS-HTTPUpgrade-TLS
Trojan-HTTPUpgrade-TLS
VLESS-REALITY
VLESS-HTTP2-REALITY
# Direct
Socks
)
ss_method_list=(
aes-128-gcm
aes-256-gcm
chacha20-ietf-poly1305
xchacha20-ietf-poly1305
2022-blake3-aes-128-gcm
2022-blake3-aes-256-gcm
2022-blake3-chacha20-poly1305
)
mainmenu=(
"添加配置"
"更改配置"
"查看配置"
"删除配置"
"运行管理"
"更新"
"卸载"
"帮助"
"其他"
"关于"
)
info_list=(
"协议 (protocol)"
"地址 (address)"
"端口 (port)"
"用户ID (id)"
"传输协议 (network)"
"伪装类型 (type)"
"伪装域名 (host)"
"路径 (path)"
"传输层安全 (TLS)"
"应用层协议协商 (Alpn)"
"密码 (password)"
"加密方式 (encryption)"
"链接 (URL)"
"目标地址 (remote addr)"
"目标端口 (remote port)"
"流控 (flow)"
"SNI (serverName)"
"指纹 (Fingerprint)"
"公钥 (Public key)"
"用户名 (Username)"
"跳过证书验证 (allowInsecure)"
"拥塞控制算法 (congestion_control)"
)
change_list=(
"更改协议"
"更改端口"
"更改域名"
"更改路径"
"更改密码"
"更改 UUID"
"更改加密方式"
"更改目标地址"
"更改目标端口"
"更改密钥"
"更改 SNI (serverName)"
"更改伪装网站"
"更改用户名 (Username)"
)
servername_list=(
www.amazon.com
www.ebay.com
www.paypal.com
www.cloudflare.com
dash.cloudflare.com
aws.amazon.com
)
is_random_ss_method=${ss_method_list[$(shuf -i 4-6 -n1)]} # random only use ss2022
is_random_servername=${servername_list[$(shuf -i 0-${#servername_list[@]} -n1) - 1]}
msg() {
echo -e "$@"
}
msg_ul() {
echo -e "\e[4m$@\e[0m"
}
# pause
pause() {
echo
echo -ne "$(_green Enter 回车键) 继续, 或按 $(_red Ctrl + C) 取消."
read -rs -d $'\n'
echo
}
get_uuid() {
tmp_uuid=$(cat /proc/sys/kernel/random/uuid)
}
get_ip() {
[[ $ip || $is_no_auto_tls || $is_gen || $is_dont_get_ip ]] && return
export "$(_wget -4 -qO- https://one.one.one.one/cdn-cgi/trace | grep ip=)" &>/dev/null
[[ ! $ip ]] && export "$(_wget -6 -qO- https://one.one.one.one/cdn-cgi/trace | grep ip=)" &>/dev/null
[[ ! $ip ]] && {
err "获取服务器 IP 失败.."
}
}
get_port() {
is_count=0
while :; do
((is_count++))
if [[ $is_count -ge 233 ]]; then
err "自动获取可用端口失败次数达到 233 次, 请检查端口占用情况."
fi
tmp_port=$(shuf -i 445-65535 -n 1)
[[ ! $(is_test port_used $tmp_port) && $tmp_port != $port ]] && break
done
}
get_pbk() {
is_tmp_pbk=($($is_core_bin generate reality-keypair | sed 's/.*://'))
is_public_key=${is_tmp_pbk[1]}
is_private_key=${is_tmp_pbk[0]}
}
show_list() {
PS3=''
COLUMNS=1
select i in "$@"; do echo; done &
wait
# i=0
# for v in "$@"; do
# ((i++))
# echo "$i) $v"
# done
# echo
}
is_test() {
case $1 in
number)
echo $2 | egrep '^[1-9][0-9]?+$'
;;
port)
if [[ $(is_test number $2) ]]; then
[[ $2 -le 65535 ]] && echo ok
fi
;;
port_used)
[[ $(is_port_used $2) && ! $is_cant_test_port ]] && echo ok
;;
domain)
echo $2 | egrep -i '^\w(\w|\-|\.)?+\.\w+$'
;;
path)
echo $2 | egrep -i '^\/\w(\w|\-|\/)?+\w$'
;;
uuid)
echo $2 | egrep -i '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
;;
esac
}
is_port_used() {
if [[ $(type -P netstat) ]]; then
[[ ! $is_used_port ]] && is_used_port="$(netstat -tunlp | sed -n 's/.*:\([0-9]\+\).*/\1/p' | sort -nu)"
echo $is_used_port | sed 's/ /\n/g' | grep ^${1}$
return
fi
if [[ $(type -P ss) ]]; then
[[ ! $is_used_port ]] && is_used_port="$(ss -tunlp | sed -n 's/.*:\([0-9]\+\).*/\1/p' | sort -nu)"
echo $is_used_port | sed 's/ /\n/g' | grep ^${1}$
return
fi
is_cant_test_port=1
msg "$is_warn 无法检测端口是否可用."
msg "请执行: $(_yellow "${cmd} update -y; ${cmd} install net-tools -y") 来修复此问题."
}
# ask input a string or pick a option for list.
ask() {
case $1 in
set_ss_method)
is_tmp_list=(${ss_method_list[@]})
is_default_arg=$is_random_ss_method
is_opt_msg="\n请选择加密方式:\n"
is_opt_input_msg="(默认\e[92m $is_default_arg\e[0m):"
is_ask_set=ss_method
;;
set_protocol)
is_tmp_list=(${protocol_list[@]})
[[ $is_no_auto_tls ]] && {
unset is_tmp_list
for v in ${protocol_list[@]}; do
[[ $(grep -i tls$ <<<$v) ]] && is_tmp_list=(${is_tmp_list[@]} $v)
done
}
is_opt_msg="\n请选择协议:\n"
is_ask_set=is_new_protocol
;;
set_change_list)
is_tmp_list=()
for v in ${is_can_change[@]}; do
is_tmp_list+=("${change_list[$v]}")
done
is_opt_msg="\n请选择更改:\n"
is_ask_set=is_change_str
is_opt_input_msg=$3
;;
string)
is_ask_set=$2
is_opt_input_msg=$3
;;
list)
is_ask_set=$2
[[ ! $is_tmp_list ]] && is_tmp_list=($3)
is_opt_msg=$4
is_opt_input_msg=$5
;;
get_config_file)
is_tmp_list=("${is_all_json[@]}")
is_opt_msg="\n请选择配置:\n"
is_ask_set=is_config_file
;;
mainmenu)
is_tmp_list=("${mainmenu[@]}")
is_ask_set=is_main_pick
is_emtpy_exit=1
;;
esac
msg $is_opt_msg
[[ ! $is_opt_input_msg ]] && is_opt_input_msg="请选择 [\e[91m1-${#is_tmp_list[@]}\e[0m]:"
[[ $is_tmp_list ]] && show_list "${is_tmp_list[@]}"
while :; do
echo -ne $is_opt_input_msg
read REPLY
[[ ! $REPLY && $is_emtpy_exit ]] && exit
[[ ! $REPLY && $is_default_arg ]] && export $is_ask_set=$is_default_arg && break
[[ "$REPLY" == "${is_str}2${is_get}3${is_opt}3" && $is_ask_set == 'is_main_pick' ]] && {
msg "\n${is_get}2${is_str}3${is_msg}3b${is_tmp}o${is_opt}y\n" && exit
}
if [[ ! $is_tmp_list ]]; then
[[ $(grep port <<<$is_ask_set) ]] && {
[[ ! $(is_test port "$REPLY") ]] && {
msg "$is_err 请输入正确的端口, 可选(1-65535)"
continue
}
if [[ $(is_test port_used $REPLY) && $is_ask_set != 'door_port' ]]; then
msg "$is_err 无法使用 ($REPLY) 端口."
continue
fi
}
[[ $(grep path <<<$is_ask_set) && ! $(is_test path "$REPLY") ]] && {
[[ ! $tmp_uuid ]] && get_uuid
msg "$is_err 请输入正确的路径, 例如: /$tmp_uuid"
continue
}
[[ $(grep uuid <<<$is_ask_set) && ! $(is_test uuid "$REPLY") ]] && {
[[ ! $tmp_uuid ]] && get_uuid
msg "$is_err 请输入正确的 UUID, 例如: $tmp_uuid"
continue
}
[[ $(grep ^y$ <<<$is_ask_set) ]] && {
[[ $(grep -i ^y$ <<<"$REPLY") ]] && break
msg "请输入 (y)"
continue
}
[[ $REPLY ]] && export $is_ask_set=$REPLY && msg "使用: ${!is_ask_set}" && break
else
[[ $(is_test number "$REPLY") ]] && is_ask_result=${is_tmp_list[$REPLY - 1]}
[[ $is_ask_result ]] && export $is_ask_set="$is_ask_result" && msg "选择: ${!is_ask_set}" && break
fi
msg "输入${is_err}"
done
unset is_opt_msg is_opt_input_msg is_tmp_list is_ask_result is_default_arg is_emtpy_exit
}
# create file
create() {
case $1 in
server)
is_tls=none
get new
# listen
is_listen='listen: "::"'
# file name
if [[ $host ]]; then
is_config_name=$2-${host}.json
is_listen='listen: "127.0.0.1"'
else
is_config_name=$2-${port}.json
fi
is_json_file=$is_conf_dir/$is_config_name
# get json
[[ $is_change || ! $json_str ]] && get protocol $2
[[ $net == "reality" ]] && is_add_public_key=",outbounds:[{type:\"direct\"},{tag:\"public_key_$is_public_key\",type:\"direct\"}]"
is_new_json=$(jq "{inbounds:[{tag:\"$is_config_name\",type:\"$is_protocol\",$is_listen,listen_port:$port,$json_str}]$is_add_public_key}" <<<{})
[[ $is_test_json ]] && return # tmp test
# only show json, dont save to file.
[[ $is_gen ]] && {
msg
jq <<<$is_new_json
msg
return
}
# del old file
[[ $is_config_file ]] && is_no_del_msg=1 && del $is_config_file
# save json to file
cat <<<$is_new_json >$is_json_file
if [[ $is_new_install ]]; then
# config.json
create config.json
fi
# caddy auto tls
[[ $is_caddy && $host && ! $is_no_auto_tls ]] && {
create caddy $net
}
# restart core
manage restart &
;;
client)
is_tls=tls
is_client=1
get info $2
[[ ! $is_client_id_json ]] && err "($is_config_name) 不支持生成客户端配置."
is_new_json=$(jq '{outbounds:[{tag:'\"$is_config_name\"',protocol:'\"$is_protocol\"','"$is_client_id_json"','"$is_stream"'}]}' <<<{})
msg
jq <<<$is_new_json
msg
;;
caddy)
load caddy.sh
[[ $is_install_caddy ]] && caddy_config new
[[ ! $(grep "$is_caddy_conf" $is_caddyfile) ]] && {
msg "import $is_caddy_conf/*.conf" >>$is_caddyfile
}
[[ ! -d $is_caddy_conf ]] && mkdir -p $is_caddy_conf
caddy_config $2
manage restart caddy &
;;
config.json)
is_log='log:{output:"/var/log/'$is_core'/access.log",level:"info","timestamp":true}'
is_dns='dns:{}'
is_ntp='ntp:{"enabled":true,"server":"time.apple.com"},'
if [[ -f $is_config_json ]]; then
[[ $(jq .ntp.enabled $is_config_json) != "true" ]] && is_ntp=
else
[[ ! $is_ntp_on ]] && is_ntp=
fi
is_outbounds='outbounds:[{tag:"direct",type:"direct"}]'
is_server_config_json=$(jq "{$is_log,$is_dns,$is_ntp$is_outbounds}" <<<{})
cat <<<$is_server_config_json >$is_config_json
manage restart &
;;
esac
}
# change config file
change() {
is_change=1
is_dont_show_info=1
if [[ $2 ]]; then
case ${2,,} in
full)
is_change_id=full
;;
new)
is_change_id=0
;;
port)
is_change_id=1
;;
host)
is_change_id=2
;;
path)
is_change_id=3
;;
pass | passwd | password)
is_change_id=4
;;
id | uuid)
is_change_id=5
;;
ssm | method | ss-method | ss_method)
is_change_id=6
;;
dda | door-addr | door_addr)
is_change_id=7
;;
ddp | door-port | door_port)
is_change_id=8
;;
key | publickey | privatekey)
is_change_id=9
;;
sni | servername | servernames)
is_change_id=10
;;
web | proxy-site)
is_change_id=11
;;
*)
[[ $is_try_change ]] && return
err "无法识别 ($2) 更改类型."
;;
esac
fi
[[ $is_try_change ]] && return
[[ $is_dont_auto_exit ]] && {
get info $1
} || {
[[ $is_change_id ]] && {
is_change_msg=${change_list[$is_change_id]}
[[ $is_change_id == 'full' ]] && {
[[ $3 ]] && is_change_msg="更改多个参数" || is_change_msg=
}
[[ $is_change_msg ]] && _green "\n快速执行: $is_change_msg"
}
info $1
[[ $is_auto_get_config ]] && msg "\n自动选择: $is_config_file"
}
is_old_net=$net
[[ $is_tcp_http ]] && net=http
[[ $host ]] && net=$is_protocol-$net-tls
[[ $is_reality && $net_type =~ 'http' ]] && net=rh2
[[ $3 == 'auto' ]] && is_auto=1
# if is_dont_show_info exist, cant show info.
is_dont_show_info=
# if not prefer args, show change list and then get change id.
[[ ! $is_change_id ]] && {
ask set_change_list
is_change_id=${is_can_change[$REPLY - 1]}
}
case $is_change_id in
full)
add $net ${@:3}
;;
0)
# new protocol
is_set_new_protocol=1
add ${@:3}
;;
1)
# new port
is_new_port=$3
[[ $host && ! $is_caddy ]] && err "($is_config_file) 不支持更改端口, 因为没啥意义."
if [[ $is_new_port && ! $is_auto ]]; then
[[ ! $(is_test port $is_new_port) ]] && err "请输入正确的端口, 可选(1-65535)"
[[ $is_new_port != 443 && $(is_test port_used $is_new_port) ]] && err "无法使用 ($is_new_port) 端口"
fi
[[ $is_auto ]] && get_port && is_new_port=$tmp_port
[[ ! $is_new_port ]] && ask string is_new_port "请输入新端口:"
if [[ $is_caddy && $host ]]; then
net=$is_old_net
is_https_port=$is_new_port
load caddy.sh
caddy_config $net
manage restart caddy &
info
else
add $net $is_new_port
fi
;;
2)
# new host
is_new_host=$3
[[ ! $host ]] && err "($is_config_file) 不支持更改域名."
[[ ! $is_new_host ]] && ask string is_new_host "请输入新域名:"
old_host=$host # del old host
add $net $is_new_host
;;
3)
# new path
is_new_path=$3
[[ ! $path ]] && err "($is_config_file) 不支持更改路径."
[[ $is_auto ]] && get_uuid && is_new_path=/$tmp_uuid
[[ ! $is_new_path ]] && ask string is_new_path "请输入新路径:"
add $net auto auto $is_new_path
;;
4)
# new password
is_new_pass=$3
if [[ $ss_password || $password ]]; then
[[ $is_auto ]] && {
get_uuid && is_new_pass=$tmp_uuid
[[ $ss_password ]] && is_new_pass=$(get ss2022)
}
else
err "($is_config_file) 不支持更改密码."
fi
[[ ! $is_new_pass ]] && ask string is_new_pass "请输入新密码:"
password=$is_new_pass
ss_password=$is_new_pass
is_socks_pass=$is_new_pass
add $net
;;
5)
# new uuid
is_new_uuid=$3
[[ ! $uuid ]] && err "($is_config_file) 不支持更改 UUID."
[[ $is_auto ]] && get_uuid && is_new_uuid=$tmp_uuid
[[ ! $is_new_uuid ]] && ask string is_new_uuid "请输入新 UUID:"
add $net auto $is_new_uuid
;;
6)
# new method
is_new_method=$3
[[ $net != 'ss' ]] && err "($is_config_file) 不支持更改加密方式."
[[ $is_auto ]] && is_new_method=$is_random_ss_method
[[ ! $is_new_method ]] && {
ask set_ss_method
is_new_method=$ss_method
}
add $net auto auto $is_new_method
;;
7)
# new remote addr
is_new_door_addr=$3
[[ $net != 'direct' ]] && err "($is_config_file) 不支持更改目标地址."
[[ ! $is_new_door_addr ]] && ask string is_new_door_addr "请输入新的目标地址:"
door_addr=$is_new_door_addr
add $net
;;
8)
# new remote port
is_new_door_port=$3
[[ $net != 'direct' ]] && err "($is_config_file) 不支持更改目标端口."
[[ ! $is_new_door_port ]] && {
ask string door_port "请输入新的目标端口:"
is_new_door_port=$door_port
}
add $net auto auto $is_new_door_port
;;
9)
# new is_private_key is_public_key
is_new_private_key=$3
is_new_public_key=$4
[[ ! $is_reality ]] && err "($is_config_file) 不支持更改密钥."
if [[ $is_auto ]]; then
get_pbk
add $net
else
[[ $is_new_private_key && ! $is_new_public_key ]] && {
err "无法找到 Public key."
}
[[ ! $is_new_private_key ]] && ask string is_new_private_key "请输入新 Private key:"
[[ ! $is_new_public_key ]] && ask string is_new_public_key "请输入新 Public key:"
if [[ $is_new_private_key == $is_new_public_key ]]; then
err "Private key 和 Public key 不能一样."
fi
is_tmp_json=$is_conf_dir/$is_config_file-$uuid
cp -f $is_conf_dir/$is_config_file $is_tmp_json
sed -i s#$is_private_key #$is_new_private_key# $is_tmp_json
$is_core_bin check -c $is_tmp_json &>/dev/null
if [[ $? != 0 ]]; then
is_key_err=1
is_key_err_msg="Private key 无法通过测试."
fi
sed -i s#$is_new_private_key #$is_new_public_key# $is_tmp_json
$is_core_bin check -c $is_tmp_json &>/dev/null
if [[ $? != 0 ]]; then
is_key_err=1
is_key_err_msg+="Public key 无法通过测试."
fi
rm $is_tmp_json
[[ $is_key_err ]] && err $is_key_err_msg
is_private_key=$is_new_private_key
is_public_key=$is_new_public_key
is_test_json=
add $net
fi
;;
10)
# new serverName
is_new_servername=$3
[[ ! $is_reality ]] && err "($is_config_file) 不支持更改 serverName."
[[ $is_auto ]] && is_new_servername=$is_random_servername
[[ ! $is_new_servername ]] && ask string is_new_servername "请输入新的 serverName:"
is_servername=$is_new_servername
[[ $(grep -i "^233boy.com$" <<<$is_servername) ]] && {
err "你干嘛~哎呦~"
}
add $net
;;
11)
# new proxy site
is_new_proxy_site=$3
[[ ! $is_caddy && ! $host ]] && {
err "($is_config_file) 不支持更改伪装网站."
}
[[ ! -f $is_caddy_conf/${host}.conf.add ]] && err "无法配置伪装网站."
[[ ! $is_new_proxy_site ]] && ask string is_new_proxy_site "请输入新的伪装网站 (例如 example.com):"
proxy_site=$(sed 's#^.*//##;s#/$##' <<<$is_new_proxy_site)
[[ $(grep -i "^233boy.com$" <<<$proxy_site) ]] && {
err "你干嘛~哎呦~"
} || {
load caddy.sh
caddy_config proxy
manage restart caddy &
}
msg "\n已更新伪装网站为: $(_green $proxy_site) \n"
;;
12)
# new socks user
[[ ! $is_socks_user ]] && err "($is_config_file) 不支持更改用户名 (Username)."
ask string is_socks_user "请输入新用户名 (Username):"
add $net
;;
esac
}
# delete config.
del() {
# dont get ip
is_dont_get_ip=1
[[ $is_conf_dir_empty ]] && return # not found any json file.
# get a config file
[[ ! $is_config_file ]] && get info $1
if [[ $is_config_file ]]; then
if [[ $is_main_start && ! $is_no_del_msg ]]; then
msg "\n是否删除配置文件?: $is_config_file"
pause
fi
rm -rf $is_conf_dir/"$is_config_file"
[[ ! $is_new_json ]] && manage restart &
[[ ! $is_no_del_msg ]] && _green "\n已删除: $is_config_file\n"
[[ $is_caddy ]] && {
is_del_host=$host
[[ $is_change ]] && {
[[ ! $old_host ]] && return # no host exist or not set new host;
is_del_host=$old_host
}
[[ $is_del_host && $host != $old_host ]] && {
rm -rf $is_caddy_conf/$is_del_host.conf $is_caddy_conf/$is_del_host.conf.add
[[ ! $is_new_json ]] && manage restart caddy &
}
}
fi
if [[ ! $(ls $is_conf_dir | grep .json) && ! $is_change ]]; then
warn "当前配置目录为空! 因为你刚刚删除了最后一个配置文件."
is_conf_dir_empty=1
fi
unset is_dont_get_ip
[[ $is_dont_auto_exit ]] && unset is_config_file
}
# uninstall
uninstall() {
if [[ $is_caddy ]]; then
is_tmp_list=("卸载 $is_core_name" "卸载 ${is_core_name} & Caddy")
ask list is_do_uninstall
else
ask string y "是否卸载 ${is_core_name}? [y]:"
fi
manage stop &>/dev/null
manage disable &>/dev/null
rm -rf $is_core_dir $is_log_dir $is_sh_bin ${is_sh_bin/$is_core/sb} /lib/systemd/system/$is_core.service
sed -i "/alias $is_core=/d" /root/.bashrc
# uninstall caddy; 2 is ask result
if [[ $REPLY == '2' ]]; then
manage stop caddy &>/dev/null
manage disable caddy &>/dev/null
rm -rf $is_caddy_dir $is_caddy_bin /lib/systemd/system/caddy.service
fi
[[ $is_install_sh ]] && return # reinstall
_green "\n卸载完成!"
msg "脚本哪里需要完善? 请反馈"
msg "反馈问题) $(msg_ul https://github.com/${is_sh_repo}/issues)\n"
}
# manage run status
manage() {
[[ $is_dont_auto_exit ]] && return
case $1 in
1 | start)
is_do=start
is_do_msg=启动
is_test_run=1
;;
2 | stop)
is_do=stop
is_do_msg=停止
;;
3 | r | restart)
is_do=restart
is_do_msg=重启
is_test_run=1
;;
*)
is_do=$1
is_do_msg=$1
;;
esac
case $2 in
caddy)
is_do_name=$2
is_run_bin=$is_caddy_bin
is_do_name_msg=Caddy
;;
*)
is_do_name=$is_core
is_run_bin=$is_core_bin
is_do_name_msg=$is_core_name
;;
esac
systemctl $is_do $is_do_name
[[ $is_test_run && ! $is_new_install ]] && {
sleep 2
if [[ ! $(pgrep -f $is_run_bin) ]]; then
is_run_fail=${is_do_name_msg,,}
[[ ! $is_no_manage_msg ]] && {
msg
warn "($is_do_msg) $is_do_name_msg 失败"
_yellow "检测到运行失败, 自动执行测试运行."
get test-run
_yellow "测试结束, 请按 Enter 退出."
}
fi
}
}
# add a config
add() {
is_lower=${1,,}
if [[ $is_lower ]]; then
case $is_lower in
ws | tcp | quic | http)
is_new_protocol=VMess-${is_lower^^}
;;
wss | h2 | hu | vws | vh2 | vhu | tws | th2 | thu)
is_new_protocol=$(sed -E "s/^V/VLESS-/;s/^T/Trojan-/;/^(W|H)/{s/^/VMess-/};s/WSS/WS/;s/HU/HTTPUpgrade/" <<<${is_lower^^})-TLS
;;
r | reality)
is_new_protocol=VLESS-REALITY
;;
rh2)
is_new_protocol=VLESS-HTTP2-REALITY
;;
ss)
is_new_protocol=Shadowsocks
;;
door | direct)
is_new_protocol=Direct
;;
tuic)
is_new_protocol=TUIC
;;
hy | hy2 | hysteria*)
is_new_protocol=Hysteria2
;;
trojan)
is_new_protocol=Trojan
;;
socks)
is_new_protocol=Socks
;;
*)
for v in ${protocol_list[@]}; do
[[ $(egrep -i "^$is_lower$" <<<$v) ]] && is_new_protocol=$v && break
done
[[ ! $is_new_protocol ]] && err "无法识别 ($1), 请使用: $is_core add [protocol] [args... | auto]"
;;
esac
fi
# no prefer protocol
[[ ! $is_new_protocol ]] && ask set_protocol
case ${is_new_protocol,,} in
*-tls)
is_use_tls=1
is_use_host=$2
is_use_uuid=$3
is_use_path=$4
is_add_opts="[host] [uuid] [/path]"
;;
vmess* | tuic*)
is_use_port=$2
is_use_uuid=$3
is_add_opts="[port] [uuid]"
;;
trojan* | hysteria*)
is_use_port=$2
is_use_pass=$3
is_add_opts="[port] [password]"
;;
*reality*)
is_reality=1
is_use_port=$2
is_use_uuid=$3
is_use_servername=$4
is_add_opts="[port] [uuid] [sni]"
;;
shadowsocks)
is_use_port=$2
is_use_pass=$3
is_use_method=$4
is_add_opts="[port] [password] [method]"
;;
direct)
is_use_port=$2
is_use_door_addr=$3
is_use_door_port=$4
is_add_opts="[port] [remote_addr] [remote_port]"
;;
socks)
is_socks=1
is_use_port=$2
is_use_socks_user=$3
is_use_socks_pass=$4
is_add_opts="[port] [username] [password]"
;;
esac
[[ $1 && ! $is_change ]] && {
msg "\n使用协议: $is_new_protocol"
# err msg tips
is_err_tips="\n\n请使用: $(_green $is_core add $1 $is_add_opts) 来添加 $is_new_protocol 配置"
}
# remove old protocol args
if [[ $is_set_new_protocol ]]; then
case $is_old_net in
h2 | ws | httpupgrade)
old_host=$host
[[ ! $is_use_tls ]] && host=
;;
reality)
net_type=
[[ ! $(grep -i reality <<<$is_new_protocol) ]] && is_reality=
;;
ss)
[[ $(is_test uuid $ss_password) ]] && uuid=$ss_password
;;
esac
[[ ! $(is_test uuid $uuid) ]] && uuid=
[[ $(is_test uuid $password) ]] && uuid=$password
fi
# no-auto-tls only use h2,ws,grpc
if [[ $is_no_auto_tls && ! $is_use_tls ]]; then
err "$is_new_protocol 不支持手动配置 tls."
fi
# prefer args.
if [[ $2 ]]; then
for v in is_use_port is_use_uuid is_use_host is_use_path is_use_pass is_use_method is_use_door_addr is_use_door_port; do
[[ ${!v} == 'auto' ]] && unset $v
done
if [[ $is_use_port ]]; then
[[ ! $(is_test port ${is_use_port}) ]] && {
err "($is_use_port) 不是一个有效的端口. $is_err_tips"
}
[[ $(is_test port_used $is_use_port) && ! $is_gen ]] && {
err "无法使用 ($is_use_port) 端口. $is_err_tips"
}
port=$is_use_port
fi
if [[ $is_use_door_port ]]; then
[[ ! $(is_test port ${is_use_door_port}) ]] && {
err "(${is_use_door_port}) 不是一个有效的目标端口. $is_err_tips"
}
door_port=$is_use_door_port
fi
if [[ $is_use_uuid ]]; then
[[ ! $(is_test uuid $is_use_uuid) ]] && {
err "($is_use_uuid) 不是一个有效的 UUID. $is_err_tips"
}
uuid=$is_use_uuid
fi
if [[ $is_use_path ]]; then
[[ ! $(is_test path $is_use_path) ]] && {
err "($is_use_path) 不是有效的路径. $is_err_tips"
}
path=$is_use_path
fi
if [[ $is_use_method ]]; then
is_tmp_use_name=加密方式
is_tmp_list=${ss_method_list[@]}
for v in ${is_tmp_list[@]}; do
[[ $(egrep -i "^${is_use_method}$" <<<$v) ]] && is_tmp_use_type=$v && break
done
[[ ! ${is_tmp_use_type} ]] && {
warn "(${is_use_method}) 不是一个可用的${is_tmp_use_name}."
msg "${is_tmp_use_name}可用如下: "
for v in ${is_tmp_list[@]}; do
msg "\t\t$v"
done
msg "$is_err_tips\n"
exit 1
}
ss_method=$is_tmp_use_type
fi
[[ $is_use_pass ]] && ss_password=$is_use_pass && password=$is_use_pass
[[ $is_use_host ]] && host=$is_use_host
[[ $is_use_door_addr ]] && door_addr=$is_use_door_addr
[[ $is_use_servername ]] && is_servername=$is_use_servername
[[ $is_use_socks_user ]] && is_socks_user=$is_use_socks_user
[[ $is_use_socks_pass ]] && is_socks_pass=$is_use_socks_pass
fi
if [[ $is_use_tls ]]; then
if [[ ! $is_no_auto_tls && ! $is_caddy && ! $is_gen && ! $is_dont_test_host ]]; then
# test auto tls
[[ $(is_test port_used 80) || $(is_test port_used 443) ]] && {
get_port
is_http_port=$tmp_port
get_port
is_https_port=$tmp_port
warn "端口 (80 或 443) 已经被占用, 你也可以考虑使用 no-auto-tls"
msg "\e[41m no-auto-tls 帮助(help)\e[0m: $(msg_ul https://233boy.com/$is_core/no-auto-tls/)\n"
msg "\n Caddy 将使用非标准端口实现自动配置 TLS, HTTP:$is_http_port HTTPS:$is_https_port\n"
msg "请确定是否继续???"
pause
}
is_install_caddy=1
fi
# set host
[[ ! $host ]] && ask string host "请输入域名:"
# test host dns
get host-test
else
# for main menu start, dont auto create args
if [[ $is_main_start ]]; then
# set port
[[ ! $port ]] && ask string port "请输入端口:"
case ${is_new_protocol,,} in
socks)
# set user
[[ ! $is_socks_user ]] && ask string is_socks_user "请设置用户名:"
# set password
[[ ! $is_socks_pass ]] && ask string is_socks_pass "请设置密码:"
;;
shadowsocks)
# set method
[[ ! $ss_method ]] && ask set_ss_method
# set password
[[ ! $ss_password ]] && ask string ss_password "请设置密码:"
;;
esac
fi
fi
# Dokodemo-Door
if [[ $is_new_protocol == 'Direct' ]]; then
# set remote addr
[[ ! $door_addr ]] && ask string door_addr "请输入目标地址:"
# set remote port
[[ ! $door_port ]] && ask string door_port "请输入目标端口:"
fi
# Shadowsocks 2022
if [[ $(grep 2022 <<<$ss_method) ]]; then
# test ss2022 password
[[ $ss_password ]] && {
is_test_json=1
create server Shadowsocks
[[ ! $tmp_uuid ]] && get_uuid
is_test_json_save=$is_conf_dir/tmp-test-$tmp_uuid
cat <<<"$is_new_json" >$is_test_json_save
$is_core_bin check -c $is_test_json_save &>/dev/null
if [[ $? != 0 ]]; then
warn "Shadowsocks 协议 ($ss_method) 不支持使用密码 ($(_red_bg $ss_password))\n\n你可以使用命令: $(_green $is_core ss2022) 生成支持的密码.\n\n脚本将自动创建可用密码:)"
ss_password=
# create new json.
json_str=
fi
is_test_json=
rm -f $is_test_json_save
}
fi
# install caddy
if [[ $is_install_caddy ]]; then
get install-caddy
fi
# create json
create server $is_new_protocol
# show config info.
info
}
# get config info
# or somes required args
get() {
case $1 in
addr)
is_addr=$host
[[ ! $is_addr ]] && {
get_ip
is_addr=$ip
}
;;
new)
[[ ! $host ]] && get_ip
[[ ! $port ]] && get_port && port=$tmp_port
[[ ! $uuid ]] && get_uuid && uuid=$tmp_uuid
;;
file)
is_file_str=$2
[[ ! $is_file_str ]] && is_file_str='.json$'
# is_all_json=("$(ls $is_conf_dir | egrep $is_file_str)")
readarray -t is_all_json <<<"$(ls $is_conf_dir | egrep -i "$is_file_str" | sed '/dynamic-port-.*-link/d' | head -233)" # limit max 233 lines for show.
[[ ! $is_all_json ]] && err "无法找到相关的配置文件: $2"
[[ ${#is_all_json[@]} -eq 1 ]] && is_config_file=$is_all_json && is_auto_get_config=1
[[ ! $is_config_file ]] && {
[[ $is_dont_auto_exit ]] && return
ask get_config_file
}
;;
info)
get file $2
if [[ $is_config_file ]]; then
is_json_str=$(cat $is_conf_dir/"$is_config_file" | sed s#//.*##)
is_json_data=$(jq '(.inbounds[0]|.type,.listen_port,(.users[0]|.uuid,.password,.username),.method,.password,.override_port,.override_address,(.transport|.type,.path,.headers.host),(.tls|.server_name,.reality.private_key)),(.outbounds[1].tag)' <<<$is_json_str)
[[ $? != 0 ]] && err "无法读取此文件: $is_config_file"
is_up_var_set=(null is_protocol port uuid password username ss_method ss_password door_port door_addr net_type path host is_servername is_private_key is_public_key)
[[ $is_debug ]] && msg "\n------------- debug: $is_config_file -------------"
i=0
for v in $(sed 's/""/null/g;s/"//g' <<<"$is_json_data"); do
((i++))
[[ $is_debug ]] && msg "$i-${is_up_var_set[$i]}: $v"
export ${is_up_var_set[$i]}="${v}"
done
for v in ${is_up_var_set[@]}; do
[[ ${!v} == 'null' ]] && unset $v
done
if [[ $is_private_key ]]; then
is_reality=1
net_type+=reality
is_public_key=${is_public_key/public_key_/}
fi
is_socks_user=$username
is_socks_pass=$password
is_config_name=$is_config_file
if [[ $is_caddy && $host && -f $is_caddy_conf/$host.conf ]]; then
is_tmp_https_port=$(egrep -o "$host:[1-9][0-9]?+" $is_caddy_conf/$host.conf | sed s/.*://)
fi
[[ $is_tmp_https_port ]] && is_https_port=$is_tmp_https_port
[[ $is_client && $host ]] && port=$is_https_port
get protocol $is_protocol-$net_type
fi
;;
protocol)
get addr # get host or server ip
is_lower=${2,,}
net=
is_users="users:[{uuid:\"$uuid\"}]"
is_tls_json='tls:{enabled:true,alpn:["h3"],key_path:"'$is_tls_key'",certificate_path:"'$is_tls_cer'"}'
case $is_lower in
vmess*)
is_protocol=vmess
[[ $is_lower =~ "tcp" || ! $net_type && $is_up_var_set ]] && net=tcp && json_str=$is_users
;;
vless*)
is_protocol=vless
;;
tuic*)
net=tuic
is_protocol=$net
[[ ! $password ]] && password=$uuid
is_users="users:[{uuid:\"$uuid\",password:\"$password\"}]"
json_str="$is_users,congestion_control:\"bbr\",$is_tls_json"
;;
trojan*)
is_protocol=trojan
[[ ! $password ]] && password=$uuid
is_users="users:[{password:\"$password\"}]"
[[ ! $host ]] && {
net=trojan
json_str="$is_users,${is_tls_json/alpn\:\[\"h3\"\],/}"
}
;;
hysteria2*)
net=hysteria2
is_protocol=$net
[[ ! $password ]] && password=$uuid
json_str="users:[{password:\"$password\"}],$is_tls_json"
;;
shadowsocks*)
net=ss
is_protocol=shadowsocks
[[ ! $ss_method ]] && ss_method=$is_random_ss_method
[[ ! $ss_password ]] && {
ss_password=$uuid
[[ $(grep 2022 <<<$ss_method) ]] && ss_password=$(get ss2022)
}
json_str="method:\"$ss_method\",password:\"$ss_password\""
;;
direct*)
net=direct
is_protocol=$net
json_str="override_port:$door_port,override_address:\"$door_addr\""
;;
socks*)
net=socks
is_protocol=$net
[[ ! $is_socks_user ]] && is_socks_user=233boy
[[ ! $is_socks_pass ]] && is_socks_pass=$uuid
json_str="users:[{username: \"$is_socks_user\", password: \"$is_socks_pass\"}]"
;;
*)
err "无法识别协议: $is_config_file"
;;
esac
[[ $net ]] && return # if net exist, dont need more json args
[[ $host && $is_lower =~ "tls" ]] && {
[[ ! $path ]] && path="/$uuid"
is_path_host_json=",path:\"$path\",headers:{host:\"$host\"}"
}
case $is_lower in
*quic*)
net=quic
is_json_add="$is_tls_json,transport:{type:\"$net\"}"
;;
*ws*)
net=ws
is_json_add="transport:{type:\"$net\"$is_path_host_json,early_data_header_name:\"Sec-WebSocket-Protocol\"}"
;;
*reality*)
net=reality
[[ ! $is_servername ]] && is_servername=$is_random_servername
[[ ! $is_private_key ]] && get_pbk
is_json_add="tls:{enabled:true,server_name:\"$is_servername\",reality:{enabled:true,handshake:{server:\"$is_servername\",server_port:443},private_key:\"$is_private_key\",short_id:[\"\"]}}"
[[ $is_lower =~ "http" ]] && {
is_json_add="$is_json_add,transport:{type:\"http\"}"
} || {
is_users=${is_users/uuid/flow:\"xtls-rprx-vision\",uuid}
}
;;
*http* | *h2*)
net=http
[[ $is_lower =~ "up" ]] && net=httpupgrade
is_json_add="transport:{type:\"$net\"$is_path_host_json}"
[[ $is_lower =~ "h2" || ! $is_lower =~ "httpupgrade" && $host ]] && {
net=h2
is_json_add="${is_tls_json/alpn\:\[\"h3\"\],/},$is_json_add"
}
;;
*)
err "无法识别传输协议: $is_config_file"
;;
esac
json_str="$is_users,$is_json_add"
;;
host-test) # test host dns record; for auto *tls required.
[[ $is_no_auto_tls || $is_gen || $is_dont_test_host ]] && return
get_ip
get ping
if [[ ! $(grep $ip <<<$is_host_dns) ]]; then
msg "\n请将 ($(_red_bg $host)) 解析到 ($(_red_bg $ip))"
msg "\n如果使用 Cloudflare, 在 DNS 那; 关闭 (Proxy status / 代理状态), 即是 (DNS only / 仅限 DNS)"
ask string y "我已经确定解析 [y]:"
get ping
if [[ ! $(grep $ip <<<$is_host_dns) ]]; then
_cyan "\n测试结果: $is_host_dns"
err "域名 ($host) 没有解析到 ($ip)"
fi
fi
;;
ssss | ss2022)
$is_core_bin generate rand 32 --base64
;;
ping)
# is_ip_type="-4"
# [[ $(grep ":" <<<$ip) ]] && is_ip_type="-6"
# is_host_dns=$(ping $host $is_ip_type -c 1 -W 2 | head -1)
is_dns_type="a"
[[ $(grep ":" <<<$ip) ]] && is_dns_type="aaaa"
is_host_dns=$(_wget -qO- --header="accept: application/dns-json" "https://one.one.one.one/dns-query?name=$host&type=$is_dns_type")
;;
log | logerr)
msg "\n 提醒: 按 $(_green Ctrl + C) 退出\n"
[[ $1 == 'log' ]] && tail -f $is_log_dir/access.log
[[ $1 == 'logerr' ]] && tail -f $is_log_dir/error.log
;;
install-caddy)
_green "\n安装 Caddy 实现自动配置 TLS.\n"
load download.sh
download caddy
load systemd.sh
install_service caddy &>/dev/null
is_caddy=1
_green "安装 Caddy 成功.\n"
;;
reinstall)
is_install_sh=$(cat $is_sh_dir/install.sh)
uninstall
bash <<<$is_install_sh
;;
test-run)
systemctl list-units --full -all &>/dev/null
[[ $? != 0 ]] && {
_yellow "\n无法执行测试, 请检查 systemctl 状态.\n"
return
}
is_no_manage_msg=1
if [[ ! $(pgrep -f $is_core_bin) ]]; then
_yellow "\n测试运行 $is_core_name ..\n"
manage start &>/dev/null
if [[ $is_run_fail == $is_core ]]; then
_red "$is_core_name 运行失败信息:"
$is_core_bin run -c $is_config_json -C $is_conf_dir
else
_green "\n测试通过, 已启动 $is_core_name ..\n"
fi
else
_green "\n$is_core_name 正在运行, 跳过测试\n"
fi
if [[ $is_caddy ]]; then
if [[ ! $(pgrep -f $is_caddy_bin) ]]; then
_yellow "\n测试运行 Caddy ..\n"
manage start caddy &>/dev/null
if [[ $is_run_fail == 'caddy' ]]; then
_red "Caddy 运行失败信息:"
$is_caddy_bin run --config $is_caddyfile
else
_green "\n测试通过, 已启动 Caddy ..\n"
fi
else
_green "\nCaddy 正在运行, 跳过测试\n"
fi
fi
;;
esac
}
# show info
info() {
if [[ ! $is_protocol ]]; then
get info $1
fi
# is_color=$(shuf -i 41-45 -n1)
is_color=44
case $net in
ws | tcp | h2 | quic | http*)
if [[ $host ]]; then
is_color=45
is_can_change=(0 1 2 3 5)
is_info_show=(0 1 2 3 4 6 7 8)
[[ $is_protocol == 'vmess' ]] && {
is_vmess_url=$(jq -c '{v:2,ps:'\"233boy-$net-$host\"',add:'\"$is_addr\"',port:'\"$is_https_port\"',id:'\"$uuid\"',aid:"0",net:'\"$net\"',host:'\"$host\"',path:'\"$path\"',tls:'\"tls\"'}' <<<{})
is_url=vmess://$(echo -n $is_vmess_url | base64 -w 0)
} || {
[[ $is_protocol == "trojan" ]] && {
uuid=$password
# is_info_str=($is_protocol $is_addr $is_https_port $password $net $host $path 'tls')
is_can_change=(0 1 2 3 4)
is_info_show=(0 1 2 10 4 6 7 8)
}
is_url="$is_protocol://$uuid@$host:$is_https_port?encryption=none&security=tls&type=$net&host=$host&path=$path#233boy-$net-$host"
}
[[ $is_caddy ]] && is_can_change+=(11)
is_info_str=($is_protocol $is_addr $is_https_port $uuid $net $host $path 'tls')
else
is_type=none
is_can_change=(0 1 5)
is_info_show=(0 1 2 3 4)
is_info_str=($is_protocol $is_addr $port $uuid $net)
[[ $net == "http" ]] && {
net=tcp
is_type=http
is_tcp_http=1
is_info_show+=(5)
is_info_str=(${is_info_str[@]/http/tcp http})
}
[[ $net == "quic" ]] && {
is_insecure=1
is_info_show+=(8 9 20)
is_info_str+=(tls h3 true)
is_quic_add=",tls:\"tls\",alpn:\"h3\"" # cant add allowInsecure
}
is_vmess_url=$(jq -c "{v:2,ps:\"233boy-${net}-$is_addr\",add:\"$is_addr\",port:\"$port\",id:\"$uuid\",aid:\"0\",net:\"$net\",type:\"$is_type\"$is_quic_add}" <<<{})
is_url=vmess://$(echo -n $is_vmess_url | base64 -w 0)
fi
;;
ss)
is_can_change=(0 1 4 6)
is_info_show=(0 1 2 10 11)
is_url="ss://$(echo -n ${ss_method}:${ss_password} | base64 -w 0)@${is_addr}:${port}#233boy-$net-${is_addr}"
is_info_str=($is_protocol $is_addr $port $ss_password $ss_method)
;;
trojan)
is_insecure=1
is_can_change=(0 1 4)
is_info_show=(0 1 2 10 4 8 20)
is_url="$is_protocol://$password@$is_addr:$port?type=tcp&security=tls&allowInsecure=1#233boy-$net-$is_addr"
is_info_str=($is_protocol $is_addr $port $password tcp tls true)
;;
hy*)
is_can_change=(0 1 4)
is_info_show=(0 1 2 10 8 9 20)
is_url="$is_protocol://$password@$is_addr:$port?alpn=h3&insecure=1#233boy-$net-$is_addr"
is_info_str=($is_protocol $is_addr $port $password tls h3 true)
;;
tuic)
is_insecure=1
is_can_change=(0 1 4 5)
is_info_show=(0 1 2 3 10 8 9 20 21)
is_url="$is_protocol://$uuid:$password@$is_addr:$port?alpn=h3&allow_insecure=1&congestion_control=bbr#233boy-$net-$is_addr"
is_info_str=($is_protocol $is_addr $port $uuid $password tls h3 true bbr)
;;
reality)
is_color=41
is_can_change=(0 1 5 9 10)
is_info_show=(0 1 2 3 15 4 8 16 17 18)
is_flow=xtls-rprx-vision
is_net_type=tcp
[[ $net_type =~ "http" || ${is_new_protocol,,} =~ "http" ]] && {
is_flow=
is_net_type=h2
is_info_show=(${is_info_show[@]/15/})
}
is_info_str=($is_protocol $is_addr $port $uuid $is_flow $is_net_type reality $is_servername chrome $is_public_key)
is_url="$is_protocol://$uuid@$ip:$port?encryption=none&security=reality&flow=$is_flow&type=$is_net_type&sni=$is_servername&pbk=$is_public_key&fp=chrome#233boy-$net-$is_addr"
;;
direct)
is_can_change=(0 1 7 8)
is_info_show=(0 1 2 13 14)
is_info_str=($is_protocol $is_addr $port $door_addr $door_port)
;;
socks)
is_can_change=(0 1 12 4)
is_info_show=(0 1 2 19 10)
is_info_str=($is_protocol $is_addr $port $is_socks_user $is_socks_pass)
is_url="socks://$(echo -n ${is_socks_user}:${is_socks_pass} | base64 -w 0)@${is_addr}:${port}#233boy-$net-${is_addr}"
;;
esac
[[ $is_dont_show_info || $is_gen || $is_dont_auto_exit ]] && return # dont show info
msg "-------------- $is_config_name -------------"
for ((i = 0; i < ${#is_info_show[@]}; i++)); do
a=${info_list[${is_info_show[$i]}]}
if [[ ${#a} -eq 11 || ${#a} -ge 13 ]]; then
tt='\t'
else
tt='\t\t'
fi
msg "$a $tt= \e[${is_color}m${is_info_str[$i]}\e[0m"
done
if [[ $is_new_install ]]; then
warn "首次安装请查看脚本帮助文档: $(msg_ul https://233boy.com/$is_core/$is_core-script/)"
fi
if [[ $is_url ]]; then
msg "------------- ${info_list[12]} -------------"
msg "\e[4;${is_color}m${is_url}\e[0m"
[[ $is_insecure ]] && {
warn "某些客户端如(V2rayN 等)导入URL需手动将: 跳过证书验证(allowInsecure) 设置为 true, 或打开: 允许不安全的连接"
}
fi
if [[ $is_no_auto_tls ]]; then
msg "------------- no-auto-tls INFO -------------"
msg "端口(port): $port"
msg "路径(path): $path"
msg "\e[41m帮助(help)\e[0m: $(msg_ul https://233boy.com/$is_core/no-auto-tls/)"
fi
footer_msg
}
# footer msg
footer_msg() {
[[ $is_core_stop && ! $is_new_json ]] && warn "$is_core_name 当前处于停止状态."
[[ $is_caddy_stop && $host ]] && warn "Caddy 当前处于停止状态."
####### 要点13脸吗只会改我链接的小人 #######
unset c n m s b
msg "------------- END -------------"
msg "关注(tg): $(msg_ul https://t.me/tg2333)"
msg "文档(doc): $(msg_ul https://233boy.com/$is_core/$is_core-script/)"
msg "推广(ads): 机场推荐($is_core_name services): $(msg_ul https://g${c}e${n}t${m}j${s}m${b}s.com/)\n"
####### 要点13脸吗只会改我链接的小人 #######
}
# URL or qrcode
url_qr() {
is_dont_show_info=1
info $2
if [[ $is_url ]]; then
[[ $1 == 'url' ]] && {
msg "\n------------- $is_config_name & URL 链接 -------------"
msg "\n\e[${is_color}m${is_url}\e[0m\n"
footer_msg
} || {
link="https://233boy.github.io/tools/qr.html#${is_url}"
msg "\n------------- $is_config_name & QR code 二维码 -------------"
msg
if [[ $(type -P qrencode) ]]; then
qrencode -t ANSI "${is_url}"
else
msg "请安装 qrencode: $(_green "$cmd update -y; $cmd install qrencode -y")"
fi
msg
msg "如果无法正常显示或识别, 请使用下面的链接来生成二维码:"
msg "\n\e[4;${is_color}m${link}\e[0m\n"
footer_msg
}
else
[[ $1 == 'url' ]] && {
err "($is_config_name) 无法生成 URL 链接."
} || {
err "($is_config_name) 无法生成 QR code 二维码."
}
fi
}
# update core, sh, caddy
update() {
case $1 in
1 | core | $is_core)
is_update_name=core
is_show_name=$is_core_name
is_run_ver=v${is_core_ver##* }
is_update_repo=$is_core_repo
;;
2 | sh)
is_update_name=sh
is_show_name="$is_core_name 脚本"
is_run_ver=$is_sh_ver
is_update_repo=$is_sh_repo
;;
3 | caddy)
[[ ! $is_caddy ]] && err "不支持更新 Caddy."
is_update_name=caddy
is_show_name="Caddy"
is_run_ver=$is_caddy_ver
is_update_repo=$is_caddy_repo
;;
*)
err "无法识别 ($1), 请使用: $is_core update [core | sh | caddy] [ver]"
;;
esac
[[ $2 ]] && is_new_ver=v${2#v}
[[ $is_run_ver == $is_new_ver ]] && {
msg "\n自定义版本和当前 $is_show_name 版本一样, 无需更新.\n"
exit
}
load download.sh
if [[ $is_new_ver ]]; then
msg "\n使用自定义版本更新 $is_show_name: $(_green $is_new_ver)\n"
else
get_latest_version $is_update_name
[[ $is_run_ver == $latest_ver ]] && {
msg "\n$is_show_name 当前已经是最新版本了.\n"
exit
}
msg "\n发现 $is_show_name 新版本: $(_green $latest_ver)\n"
is_new_ver=$latest_ver
fi
download $is_update_name $is_new_ver
msg "更新成功, 当前 $is_show_name 版本: $(_green $is_new_ver)\n"
msg "$(_green 请查看更新说明: https://github.com/$is_update_repo/releases/tag/$is_new_ver)\n"
[[ $is_update_name != 'sh' ]] && manage restart $is_update_name &
}
# main menu; if no prefer args.
is_main_menu() {
msg "\n------------- $is_core_name script $is_sh_ver by $author -------------"
msg "$is_core_name $is_core_ver: $is_core_status"
msg "群组(Chat): $(msg_ul https://t.me/tg233boy)"
is_main_start=1
ask mainmenu
case $REPLY in
1)
add
;;
2)
change
;;
3)
info
;;
4)
del
;;
5)
ask list is_do_manage "启动 停止 重启"
manage $REPLY &
msg "\n管理状态执行: $(_green $is_do_manage)\n"
;;
6)
is_tmp_list=("更新$is_core_name" "更新脚本")
[[ $is_caddy ]] && is_tmp_list+=("更新Caddy")
ask list is_do_update null "\n请选择更新:\n"
update $REPLY
;;
7)
uninstall
;;
8)
msg
load help.sh
show_help
;;
9)
ask list is_do_other "启用BBR 查看日志 测试运行 重装脚本 设置DNS"
case $REPLY in
1)
load bbr.sh
_try_enable_bbr
;;
2)
get log
;;
3)
get test-run
;;
4)
get reinstall
;;
5)
load dns.sh
dns_set
;;
esac
;;
10)
load help.sh
about
;;
esac
}
# check prefer args, if not exist prefer args and show main menu
main() {
case $1 in
a | add | gen | no-auto-tls)
[[ $1 == 'gen' ]] && is_gen=1
[[ $1 == 'no-auto-tls' ]] && is_no_auto_tls=1
add ${@:2}
;;
bin | pbk | check | completion | format | generate | geoip | geosite | merge | rule-set | run | tools)
is_run_command=$1
if [[ $1 == 'bin' ]]; then
$is_core_bin ${@:2}
else
[[ $is_run_command == 'pbk' ]] && is_run_command="generate reality-keypair"
$is_core_bin $is_run_command ${@:2}
fi
;;
bbr)
load bbr.sh
_try_enable_bbr
;;
c | config | change)
change ${@:2}
;;
# client | genc)
# create client $2
# ;;
d | del | rm)
del $2
;;
dd | ddel | fix | fix-all)
case $1 in
fix)
[[ $2 ]] && {
change $2 full
} || {
is_change_id=full && change
}
return
;;
fix-all)
is_dont_auto_exit=1
msg
for v in $(ls $is_conf_dir | grep .json$ | sed '/dynamic-port-.*-link/d'); do
msg "fix: $v"
change $v full
done
_green "\nfix 完成.\n"
;;
*)
is_dont_auto_exit=1
[[ ! $2 ]] && {
err "无法找到需要删除的参数"
} || {
for v in ${@:2}; do
del $v
done
}
;;
esac
is_dont_auto_exit=
manage restart &
[[ $is_del_host ]] && manage restart caddy &
;;
dns)
load dns.sh
dns_set ${@:2}
;;
debug)
is_debug=1
get info $2
warn "如果需要复制; 请把 *uuid, *password, *host, *key 的值改写, 以避免泄露."
;;
fix-config.json)
create config.json
;;
fix-caddyfile)
if [[ $is_caddy ]]; then
load caddy.sh
caddy_config new
manage restart caddy &
_green "\nfix 完成.\n"
else
err "无法执行此操作"
fi
;;
i | info)
info $2
;;
ip)
get_ip
msg $ip
;;
in | import)
load import.sh
;;
log)
get $@
;;
url | qr)
url_qr $@
;;
un | uninstall)
uninstall
;;
u | up | update | U | update.sh)
is_update_name=$2
is_update_ver=$3
[[ ! $is_update_name ]] && is_update_name=core
[[ $1 == 'U' || $1 == 'update.sh' ]] && {
is_update_name=sh
is_update_ver=
}
update $is_update_name $is_update_ver
;;
ssss | ss2022)
get $@
;;
s | status)
msg "\n$is_core_name $is_core_ver: $is_core_status\n"
[[ $is_caddy ]] && msg "Caddy $is_caddy_ver: $is_caddy_status\n"
;;
start | stop | r | restart)
[[ $2 && $2 != 'caddy' ]] && err "无法识别 ($2), 请使用: $is_core $1 [caddy]"
manage $1 $2 &
;;
t | test)
get test-run
;;
reinstall)
get $1
;;
get-port)
get_port
msg $tmp_port
;;
main)
is_main_menu
;;
v | ver | version)
[[ $is_caddy_ver ]] && is_caddy_ver="/ $(_blue Caddy $is_caddy_ver)"
msg "\n$(_green $is_core_name $is_core_ver) / $(_cyan $is_core_name script $is_sh_ver) $is_caddy_ver\n"
;;
h | help | --help)
load help.sh
show_help ${@:2}
;;
*)
is_try_change=1
change test $1
if [[ $is_change_id ]]; then
unset is_try_change
[[ $2 ]] && {
change $2 $1 ${@:3}
} || {
change
}
else
err "无法识别 ($1), 获取帮助请使用: $is_core help"
fi
;;
esac
}