#!/bin/sh /etc/rc.common # # Copyright 2019-2020 Xingwang Liao # Copyright 2020-2022 Ycarus (Yannick Chabanois) for OpenMPTCProuter # Licensed to the public under the MIT License. # START=99 USE_PROCD=1 EXTRA_COMMANDS="rules_up rules_down rules_exist" NAME=xray CONFIG_FOLDER=/var/etc/$NAME xrrules_uc="/usr/share/xray-rules/xr-rules.uc" xrrules_nft="/etc/nftables.d/90-proxy-x2ray-rules.nft" FILE_XRAY_DNSMASQ=/tmp/dnsmasq.d/$NAME FILE_XRAY_DNSMASQ_CACHE=/tmp/$NAME.dnsmasq.cache IPSET_SRC_IGNORE_V4=xray_src_ignore_v4 IPSET_SRC_IGNORE_V6=xray_src_ignore_v6 IPSET_DST_PROXY_V4=xray_dst_proxy_v4 IPSET_DST_PROXY_V6=xray_dst_proxy_v6 IPSET_SRC_DIRECT_V4=ss_rules_src_bypass IPSET_DST_DIRECT_V4=ss_rules_dst_bypass IPSET_DST_DIRECT_V6=ss_rules6_dst_bypass OUTBOUND_SERVERS= OUTBOUND_SERVERS_V4= OUTBOUND_SERVERS_V6= TRANSPARENT_PROXY_EXPECTED=0 TRANSPARENT_PROXY_PORT= TRANSPARENT_PROXY_USE_TPROXY= TRANSPARENT_PROXY_ADDITION= DNSMASQ_RESTART_EXPECTED=0 . /usr/share/libubox/jshn.sh _log() { local level="$1" ; shift local msg="$@" logger -p "daemon.$level" -t "$NAME" "$msg" echo "[$level] $msg" >&2 } _info() { _log "info" $@ } _err() { _log "err" $@ } version_over_5_4() { MAJOR_VERSION=$(uname -r | awk -F '.' '{print $1}') MINOR_VERSION=$(uname -r | awk -F '.' '{print $2}') if [ $MAJOR_VERSION -ge 5 ] && [ $MINOR_VERSION -gt 13 ] || [ $MAJOR_VERSION -gt 5 ] ; then return 0 else return 1 fi } get_value_from_json() { local json="$1" local key="$2" test -n "$json" || return local value="" local old_ns json_set_namespace "json_key" old_ns json_load "$json" json_get_var "$key" value json_cleanup json_set_namespace "$old_ns" echo "$value" } get_commands_from_json() { local json="$1" test -n "$json" || return jshn -r "$json" 2>/dev/null | grep -v "json_init" } get_file_content() { local filename="$1" test -n "$filename" || return test -r "/etc/xray/${filename}.txt" || return cat "/etc/xray/${filename}.txt" | grep -v "^$" | grep -v "^#" } append_server_ipv4() { local addr="$1" test -n "$addr" || return if [ -z "$OUTBOUND_SERVERS_V4" ] ; then OUTBOUND_SERVERS_V4="$addr" else OUTBOUND_SERVERS_V4="$(cat >&1 <<-EOF $OUTBOUND_SERVERS_V4 $addr EOF )" fi } append_server_ipv6() { local addr="$1" test -n "$addr" || return if [ -z "$OUTBOUND_SERVERS_V6" ] ; then OUTBOUND_SERVERS_V6="$addr" else OUTBOUND_SERVERS_V6="$(cat >&1 <<-EOF $OUTBOUND_SERVERS_V6 $addr EOF )" fi } append_server_address() { local addr="$1" test -n "$addr" || return local ipv4 for ipv4 in $(resolveip -4 -t 5 "$addr") ; do append_server_ipv4 "$ipv4" OUTBOUND_SERVERS="$OUTBOUND_SERVERS $ipv4" done local ipv6 for ipv6 in $(resolveip -6 -t 5 "$addr") ; do append_server_ipv6 "$ipv6" OUTBOUND_SERVERS="$OUTBOUND_SERVERS $ipv6" done } xray_section_validate() { uci_validate_section "$NAME" "xray" "$1" \ 'enabled:bool:0' \ 'xray_file:string' \ 'asset_location:directory' \ 'mem_percentage:and(uinteger, max(100)):80' \ 'config_file:file' \ 'loglevel:or("debug", "info", "warning", "error", "none")' \ 'access_log:string' \ 'error_log:string' \ 'stats_enabled:bool:0' \ 'transport_enabled:bool:0' \ 'inbounds:list(uci("xray", "@inbound"))' \ 'outbounds:list(uci("xray", "@outbound"))' } dns_section_validate() { uci_validate_section "$NAME" "dns" "$1" \ 'enabled:bool:0' \ 'tag:string' \ 'client_ip:ipaddr' \ 'hosts:list(string)' \ 'servers:list(uci("xray", "@dns_server"))' } dns_server_section_validate() { uci_validate_section "$NAME" "dns_server" "$1" \ 'address:string' \ 'port:port' \ 'domains:list(string)' \ 'expect_ips:list(string)' } routing_section_validate() { uci_validate_section "$NAME" "routing" "$1" \ 'enabled:bool:0' \ 'domain_strategy:or("AsIs", "IPIfNonMatch", "IPOnDemand")' \ 'rules:list(uci("xray", "@routing_rule"))' \ 'balancers:list(uci("xray", "@routing_balancer"))' } routing_rule_section_validate() { uci_validate_section "$NAME" "routing_rule" "$1" \ 'type:"field"' \ 'domain:list(string)' \ 'ip:list(string)' \ 'port:or(port, portrange)' \ 'network:list(or("tcp", "udp"))' \ 'source:list(string)' \ 'user:list(string)' \ 'inbound_tag:list(string)' \ 'protocol:list(or("http", "tls", "bittorrent"))' \ 'attrs:string' \ 'outbound_tag:string' \ 'balancer_tag:string' } routing_balancer_section_validate() { uci_validate_section "$NAME" "routing_balancer" "$1" \ 'tag:string' \ 'selector:list(string)' } policy_section_validate() { uci_validate_section "$NAME" "policy" "$1" \ 'enabled:bool:0' \ 'levels:list(uci("xray", "@policy_level"))' \ 'system_stats_inbound_uplink:bool:0' \ 'system_stats_inbound_downlink:bool:0' } policy_level_section_validate() { uci_validate_section "$NAME" "policy_level" "$1" \ 'level:uinteger' \ 'handshake:uinteger:4' \ 'conn_idle:uinteger:300' \ 'uplink_only:uinteger:2' \ 'downlink_only:uinteger:5' \ 'stats_user_uplink:bool:0' \ 'stats_user_downlink:bool:0' \ 'buffer_size:uinteger' } reverse_section_validate() { uci_validate_section "$NAME" "reverse" "$1" \ 'enabled:bool:0' \ 'bridges:list(string)' \ 'portals:list(string)' } inbound_section_validate() { uci_validate_section "$NAME" "inbound" "$1" \ 'port:or(port, portrange, string)' \ 'listen:ipaddr' \ 'protocol:string' \ 's_dokodemo_door_address:host' \ 's_dokodemo_door_port:port' \ 's_dokodemo_door_network:list(or("tcp", "udp"))' \ 's_dokodemo_door_timeout:uinteger' \ 's_dokodemo_door_follow_redirect:bool:0' \ 's_dokodemo_door_user_level:uiterger' \ 's_http_account_user:string' \ 's_http_account_pass:string' \ 's_http_allow_transparent:bool:0' \ 's_http_timeout:uinteger' \ 's_http_user_level:uinteger' \ 's_mtproto_user_email:string' \ 's_mtproto_user_secret:string' \ 's_mtproto_user_level:uinteger' \ 's_shadowsocks_email:string' \ 's_shadowsocks_method:string' \ 's_shadowsocks_password:string' \ 's_shadowsocks_level:uinteger' \ 's_shadowsocks_ota:bool:0' \ 's_shadowsocks_network:list(or("tcp", "udp")):tcp' \ 's_socks_auth:or("noauth", "password")' \ 's_socks_account_user:string' \ 's_socks_account_pass:string' \ 's_socks_udp:bool:0' \ 's_socks_ip:host' \ 's_socks_user_level:uinteger' \ 's_vmess_client_id:string' \ 's_vmess_client_alter_id:and(uinteger, max(65535))' \ 's_vmess_client_email:string' \ 's_vmess_client_user_level:uinteger' \ 's_vmess_default_alter_id:and(uinteger, max(65535))' \ 's_vmess_default_user_level:uinteger' \ 's_vmess_detour_to:string' \ 's_vmess_disable_insecure_encryption:bool:0' \ 's_vless_client_id:string' \ 's_vless_client_alter_id:and(uinteger, max(65535))' \ 's_vless_client_email:string' \ 's_vless_client_user_level:uinteger' \ 's_vless_default_alter_id:and(uinteger, max(65535))' \ 's_vless_default_user_level:uinteger' \ 's_vless_detour_to:string' \ 's_vless_disable_insecure_encryption:bool:0' \ 's_trojan_client_id:string' \ 's_trojan_client_alter_id:and(uinteger, max(65535))' \ 's_trojan_client_email:string' \ 's_trojan_client_user_level:uinteger' \ 's_trojan_default_alter_id:and(uinteger, max(65535))' \ 's_trojan_default_user_level:uinteger' \ 's_trojan_detour_to:string' \ 's_trojan_disable_insecure_encryption:bool:0' \ 's_socks_client_id:string' \ 's_socks_client_email:string:"openmptcprouter"' \ 'ss_network:or("tcp", "kcp", "ws", "http", "domainsocket", "quic")' \ 'ss_security:or("none", "tls")' \ 'ss_tls_server_name:host' \ 'ss_tls_alpn:string' \ 'ss_tls_allow_insecure:bool:0' \ 'ss_tls_allow_insecure_ciphers:bool:0' \ 'ss_tls_disable_system_root:bool:0' \ 'ss_tls_cert_usage:or("encipherment", "verify", "issue")' \ 'ss_tls_cert_file:string' \ 'ss_tls_key_file:string' \ 'ss_tcp_header_type:or("none", "http")' \ 'ss_tcp_header_request_version:string' \ 'ss_tcp_header_request_method:string:GET' \ 'ss_tcp_header_request_path:string' \ 'ss_tcp_header_request_headers:list(string)' \ 'ss_tcp_header_response_version:string' \ 'ss_tcp_header_response_status:string' \ 'ss_tcp_header_response_reason:string' \ 'ss_tcp_header_response_headers:list(string)' \ 'ss_kcp_mtu:and(min(576), max(1460))' \ 'ss_kcp_tti:and(min(10), max(100))' \ 'ss_kcp_uplink_capacity:uinteger' \ 'ss_kcp_downlink_capacity:uinteger' \ 'ss_kcp_congestion:bool:0' \ 'ss_kcp_read_buffer_size:uinteger' \ 'ss_kcp_write_buffer_size:uinteger' \ 'ss_kcp_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ 'ss_websocket_path:string' \ 'ss_websocket_headers:list(string)' \ 'ss_http_host:list(host)' \ 'ss_http_path:string' \ 'ss_domainsocket_path:string' \ 'ss_quic_security:or("aes-128-gcm", "chacha20-poly1305", "none")' \ 'ss_quic_key:string' \ 'ss_quic_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ 'ss_sockopt_tcp_fast_open:or("0", "1")' \ 'ss_sockopt_mptcp:or("0", "1")' \ 'ss_sockopt_tproxy:or("redirect", "tproxy", "off")' \ 'tag:string' \ 'sniffing_enabled:bool:0' \ 'sniffing_dest_override:list(or("http", "tls"))' \ 'allocate_strategy:or("always", "random")' \ 'allocate_refresh:uinteger' \ 'allocate_concurrency:uinteger' } outbound_section_validate() { uci_validate_section "$NAME" "outbound" "$1" \ 'send_through:ipaddr' \ 'protocol:string' \ 'tag:string' \ 's_blackhole_reponse_type:or("none", "http")' \ 's_dns_network:or("tcp", "udp")' \ 's_dns_address:string' \ 's_dns_port:port' \ 's_freedom_domain_strategy:or("AsIs", "UseIP", "UseIPv4", "UseIPv6")' \ 's_freedom_redirect:string' \ 's_freedom_user_level:uinteger' \ 's_http_server_address:host' \ 's_http_server_port:port' \ 's_http_account_user:string' \ 's_http_account_pass:string' \ 's_shadowsocks_email:string' \ 's_shadowsocks_address:host' \ 's_shadowsocks_port:port' \ 's_shadowsocks_method:string' \ 's_shadowsocks_password:string' \ 's_shadowsocks_level:uinteger' \ 's_shadowsocks_ota:bool:0' \ 's_socks_server_address:host' \ 's_socks_server_port:port' \ 's_socks_account_user:string' \ 's_socks_account_pass:string' \ 's_socks_user_level:uinteger' \ 's_vmess_address:host' \ 's_vmess_port:port' \ 's_vmess_user_id:string' \ 's_vmess_user_alter_id:and(uinteger, max(65535))' \ 's_vmess_user_security:or("auto", "aes-128-gcm", "chacha20-poly1305", "none")' \ 's_vmess_user_level:uinteger' \ 's_vless_address:host' \ 's_vless_port:port' \ 's_vless_user_id:string' \ 's_vless_user_alter_id:and(uinteger, max(65535))' \ 's_vless_user_security:or("auto", "aes-128-gcm", "chacha20-poly1305", "none")' \ 's_vless_user_encryption:or("auto", "none")' \ 's_vless_user_level:uinteger' \ 's_vless_reality_address:host' \ 's_vless_reality_port:port' \ 's_vless_reality_user_id:string' \ 's_vless_reality_user_alter_id:and(uinteger, max(65535))' \ 's_vless_reality_user_security:or("auto", "aes-128-gcm", "chacha20-poly1305", "none")' \ 's_vless_reality_user_encryption:or("auto", "none")' \ 's_vless_reality_flow:string' \ 's_vless_reality_public_key:string' \ 's_vless_reality_user_level:uinteger' \ 's_trojan_address:host' \ 's_trojan_port:port' \ 's_trojan_user_id:string' \ 's_trojan_user_alter_id:and(uinteger, max(65535))' \ 's_trojan_user_security:or("auto", "aes-128-gcm", "chacha20-poly1305", "none")' \ 's_trojan_user_encryption:or("auto", "none")' \ 's_trojan_user_level:uinteger' \ 's_socks_address:host' \ 's_socks_port:port' \ 's_socks_user_id:string' \ 's_socks_email:string:openmptcprouter' \ 'ss_network:or("tcp", "kcp", "ws", "http", "domainsocket", "quic")' \ 'ss_security:or("none", "tls")' \ 'ss_tls_server_name:host' \ 'ss_tls_alpn:string' \ 'ss_tls_allow_insecure:bool:0' \ 'ss_tls_allow_insecure_ciphers:bool:0' \ 'ss_tls_disable_system_root:bool:0' \ 'ss_tls_cert_usage:or("encipherment", "verify", "issue")' \ 'ss_tls_cert_file:string' \ 'ss_tls_key_file:string' \ 'ss_tcp_header_type:or("none", "http")' \ 'ss_tcp_header_request_version:string' \ 'ss_tcp_header_request_method:string' \ 'ss_tcp_header_request_path:string' \ 'ss_tcp_header_request_headers:list(string)' \ 'ss_tcp_header_response_version:string' \ 'ss_tcp_header_response_status:string' \ 'ss_tcp_header_response_reason:string' \ 'ss_tcp_header_response_headers:list(string)' \ 'ss_kcp_mtu:and(min(576), max(1460))' \ 'ss_kcp_tti:and(min(10), max(100))' \ 'ss_kcp_uplink_capacity:uinteger' \ 'ss_kcp_downlink_capacity:uinteger' \ 'ss_kcp_congestion:bool:0' \ 'ss_kcp_read_buffer_size:uinteger' \ 'ss_kcp_write_buffer_size:uinteger' \ 'ss_kcp_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ 'ss_websocket_path:string' \ 'ss_websocket_headers:list(string)' \ 'ss_http_host:list(host)' \ 'ss_http_path:string' \ 'ss_domainsocket_path:string' \ 'ss_quic_security:or("aes-128-gcm", "chacha20-poly1305", "none")' \ 'ss_quic_key:string' \ 'ss_quic_header_type:or("none", "srtp", "utp", "wechat-video", "dtls", "wireguard")' \ 'ss_sockopt_mark:uinteger' \ 'ss_sockopt_tcp_fast_open:or("0", "1")' \ 'ss_sockopt_mptcp:or("0", "1")' \ 'stream_settings:string' \ 'proxy_settings_tag:string' \ 'mux_enabled:bool:0' \ 'mux_concurrency:uinteger:8' } add_xray_redirect_rules() { local ext_args="$1" local lan_devices="$2" local lan_ipaddrs="$3" local port="$TRANSPARENT_PROXY_PORT" local addition="$TRANSPARENT_PROXY_ADDITION" local ipset_src_direct="$IPSET_SRC_DIRECT_V4" local ipset_dst_direct="$IPSET_DST_DIRECT_V4" test -n "$port" || return # # This part need a rewrite # xray-rules -f # #logger -t "xray" "xray-rules -l ${port} -L ${port} -s $OUTBOUND_SERVERS_V4 --rule-name def --src-default forward --dst-default forward --local-default forward" # commandline="-l ${port} -s $OUTBOUND_SERVERS_V4 --rule-name def --src-default forward --dst-default forward --local-default forward" # [ "$(uci -q get xray.main_transparent_proxy.redirect_udp)" = "1" ] && [ "$(uci -q get xray.omrout.protocol)" != "socks" ] && commandline="$commandline -L ${port}" # xray-rules $commandline # [ "$(uci -q get xray.main.inbounds | grep omr6)" != "" ] && [ -n "$OUTBOUND_SERVERS_V6" ] && { # xray-rules6 -f # commandline="-l $((port+1)) -L $((port+1)) -s $OUTBOUND_SERVERS_V6 --rule-name def --src-default forward --dst-default forward --local-default forward" # [ "$(uci -q get xray.main_transparent_proxy.redirect_udp)" = "1" ] && ([ "$(uci -q get xray.omrout.protocol)" = "vless-reality" ] || [ "$(uci -q get xray.omrout.protocol)" = "vless" ] || [ "$(uci -q get xray.omrout.protocol)" = "vmess" ]) && commandline="$commandline -L ${port+1}" # xray-rules6 $commandline # } remote_servers="$(echo $OUTBOUND_SERVERS \ | tr ' ' '\n' \ | sort -u \ | xargs -n 1 resolveip \ | sort -u)" [ "$(uci -q get xray.main_transparent_proxy.redirect_udp)" = "1" ] && [ "$(uci -q get xray.omrout.protocol)" != "socks" ] && portudp="$port" ifnames="$(uci -q get shadowsocks-libev.ss_rules.ifnames)" local tmp="/tmp/xrrules" json_init json_add_string o_remote_servers "$remote_servers" json_add_int o_redir_tcp_port "$port" json_add_int o_redir_udp_port "$portudp" json_add_string o_ifnames "$ifnames" json_add_string o_local_default "forward" json_add_string o_src_bypass "$src_ips_bypass" json_add_string o_src_forward "$src_ips_forward" json_add_string o_src_checkdst "$src_ips_checkdst" json_add_string o_src_default "checkdst" json_add_string o_dst_bypass "$dst_ips_bypass" json_add_string o_dst_forward "$dst_ips_forward" json_add_string o_dst_bypass_file "$dst_ips_bypass_file" json_add_string o_dst_forward_file "$dst_ips_forward_file" json_add_string o_dst_default "forward" json_add_string o_nft_tcp_extra "$nft_tcp_extra" json_add_string o_nft_udp_extra "$nft_udp_extra" json_dump -i >"$tmp.json" if utpl -S -F "$tmp.json" "$xrrules_uc" >"$tmp.nft" \ && ! cmp -s "$tmp.nft" "$xrrules_nft"; then echo "table inet chk {include \"$tmp.nft\";}" >"$tmp.nft.chk" if nft -f "$tmp.nft.chk" -c; then mv "$tmp.nft" "$xrrules_nft" fw4 -q restart fi rm -f "$tmp.nft.chk" fi rm -f "$tmp.json" rm -f "$tmp.nft" [ -f /etc/init.d/omr-bypass ] && [ -z "$(pgrep -f omr-bypass)" ] && { logger -t "xray" "Reload omr-bypass rules" /etc/init.d/omr-bypass reload_rules } } init_rules_for_listfile() { local direct_list_dns="$1" local proxy_list_dns="$2" echo "# AUTO-GENERATED FILE. DO NOT MODIFY." >"$FILE_XRAY_DNSMASQ_CACHE" # For direct list local direct_content direct_content="$(get_file_content "directlist")" if [ -n "$direct_content" ] ; then echo "$direct_content" | \ grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}(/[0-9]{1,2})?" | \ sed "s/.*/add $IPSET_DST_DIRECT_V4 & timeout 0/" | \ ipset -! restore 2>/dev/null echo "$direct_content" | \ grep -oE "([0-9a-fA-F]{0,4}:){1,7}([0-9a-fA-F]){0,4}(/[0-9]{1,2})?" | \ sed "s/.*/add $IPSET_DST_DIRECT_V6 & timeout 0/" | \ ipset -! restore 2>/dev/null if [ -n "$direct_list_dns" ] ; then echo "$direct_content" | \ grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ sed "s|.*|server=/&/$direct_list_dns\nipset=/&/$IPSET_DST_DIRECT_V4,$IPSET_DST_DIRECT_V6|" \ >>"$FILE_XRAY_DNSMASQ_CACHE" else echo "$direct_content" | \ grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ sed "s|.*|ipset=/&/$IPSET_DST_DIRECT_V4,$IPSET_DST_DIRECT_V6|" \ >>"$FILE_XRAY_DNSMASQ_CACHE" fi fi # For proxy list local proxy_content proxy_content="$(get_file_content "proxylist")" if [ -n "$proxy_content" ] ; then echo "$proxy_content" | \ grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}(/[0-9]{1,2})?" | \ sed "s/.*/add $IPSET_DST_PROXY_V4 & timeout 0/" | \ ipset -! restore 2>/dev/null echo "$proxy_content" | \ grep -oE "([0-9a-fA-F]{0,4}:){1,7}([0-9a-fA-F]){0,4}(/[0-9]{1,2})?" | \ sed "s/.*/add $IPSET_DST_PROXY_V6 & timeout 0/" | \ ipset -! restore 2>/dev/null if [ -n "$proxy_list_dns" ] ; then echo "$proxy_content" | \ grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ sed "s|.*|server=/&/$proxy_list_dns\nipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ >>"$FILE_XRAY_DNSMASQ_CACHE" else echo "$proxy_content" | \ grep -oE "([0-9a-zA-Z_-]+\.)+[a-zA-Z]{2,}$" | \ sed "s|.*|ipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ >>"$FILE_XRAY_DNSMASQ_CACHE" fi fi # For local devices outbound list local src_content src_content="$(get_file_content "srcdirectlist")" if [ -n "$src_content" ] ; then echo "$src_content" | \ grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}(/[0-9]{1,2})?" | \ sed "s/.*/add $IPSET_SRC_DIRECT_V4 & timeout 0/" | \ ipset -! restore 2>/dev/null fi } gracefully_restart_dnsmasq() { if [ "x$DNSMASQ_RESTART_EXPECTED" = "x1" ] && [ -x "/etc/init.d/dnsmasq" ] ; then _info "Restarting dnsmasq..." /etc/init.d/dnsmasq restart >/dev/null 2>&1 DNSMASQ_RESTART_EXPECTED=0 fi } add_dns_settings() { local section="${1}_dns" if ! dns_section_validate "$section" ; then _err "Invalid DNS config: $section, skip" return 1 fi if [ "x$enabled" != "x1" ] ; then _info "DNS disabled: $section" return 0 fi json_add_object "dns" test -n "$tag" && \ json_add_string "tag" "$tag" test -n "$client_ip" && \ json_add_string "clientIp" "$client_ip" if [ -n "$hosts" ] ; then json_add_object "hosts" local h for h in $hosts ; do local domain="$(echo "$h" | cut -d'|' -f1)" local ip="$(echo "$h" | cut -d'|' -f2)" if [ -n "$domain" ] && [ -n "$ip" ] ; then json_add_string "$domain" "$ip" fi done json_close_object # hosts fi if [ -n "$servers" ] ; then json_add_array "servers" for ss in $servers ; do if dns_server_section_validate "$ss" ; then if [ -z "$address" ] ; then continue fi if [ -z "${port}${domains}${expect_ips}" ] ; then json_add_string "" "$address" else json_add_object "" json_add_string "address" "$address" if [ -n "$port" ] ; then json_add_int "port" "$port" else json_add_int "port" "53" fi if [ -n "$domains" ] ; then json_add_array "domains" local d for d in $domains ; do json_add_string "" "$d" done json_close_array # domains fi if [ -n "$expect_ips" ] ; then json_add_array "expectIPs" local e for e in $expect_ips ; do json_add_string "" "$e" done json_close_array # expectIPs fi json_close_object fi fi done json_close_array # servers fi json_close_object # dns } add_routing_settings() { local section="${1}_routing" if ! routing_section_validate "$section" ; then _err "Invalid routing config: $section, skip" return 1 fi if [ "x$enabled" != "x1" ] ; then _info "Routing disabled: $section" return 0 fi json_add_object "routing" test -n "$domain_strategy" && \ json_add_string "domainStrategy" "$domain_strategy" if [ -n "$rules" ] ; then json_add_array "rules" local rs for rs in $rules ; do if routing_rule_section_validate "$rs" ; then json_add_object "" json_add_string "type" "$type" if [ -n "$domain" ] ; then json_add_array "domain" local d for d in $domain ; do json_add_string "" "$d" done json_close_array # domain fi if [ -n "$ip" ] ; then json_add_array "ip" local i for i in $ip ; do json_add_string "" "$i" done json_close_array # ip fi if [ -n "$port" ] ; then json_add_string "port" "$(echo "$port" | tr -s ' ' ',')" fi if [ -n "$network" ] ; then json_add_string "network" "$(echo "$network" | tr -s ' ' ',')" fi if [ -n "$source" ] ; then json_add_array "source" local s for s in $source ; do json_add_string "" "$s" done json_close_array # source fi if [ -n "$user" ] ; then json_add_array "user" local u for u in $user ; do json_add_string "" "$u" done json_close_array # user fi if [ -n "$inbound_tag" ] ; then json_add_array "inboundTag" local it for it in $inbound_tag ; do json_add_string "" "$it" done json_close_array # inboundTag fi if [ -n "$protocol" ] ; then json_add_array "protocol" local p for p in $protocol ; do json_add_string "" "$p" done json_close_array # protocol fi test -n "$attrs" && \ json_add_string "attrs" "$attrs" test -n "$outbound_tag" && \ json_add_string "outboundTag" "$outbound_tag" test -n "$balancer_tag" && \ json_add_string "balancerTag" "$balancer_tag" json_close_object fi done json_close_array # rules fi if [ -n "$balancers" ] ; then json_add_array "balancers" local bs for bs in $balancers ; do if routing_balancer_section_validate "$bs" ; then json_add_object "" json_add_string "tag" "$tag" json_add_array "selector" local s for s in $selector ; do json_add_string "" "$s" done json_close_array # selector json_close_object fi done json_close_array # balancers fi json_close_object } add_policy_settings() { local section="${1}_policy" if ! policy_section_validate "$section" ; then _err "Invalid policy config: $section, skip" return 1 fi if [ "x$enabled" != "x1" ] ; then _info "Policy disabled: $section" return 0 fi json_add_object "policy" if [ -n "$levels" ] ; then json_add_object "levels" local l_s for l_s in $levels ; do if policy_level_section_validate "$l_s" ; then json_add_object "$level" json_add_int "handshake" "$handshake" json_add_int "connIdle" "$conn_idle" json_add_int "uplinkOnly" "$uplink_only" json_add_int "downlinkOnly" "$downlink_only" json_add_boolean "statsUserUplink" "$stats_user_uplink" json_add_boolean "statsUserDownlink" "$stats_user_downlink" test -n "$buffer_size" && \ json_add_int "bufferSize" "$buffer_size" json_close_object fi done json_close_object # levels fi json_add_object "system" json_add_boolean "statsInboundUplink" "$system_stats_inbound_uplink" json_add_boolean "statsInboundDownlink" "$system_stats_inbound_downlink" json_close_object # system json_close_object # policy } add_reverse_settings() { local section="${1}_reverse" if ! reverse_section_validate "$section" ; then _err "Invalid reverse config: $section, skip" return 1 fi if [ "x$enabled" != "x1" ] ; then _info "Reverse disabled: $section" return 0 fi json_add_object "reverse" if [ -n "$bridges" ] ; then json_add_array "bridges" local b for b in $bridges ; do local tag="$(echo "$b" | cut -d'|' -f1)" local domain="$(echo "$b" | cut -d'|' -f2)" if [ -n "$tag" ] && [ -n "$domain" ] ; then json_add_object "" json_add_string "tag" "$tag" json_add_string "domain" "$domain" json_close_object fi done json_close_array # bridges fi if [ -n "$portals" ] ; then json_add_array "portals" local p for p in $portals ; do local tag="$(echo "$p" | cut -d'|' -f1)" local domain="$(echo "$p" | cut -d'|' -f2)" if [ -n "$tag" ] && [ -n "$domain" ] ; then json_add_object "" json_add_string "tag" "$tag" json_add_string "domain" "$domain" json_close_object fi done json_close_array # portals fi json_close_object # reverse } add_transport_settings() { local json json="$(get_file_content "transport")" if [ -z "$json" ] ; then _err "Invalid transport config: $key" return 1 fi json_add_object "transport" eval "$(get_commands_from_json "$json")" json_close_object # transport } add_inbound_setting() { local section="$1" if ! inbound_section_validate "$section" ; then _err "Invalid inbound section: $section" return 1 fi json_add_object "" test -n "$listen" && \ json_add_string "listen" "$listen" json_add_int "port" "$port" json_add_string "protocol" "$protocol" case "${protocol:-x}" in "dokodemo-door") json_add_object "settings" if [ -n "$port" ] && [ "x$port" = "x$TRANSPARENT_PROXY_PORT" ] ; then local settings_network="tcp" test -n "$TRANSPARENT_PROXY_ADDITION" && \ settings_network="$settings_network,udp" json_add_boolean "followRedirect" "1" json_add_string "network" "$settings_network" else test -n "$s_dokodemo_door_address" && \ json_add_string "address" "$s_dokodemo_door_address" test -n "$s_dokodemo_door_port" && \ json_add_int "port" "$s_dokodemo_door_port" test -n "$s_dokodemo_door_follow_redirect" && \ json_add_boolean "followRedirect" "$s_dokodemo_door_follow_redirect" test -n "$s_dokodemo_door_network" && \ json_add_string "network" "$(echo "$s_dokodemo_door_network" | tr -s ' ' ',')" fi test -n "$s_dokodemo_door_timeout" && \ json_add_int "timeout" "$s_dokodemo_door_timeout" test -n "$s_dokodemo_door_user_level" && \ json_add_int "userLevel" "$s_dokodemo_door_user_level" json_close_object # settings ;; "http") json_add_object "settings" if [ -n "$s_http_account_user" ] ; then json_add_array "accounts" json_add_object "" json_add_string "user" "$s_http_account_user" json_add_string "pass" "$s_http_account_pass" json_close_object json_close_array # accounts fi json_add_boolean "allowTransparent" "$s_http_allow_transparent" test -n "$s_http_timeout" && \ json_add_int "timeout" "$s_http_timeout" test -n "$s_http_user_level" && \ json_add_int "userLevel" "$s_http_user_level" json_close_object # settings ;; "mtproto") json_add_object "settings" if [ -n "$s_mtproto_user_email" ] ; then json_add_array "users" json_add_object "" json_add_string "email" "$s_mtproto_user_email" json_add_string "secret" "$s_mtproto_user_secret" test -n "$s_mtproto_user_level" && \ json_add_int "level" "$s_mtproto_user_level" json_close_object json_close_array # users fi json_close_object # settings ;; "shadowsocks") json_add_object "settings" json_add_string "method" "$s_shadowsocks_method" json_add_string "password" "$s_shadowsocks_password" test -n "$s_shadowsocks_email" && \ json_add_string "email" "$s_shadowsocks_email" test -n "$s_shadowsocks_level" && \ json_add_int "level" "$s_shadowsocks_level" json_add_boolean "ota" "$s_shadowsocks_ota" json_add_string "network" "$(echo "$s_shadowsocks_network" | tr -s ' ' ',')" json_close_object # settings ;; "socks") json_add_object "settings" json_add_string "auth" "$s_socks_auth" if [ -n "$s_socks_account_user" ] ; then json_add_array "accounts" json_add_object "" json_add_string "user" "$s_socks_account_user" json_add_string "pass" "$s_socks_account_pass" json_close_object json_close_array # accounts fi if [ -n "$s_socks_client_id" ] ; then json_add_array "accounts" json_add_object "" json_add_string "user" "$s_socks_email" json_add_string "pass" "$s_socks_client_id" json_close_object json_close_array # accounts fi json_add_boolean "udp" "$s_socks_udp" test -n "$s_socks_ip" && \ json_add_string "ip" "$s_socks_ip" test -n "$s_socks_user_level" && \ json_add_int "userLevel" "$s_socks_user_level" json_close_object # settings ;; "vmess") json_add_object "settings" if [ -n "$s_vmess_client_id" ] ; then json_add_array "clients" json_add_object "" json_add_string "id" "$s_vmess_client_id" test -n "$s_vmess_client_alter_id" && \ json_add_int "alterId" "$s_vmess_client_alter_id" test -n "$s_vmess_client_email" && \ json_add_string "email" "$s_vmess_client_email" test -n "$s_vmess_client_user_level" && \ json_add_int "level" "$s_vmess_client_user_level" json_close_object json_close_array # clients fi json_add_object "default" test -n "$s_vmess_default_alter_id" && \ json_add_int "alterId" "$s_vmess_default_alter_id" test -n "$s_vmess_default_user_level" && \ json_add_int "level" "$s_vmess_default_user_level" json_close_object # default if [ -n "$s_vmess_detour_to" ] ; then json_add_object "detour" json_add_string "to" "$s_vmess_detour_to" json_close_object # detour fi json_add_boolean "disableInsecureEncryption" "$s_vmess_disable_insecure_encryption" json_close_object # settings ;; "vless") json_add_object "settings" if [ -n "$s_vless_client_id" ] ; then json_add_array "clients" json_add_object "" json_add_string "id" "$s_vless_client_id" test -n "$s_vless_client_alter_id" && \ json_add_int "alterId" "$s_vless_client_alter_id" test -n "$s_vless_client_email" && \ json_add_string "email" "$s_vless_client_email" test -n "$s_vless_client_user_level" && \ json_add_int "level" "$s_vless_client_user_level" json_close_object json_close_array # clients fi json_add_object "default" test -n "$s_vless_default_alter_id" && \ json_add_int "alterId" "$s_vless_default_alter_id" test -n "$s_vless_default_user_level" && \ json_add_int "level" "$s_vless_default_user_level" json_close_object # default if [ -n "$s_vless_detour_to" ] ; then json_add_object "detour" json_add_string "to" "$s_vless_detour_to" json_close_object # detour fi json_add_boolean "disableInsecureEncryption" "$s_vless_disable_insecure_encryption" json_close_object # settings ;; "trojan") json_add_object "settings" if [ -n "$s_trojan_client_id" ] ; then json_add_array "clients" json_add_object "" json_add_string "password" "$s_trojan_client_id" test -n "$s_trojan_client_alter_id" && \ json_add_int "alterId" "$s_trojan_client_alter_id" test -n "$s_trojan_client_email" && \ json_add_string "email" "$s_trojan_client_email" test -n "$s_trojan_client_user_level" && \ json_add_int "level" "$s_trojan_client_user_level" json_close_object json_close_array # clients fi json_add_object "default" test -n "$s_trojan_default_alter_id" && \ json_add_int "alterId" "$s_trojan_default_alter_id" test -n "$s_trojan_default_user_level" && \ json_add_int "level" "$s_trojan_default_user_level" json_close_object # default if [ -n "$s_trojan_detour_to" ] ; then json_add_object "detour" json_add_string "to" "$s_trojan_detour_to" json_close_object # detour fi json_add_boolean "disableInsecureEncryption" "$s_trojan_disable_insecure_encryption" json_close_object # settings ;; esac json_add_object "streamSettings" test -n "$ss_network" && \ json_add_string "network" "$ss_network" test -n "$ss_security" && \ json_add_string "security" "$ss_security" if [ "x$ss_security" = "xtls" ] && [ "x$protocol" != "xshadowsocks" ]; then json_add_object "tlsSettings" test -n "$ss_tls_server_name" && \ json_add_string "serverName" "$ss_tls_server_name" if [ -n "$ss_tls_alpn" ] ; then json_add_array "alpn" json_add_string "" "$ss_tls_alpn" json_close_array # alpn fi json_add_boolean "allowInsecure" "$ss_tls_allow_insecure" json_add_boolean "allowInsecureCiphers" "$ss_tls_allow_insecure_ciphers" json_add_boolean "disableSystemRoot" "$ss_tls_disable_system_root" json_add_array "certificates" if [ -n "$ss_tls_cert_file" ] ; then json_add_object "" json_add_string "certificateFile" "$ss_tls_cert_file" json_add_string "keyFile" "$ss_tls_key_file" test -n "$ss_tls_cert_usage" && \ json_add_string "usage" "$ss_tls_cert_usage" json_close_object fi json_close_array # certificates json_close_object # tlsSettings fi case "${ss_network:-x}" in "tcp") json_add_object "tcpSettings" if [ -n "$ss_tcp_header_type" ] ; then json_add_object "header" json_add_string "type" "$ss_tcp_header_type" if [ "$ss_tcp_header_type" = "http" ] ; then json_add_object "request" test -n "$ss_tcp_header_request_version" && \ json_add_string "version" "$ss_tcp_header_request_version" json_add_string "method" "$ss_tcp_header_request_method" if [ -n "$ss_tcp_header_request_path" ] ; then json_add_array "path" json_add_string "" "$ss_tcp_header_request_path" json_close_array # path fi if [ -n "$ss_tcp_header_request_headers" ] ; then json_add_object "headers" handle_request_header() { local h="$1" local name="$(echo "$h" | cut -d'=' -f1)" local value="$(echo "$h" | cut -d'=' -f2)" if [ -n "$name" ] && [ -n "$value" ] ; then json_add_array "$name" json_add_string "" "$value" json_close_array fi } config_list_foreach "$section" "ss_tcp_header_request_headers" handle_request_header json_close_object # headers fi json_close_object # request json_add_object "response" test -n "$ss_tcp_header_response_version" && \ json_add_string "version" "$ss_tcp_header_response_version" test -n "$ss_tcp_header_response_status" && \ json_add_string "status" "$ss_tcp_header_response_status" test -n "$ss_tcp_header_response_reason" && \ json_add_string "reason" "$ss_tcp_header_response_reason" if [ -n "$ss_tcp_header_response_headers" ] ; then json_add_object "headers" handle_response_header() { local h="$1" local name="$(echo "$h" | cut -d'=' -f1)" local value="$(echo "$h" | cut -d'=' -f2)" if [ -n "$name" ] && [ -n "$value" ] ; then json_add_array "$name" json_add_string "" "$value" json_close_array fi } config_list_foreach "$section" "ss_tcp_header_response_headers" handle_response_header json_close_object # headers fi json_close_object # response fi json_close_object # header fi json_close_object # tcpSettings ;; "kcp") json_add_object "kcpSettings" test -n "$ss_kcp_mtu" && \ json_add_int "mtu" "$ss_kcp_mtu" test -n "$ss_kcp_tti" && \ json_add_int "tti" "$ss_kcp_tti" test -n "$ss_kcp_uplink_capacity" && \ json_add_int "uplinkCapacity" "$ss_kcp_uplink_capacity" test -n "$ss_kcp_downlink_capacity" && \ json_add_int "downlinkCapacity" "$ss_kcp_downlink_capacity" json_add_boolean "congestion" "$ss_kcp_congestion" test -n "$ss_kcp_read_buffer_size" && \ json_add_int "readBufferSize" "$ss_kcp_read_buffer_size" test -n "$ss_kcp_write_buffer_size" && \ json_add_int "writeBufferSize" "$ss_kcp_write_buffer_size" if [ -n "$ss_kcp_header_type" ] ; then json_add_object "header" json_add_string "type" "$ss_kcp_header_type" json_close_object # header fi json_close_object # kcpSettings ;; "ws") json_add_object "wsSettings" test -n "$ss_websocket_path" && \ json_add_string "path" "$ss_websocket_path" if [ -n "$ss_websocket_headers" ] ; then json_add_object "headers" handle_websocket_header() { local h="$1" local name="$(echo "$h" | cut -d'=' -f1)" local value="$(echo "$h" | cut -d'=' -f2)" if [ -n "$name" ] && [ -n "$value" ] ; then json_add_string "$name" "$value" fi } config_list_foreach "$section" "ss_websocket_headers" handle_websocket_header json_close_object # headers fi json_close_object # wsSettings ;; "http") json_add_object "httpSettings" if [ -n "$ss_http_host" ] ; then json_add_array "host" local h for h in $ss_http_host ; do json_add_string "" "$h" done json_close_array # host fi test -n "$ss_http_path" && \ json_add_string "path" "$ss_http_path" json_close_object # httpSettings ;; "domainsocket") json_add_object "dsSettings" test -n "$ss_domainsocket_path" && \ json_add_string "path" "$ss_domainsocket_path" json_close_object # dsSettings ;; "quic") json_add_object "quicSettings" test -n "$ss_quic_security" && \ json_add_string "security" "$ss_quic_security" test -n "$ss_quic_key" && \ json_add_string "key" "$ss_quic_key" if [ -n "$ss_quic_header_type" ] ; then json_add_object "header" json_add_string "type" "$ss_quic_header_type" json_close_object # header fi json_close_object # quicSettings ;; esac json_add_object "sockopt" # if [ -n "$port" ] && [ "x$port" = "x$TRANSPARENT_PROXY_PORT" ] ; then # if [ "x$TRANSPARENT_PROXY_USE_TPROXY" = "x1" ] ; then # json_add_string "tproxy" "tproxy" # else # json_add_string "tproxy" "redirect" # fi # else test -n "$ss_sockopt_tcp_fast_open" && \ json_add_boolean "tcpFastOpen" "$ss_sockopt_tcp_fast_open" if version_over_5_4; then test -n "$ss_sockopt_mptcp" && \ json_add_boolean "tcpMptcp" "$ss_sockopt_mptcp" fi test -n "$ss_sockopt_tproxy" && \ json_add_string "tproxy" "$ss_sockopt_tproxy" # fi json_close_object # sockopt json_close_object # streamSettings test -n "$tag" && \ json_add_string "tag" "$tag" json_add_object "sniffing" json_add_boolean "enabled" "$sniffing_enabled" if [ -n "$sniffing_dest_override" ] ; then json_add_array "destOverride" local d for d in $sniffing_dest_override ; do json_add_string "" "$d" done json_close_array # destOverride fi json_close_object # sniffing if [ -n "$allocate_strategy" ] ; then json_add_object "allocate" json_add_string "strategy" "$allocate_strategy" test -n "$allocate_refresh" && \ json_add_int "refresh" "$allocate_refresh" test -n "$allocate_concurrency" && \ json_add_int "concurrency" "$allocate_concurrency" json_close_object # allocate fi json_close_object } add_outbound_setting() { local section="$1" if ! outbound_section_validate "$section" ; then _err "Invalid outbound section: $section" return 1 fi json_add_object "" test -n "$send_through" && \ json_add_string "sendThrough" "$send_through" if [ "$protocol" = "vless-reality" ]; then json_add_string "protocol" "vless" else json_add_string "protocol" "$protocol" fi case "${protocol:-x}" in "blackhole") json_add_object "settings" if [ -n "$s_blackhole_reponse_type" ] ; then json_add_object "response" json_add_string "type" "$s_blackhole_reponse_type" json_close_object # response fi json_close_object # settings ;; "dns") json_add_object "settings" test -n "$s_dns_network" && \ json_add_string "network" "$s_dns_network" if [ -n "$s_dns_address" ] ; then json_add_string "address" "$s_dns_address" append_server_address "$s_dns_address" fi test -n "$s_dns_port" && \ json_add_int "port" "$s_dns_port" json_close_object # settings ;; "freedom") json_add_object "settings" test -n "$s_freedom_domain_strategy" && \ json_add_string "domainStrategy" "$s_freedom_domain_strategy" test -n "$s_freedom_redirect" && \ json_add_string "redirect" "$s_freedom_redirect" test -n "$s_freedom_user_level" && \ json_add_int "userLevel" "$s_freedom_user_level" json_close_object # settings ;; "http") json_add_object "settings" json_add_array "servers" json_add_object "" json_add_string "address" "$s_http_server_address" append_server_address "$s_http_server_address" test -n "$s_http_server_port" && \ json_add_int "port" "$s_http_server_port" if [ -n "$s_http_account_user" ] ; then json_add_array "users" json_add_object "" json_add_string "user" "$s_http_account_user" json_add_string "pass" "$s_http_account_pass" json_close_object json_close_array # users fi json_close_object json_close_array # servers json_close_object # settings ;; "mtproto") json_add_object "settings" json_close_object ;; "shadowsocks") json_add_object "settings" json_add_array "servers" json_add_object "" test -n "$s_shadowsocks_email" && \ json_add_string "email" "$s_shadowsocks_email" json_add_string "address" "$s_shadowsocks_address" append_server_address "$s_shadowsocks_address" json_add_int "port" "$s_shadowsocks_port" json_add_string "method" "$s_shadowsocks_method" json_add_string "password" "$s_shadowsocks_password" test -n "$s_shadowsocks_level" && \ json_add_int "level" "$s_shadowsocks_level" json_add_boolean "ota" "$s_shadowsocks_ota" json_close_object json_close_array # servers json_close_object # settings ;; "vmess") json_add_object "settings" json_add_array "vnext" json_add_object "" json_add_string "address" "$s_vmess_address" append_server_address "$s_vmess_address" json_add_int "port" "$s_vmess_port" json_add_array "users" json_add_object "" json_add_string "id" "$s_vmess_user_id" json_add_int "alterId" "$s_vmess_user_alter_id" test -n "$s_vmess_user_security" && \ json_add_string "security" "$s_vmess_user_security" test -n "$s_vmess_user_level" && \ json_add_int "level" "$s_vmess_user_level" json_close_object json_close_array # users json_close_object json_close_array # vnext json_close_object # settings ;; "vless") json_add_object "settings" json_add_array "vnext" json_add_object "" json_add_string "address" "$s_vless_address" append_server_address "$s_vless_address" json_add_int "port" "$s_vless_port" json_add_array "users" json_add_object "" json_add_string "id" "$s_vless_user_id" json_add_int "alterId" "$s_vless_user_alter_id" test -n "$s_vless_user_security" && \ json_add_string "security" "$s_vless_user_security" test -n "$s_vless_user_encryption" && \ json_add_string "encryption" "$s_vless_user_encryption" test -n "$s_vless_user_level" && \ json_add_int "level" "$s_vless_user_level" json_close_object json_close_array # users json_close_object json_close_array # vnext json_close_object # settings ;; "vless-reality") json_add_object "settings" json_add_array "vnext" json_add_object "" json_add_string "address" "$s_vless_reality_address" append_server_address "$s_vless_reality_address" json_add_int "port" "$s_vless_reality_port" json_add_array "users" json_add_object "" json_add_string "id" "$s_vless_reality_user_id" json_add_int "alterId" "$s_vless_reality_user_alter_id" test -n "$s_vless_reality_user_security" && \ json_add_string "security" "$s_vless_reality_user_security" test -n "$s_vless_reality_user_encryption" && \ json_add_string "encryption" "$s_vless_reality_user_encryption" test -n "$s_vless_reality_user_level" && \ json_add_int "level" "$s_vless_reality_user_level" test -n "$s_vless_reality_flow" && \ json_add_string "flow" "$s_vless_reality_flow" json_close_object json_close_array # users json_close_object json_close_array # vnext json_close_object # settings ;; "trojan") json_add_object "settings" json_add_array "servers" json_add_object "" json_add_string "address" "$s_trojan_address" append_server_address "$s_trojan_address" json_add_int "port" "$s_trojan_port" json_add_string "password" "$s_trojan_user_id" json_close_object json_close_array # trojan json_close_object # settings ;; "socks") json_add_object "settings" json_add_array "servers" json_add_object "" json_add_string "address" "$s_socks_address" append_server_address "$s_socks_address" json_add_int "port" "$s_socks_port" json_add_array "users" json_add_object "" json_add_string "pass" "$s_socks_user_id" json_add_string "user" "$s_socks_email" json_close_object json_close_array # users json_close_object json_close_array # socks json_close_object # settings ;; esac json_add_object "streamSettings" test -n "$ss_network" && \ json_add_string "network" "$ss_network" if [ "x$protocol" != "xshadowsocks" ]; then test -n "$ss_security" && \ json_add_string "security" "$ss_security" if [ "x$ss_security" = "xtls" ]; then json_add_object "tlsSettings" test -n "$ss_tls_server_name" && \ json_add_string "serverName" "$ss_tls_server_name" if [ -n "$ss_tls_alpn" ] ; then json_add_array "alpn" json_add_string "" "$ss_tls_alpn" json_close_array fi json_add_boolean "allowInsecure" "$ss_tls_allow_insecure" json_add_boolean "allowInsecureCiphers" "$ss_tls_allow_insecure_ciphers" json_add_boolean "disableSystemRoot" "$ss_tls_disable_system_root" json_add_array "certificates" if [ -n "$ss_tls_cert_file" ] ; then json_add_object "" json_add_string "certificateFile" "$ss_tls_cert_file" json_add_string "keyFile" "$ss_tls_key_file" test -n "$ss_tls_cert_usage" && \ json_add_string "usage" "$ss_tls_cert_usage" json_close_object fi json_close_array # certificates json_close_object # tlsSettings fi fi if [ "x$protocol" = "xvless-reality" ]; then json_add_string "security" "reality" json_add_object "realitySettings" json_add_string "fingerprint" "chrome" json_add_string "serverName" "" json_add_string "publicKey" "$s_vless_reality_public_key" json_add_string "spiderX" "" json_add_string "shortId" "" json_close_object fi case "${ss_network:-x}" in "tcp") json_add_object "tcpSettings" if [ -n "$ss_tcp_header_type" ] ; then json_add_object "header" json_add_string "type" "$ss_tcp_header_type" if [ "$ss_tcp_header_type" = "http" ] ; then json_add_object "request" test -n "$ss_tcp_header_request_version" && \ json_add_string "version" "$ss_tcp_header_request_version" json_add_string "method" "$ss_tcp_header_request_method" if [ -n "$ss_tcp_header_request_path" ] ; then json_add_array "path" json_add_string "" "$ss_tcp_header_request_path" json_close_array fi if [ -n "$ss_tcp_header_request_headers" ] ; then json_add_object "headers" handle_request_header() { local h="$1" local name="$(echo "$h" | cut -d'=' -f1)" local value="$(echo "$h" | cut -d'=' -f2)" if [ -n "$name" ] && [ -n "$value" ] ; then json_add_array "$name" json_add_string "" "$value" json_close_array fi } config_list_foreach "$section" "ss_tcp_header_request_headers" handle_request_header json_close_object # headers fi json_close_object # request json_add_object "response" test -n "$ss_tcp_header_response_version" && \ json_add_string "version" "$ss_tcp_header_response_version" test -n "$ss_tcp_header_response_status" && \ json_add_string "status" "$ss_tcp_header_response_status" test -n "$ss_tcp_header_response_reason" && \ json_add_string "reason" "$ss_tcp_header_response_reason" if [ -n "$ss_tcp_header_response_headers" ] ; then json_add_object "headers" handle_response_header() { local h="$1" local name="$(echo "$h" | cut -d'=' -f1)" local value="$(echo "$h" | cut -d'=' -f2)" if [ -n "$name" ] && [ -n "$value" ] ; then json_add_array "$name" json_add_string "" "$value" json_close_array fi } config_list_foreach "$section" "ss_tcp_header_response_headers" handle_response_header json_close_object # headers fi json_close_object # response fi json_close_object # header fi json_close_object # tcpSettings ;; "kcp") json_add_object "kcpSettings" test -n "$ss_kcp_mtu" && \ json_add_int "mtu" "$ss_kcp_mtu" test -n "$ss_kcp_tti" && \ json_add_int "tti" "$ss_kcp_tti" test -n "$ss_kcp_uplink_capacity" && \ json_add_int "uplinkCapacity" "$ss_kcp_uplink_capacity" test -n "$ss_kcp_downlink_capacity" && \ json_add_int "downlinkCapacity" "$ss_kcp_downlink_capacity" json_add_boolean "congestion" "$ss_kcp_congestion" test -n "$ss_kcp_read_buffer_size" && \ json_add_int "readBufferSize" "$ss_kcp_read_buffer_size" test -n "$ss_kcp_write_buffer_size" && \ json_add_int "writeBufferSize" "$ss_kcp_write_buffer_size" if [ -n "$ss_kcp_header_type" ] ; then json_add_object "header" json_add_string "type" "$ss_kcp_header_type" json_close_object fi json_close_object # kcpSettings ;; "ws") json_add_object "wsSettings" test -n "$ss_websocket_path" && \ json_add_string "path" "$ss_websocket_path" if [ -n "$ss_websocket_headers" ] ; then json_add_object "headers" handle_websocket_header() { local h="$1" local name="$(echo "$h" | cut -d'=' -f1)" local value="$(echo "$h" | cut -d'=' -f2)" if [ -n "$name" ] && [ -n "$value" ] ; then json_add_string "$name" "$value" fi } config_list_foreach "$section" "ss_websocket_headers" handle_websocket_header json_close_object # headers fi json_close_object # wsSettings ;; "http") json_add_object "httpSettings" if [ -n "$ss_http_host" ] ; then json_add_array "host" local h for h in $ss_http_host ; do json_add_string "" "$h" done json_close_array # host fi test -n "$ss_http_path" && \ json_add_string "path" "$ss_http_path" json_close_object # httpSettings ;; "domainsocket") json_add_object "dsSettings" test -n "$ss_domainsocket_path" && \ json_add_string "path" "$ss_domainsocket_path" json_close_object # dsSettings ;; "quic") json_add_object "quicSettings" test -n "$ss_quic_security" && \ json_add_string "security" "$ss_quic_security" test -n "$ss_quic_key" && \ json_add_string "key" "$ss_quic_key" if [ -n "$ss_quic_header_type" ] ; then json_add_object "header" json_add_string "type" "$ss_quic_header_type" json_close_object # header fi json_close_object # quicSettings ;; esac json_add_object "sockopt" if [ -n "$TRANSPARENT_PROXY_PORT" ] ; then json_add_int "mark" "255" else test -n "$ss_sockopt_mark" && \ json_add_int "mark" "$ss_sockopt_mark" fi test -n "$ss_sockopt_tcp_fast_open" && \ json_add_boolean "tcpFastOpen" "$ss_sockopt_tcp_fast_open" if version_over_5_4; then test -n "$ss_sockopt_mptcp" && \ json_add_boolean "tcpMptcp" "$ss_sockopt_mptcp" fi json_close_object # sockopt json_close_object # streamSettings test -n "$tag" && \ json_add_string "tag" "$tag" if [ -n "$proxy_settings_tag" ] ; then json_add_object "proxySettings" json_add_string "tag" "$proxy_settings_tag" json_close_object # proxySettings fi if [ "x$mux_enabled" = "x1" ] ; then json_add_object "mux" json_add_boolean "enabled" "1" json_add_int "concurrency" "$mux_concurrency" json_close_object # mux fi json_close_object } init_transparent_proxy() { local tp_cfg="main_transparent_proxy" local redirect_port use_tproxy redirect_udp redirect_dns config_get redirect_port "$tp_cfg" "redirect_port" config_get_bool use_tproxy "$tp_cfg" "use_tproxy" "0" config_get_bool redirect_udp "$tp_cfg" "redirect_udp" "0" config_get_bool redirect_dns "$tp_cfg" "redirect_dns" "0" if [ -n "$redirect_port" ] && \ ! validate_data "port" "$redirect_port" 2>/dev/null ; then _err "Transparent proxy redirect port is invalid: $redirect_port" return 1 fi TRANSPARENT_PROXY_PORT="$redirect_port" TRANSPARENT_PROXY_USE_TPROXY="$use_tproxy" if [ "x$redirect_udp" = "x1" ] && [ "$(uci -q get xray.omrout.protocol)" != "socks" ]; then TRANSPARENT_PROXY_ADDITION="udp" elif [ "x$redirect_dns" = "x1" ] ; then TRANSPARENT_PROXY_ADDITION="dns" else TRANSPARENT_PROXY_ADDITION= fi } setup_transparent_proxy() { if [ -z "$TRANSPARENT_PROXY_PORT" ] ; then #_info "Transparent proxy disabled." return 0 fi if [ "x$TRANSPARENT_PROXY_EXPECTED" != "x1" ] ; then #_info "No xray instance enabled, skip transparent proxy." return 0 fi _info "Setting transparent proxy on port: $TRANSPARENT_PROXY_PORT" local tp_cfg="main_transparent_proxy" local lan_ifaces only_privileged_ports proxy_mode direct_list_dns proxy_list_dns config_get lan_ifaces "$tp_cfg" "lan_ifaces" config_get_bool only_privileged_ports "$tp_cfg" "only_privileged_ports" "0" config_get proxy_mode "$tp_cfg" "proxy_mode" config_get direct_list_dns "$tp_cfg" "direct_list_dns" config_get proxy_list_dns "$tp_cfg" "proxy_list_dns" _info "Transparent proxy mode: $proxy_mode" #init_rules_for_listfile "$direct_list_dns" "$proxy_list_dns" local ext_args case "${proxy_mode:-default}" in "cn_direct") local chnroute="$(get_file_content "chnroute")" local chnroute6="$(get_file_content "chnroute6")" if [ -n "$chnroute" ] ; then ipset -! restore <<-EOF 2>/dev/null $(echo "$chnroute" | sed "s/.*/add $IPSET_DST_DIRECT_V4 & timeout 0/") EOF fi if [ -n "$chnroute6" ] ; then ipset -! restore <<-EOF 2>/dev/null $(echo "$chnroute6" | sed "s/.*/add $IPSET_DST_DIRECT_V6 & timeout 0/") EOF fi ext_args= ;; "cn_proxy") local chnroute="$(get_file_content "chnroute")" local chnroute6="$(get_file_content "chnroute6")" if [ -n "$chnroute" ] ; then ipset -! restore <<-EOF 2>/dev/null $(echo "$chnroute" | sed "s/.*/add $IPSET_DST_PROXY_V4 & timeout 0/") EOF fi if [ -n "$chnroute6" ] ; then ipset -! restore <<-EOF 2>/dev/null $(echo "$chnroute6" | sed "s/.*/add $IPSET_DST_PROXY_V6 & timeout 0/") EOF fi ext_args="-m set --match-set $IPSET_DST_PROXY_V4 dst" ;; "gfwlist_proxy") local gfwlist="$(get_file_content "gfwlist")" if [ -n "$gfwlist" ] ; then if [ -n "$proxy_list_dns" ] ; then echo "$gfwlist" | \ sed "s|.*|server=/&/$proxy_list_dns\nipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ >> "$FILE_XRAY_DNSMASQ_CACHE" else echo "$gfwlist" | \ sed "s|.*|ipset=/&/$IPSET_DST_PROXY_V4,$IPSET_DST_PROXY_V6|" \ >> "$FILE_XRAY_DNSMASQ_CACHE" fi fi ext_args="-m set --match-set $IPSET_DST_PROXY_V4 dst" ;; *) ext_args= ;; esac if [ "x$only_privileged_ports" = "x1" ] ; then ext_args="--dport 0:1023 $ext_args" fi if [ -f $FILE_XRAY_DNSMASQ_CACHE ] && [ -n "$(cat "$FILE_XRAY_DNSMASQ_CACHE" | grep -v "^$" | grep -v "^#")" ] ; then local dir="$(dirname "$FILE_XRAY_DNSMASQ")" test -d "$dir" || mkdir -p "$dir" cat "$FILE_XRAY_DNSMASQ_CACHE" >"$FILE_XRAY_DNSMASQ" 2>/dev/null DNSMASQ_RESTART_EXPECTED=1 fi rm -f "$FILE_XRAY_DNSMASQ_CACHE" local lan_devices lan_ipaddrs if [ -n "$lan_ifaces" ] ; then . /lib/functions/network.sh local lan for lan in $lan_ifaces ; do local device ipaddrs ipaddr network_get_device device "$lan" network_get_ipaddrs ipaddrs "$lan" if [ -n "$device" ] ; then if [ -n "$lan_devices" ] ; then lan_devices="$lan_devices $device" else lan_devices="$device" fi fi if [ -n "$ipaddrs" ] ; then for ipaddr in $ipaddrs ; do if [ -n "$lan_ipaddrs" ] ; then lan_ipaddrs="$ipaddr" else lan_ipaddrs="$lan_ipaddrs $ipaddr" fi done fi done fi logger -t "xray" "add rules" add_xray_redirect_rules "$ext_args" "$lan_devices" "$lan_ipaddrs" } clear_transparent_proxy() { if [ -f "${xrrules_nft}" ] || [ -f "${xrrules_nft}.down" ]; then rm -f "$xrrules_nft" rm -f "$xrrules_nft.down" fw4 -q restart fi if [ -s "$FILE_XRAY_DNSMASQ" ] ; then rm -f "$FILE_XRAY_DNSMASQ" DNSMASQ_RESTART_EXPECTED=1 fi } start_instance() { local section="$1" if ! xray_section_validate "$section" ; then _err "Invalid config." return 1 fi if [ "x$enabled" != "x1" ] ; then #_info "Service disabled: $section" return 0 fi if [ -z "$xray_file" ] || [ ! -s "$xray_file" ] ; then _err "Invalid xRay file." return 1 fi test -x "$xray_file" || chmod 755 "$xray_file" local temp_config if [ -n "$config_file" ] ; then if [ ! -s "$config_file" ] ; then _err "Config file not found: $config_file" return 1 fi if ! ( eval "$xray_file test -config \"$config_file\" >/dev/null 2>&1" ) ; then _err "Validate config file failed: $config_file" return 1 fi local file_content="$(cat "$config_file")" local config_commands="$(get_commands_from_json "$file_content")" local addr for addr in $(echo "$config_commands" | sed -n "s/^json.*'address'[[:space:]]'\([^']*\)'.*/\1/p") ; do append_server_address "$addr" done temp_config="$config_file" else test -d "$CONFIG_FOLDER" || mkdir -p "$CONFIG_FOLDER" temp_config="$CONFIG_FOLDER/xray.${section}.json" local old_ns json_set_namespace "$section" old_ns json_init json_add_object "log" test -n "$access_log" && \ json_add_string "access" "$access_log" if [ -n "$loglevel" ] && [ "$loglevel" != "none" ] ; then json_add_string "loglevel" "$loglevel" json_add_string "error" "$error_log" fi json_close_object # log if [ "x$stats_enabled" = "x1" ] ; then json_add_object "stats" json_close_object # stats fi add_dns_settings "$section" add_routing_settings "$section" add_policy_settings "$section" add_reverse_settings "$section" if [ "x$transport_enabled" = "x1" ] ; then add_transport_settings fi if [ -n "$inbounds" ] ; then json_add_array "inbounds" local is for is in $inbounds ; do add_inbound_setting "$is" done json_close_array # inbounds fi if [ -n "$outbounds" ] ; then json_add_array "outbounds" local os for os in $outbounds ; do add_outbound_setting "$os" done json_close_array # outbounds fi json_dump -i >"$temp_config" json_cleanup json_set_namespace "$old_ns" if [ ! -s "$temp_config" ] ; then _err "Error when create config file: $temp_config" return 1 fi fi PROG="$NAME.$section" TRANSPARENT_PROXY_EXPECTED=1 procd_open_instance "$PROG" procd_set_param command "$xray_file" procd_append_param command run procd_append_param command -config "$temp_config" procd_set_param respawn if [ -n "$asset_location" ] && [ -d "$asset_location" ] ; then procd_set_param env XRAY_LOCATION_ASSET="$asset_location" fi # cat /proc/PID/limits to see if limits works procd_set_param limits nofile="102400 102400" procd_append_param limits core="0 0" if [ "$mem_percentage" -gt "0" ] ; then local mem_total="$(awk '/MemTotal/ {print $2}' /proc/meminfo)" if [ -n "$mem_total" ] ; then local use_mem="$(expr $mem_total \* $mem_percentage \* 10)" procd_append_param limits as="$use_mem $use_mem" fi fi procd_set_param file "$temp_config" procd_set_param stderr 1 # forward stderr of the command to logd procd_set_param stdout 1 procd_set_param pidfile "/var/run/${NAME}.${section}.pid" procd_close_instance } rules_exist() { #[ -n "$(iptables -w -t nat -L -n 2>/dev/null | grep xr_)" ] && return 0 if [ -f "$xrrules_nft" ]; then return 0 else return 1 fi } rules_up() { if [ -f "${xrrules_nft}.down" ]; then mv -f "${xrrules_nft}.down" "$xrrules_nft" fw4 -q restart fi } rules_down() { if [ -f "${xrrules_nft}" ]; then mv -f "$xrrules_nft" "${xrrules_nft}.down" fw4 -q restart fi } start_service() { [ -n "$(uci -q get firewall.xray)" ] && { uci -q del firewall.xray uci -q commit firewall } clear_transparent_proxy config_load "$NAME" if ! init_transparent_proxy ; then gracefully_restart_dnsmasq return 1 fi config_foreach start_instance "xray" setup_transparent_proxy gracefully_restart_dnsmasq rules_up unset OUTBOUND_SERVERS_V4 \ OUTBOUND_SERVERS_V6 \ TRANSPARENT_PROXY_EXPECTED \ TRANSPARENT_PROXY_PORT \ TRANSPARENT_PROXY_ADDITION \ DNSMASQ_RESTART_EXPECTED } stop_service() { if [ "x$action" = "xrestart" ] ; then # skip when restarting, start_service will do this return 0 fi clear_transparent_proxy rules_down gracefully_restart_dnsmasq test -d "$CONFIG_FOLDER" && rm -rf "$CONFIG_FOLDER" } restart_service() { stop start } reload_service() { stop start } service_triggers() { procd_add_reload_trigger "$NAME" }