#!/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 AnyTLS # 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 ) # shuf fallback for systems without shuf (e.g., Alpine BusyBox) if ! type -P shuf &>/dev/null; then shuf() { local min max n while [[ $# -gt 0 ]]; do case $1 in -i) IFS=- read min max <<<"$2"; shift 2 ;; -n) n=$2; shift 2 ;; esac done echo $(( RANDOM % (max - min + 1) + min )) } fi 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 | grep -E '^[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 | grep -E -i '^\w(\w|\-|\.)?+\.\w+$' ;; path) echo $2 | grep -E -i '^\/\w(\w|\-|\/)?+\w$' ;; uuid) echo $2 | grep -E -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"' elif [[ $is_anytls_domain ]]; then is_config_name=$2-${is_anytls_domain}.json 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 || $is_no_auto_tls ]] && 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 && -f $is_caddy_conf/$is_del_host.conf ]] && { 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} if [[ $is_systemd ]]; then rm -f /lib/systemd/system/$is_core.service elif [[ $is_openrc ]]; then rm -f /etc/init.d/$is_core fi sed -i "/$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 if [[ $is_systemd ]]; then rm -rf $is_caddy_dir $is_caddy_bin /lib/systemd/system/caddy.service elif [[ $is_openrc ]]; then rm -rf $is_caddy_dir $is_caddy_bin /etc/init.d/caddy fi 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 if [[ $is_systemd ]]; then systemctl $is_do $is_do_name 2>/dev/null elif [[ $is_openrc ]]; then case $is_do in enable) rc-update add $is_do_name default 2>/dev/null ;; disable) rc-update del $is_do_name default 2>/dev/null ;; *) rc-service $is_do_name $is_do 2>/dev/null ;; esac fi [[ $is_test_run && ! $is_new_install ]] && { sleep 2 if [[ ! $(pgrep -f $is_run_bin 2>/dev/null || grep -l "$is_run_bin" /proc/*/cmdline 2>/dev/null) ]]; 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 ;; anytls) is_new_protocol=AnyTLS ;; socks) is_new_protocol=Socks ;; *) for v in ${protocol_list[@]}; do [[ $(grep -E -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 if [[ ${is_new_protocol,,} == 'anytls' ]]; then is_core_major=$(echo "$is_core_ver" | cut -d. -f1) is_core_minor=$(echo "$is_core_ver" | cut -d. -f2) if [[ ${is_core_major:-0} -lt 1 || ${is_core_major:-0} -eq 1 && ${is_core_minor:-0} -lt 12 ]]; then err "当前 sing-box 版本 ($is_core_ver) 不支持 AnyTLS,请先升级 sing-box core 到 1.12.0 或更高版本。" fi fi 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]" ;; anytls*) is_use_port=$2 is_use_pass=$3 [[ $4 ]] && is_anytls_domain=$4 is_add_opts="[port] [password] [domain]" ;; 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 ]] && unset host is_no_auto_tls ;; 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 [[ $(grep -E -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 # anytls with domain (ACME TLS) if [[ $is_anytls_domain && ! $is_change && ! $is_gen ]]; then get_ip host=$is_anytls_domain get host-test host= 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 [[ $(grep ":" <<<$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 | grep -E $is_file_str)") readarray -t is_all_json <<<"$(ls $is_conf_dir | grep -E -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 # extract anytls ACME domain [[ $is_protocol == 'anytls' ]] && { is_anytls_domain=$(jq -r '(.inbounds[0].tls.certificate_provider.domain[0] // .inbounds[0].tls.acme.domain[0]) // empty' <<<$is_json_str 2>/dev/null) } is_config_name=$is_config_file if [[ $is_caddy && $host && -f $is_caddy_conf/$host.conf ]]; then is_tmp_https_port=$(grep -E -o "$host:[1-9][0-9]?+" $is_caddy_conf/$host.conf | sed s/.*://) fi if [[ $host && ! -f $is_caddy_conf/$host.conf ]]; then is_no_auto_tls=1 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\"" ;; anytls*) net=anytls is_protocol=$net [[ ! $password ]] && password=$uuid is_users="users:[{password:\"$password\"}]" if [[ $is_anytls_domain ]]; then # sing-box >= 1.14.0 uses certificate_provider; older uses acme is_core_minor=$(echo "$is_core_ver" | cut -d. -f2) if [[ ${is_core_minor:-0} -ge 14 ]]; then is_anytls_tls="tls:{enabled:true,certificate_provider:{type:\"acme\",domain:[\"$is_anytls_domain\"]}}" else is_anytls_tls="tls:{enabled:true,acme:{domain:[\"$is_anytls_domain\"]}}" fi else is_anytls_tls="${is_tls_json/alpn\:\[\"h3\"\],/}" fi json_str="$is_users,$is_anytls_tls" ;; 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) if [[ $(grep 128 <<<$ss_method) ]]; then $is_core_bin generate rand 16 --base64 else $is_core_bin generate rand 32 --base64 fi ;; 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") ;; 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) if [[ $is_systemd ]]; then systemctl list-units --full -all &>/dev/null [[ $? != 0 ]] && { _yellow "\n无法执行测试, 请检查 systemctl 状态.\n" return } fi is_no_manage_msg=1 if [[ ! $(pgrep -f $is_core_bin 2>/dev/null || grep -l "$is_core_bin" /proc/*/cmdline 2>/dev/null) ]]; 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 2>/dev/null || grep -l "$is_caddy_bin" /proc/*/cmdline 2>/dev/null) ]]; 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@$is_addr:$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" ;; anytls) is_can_change=(0 1 4) if [[ $is_anytls_domain ]]; then is_info_show=(0 1 2 10 8) is_info_str=($is_protocol $is_anytls_domain $port $password tls) is_url="anytls://$password@$is_anytls_domain:$port#233boy-$net-$is_anytls_domain" else is_insecure=1 is_info_show=(0 1 2 10 8 20) is_info_str=($is_protocol $is_addr $port $password tls true) is_url="anytls://$password@$is_addr:$port?allowInsecure=1#233boy-$net-$is_addr" fi ;; 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) load log.sh log_set ;; 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) load log.sh log_set $2 ;; 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 }