#!/bin/bash protocol_list=( VMess-TCP VMess-mKCP VMess-QUIC VMess-H2-TLS VMess-WS-TLS VMess-gRPC-TLS VLESS-H2-TLS VLESS-WS-TLS VLESS-gRPC-TLS # VLESS-XTLS-uTLS-REALITY Trojan-H2-TLS Trojan-WS-TLS Trojan-gRPC-TLS Shadowsocks # Dokodemo-Door VMess-TCP-dynamic-port VMess-mKCP-dynamic-port VMess-QUIC-dynamic-port 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 ) header_type_list=( none srtp utp wechat-video dtls wireguard ) mainmenu=( "添加配置" "更改配置" "查看配置" "删除配置" "运行管理" "更新" "卸载" "帮助" "其他" "关于" ) info_list=( "协议 (protocol)" "地址 (address)" "端口 (port)" "用户ID (id)" "传输协议 (network)" "伪装类型 (type)" "伪装域名 (host)" "路径 (path)" "传输层安全 (TLS)" "mKCP seed" "密码 (password)" "加密方式 (encryption)" "链接 (URL)" "目标地址 (remote addr)" "目标端口 (remote port)" "流控 (flow)" "SNI (serverName)" "指纹 (Fingerprint)" "公钥 (Public key)" "用户名 (Username)" ) change_list=( "更改协议" "更改端口" "更改域名" "更改路径" "更改密码" "更改 UUID" "更改加密方式" "更改伪装类型" "更改目标地址" "更改目标端口" "更改密钥" "更改 SNI (serverName)" "更改动态端口" "更改伪装网站" "更改 mKCP seed" "更改用户名 (Username)" ) servername_list=( www.amazon.com www.microsoft.com www.apple.com dash.cloudflare.com dl.google.com aws.amazon.com ) is_random_ss_method=${ss_method_list[$(shuf -i 0-${#ss_method_list[@]} -n1) - 1]} is_random_header_type=${header_type_list[$(shuf -i 1-5 -n1)]} # random dont use none 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 ]] && return export "$(_wget -4 -qO- https://www.cloudflare.com/cdn-cgi/trace | grep ip=)" &>/dev/null [[ ! $ip ]] && export "$(_wget -6 -qO- https://www.cloudflare.com/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 x25519 | sed 's/.*://')) is_private_key=${is_tmp_pbk[0]} is_public_key=${is_tmp_pbk[1]} } 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_header_type) is_tmp_list=(${header_type_list[@]}) is_default_arg=$is_random_header_type [[ $(grep -i tcp <<<"$is_new_protocol-$net") ]] && { is_tmp_list=(none http) is_default_arg=none } is_opt_msg="\n请选择伪装类型:\n" is_opt_input_msg="(默认\e[92m $is_default_arg\e[0m):" is_ask_set=header_type [[ $is_use_header_type ]] && return ;; 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 # file name if [[ $host ]]; then is_config_name=$2-${host}.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 case $net in ws | h2 | grpc | http) is_listen='"listen": "127.0.0.1"' ;; *) is_listen='"listen": "0.0.0.0"' ;; esac is_sniffing='sniffing:{enabled:true,destOverride:["http","tls"]}' is_new_json=$(jq '{inbounds:[{tag:'\"$is_config_name\"',port:'"$port"','"$is_listen"',protocol:'\"$is_protocol\"','"$json_str"','"$is_sniffing"'}]}' <<<{}) if [[ $is_dynamic_port ]]; then [[ ! $is_dynamic_port_range ]] && get dynamic-port is_new_dynamic_port_json=$(jq '{inbounds:[{tag:'\"$is_config_name-link.json\"',port:'\"$is_dynamic_port_range\"','"$is_listen"',protocol:"vmess",'"$is_stream"','"$is_sniffing"',allocate:{strategy:"random"}}]}' <<<{}) fi [[ $is_test_json ]] && return # tmp test # only show json, dont save to file. [[ $is_gen ]] && { msg jq <<<$is_new_json msg [[ $is_new_dynamic_port_json ]] && jq <<<$is_new_dynamic_port_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 [[ $is_new_dynamic_port_json ]] && { is_dynamic_port_link_file=$is_json_file-link.json cat <<<$is_new_dynamic_port_json >$is_dynamic_port_link_file } if [[ $is_new_install ]]; then # config.json create config.json else # use api add config api add $is_json_file $is_dynamic_port_link_file &>/dev/null fi # caddy auto tls [[ $is_caddy && $host ]] && { create caddy $net } # restart core [[ $is_api_fail ]] && 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) get_port is_log='log:{access:"/var/log/'"$is_core"'/access.log",error:"/var/log/'"$is_core"'/error.log",loglevel:"warning"}' is_dns='dns:{}' is_api='api:{tag:"api",services:["HandlerService","LoggerService","StatsService"]}' is_stats='stats:{}' is_policy_system='system:{statsInboundUplink:true,statsInboundDownlink:true,statsOutboundUplink:true,statsOutboundDownlink:true}' is_policy='policy:{levels:{"0":{handshake:'"$((${tmp_port:0:1} + 1))"',connIdle:'"${tmp_port:0:3}"',uplinkOnly:'"$((${tmp_port:2:1} + 1))"',downlinkOnly:'"$((${tmp_port:3:1} + 3))"',statsUserUplink:true,statsUserDownlink:true}},'"$is_policy_system"'}' is_ban_ad='{type:"field",domain:["geosite:category-ads-all"],marktag:"ban_ad",outboundTag:"block"}' is_ban_bt='{type:"field",protocol:["bittorrent"],marktag:"ban_bt",outboundTag:"block"}' is_ban_cn='{type:"field",ip:["geoip:cn"],marktag:"ban_geoip_cn",outboundTag:"block"}' is_routing='routing:{domainStrategy:"IPIfNonMatch",rules:[{type:"field",inboundTag:["api"],outboundTag:"api"},'"$is_ban_bt"','"$is_ban_cn"',{type:"field",ip:["geoip:private"],outboundTag:"block"}]}' is_inbounds='inbounds:[{tag:"api",port:'"$tmp_port"',listen:"127.0.0.1",protocol:"dokodemo-door",settings:{address:"127.0.0.1"}}]' is_outbounds='outbounds:[{tag:"direct",protocol:"freedom"},{tag:"block",protocol:"blackhole"}]' is_server_config_json=$(jq '{'"$is_log"','"$is_dns"','"$is_api"','"$is_stats"','"$is_policy"','"$is_routing"','"$is_inbounds"','"$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 ;; type | header | header-type | header_type) is_change_id=7 ;; dda | door-addr | door_addr) is_change_id=8 ;; ddp | door-port | door_port) is_change_id=9 ;; key | publickey | privatekey) is_change_id=10 ;; sni | servername | servernames) is_change_id=11 ;; dp | dyp | dynamic | dynamicport | dynamic-port) is_change_id=12 ;; web | proxy-site) is_change_id=13 ;; seed | kcpseed | kcp-seed | kcp_seed) is_change_id=14 ;; *) [[ $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_protocol == 'vless' && ! $is_reality ]] && net=v$net [[ $is_protocol == 'trojan' ]] && net=t$net [[ $is_dynamic_port ]] && net=${net}d [[ $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 ]] && err "($is_config_file) 不支持更改端口, 因为没啥意义." [[ $is_auto ]] && get_port && is_new_port=$tmp_port [[ ! $is_new_port ]] && ask string is_new_port "请输入新端口:" add $net $is_new_port ;; 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 [[ $net == 'ss' || $is_trojan || $is_socks_pass ]]; then [[ $is_auto ]] && get_uuid && is_new_pass=$tmp_uuid else err "($is_config_file) 不支持更改密码." fi [[ ! $is_new_pass ]] && ask string is_new_pass "请输入新密码:" trojan_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 header type is_new_header_type=$3 [[ ! $header_type ]] && err "($is_config_file) 不支持更改伪装类型." [[ $is_auto ]] && { is_new_header_type=$is_random_header_type if [[ $net == 'tcp' ]]; then is_tmp_header_type=(none http) is_new_header_type=${is_tmp_header_type[$(shuf -i 0-1 -n1)]} fi } [[ ! $is_new_header_type ]] && { ask set_header_type is_new_header_type=$header_type } add $net auto auto $is_new_header_type ;; 8) # new remote addr is_new_door_addr=$3 [[ $net != 'door' ]] && err "($is_config_file) 不支持更改目标地址." [[ ! $is_new_door_addr ]] && ask string is_new_door_addr "请输入新的目标地址:" door_addr=$is_new_door_addr add $net ;; 9) # new remote port is_new_door_port=$3 [[ $net != 'door' ]] && 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 ;; 10) # 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_private_key=$is_new_private_key is_test_json=1 # create server $is_protocol-$net | $is_core_bin -test &>/dev/null create server $is_protocol-$net $is_core_bin -test <<<"$is_new_json" &>/dev/null if [[ $? != 0 ]]; then err "Private key 无法通过测试." fi is_private_key=$is_new_public_key # create server $is_protocol-$net | $is_core_bin -test &>/dev/null create server $is_protocol-$net $is_core_bin -test <<<"$is_new_json" &>/dev/null if [[ $? != 0 ]]; then err "Public key 无法通过测试." fi is_private_key=$is_new_private_key is_public_key=$is_new_public_key is_test_json= add $net fi ;; 11) # 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 ;; 12) # new dynamic-port is_new_dynamic_port_start=$3 is_new_dynamic_port_end=$4 [[ ! $is_dynamic_port ]] && err "($is_config_file) 不支持更改动态端口." if [[ $is_auto ]]; then get dynamic-port add $net else [[ $is_new_dynamic_port_start && ! $is_new_dynamic_port_end ]] && { err "无法找到动态结束端口." } [[ ! $is_new_dynamic_port_start ]] && ask string is_new_dynamic_port_start "请输入新的动态开始端口:" [[ ! $is_new_dynamic_port_end ]] && ask string is_new_dynamic_port_end "请输入新的动态结束端口:" add $net auto auto auto $is_new_dynamic_port_start $is_new_dynamic_port_end fi ;; 13) # 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" ;; 14) # new kcp seed is_new_kcp_seed=$3 [[ ! $kcp_seed ]] && err "($is_config_file) 不支持更改 mKCP seed." [[ $is_auto ]] && get_uuid && is_new_kcp_seed=$tmp_uuid [[ ! $is_new_kcp_seed ]] && ask string is_new_kcp_seed "请输入新 mKCP seed:" kcp_seed=$is_new_kcp_seed add $net ;; 15) # new socks user [[ ! $is_socks_user ]] && err "($is_config_file) 不支持更改用户名 (Username)." ask string is_socks_user "请输入新用户名 (Username):" add $net ;; esac } # delete config. del() { [[ $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 api del $is_conf_dir/"$is_config_file" $is_dynamic_port_file &>/dev/null rm -rf $is_conf_dir/"$is_config_file" $is_dynamic_port_file [[ $is_api_fail && ! $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 [[ $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 /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_no_manage_msg ]] && { msg warn "($is_do_msg) $is_do_name_msg 失败" _yellow "检测到运行失败, 自动执行测试运行." get test-run _yellow "测试结束, 请按 Enter 退出." } is_run_fail=${is_do_name_msg,,} fi } } # use api add or del inbounds api() { [[ $is_core_ver_lt_5 ]] && { warn "$is_core_ver 版本不支持使用 API 操作. 请升级内核版本: $is_core update core" is_api_fail=1 return } [[ ! $1 ]] && err "无法识别 API 的参数." [[ $is_core_stop ]] && { warn "$is_core_name 当前处于停止状态." return } case $1 in add) is_api_do=adi ;; del) is_api_do=rmi ;; s) is_api_do=stats ;; t | sq) is_api_do=statsquery ;; esac [[ ! $is_api_do ]] && is_api_do=$1 [[ ! $is_api_port ]] && { is_api_port=$(jq '.inbounds[] | select(.tag == "api") | .port' $is_config_json) [[ $? != 0 ]] && { warn "读取 API 端口失败, 无法使用 API 操作." return } } $is_core_bin api $is_api_do --server=127.0.0.1:$is_api_port ${@:2} [[ $? != 0 ]] && { is_api_fail=1 } } # add a config add() { is_lower=${1,,} if [[ $is_lower ]]; then case $is_lower in tcp | kcp | quic | tcpd | kcpd | quicd) is_new_protocol=VMess-$(sed 's/^K/mK/;s/D$/-dynamic-port/' <<<${is_lower^^}) ;; ws | h2 | grpc | vws | vh2 | vgrpc | tws | th2 | tgrpc) is_new_protocol=$(sed -E "s/^V/VLESS-/;s/^T/Trojan-/;/^(W|H|G)/{s/^/VMess-/};s/G/g/" <<<${is_lower^^})-TLS ;; # r | reality) # is_new_protocol=VLESS-XTLS-uTLS-REALITY # ;; ss) is_new_protocol=Shadowsocks ;; door) is_new_protocol=Dokodemo-Door ;; socks) is_new_protocol=Socks ;; http) is_new_protocol=local-$is_lower ;; *) 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 ;; vmess*) is_use_port=$2 is_use_uuid=$3 is_use_header_type=$4 is_use_dynamic_port_start=$5 is_use_dynamic_port_end=$6 [[ $(grep dynamic-port <<<$is_new_protocol) ]] && is_dynamic_port=1 ;; # *reality*) # is_reality=1 # is_use_port=$2 # is_use_uuid=$3 # is_use_servername=$4 # ;; shadowsocks) is_use_port=$2 is_use_pass=$3 is_use_method=$4 ;; *door) is_use_port=$2 is_use_door_addr=$3 is_use_door_port=$4 ;; socks) is_socks=1 is_use_port=$2 is_use_socks_user=$3 is_use_socks_pass=$4 ;; *http) is_use_port=$2 ;; esac [[ $1 && ! $is_change ]] && msg "\n使用协议: $is_new_protocol" # remove old protocol args if [[ $is_set_new_protocol ]]; then case $is_old_net in tcp) unset header_type net ;; kcp | quic) kcp_seed= [[ $(grep tcp <<<$is_new_protocol) ]] && header_type= ;; h2 | ws | grpc) old_host=$host if [[ ! $is_use_tls ]]; then host= else [[ $is_old_net == 'grpc' ]] && { path=/$path } fi ;; reality) [[ ! $(grep -i reality <<<$is_new_protocol) ]] && is_reality= ;; ss) [[ $(is_test uuid $ss_password) ]] && uuid=$ss_password ;; esac [[ $is_dynamic_port && ! $(grep dynamic-port <<<$is_new_protocol) ]] && { is_dynamic_port= } [[ $is_trojan && ! $(is_test uuid $trojan_password) ]] && uuid= 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_header_type is_use_host is_use_path is_use_pass is_use_method is_use_door_addr is_use_door_port is_use_dynamic_port_start is_use_dynamic_port_end; do [[ ${!v} == 'auto' ]] && unset $v done if [[ $is_use_port ]]; then [[ ! $(is_test port ${is_use_port}) ]] && { err "($is_use_port) 不是一个有效的端口." } [[ $(is_test port_used $is_use_port) ]] && { err "无法使用 ($is_use_port) 端口." } port=$is_use_port fi if [[ $is_use_door_port ]]; then [[ ! $(is_test port ${is_use_door_port}) ]] && { err "(${is_use_door_port}) 不是一个有效的目标端口." } door_port=$is_use_door_port fi if [[ $is_use_uuid ]]; then [[ ! $(is_test uuid $is_use_uuid) ]] && { err "($is_use_uuid) 不是一个有效的 UUID." } uuid=$is_use_uuid fi if [[ $is_use_path ]]; then [[ ! $(is_test path $is_use_path) ]] && { err "($is_use_path) 不是有效的路径." } path=$is_use_path fi if [[ $is_use_header_type || $is_use_method ]]; then is_tmp_use_name=加密方式 is_tmp_list=${ss_method_list[@]} [[ ! $is_use_method ]] && { is_tmp_use_name=伪装类型 ask set_header_type } for v in ${is_tmp_list[@]}; do [[ $(egrep -i "^${is_use_header_type}${is_use_method}$" <<<$v) ]] && is_tmp_use_type=$v && break done [[ ! ${is_tmp_use_type} ]] && { warn "(${is_use_header_type}${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 exit 1 } ss_method=$is_tmp_use_type header_type=$is_tmp_use_type fi if [[ $is_dynamic_port && $is_use_dynamic_port_start ]]; then get dynamic-port-test fi [[ $is_use_pass ]] && ss_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 ]]; then # test auto tls [[ $(is_test port_used 80) || $(is_test port_used 443) ]] && { warn "端口 (80 或 443) 已经被占用, 无法完成自动配置 TLS. 请考虑使用 no-auto-tls" msg "\e[41m帮助(help)\e[0m: $(msg_ul https://233boy.com/$is_core/no-auto-tls/)\n" exit 1 } 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 *tcp* | *kcp* | *quic*) [[ ! $header_type ]] && ask set_header_type ;; 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 # set dynamic port [[ $is_dynamic_port && ! $is_dynamic_port_range ]] && { ask string is_use_dynamic_port_start "请输入动态开始端口:" ask string is_use_dynamic_port_end "请输入动态结束端口:" get dynamic-port-test } fi fi # Dokodemo-Door if [[ $is_new_protocol == 'Dokodemo-Door' ]]; 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 | $is_core_bin -test &>/dev/null create server Shadowsocks $is_core_bin -test <<<"$is_new_json" &>/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= } 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") is_json_data_base=$(jq '.inbounds[0]|.protocol,.port,.settings.clients[0].id,.settings.clients[0].password,.settings.method,.settings.password,.settings.address,.settings.port,.settings.detour.to,.settings.accounts[0].user,.settings.accounts[0].pass' <<<$is_json_str) [[ $? != 0 ]] && err "无法读取此文件: $is_config_file" is_json_data_more=$(jq '.inbounds[0]|.streamSettings|.network,.security,.tcpSettings.header.type,.kcpSettings.seed,.kcpSettings.header.type,.quicSettings.header.type,.wsSettings.path,.httpSettings.path,.grpcSettings.serviceName' <<<$is_json_str) is_json_data_host=$(jq '.inbounds[0]|.streamSettings|.grpc_host,.wsSettings.headers.Host,.httpSettings.host[0]' <<<$is_json_str) is_json_data_reality=$(jq '.inbounds[0]|.streamSettings|.realitySettings.serverNames[0],.realitySettings.publicKey,.realitySettings.privateKey' <<<$is_json_str) is_up_var_set=(null is_protocol port uuid trojan_password ss_method ss_password door_addr door_port is_dynamic_port is_socks_user is_socks_pass net is_reality tcp_type kcp_seed kcp_type quic_type ws_path h2_path grpc_path grpc_host ws_host h2_host is_servername is_public_key is_private_key) [[ $is_debug ]] && msg "\n------------- debug: $is_config_file -------------" i=0 for v in $(sed 's/""/null/g;s/"//g' <<<"$is_json_data_base $is_json_data_more $is_json_data_host $is_json_data_reality"); 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 path="${ws_path}${h2_path}${grpc_path}" host="${ws_host}${h2_host}${grpc_host}" header_type="${tcp_type}${kcp_type}${quic_type}" if [[ $is_reality == 'reality' ]]; then net=reality else is_reality= fi [[ ! $kcp_seed ]] && is_no_kcp_seed=1 is_config_name=$is_config_file if [[ $is_dynamic_port ]]; then is_dynamic_port_file=$is_conf_dir/$is_dynamic_port is_dynamic_port_range=$(jq -r '.inbounds[0].port' $is_dynamic_port_file) [[ $? != 0 ]] && err "无法读取动态端口文件: $is_dynamic_port" fi [[ $is_client && $host ]] && port=443 get protocol $is_protocol-$net fi ;; protocol) get addr # get host or server ip is_lower=${2,,} net= case $is_lower in vmess*) is_protocol=vmess if [[ $is_dynamic_port ]]; then is_server_id_json='settings:{clients:[{id:'\"$uuid\"'}],detour:{to:'\"$is_config_name-link.json\"'}}' else is_server_id_json='settings:{clients:[{id:'\"$uuid\"'}]}' fi is_client_id_json='settings:{vnext:[{address:'\"$is_addr\"',port:'"$port"',users:[{id:'\"$uuid\"'}]}]}' ;; vless*) is_protocol=vless is_server_id_json='settings:{clients:[{id:'\"$uuid\"'}],decryption:"none"}' is_client_id_json='settings:{vnext:[{address:'\"$is_addr\"',port:'"$port"',users:[{id:'\"$uuid\"',encryption:"none"}]}]}' if [[ $is_reality ]]; then is_server_id_json='settings:{clients:[{id:'\"$uuid\"',flow:"xtls-rprx-vision"}],decryption:"none"}' is_client_id_json='settings:{vnext:[{address:'\"$is_addr\"',port:'"$port"',users:[{id:'\"$uuid\"',encryption:"none",flow:"xtls-rprx-vision"}]}]}' fi ;; trojan*) is_protocol=trojan [[ ! $trojan_password ]] && trojan_password=$uuid is_server_id_json='settings:{clients:[{password:'\"$trojan_password\"'}]}' is_client_id_json='settings:{servers:[{address:'\"$is_addr\"',port:'"$port"',password:'\"$trojan_password\"'}]}' is_trojan=1 ;; shadowsocks*) is_protocol=shadowsocks net=ss [[ ! $ss_method ]] && ss_method=$is_random_ss_method [[ ! $ss_password ]] && { ss_password=$uuid [[ $(grep 2022 <<<$ss_method) ]] && ss_password=$(get ss2022) } is_client_id_json='settings:{servers:[{address:'\"$is_addr\"',port:'"$port"',method:'\"$ss_method\"',password:'\"$ss_password\"',}]}' json_str='settings:{method:'\"$ss_method\"',password:'\"$ss_password\"',network:"tcp,udp"}' ;; dokodemo-door*) is_protocol=dokodemo-door net=door json_str='settings:{port:'\"$door_port\"',address:'\"$door_addr\"',network:"tcp,udp"}' ;; *http*) is_protocol=http net=http json_str='settings:{"timeout": 233}' ;; *socks*) is_protocol=socks net=socks [[ ! $is_socks_user ]] && is_socks_user=233boy [[ ! $is_socks_pass ]] && is_socks_pass=$uuid json_str='settings:{auth:"password",accounts:[{user:'\"$is_socks_user\"',pass:'\"$is_socks_pass\"'}],udp:true}' ;; *) err "无法识别协议: $is_config_file" ;; esac [[ $net ]] && return # if net exist, dont need more json args case $is_lower in *tcp*) net=tcp [[ ! $header_type ]] && header_type=none is_stream='streamSettings:{network:"tcp",tcpSettings:{header:{type:'\"$header_type\"'}}}' json_str=''"$is_server_id_json"','"$is_stream"'' ;; *kcp* | *mkcp) net=kcp [[ ! $header_type ]] && header_type=$is_random_header_type [[ ! $is_no_kcp_seed && ! $kcp_seed ]] && kcp_seed=$uuid is_stream='streamSettings:{network:"kcp",kcpSettings:{seed:'\"$kcp_seed\"',header:{type:'\"$header_type\"'}}}' json_str=''"$is_server_id_json"','"$is_stream"'' ;; *quic*) net=quic [[ ! $header_type ]] && header_type=$is_random_header_type is_stream='streamSettings:{network:"quic",quicSettings:{header:{type:'\"$header_type\"'}}}' json_str=''"$is_server_id_json"','"$is_stream"'' ;; *ws* | *websocket) net=ws [[ ! $path ]] && path="/$uuid" is_stream='streamSettings:{network:"ws",security:'\"$is_tls\"',wsSettings:{path:'\"$path\"',headers:{Host:'\"$host\"'}}}' json_str=''"$is_server_id_json"','"$is_stream"'' ;; *grpc* | *gun) net=grpc [[ ! $path ]] && path="$uuid" [[ $path ]] && path=$(sed 's#/##g' <<<$path) is_stream='streamSettings:{network:"grpc",grpc_host:'\"$host\"',security:'\"$is_tls\"',grpcSettings:{serviceName:'\"$path\"'}}' json_str=''"$is_server_id_json"','"$is_stream"'' ;; *h2* | *http*) net=h2 [[ ! $path ]] && path="/$uuid" is_stream='streamSettings:{network:"h2",security:'\"$is_tls\"',httpSettings:{path:'\"$path\"',host:['\"$host\"']}}' json_str=''"$is_server_id_json"','"$is_stream"'' ;; *reality*) net=reality [[ ! $is_servername ]] && is_servername=$is_random_servername [[ ! $is_private_key ]] && get_pbk is_stream='streamSettings:{network:"tcp",security:"reality",realitySettings:{dest:'\"${is_servername}\:443\"',serverNames:['\"${is_servername}\"',""],publicKey:'\"$is_public_key\"',privateKey:'\"$is_private_key\"',shortIds:[""]}}' if [[ $is_client ]]; then is_stream='streamSettings:{network:"tcp",security:"reality",realitySettings:{serverName:'\"${is_servername}\"',"fingerprint": "ios",publicKey:'\"$is_public_key\"',"shortId": "","spiderX": "/"}}' fi json_str=''"$is_server_id_json"','"$is_stream"'' ;; *) err "无法识别传输协议: $is_config_file" ;; esac ;; dynamic-port) # create random dynamic port if [[ $port -ge 60000 ]]; then is_dynamic_port_end=$(shuf -i $(($port - 2333))-$port -n1) is_dynamic_port_start=$(shuf -i $(($is_dynamic_port_end - 2333))-$is_dynamic_port_end -n1) else is_dynamic_port_start=$(shuf -i $port-$(($port + 2333)) -n1) is_dynamic_port_end=$(shuf -i $is_dynamic_port_start-$(($is_dynamic_port_start + 2333)) -n1) fi is_dynamic_port_range="$is_dynamic_port_start-$is_dynamic_port_end" ;; dynamic-port-test) # test dynamic port [[ ! $(is_test port ${is_use_dynamic_port_start}) || ! $(is_test port ${is_use_dynamic_port_end}) ]] && { err "无法正确处理动态端口 ($is_use_dynamic_port_start-$is_use_dynamic_port_end) 范围." } [[ $(is_test port_used $is_use_dynamic_port_start) ]] && { err "动态端口 ($is_use_dynamic_port_start-$is_use_dynamic_port_end), 但 ($is_use_dynamic_port_start) 端口无法使用." } [[ $(is_test port_used $is_use_dynamic_port_end) ]] && { err "动态端口 ($is_use_dynamic_port_start-$is_use_dynamic_port_end), 但 ($is_use_dynamic_port_end) 端口无法使用." } [[ $is_use_dynamic_port_end -le $is_use_dynamic_port_start ]] && { err "无法正确处理动态端口 ($is_use_dynamic_port_start-$is_use_dynamic_port_end) 范围." } [[ $is_use_dynamic_port_start == $port || $is_use_dynamic_port_end == $port ]] && { err "动态端口 ($is_use_dynamic_port_start-$is_use_dynamic_port_end) 范围和主端口 ($port) 冲突." } is_dynamic_port_range="$is_use_dynamic_port_start-$is_use_dynamic_port_end" ;; host-test) # test host dns record; for auto *tls required. [[ $is_no_auto_tls || $is_gen ]] && 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) openssl rand -base64 32 [[ $? != 0 ]] && err "无法生成 Shadowsocks 2022 密码, 请安装 openssl." ;; ping) is_host_dns=$(ping $host -c 1 -W 2 | head -1) ;; 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 [[ $is_core_stop ]]; 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 -confdir $is_conf_dir else _green "\n测试通过, 已启动 $is_core_name ..\n" fi else _green "\n$is_core_name 正在运行, 跳过测试\n" fi if [[ $is_caddy ]]; then if [[ $is_caddy_stop ]]; 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 tcp | kcp | quic) is_can_change=(0 1 5 7) is_info_show=(0 1 2 3 4 5) is_vmess_url=$(jq -c '{v:2,ps:'\"233boy-${net}-$is_addr\"',add:'\"$is_addr\"',port:'\"$port\"',id:'\"$uuid\"',net:'\"$net\"',type:'\"$header_type\"',path:'\"$kcp_seed\"'}' <<<{}) is_url=vmess://$(base64 -w 0 <<<$is_vmess_url) is_tmp_port=$port [[ $is_dynamic_port ]] && { is_can_change+=(12) is_tmp_port="$port & 动态端口: $is_dynamic_port_range" } [[ $kcp_seed ]] && { is_info_show+=(9) is_can_change+=(14) } is_info_str=($is_protocol $is_addr "$is_tmp_port" $uuid $net $header_type $kcp_seed) ;; ss) is_can_change=(0 1 4 6) is_info_show=(0 1 2 10 11) is_url="ss://$(base64 -w 0 <<<"${ss_method}:${ss_password}")@${is_addr}:${port}#233boy-ss-${is_addr}" is_info_str=($is_protocol $is_addr $port $ss_password $ss_method) ;; ws | h2 | grpc) is_color=45 is_can_change=(0 2 3 5) is_info_show=(0 1 2 3 4 6 7 8) is_url_path=path [[ $net == 'grpc' ]] && { path=$(sed 's#/##g' <<<$path) is_url_path=serviceName } [[ $is_protocol == 'vmess' ]] && { is_vmess_url=$(jq -c '{v:2,ps:'\"233boy-$host\"',add:'\"$is_addr\"',port:'\"443\"',id:'\"$uuid\"',net:'\"$net\"',host:'\"$host\"',path:'\"$path\"',tls:'\"tls\"'}' <<<{}) is_url=vmess://$(base64 -w 0 <<<$is_vmess_url) } || { [[ $is_trojan ]] && { uuid=$trojan_password is_info_str=($is_protocol $is_addr 443 $trojan_password $net $host $path 'tls') is_can_change=(0 2 3 4) is_info_show=(0 1 2 10 4 6 7 8) } is_url="$is_protocol://$uuid@$host:443?encryption=none&security=tls&type=$net&host=$host&${is_url_path}=$(sed 's#/#%2F#g' <<<$path)#233boy-$host" } [[ $is_caddy ]] && is_can_change+=(13) is_info_str=($is_protocol $is_addr 443 $uuid $net $host $path 'tls') ;; reality) is_color=41 is_can_change=(0 1 5 10 11) is_info_show=(0 1 2 3 15 8 16 17 18) is_info_str=($is_protocol $is_addr $port $uuid xtls-rprx-vision reality $is_servername "ios" $is_public_key) is_url="$is_protocol://$uuid@$ip:$port?encryption=none&security=reality&flow=xtls-rprx-vision&type=tcp&sni=$is_servername&pbk=$is_public_key&fp=ios#233boy-$is_addr" ;; door) is_can_change=(0 1 8 9) 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 15 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://$(base64 -w 0 <<<"${is_socks_user}:${is_socks_pass}")@${is_addr}:${port}#233boy-socks-${is_addr}" ;; http) is_can_change=(0 1) is_info_show=(0 1 2) is_info_str=($is_protocol 127.0.0.1 $port) ;; 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_url ]]; then msg "------------- ${info_list[12]} -------------" msg "\e[4;${is_color}m${is_url}\e[0m" fi if [[ $is_no_auto_tls ]]; then is_tmp_path=$path [[ $net == 'grpc' ]] && is_tmp_path="/$path/*" msg "------------- no-auto-tls INFO -------------" msg "端口(port): $port" msg "路径(path): $is_tmp_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 qrencode -t ANSI "${is_url}" 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##* } ;; 2 | sh) is_update_name=sh is_show_name="$is_core_name 脚本" is_run_ver=$is_sh_ver ;; 3 | caddy) [[ ! $is_caddy ]] && err "不支持更新 Caddy." is_update_name=caddy is_show_name="Caddy" is_run_ver=$is_caddy_ver ;; *) 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" 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_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 查看日志 查看错误日志 测试运行 重装脚本" case $REPLY in 1) load bbr.sh _try_enable_bbr ;; 2) get log ;; 3) get logerr ;; 4) get test-run ;; 5) get reinstall ;; 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} ;; api | bin | convert | tls | run | uuid) [[ $is_core_ver_lt_5 ]] && { warn "$is_core_ver 版本不支持使用命令. 请升级内核版本: $is_core update core" return } is_run_command=$1 if [[ $1 == 'bin' ]]; then $is_core_bin ${@:2} else # [[ $is_run_command == 'pbk' ]] && is_run_command=x25519 $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 msg "\nfix 完成.\n" ;; *) is_dont_auto_exit=1 [[ ! $2 ]] && { err "无法找到需要删除的参数" } || { for v in ${@:2}; do del $v done } ;; esac is_dont_auto_exit= [[ $is_api_fail ]] && manage restart & [[ $is_del_host ]] && manage restart caddy & ;; debug) is_debug=1 get info $2 warn "如果需要复制; 请把 *uuid, *password, *host, *key 的值改写, 以避免泄露." ;; fix-config.json) create config.json ;; i | info) info $2 ;; ip) get_ip msg $ip ;; log | logerr) 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_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_ver) / $(_cyan $is_core_name script $is_sh_ver) $is_caddy_ver\n" ;; xapi) api ${@:2} ;; 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 }