diff --git a/xray-core/Makefile b/xray-core/Makefile new file mode 100644 index 000000000..4caa7ef87 --- /dev/null +++ b/xray-core/Makefile @@ -0,0 +1,68 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=xray-core +PKG_VERSION:=1.8.5 +PKG_RELEASE:=1 + +PKG_LICENSE:=MPLv2 +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Yannick Chabanois +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/XTLS/Xray-core.git +PKG_SOURCE_VERSION:=585d5ba7c8b64f6da60837546a70bbcfd2350c64 + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 + +GO_PKG:=github.com/XTLS/Xray-core + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/openmptcprouter/golang/golang-package.mk + +define Package/$(PKG_NAME) + SECTION:=Custom + CATEGORY:=Extra packages + TITLE:=Xray-core + DEPENDS:=$(GO_ARCH_DEPENDS) + PROVIDES:=xray-core +endef + +define Package/$(PKG_NAME)/description + Xray-core bare bones binary (compiled without cgo) +endef + +define Package/$(PKG_NAME)/config +menu "Xray Configuration" + depends on PACKAGE_$(PKG_NAME) + +config PACKAGE_XRAY_ENABLE_GOPROXY_IO + bool "Use goproxy.io to speed up module fetching (recommended for some network situations)" + default n + +endmenu +endef + +USE_GOPROXY:= +ifdef CONFIG_PACKAGE_XRAY_ENABLE_GOPROXY_IO + USE_GOPROXY:=GOPROXY=https://goproxy.io,direct +endif + +MAKE_PATH:=$(GO_PKG_WORK_DIR_NAME)/build/src/$(GO_PKG) +MAKE_VARS += $(GO_PKG_VARS) + +#define Build/Patch +# $(CP) $(PKG_BUILD_DIR)/../Xray-core-$(PKG_VERSION)/* $(PKG_BUILD_DIR) +# $(Build/Patch/Default) +#endef + +define Build/Compile + cd $(PKG_BUILD_DIR); $(GO_PKG_VARS) $(USE_GOPROXY) CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o $(PKG_INSTALL_DIR)/bin/xray ./main; +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/bin/xray $(1)/usr/bin/xray + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/xray-core/files/etc/firewall.xray-rules b/xray-core/files/etc/firewall.xray-rules new file mode 100644 index 000000000..2aac68055 --- /dev/null +++ b/xray-core/files/etc/firewall.xray-rules @@ -0,0 +1,2 @@ +#!/bin/sh +/etc/init.d/xray rules_up diff --git a/xray-core/files/etc/init.d/xray b/xray-core/files/etc/init.d/xray new file mode 100755 index 000000000..f3c0566c3 --- /dev/null +++ b/xray-core/files/etc/init.d/xray @@ -0,0 +1,2282 @@ +#!/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 + +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_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" + done + + local ipv6 + for ipv6 in $(resolveip -6 -t 5 "$addr") ; do + append_server_ipv6 "$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_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" ] || [ "$(uci -q get xray.omrout.protocol)" = "vmess" ]) && commandline="$commandline -L ${port+1}" + xray-rules6 $commandline + } + [ -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 "mptcp" "$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" + json_add_string "protocol" "$protocol" + + 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 + ;; + "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 + + 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() { + xray-rules -f + + 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 V2Ray 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 v2r_)" ] && return 0 + return 1 +} + +rules_up() { + rules_exist && { + [ -f /bin/blocklanfw ] && /bin/blocklanfw 2>&1 >/dev/null + return 0 + } + enabled="0" + config_load xray + config_get enabled main enabled "0" + [ "$enabled" = "0" ] && return + if [ "$(uci -q get xray.omrout.protocol)" = "vless" ]; then + OUTBOUND_SERVERS_V4="$(uci -q get xray.omrout.s_vless_address)" + OUTBOUND_SERVERS_V6="$(uci -q get xray.omrout.s_vless_address)" + elif [ "$(uci -q get xray.omrout.protocol)" = "vmess" ]; then + OUTBOUND_SERVERS_V4="$(uci -q get xray.omrout.s_vmess_address)" + OUTBOUND_SERVERS_V6="$(uci -q get xray.omrout.s_vmess_address)" + elif [ "$(uci -q get xray.omrout.protocol)" = "trojan" ]; then + OUTBOUND_SERVERS_V4="$(uci -q get xray.omrout.s_trojan_address)" + OUTBOUND_SERVERS_V6="$(uci -q get xray.omrout.s_trojan_address)" + elif [ "$(uci -q get xray.omrout.protocol)" = "socks" ]; then + OUTBOUND_SERVERS_V4="$(uci -q get xray.omrout.s_socks_address)" + OUTBOUND_SERVERS_V6="$(uci -q get xray.omrout.s_socks_address)" + fi + TRANSPARENT_PROXY_PORT="$(uci -q get xray.omr.port)" + [ -n "$OUTBOUND_SERVERS_V4" ] || [ -n "$OUTBOUND_SERVERS_V6" ] && { + logger -t "xray" "Rules UP" + add_xray_redirect_rules + } + [ -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 + } + [ -f /bin/blocklanfw ] && /bin/blocklanfw 2>&1 >/dev/null +} + +rules_down() { + rules_exist || return 0 + logger -t "xray" "Rules DOWN" + local bin="/usr/bin/xray-rules" + [ -x "$bin" ] && { + "$bin" -f >/dev/null 2>&1 + } + local bin6="/usr/bin/xray-rules6" + [ -x "$bin6" ] && { + "$bin6" -f >/dev/null 2>&1 + } +} + +start_service() { + 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" +} + +service_triggers() { + procd_add_reload_trigger "$NAME" +} diff --git a/xray-core/files/etc/uci-defaults/3010-omr-xray b/xray-core/files/etc/uci-defaults/3010-omr-xray new file mode 100644 index 000000000..7897ae2cf --- /dev/null +++ b/xray-core/files/etc/uci-defaults/3010-omr-xray @@ -0,0 +1,202 @@ +#!/bin/sh + +if [ -z "$(uci -q get xray.main)" ]; then + touch /etc/config/xray + uci batch <<-EOF + set xray.main=xray + set xray.main.xray_file='/usr/bin/xray' + set xray.main.mem_percentage='0' + set xray.main.loglevel='error' + set xray.main.access_log='/dev/null' + set xray.main.error_log='/dev/null' + set xray.main.enabled='0' + set xray.main.outbounds='omrout' + set xray.main.inbounds='omr' + add_list xray.main.inbounds='omrtest' + set xray.main_dns=dns + set xray.main_dns.hosts='example.com|127.0.0.1' + set xray.main_dns.enabled='0' + set xray.main_policy=policy + set xray.main_policy.enabled='1' + set xray.main_policy.levels='policy_level_0' + set xray.policy_level_0=policy_level + set xray.policy_level_0.level='0' + set xray.policy_level_0.handshake='4' + set xray.policy_level_0.conn_idle='1200' + set xray.policy_level_0.uplink_only='0' + set xray.policy_level_0.downlink_only='0' + set xray.policy_level_0.buffer_size='512' + set xray.main_transparent_proxy=transparent_proxy + set xray.main_transparent_proxy.proxy_mode='default' + set xray.main_transparent_proxy.apnic_delegated_mirror='apnic' + set xray.main_transparent_proxy.gfwlist_mirror='github' + set xray.main_transparent_proxy.redirect_udp='0' + set xray.main_transparent_proxy.redirect_port='1897' + set xray.omrout=outbound + set xray.omrout.tag='omrout_tunnel' + set xray.omrout.protocol='vless' + set xray.omrout.s_vmess_address='' + set xray.omrout.s_vmess_port='65230' + set xray.omrout.s_vmess_user_id='' + set xray.omrout.s_vmess_user_security='none' + set xray.omrout.s_vmess_user_alter_id='0' + set xray.omrout.s_vless_address='' + set xray.omrout.s_vless_port='65228' + set xray.omrout.s_vless_user_id='' + set xray.omrout.s_vless_user_security='none' + set xray.omrout.s_vless_user_encryption='none' + set xray.omrout.s_vless_user_alter_id='0' + set xray.omrout.s_trojan_address='' + set xray.omrout.s_trojan_port='65229' + set xray.omrout.s_trojan_user_id='' + set xray.omrout.s_trojan_user_security='none' + set xray.omrout.s_trojan_user_encryption='none' + set xray.omrout.s_trojan_user_alter_id='0' + set xray.omrout.s_socks_address='' + set xray.omrout.s_socks_port='65231' + set xray.omrout.s_socks_user_id='' + set xray.omrout.s_socks_user_security='none' + set xray.omrout.s_socks_user_encryption='none' + set xray.omrout.s_socks_user_alter_id='0' + set xray.omrout.ss_network='tcp' + set xray.omrout.ss_security='tls' + set xray.omrout.ss_tls_allow_insecure='1' + set xray.omrout.ss_tls_disable_system_root='1' + set xray.omrout.ss_tls_cert_usage='verify' + set xray.omrout.ss_tls_cert_file='/etc/luci-uploads/client.crt' + set xray.omrout.ss_tls_key_file='/etc/luci-uploads/client.key' + set xray.omrout.s_shadowsocks_port='65252' + set xray.omrout.mux_concurrency='8' + set xray.omr=inbound + set xray.omr.tag='omrtunnel' + set xray.omr.listen='0.0.0.0' + set xray.omr.port='1897' + set xray.omr.protocol='dokodemo-door' + set xray.omr.s_dokodemo_door_network='tcp' + add_list xray.omr.s_dokodemo_door_network='udp' + set xray.omr.ss_sockopt_tproxy='redirect' + set xray.omr.ss_sockopt_tcp_fast_open='1' + set xray.omr.ss_sockopt_mptcp='1' + set xray.omr.s_dokodemo_door_follow_redirect='1' + set xray.omr6=inbound + set xray.omr6.tag='omrtunnel6' + set xray.omr6.listen='::' + set xray.omr6.port='1898' + set xray.omr6.protocol='dokodemo-door' + set xray.omr6.s_dokodemo_door_network='tcp' + add_list xray.omr6.s_dokodemo_door_network='udp' + set xray.omr6.ss_sockopt_tproxy='tproxy' + set xray.omr6.ss_sockopt_tcp_fast_open='1' + set xray.omr6.s_dokodemo_door_follow_redirect='1' + set xray.omrtest=inbound + set xray.omrtest.port='1111' + set xray.omrtest.protocol='socks' + set xray.omrtest.listen='127.0.0.1' + set xray.omrtest.s_socks_auth='noauth' + set xray.omrtest.s_socks_udp='1' + set xray.omrtest.s_socks_ip='127.0.0.1' + set xray.omrtest.s_socks_userlevel='0' + commit xray + EOF +fi +uci -q batch <<-EOF >/dev/null + set xray.omr.listen='0.0.0.0' + commit xray +EOF + +if [ "$(uci -q get firewall.xray)" = "" ]; then + uci -q batch <<-EOF >/dev/null + set firewall.xray=include + set firewall.xray.path=/etc/firewall.xray-rules + set firewall.xray.reload=0 + commit firewall + EOF +fi +if [ "$(uci -q get firewall.xray.path)" != "/etc/firewall.xray-rules" ]; then + uci -q batch <<-EOF >/dev/null + set firewall.xray.path=/etc/firewall.xray-rules + commit firewall + EOF +fi + +if [ "$(uci -q get xray.main_reverse.bridges | grep omrbridge)" = "" ]; then + uci -q batch <<-EOF >/dev/null + set xray.main_reverse=reverse + set xray.main_reverse.enabled=1 + set xray.main_reverse.bridges='omrbridge|omr.lan' + commit xray + EOF +fi +if [ "$(uci -q get xray.omrrouting)" = "" ]; then + uci -q batch <<-EOF >/dev/null + set xray.omrexit=outbound + set xray.omrexit.protocol='freedom' + set xray.omrexit.tag='out' + add_list xray.main.outbounds=omrexit + set xray.omrrouting=routing_rule + set xray.omrrouting.type='field' + set xray.omrrouting.inbound_tag='omrbridge' + set xray.omrrouting.outbound_tag='omrout_tunnel' + set xray.omrrouting.domain='full:omr.lan' + set xray.omrroutingo=routing_rule + set xray.omrroutingo.type='field' + set xray.omrroutingo.inbound_tag='omrbridge' + set xray.omrroutingo.outbound_tag='out' + set xray.main_routing=routing + set xray.main_routing.enabled=1 + set xray.main_routing.rules='omrrouting' + add_list xray.main_routing.rules='omrroutingo' + commit xray + EOF +fi + +if [ "$(uci -q get xray.main.error_log)" != "/dev/null" ]; then + uci -q batch <<-EOF >/dev/null + set xray.main.error_log='/dev/null' + commit xray + EOF +fi +#if [ "$(uci -q get xray.main.mem_percentage)" = "0" ]; then +# uci -q batch <<-EOF >/dev/null +# set xray.main.mem_percentage='80' +# commit xray +# EOF +#fi +if [ "$(uci -q get xray.policy_level_0.conn_idle)" = "2400" ]; then + uci -q batch <<-EOF >/dev/null + set xray.policy_level_0.conn_idle='1200' + commit xray + EOF +fi + +if [ "$(uci -q get xray.omrout.s_vmess_port)" = "65228" ]; then + uci -q batch <<-EOF >/dev/null + set xray.omrout.s_vmess_port='65230' + commit xray + EOF +fi + +if [ "$(uci -q get xray.omrout.s_trojan_port)" = "" ]; then + uci -q batch <<-EOF >/dev/null + set xray.omrout.s_trojan_address='' + set xray.omrout.s_trojan_port='65229' + set xray.omrout.s_trojan_user_id='' + set xray.omrout.s_trojan_user_security='none' + set xray.omrout.s_trojan_user_encryption='none' + set xray.omrout.s_trojan_user_alter_id='0' + commit xray + EOF +fi +if [ "$(uci -q get xray.omrout.s_socks_port)" = "" ]; then + uci -q batch <<-EOF >/dev/null + set xray.omrout.s_socks_address='' + set xray.omrout.s_socks_port='65231' + set xray.omrout.s_socks_user_id='' + set xray.omrout.s_socks_user_security='none' + set xray.omrout.s_socks_user_encryption='none' + set xray.omrout.s_socks_user_alter_id='0' + commit xray + EOF +fi + +exit 0 \ No newline at end of file diff --git a/xray-core/files/usr/bin/xray-rules b/xray-core/files/usr/bin/xray-rules new file mode 100755 index 000000000..d96ebc73b --- /dev/null +++ b/xray-core/files/usr/bin/xray-rules @@ -0,0 +1,319 @@ +#!/bin/sh -e +# +# Copyright (C) 2017 Yousong Zhou +# Copyright (C) 2018-2021 Ycarus (Yannick Chabanois) for OpenMPTCProuter +# +# The design idea was derived from ss-rules by Jian Chang +# +# This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# + +if [ -f /usr/sbin/iptables-legacy ]; then + IPTABLES="/usr/sbin/iptables-legacy" + IPTABLESRESTORE="/usr/sbin/iptables-legacy-restore" + IPTABLESSAVE="/usr/sbin/iptables-legacy-save" +else + IPTABLES="/usr/sbin/iptables" + IPTABLESRESTORE="/usr/sbin/iptables-restore" + IPTABLESSAVE="/usr/sbin/iptables-save" +fi + + + +xr_rules_usage() { + cat >&2 < Local port number of ss-redir with TCP mode + -L Local port number of ss-redir with UDP mode + -s List of ip addresses of remote shadowsocks server + --ifnames Only apply rules on packets from these ifnames + --src-bypass + --src-forward + --src-checkdst + --src-default + Packets will have their src ip checked in order against + bypass, forward, checkdst list and will bypass, forward + through, or continue to have their dst ip checked + respectively on the first match. Otherwise, --src-default + decide the default action + --dst-bypass + --dst-forward + --dst-bypass-file + --dst-forward-file + --dst-default + Same as with their --src-xx equivalent + --dst-forward-recentrst + Forward those packets whose destinations have recently + sent to us multiple tcp-rst packets + --local-default + Default action for local out TCP traffic + +The following ipsets will be created by ss-rules. They are also intended to be +populated by other programs like dnsmasq with ipset support + + ss_rules_src_bypass + ss_rules_src_forward + ss_rules_src_checkdst + ss_rules_dst_bypass + ss_rules_dst_bypass_all + ss_rules_dst_forward +EOF +} + +o_dst_bypass_=" + 0.0.0.0/8 + 10.0.0.0/8 + 100.64.0.0/10 + 127.0.0.0/8 + 169.254.0.0/16 + 172.16.0.0/12 + 192.0.0.0/24 + 192.0.2.0/24 + 192.31.196.0/24 + 192.52.193.0/24 + 192.88.99.0/24 + 192.168.0.0/16 + 192.175.48.0/24 + 198.18.0.0/15 + 198.51.100.0/24 + 203.0.113.0/24 + 224.0.0.0/4 + 240.0.0.0/4 + 255.255.255.255 +" +o_src_default=bypass +o_dst_default=bypass +o_local_default=bypass + +__errmsg() { + echo "xray-rules: $*" >&2 +} + +xr_rules_parse_args() { + while [ "$#" -gt 0 ]; do + case "$1" in + -h|--help) xr_rules_usage; exit 0;; + -f|--flush) xr_rules_flush; exit 0;; + -l) o_redir_tcp_port="$2"; shift 2;; + -L) o_redir_udp_port="$2"; shift 2;; + -s) o_remote_servers="$2"; shift 2;; + --ifnames) o_ifnames="$2"; shift 2;; + --ipt-extra) o_ipt_extra="$2"; shift 2;; + --src-default) o_src_default="$2"; shift 2;; + --dst-default) o_dst_default="$2"; shift 2;; + --local-default) o_local_default="$2"; shift 2;; + --src-bypass) o_src_bypass="$2"; shift 2;; + --src-forward) o_src_forward="$2"; shift 2;; + --src-checkdst) o_src_checkdst="$2"; shift 2;; + --dst-bypass) o_dst_bypass="$2"; shift 2;; + --dst-bypass_all) o_dst_bypass_all="$2"; shift 2;; + --dst-forward) o_dst_forward="$2"; shift 2;; + --dst-forward-recentrst) o_dst_forward_recentrst=1; shift 1;; + --dst-bypass-file) o_dst_bypass_file="$2"; shift 2;; + --dst-forward-file) o_dst_forward_file="$2"; shift 2;; + --rule-name) rule="$2"; shift 2;; + *) __errmsg "unknown option $1"; return 1;; + esac + done + + if [ -z "$o_redir_tcp_port" -a -z "$o_redir_udp_port" ]; then + __errmsg "Requires at least -l or -L option" + return 1 + fi + if [ -n "$o_dst_forward_recentrst" ] && ! $IPTABLES -w -m recent -h >/dev/null; then + __errmsg "Please install iptables-mod-conntrack-extra with opkg" + return 1 + fi + o_remote_servers="$(for s in $o_remote_servers; do resolveip -4 "$s"; done)" +} + +xr_rules_flush() { + local setname + + $IPTABLESSAVE --counters 2>/dev/null | grep -v xr_ | $IPTABLESRESTORE --counters + while ip rule del fwmark 1 lookup 100 2>/dev/null; do true; done + ip route flush table 100 || true + for setname in $(ipset -n list | grep "ssr_${rule}"); do + ipset destroy "$setname" 2>/dev/null || true + done +} + +xr_rules_ipset_init() { + ipset --exist restore <<-EOF + create ssr_${rule}_src_bypass hash:net hashsize 64 + create ssr_${rule}_src_forward hash:net hashsize 64 + create ssr_${rule}_src_checkdst hash:net hashsize 64 + create ss_rules_dst_bypass_all hash:net hashsize 64 + create ssr_${rule}_dst_bypass hash:net hashsize 64 + create ssr_${rule}_dst_bypass_ hash:net hashsize 64 + create ssr_${rule}_dst_forward hash:net hashsize 64 + create ss_rules_dst_forward_recentrst_ hash:ip hashsize 64 timeout 3600 + $(xr_rules_ipset_mkadd ssr_${rule}_dst_bypass_ "$o_dst_bypass_ $o_remote_servers") + $(xr_rules_ipset_mkadd ss_rules_dst_bypass_all "$o_dst_bypass_all") + $(xr_rules_ipset_mkadd ssr_${rule}_dst_bypass "$o_dst_bypass $(cat "$o_dst_bypass_file" 2>/dev/null | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')") + $(xr_rules_ipset_mkadd ssr_${rule}_src_bypass "$o_src_bypass") + $(xr_rules_ipset_mkadd ssr_${rule}_src_forward "$o_src_forward") + $(xr_rules_ipset_mkadd ssr_${rule}_src_checkdst "$o_src_checkdst") + $(xr_rules_ipset_mkadd ssr_${rule}_dst_forward "$o_dst_forward $(cat "$o_dst_forward_file" 2>/dev/null | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')") + EOF +} + +xr_rules_ipset_mkadd() { + local setname="$1"; shift + local i + + for i in $*; do + echo "add $setname $i" + done +} + +xr_rules_iptchains_init() { + xr_rules_iptchains_init_mark + xr_rules_iptchains_init_tcp + xr_rules_iptchains_init_udp +} + +xr_rules_iptchains_init_mark() { + if [ "$($IPTABLES -w -t mangle -L PREROUTING | grep ss_rules_dst_bypass_all)" = "" ]; then + $IPTABLESRESTORE --noflush <<-EOF + *mangle + -A PREROUTING -m set --match-set ss_rules_dst_bypass_all dst -j MARK --set-mark 0x539 + COMMIT + EOF + fi +} + +xr_rules_iptchains_init_tcp() { + local local_target + + [ -n "$o_redir_tcp_port" ] || return 0 + + xr_rules_iptchains_init_ nat tcp + + case "$o_local_default" in + checkdst) local_target=xr_${rule}_dst ;; + forward) local_target=xr_${rule}_forward ;; + bypass|*) return 0;; + esac + + $IPTABLESRESTORE --noflush <<-EOF + *nat + :xr_${rule}_local_out - + -I OUTPUT 1 -p tcp -j xr_${rule}_local_out + -A xr_${rule}_local_out -m set --match-set ssr_${rule}_dst_bypass dst -j RETURN + -A xr_${rule}_local_out -m set --match-set ss_rules_dst_bypass_all dst -j RETURN + -A xr_${rule}_local_out -m set --match-set ssr_${rule}_dst_bypass_ dst -j RETURN + -A xr_${rule}_local_out -m mark --mark 0x539 -j RETURN + -A xr_${rule}_local_out -p tcp $o_ipt_extra -j $local_target -m comment --comment "local_default: $o_local_default" + COMMIT + EOF +} + +xr_rules_iptchains_init_udp() { + [ -n "$o_redir_udp_port" ] || return 0 + xr_rules_iptchains_init_ mangle udp +} + +xr_rules_iptchains_init_() { + local table="$1" + local proto="$2" + local forward_rules + local src_default_target dst_default_target + local recentrst_mangle_rules recentrst_addset_rules + + case "$proto" in + tcp) + forward_rules="-A xr_${rule}_forward -p tcp -j REDIRECT --to-ports $o_redir_tcp_port" + if [ -n "$o_dst_forward_recentrst" ]; then + recentrst_mangle_rules=" + *mangle + -I PREROUTING 1 -p tcp -m tcp --tcp-flags RST RST -m recent --name xr_recentrst --set --rsource + COMMIT + " + recentrst_addset_rules=" + -A xr_${rule}_dst -m recent --name xr_recentrst --rcheck --rdest --seconds 3 --hitcount 3 -j SET --add-set ss_rules_dst_forward_recentrst_ dst --exist + -A xr_${rule}_dst -m set --match-set ss_rules_dst_forward_recentrst_ dst -j xr_${rule}_forward + " + fi + ;; + udp) + ip rule add fwmark 1 lookup 100 || true + ip route add local default dev lo table 100 || true + forward_rules="-A xr_${rule}_forward -p udp -j TPROXY --on-port "$o_redir_udp_port" --tproxy-mark 0x01/0x01" + ;; + esac + case "$o_src_default" in + forward) src_default_target=xr_${rule}_forward ;; + checkdst) src_default_target=xr_${rule}_dst ;; + bypass|*) src_default_target=RETURN ;; + esac + case "$o_dst_default" in + forward) dst_default_target=xr_${rule}_forward ;; + bypass|*) dst_default_target=RETURN ;; + esac + sed -e '/^\s*$/d' -e 's/^\s\+//' <<-EOF | $IPTABLESRESTORE --noflush + *$table + :xr_${rule}_pre_src - + :xr_${rule}_src - + :xr_${rule}_dst - + :xr_${rule}_forward - + $(xr_rules_iptchains_mkprerules "$proto") + -A xr_${rule}_pre_src -m set --match-set ssr_${rule}_dst_bypass_ dst -j RETURN + -A xr_${rule}_pre_src -m set --match-set ss_rules_dst_bypass_all dst -j MARK --set-mark 0x539 + -A xr_${rule}_pre_src -m set --match-set ss_rules_dst_bypass_all dst -j RETURN + -A xr_${rule}_pre_src -m set --match-set ssr_${rule}_dst_bypass dst -j RETURN + -A xr_${rule}_pre_src -m mark --mark 0x539 -j RETURN + -A xr_${rule}_dst -m set --match-set ss_rules_dst_bypass_all dst -j RETURN + -A xr_${rule}_dst -m set --match-set ssr_${rule}_dst_bypass dst -j RETURN + -A xr_${rule}_pre_src -p $proto $o_ipt_extra -j xr_${rule}_src + -A xr_${rule}_src -m set --match-set ssr_${rule}_src_bypass src -j RETURN + -A xr_${rule}_src -m set --match-set ssr_${rule}_src_forward src -j xr_${rule}_forward + -A xr_${rule}_src -m set --match-set ssr_${rule}_src_checkdst src -j xr_${rule}_dst + -A xr_${rule}_src -j $src_default_target -m comment --comment "src_default: $o_src_default" + -A xr_${rule}_dst -m set --match-set ssr_${rule}_dst_forward dst -j xr_${rule}_forward + $recentrst_addset_rules + -A xr_${rule}_dst -j $dst_default_target -m comment --comment "dst_default: $o_dst_default" + $forward_rules + COMMIT + $recentrst_mangle_rules + EOF +} + +xr_rules_iptchains_mkprerules() { + local proto="$1" + + if [ -z "$o_ifnames" ]; then + echo "-A PREROUTING -p $proto -j xr_${rule}_pre_src" + else + echo $o_ifnames \ + | tr ' ' '\n' \ + | sed "s/.*/-I PREROUTING 1 -i \\0 -p $proto -j xr_${rule}_pre_src/" + fi +} + +xr_rules_fw_drop() { + fw3 -4 print 2>/dev/null | awk '/iptables/&&/zone_lan_forward/&&/tcp/&&/-t filter/&&/-j reject/ {for(i=6; i<=NF; i++) { printf "%s ",$i } print "\n" }' | + while IFS=$"\n" read -r c; do + fwrule=$(echo "$c" | sed 's/reject/REDIRECT --to-ports 65535/') + if [ -n "$fwrule" ] && [ -z "$($IPTABLESSAVE 2>/dev/null | grep zone_lan_prerouting | grep '${fwrule}')" ]; then + eval "$IPTABLES -w -t nat -A zone_lan_prerouting ${fwrule} 2>&1 >/dev/null" + fi + done + fw3 -4 print 2>/dev/null | awk '/iptables/&&/zone_lan_forward/&&/tcp/&&/-t filter/&&/-j drop/ {for(i=6; i<=NF; i++) { printf "%s ",$i } print "\n" }' | + while IFS=$"\n" read -r c; do + fwrule=$(echo "$c" | sed 's/drop/REDIRECT --to-ports 65535/') + if [ -n "$fwrule" ] && [ -z "$($IPTABLESSAVE 2>/dev/null | grep zone_lan_prerouting | grep '${fwrule}')" ]; then + eval "$IPTABLES -t nat -A zone_lan_prerouting ${fwrule} 2>&1 >/dev/null" + fi + done +} + +xr_rules_parse_args "$@" +#xr_rules_flush +xr_rules_ipset_init +xr_rules_iptchains_init +xr_rules_fw_drop \ No newline at end of file diff --git a/xray-core/files/usr/bin/xray-rules6 b/xray-core/files/usr/bin/xray-rules6 new file mode 100755 index 000000000..accbc7d0f --- /dev/null +++ b/xray-core/files/usr/bin/xray-rules6 @@ -0,0 +1,310 @@ +#!/bin/sh -e +# +# Copyright (C) 2017 Yousong Zhou +# Copyright (C) 2018-2021 Ycarus (Yannick Chabanois) +# +# The design idea was derived from ss-rules by Jian Chang +# +# This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# + +if [ -f /usr/sbin/iptables-legacy ]; then + IP6TABLES="/usr/sbin/ip6tables-legacy" + IP6TABLESRESTORE="/usr/sbin/ip6tables-legacy-restore" + IP6TABLESSAVE="/usr/sbin/ip6tables-legacy-save" +else + IP6TABLES="/usr/sbin/ip6tables" + IP6TABLESRESTORE="/usr/sbin/ip6tables-restore" + IP6TABLESSAVE="/usr/sbin/ip6tables-save" +fi + + + +xray_rules6_usage() { + cat >&2 < Local port number of ss-redir with TCP mode + -L Local port number of ss-redir with UDP mode + -s List of ip addresses of remote shadowsocks server + --ifnames Only apply rules on packets from these ifnames + --src-bypass + --src-forward + --src-checkdst + --src-default + Packets will have their src ip checked in order against + bypass, forward, checkdst list and will bypass, forward + through, or continue to have their dst ip checked + respectively on the first match. Otherwise, --src-default + decide the default action + --dst-bypass + --dst-forward + --dst-bypass-file + --dst-forward-file + --dst-default + Same as with their --src-xx equivalent + --dst-forward-recentrst + Forward those packets whose destinations have recently + sent to us multiple tcp-rst packets + --local-default + Default action for local out TCP traffic + +The following ipsets will be created by xray-rules. They are also intended to be +populated by other programs like dnsmasq with ipset support + + xray_rules6_src_bypass + xray_rules6_src_forward + xray_rules6_src_checkdst + xray_rules6_dst_bypass + xray_rules6_dst_forward +EOF +} + +o_dst_bypass_=" + fe80::/10 + fd00::/8 + ::1 +" +o_src_default=bypass +o_dst_default=bypass +o_local_default=bypass + +__errmsg() { + echo "xray-rules6: $*" >&2 +} + +xray_rules6_parse_args() { + while [ "$#" -gt 0 ]; do + case "$1" in + -h|--help) xray_rules6_usage; exit 0;; + -f|--flush) xray_rules6_flush; exit 0;; + -l) o_redir_tcp_port="$2"; shift 2;; + -L) o_redir_udp_port="$2"; shift 2;; + -s) o_remote_servers="$2"; shift 2;; + --ifnames) o_ifnames="$2"; shift 2;; + --ipt-extra) o_ipt_extra="$2"; shift 2;; + --src-default) o_src_default="$2"; shift 2;; + --dst-default) o_dst_default="$2"; shift 2;; + --local-default) o_local_default="$2"; shift 2;; + --src-bypass) o_src_bypass="$2"; shift 2;; + --src-forward) o_src_forward="$2"; shift 2;; + --src-checkdst) o_src_checkdst="$2"; shift 2;; + --dst-bypass) o_dst_bypass="$2"; shift 2;; + --dst-bypass_all) o_dst_bypass_all="$2"; shift 2;; + --dst-forward) o_dst_forward="$2"; shift 2;; + --dst-forward-recentrst) o_dst_forward_recentrst=1; shift 1;; + --dst-bypass-file) o_dst_bypass_file="$2"; shift 2;; + --dst-forward-file) o_dst_forward_file="$2"; shift 2;; + --rule-name) rule="$2"; shift 2;; + *) __errmsg "unknown option $1"; return 1;; + esac + done + + if [ -z "$o_redir_tcp_port" -a -z "$o_redir_udp_port" ]; then + __errmsg "Requires at least -l or -L option" + return 1 + fi + if [ -n "$o_dst_forward_recentrst" ] && ! $IP6TABLES -w -m recent -h >/dev/null; then + __errmsg "Please install ip6tables-mod-conntrack-extra with opkg" + return 1 + fi + o_remote_servers="$(for s in $o_remote_servers; do resolveip -6 "$s"; done)" +} + +xray_rules6_flush() { + local setname + + $IP6TABLESSAVE --counters 2>/dev/null | grep -v xr6_ | $IP6TABLESRESTORE --counters + while ip -f inet6 rule del fwmark 1 lookup 100 2>/dev/null; do true; done + ip -f inet6 route flush table 100 || true + for setname in $(ipset -n list | grep "ssr6_${rule}"); do + ipset destroy "$setname" 2>/dev/null || true + done +} + +xray_rules6_ipset_init() { + ipset --exist restore <<-EOF + create ssr6_${rule}_src_bypass hash:net family inet6 hashsize 64 + create ssr6_${rule}_src_forward hash:net family inet6 hashsize 64 + create ssr6_${rule}_src_checkdst hash:net family inet6 hashsize 64 + create ssr6_${rule}_dst_bypass hash:net family inet6 hashsize 64 + create ss_rules6_dst_bypass_all hash:net family inet6 hashsize 64 + create ssr6_${rule}_dst_bypass_ hash:net family inet6 hashsize 64 + create ssr6_${rule}_dst_forward hash:net family inet6 hashsize 64 + create ss_rules6_dst_forward_recrst_ hash:ip family inet6 hashsize 64 timeout 3600 + $(xray_rules6_ipset_mkadd ssr6_${rule}_dst_bypass_ "$o_dst_bypass_ $o_remote_servers") + $(xray_rules6_ipset_mkadd ss_rules6_dst_bypass_all "$o_dst_bypass $(cat "$o_dst_bypass_file" 2>/dev/null | grep -o '\([0-9a-fA-F]\{0,4\}:\)\{1,7\}[0-9a-fA-F]\{0,4\}')") + $(xray_rules6_ipset_mkadd ssr6_${rule}_dst_bypass "$o_dst_bypass $(cat "$o_dst_bypass_file" 2>/dev/null | grep -o '\([0-9a-fA-F]\{0,4\}:\)\{1,7\}[0-9a-fA-F]\{0,4\}')") + $(xray_rules6_ipset_mkadd ssr6_${rule}_src_bypass "$o_src_bypass") + $(xray_rules6_ipset_mkadd ssr6_${rule}_src_forward "$o_src_forward") + $(xray_rules6_ipset_mkadd ssr6_${rule}_src_checkdst "$o_src_checkdst") + $(xray_rules6_ipset_mkadd ssr6_${rule}_dst_forward "$o_dst_forward $(cat "$o_dst_forward_file" 2>/dev/null | grep -o '\([0-9a-fA-F]\{0,4\}:\)\{1,7\}[0-9a-fA-F]\{0,4\}')") + EOF +} + +xray_rules6_ipset_mkadd() { + local setname="$1"; shift + local i + + for i in $*; do + echo "add $setname $i" + done +} + +xray_rules6_iptchains_init() { + xray_rules6_iptchains_init_mark + xray_rules6_iptchains_init_tcp + xray_rules6_iptchains_init_udp +} + +xray_rules6_iptchains_init_mark() { + $IP6TABLESRESTORE --noflush <<-EOF + *mangle + -A PREROUTING -m set --match-set ss_rules6_dst_bypass_all dst -j MARK --set-mark 0x6539 + COMMIT + EOF +} + + +xray_rules6_iptchains_init_tcp() { + local local_target + + [ -n "$o_redir_tcp_port" ] || return 0 + + #xray_rules6_iptchains_init_ nat tcp + xray_rules6_iptchains_init_ mangle tcp + + case "$o_local_default" in + checkdst) local_target=xr6_${rule}_dst ;; + forward) local_target=xr6_${rule}_forward ;; + bypass|*) return 0;; + esac + +# echo "tcp mangle" +# $IP6TABLESRESTORE --noflush <<-EOF +# *mangle +# :xr6_${rule}_local_out - +# -I OUTPUT 1 -p tcp -j xr6_${rule}_local_out +# -A xr6_${rule}_local_out -m set --match-set ss_rules6_dst_bypass dst -j RETURN +# -A xr6_${rule}_local_out -m set --match-set ss_rules6_dst_bypass_all dst -j RETURN +# -A xr6_${rule}_local_out -m set --match-set ss_rules6_dst_bypass_ dst -j RETURN +# -A xr6_${rule}_local_out -m mark --mark 0x6539 -j RETURN +# -A xr6_${rule}_local_out -p tcp $o_ipt_extra -j $local_target -m comment --comment "local_default: $o_local_default" +# COMMIT +# EOF +# echo "done" +} + +xray_rules6_iptchains_init_udp() { + [ -n "$o_redir_udp_port" ] || return 0 + xray_rules6_iptchains_init_ mangle udp +} + +xray_rules6_iptchains_init_() { + local table="$1" + local proto="$2" + local forward_rules + local src_default_target dst_default_target + local recentrst_mangle_rules recentrst_addset_rules + + case "$proto" in + tcp) + #forward_rules="-A xr6_${rule}_forward -p tcp -j REDIRECT --to-ports $o_redir_tcp_port" + forward_rules="-A xr6_${rule}_forward -p tcp -j TPROXY --on-port $o_redir_tcp_port --tproxy-mark 0x01/0x01" + if [ -n "$o_dst_forward_recentrst" ]; then + recentrst_mangle_rules=" + *mangle + -I PREROUTING 1 -p tcp -m tcp --tcp-flags RST RST -m recent --name ss_rules6_recentrst --set --rsource + COMMIT + " + recentrst_addset_rules=" + -A xr6_${rule}_dst -m recent --name ss_rules6_recentrst --rcheck --rdest --seconds 3 --hitcount 3 -j SET --add-set ss_rules6_dst_forward_recrst_ dst --exist + -A xr6_${rule}_dst -m set --match-set ss_rules6_dst_forward_recrst_ dst -j xr6_${rule}_forward + " + fi + ;; + udp) + ip -f inet6 rule add fwmark 1 lookup 100 || true + ip -f inet6 route add local default dev lo table 100 || true + forward_rules=" + -A xr6_${rule}_forward -p udp -j TPROXY --on-port "$o_redir_udp_port" --tproxy-mark 0x01/0x01 + -A xr6_${rule}_forward -p tcp -j TPROXY --on-port "$o_redir_udp_port" --tproxy-mark 0x01/0x01 + " + ;; + esac + case "$o_src_default" in + forward) src_default_target=xr6_${rule}_forward ;; + checkdst) src_default_target=xr6_${rule}_dst ;; + bypass|*) src_default_target=RETURN ;; + esac + case "$o_dst_default" in + forward) dst_default_target=xr6_${rule}_forward ;; + bypass|*) dst_default_target=RETURN ;; + esac + sed -e '/^\s*$/d' -e 's/^\s\+//' <<-EOF | $IP6TABLESRESTORE --noflush + *$table + :xr6_${rule}_pre_src - + :xr6_${rule}_src - + :xr6_${rule}_dst - + :xr6_${rule}_forward - + $(xray_rules6_iptchains_mkprerules "udp") + $(xray_rules6_iptchains_mkprerules "tcp") + -A xr6_${rule}_pre_src -m set --match-set ssr6_${rule}_dst_bypass_ dst -j RETURN + -A xr6_${rule}_pre_src -m set --match-set ss_rules6_dst_bypass_all dst -j MARK --set-mark 0x6539 + -A xr6_${rule}_pre_src -m set --match-set ss_rules6_dst_bypass_all dst -j RETURN + -A xr6_${rule}_pre_src -m set --match-set ssr6_${rule}_dst_bypass dst -j RETURN + -A xr6_${rule}_pre_src -m mark --mark 0x6539 -j RETURN + -A xr6_${rule}_dst -m set --match-set ss_rules6_dst_bypass_all dst -j RETURN + -A xr6_${rule}_dst -m set --match-set ssr6_${rule}_dst_bypass dst -j RETURN + -A xr6_${rule}_pre_src -p tcp $o_ipt_extra -j xr6_${rule}_src + -A xr6_${rule}_pre_src -p udp $o_ipt_extra -j xr6_${rule}_src + -A xr6_${rule}_src -m set --match-set ssr6_${rule}_src_bypass src -j RETURN + -A xr6_${rule}_src -m set --match-set ssr6_${rule}_src_forward src -j xr6_${rule}_forward + -A xr6_${rule}_src -m set --match-set ssr6_${rule}_src_checkdst src -j xr6_${rule}_dst + -A xr6_${rule}_src -j $src_default_target -m comment --comment "src_default: $o_src_default" + -A xr6_${rule}_dst -m set --match-set ssr6_${rule}_dst_forward dst -j xr6_${rule}_forward + $recentrst_addset_rules + -A xr6_${rule}_dst -j $dst_default_target -m comment --comment "dst_default: $o_dst_default" + $forward_rules + COMMIT + $recentrst_mangle_rules + EOF +} + +xray_rules6_iptchains_mkprerules() { + local proto="$1" + + if [ -z "$o_ifnames" ]; then + echo "-A PREROUTING -p $proto -j xr6_${rule}_pre_src" + else + echo $o_ifnames \ + | tr ' ' '\n' \ + | sed "s/.*/-I PREROUTING 1 -i \\0 -p $proto -j xr6_${rule}_pre_src/" + fi +} + +xray_rules6_fw_drop() { + fw3 -6 print 2>/dev/null | awk '/iptables/&&/zone_lan_forward/&&/tcp/&&/-t filter/&&/-j reject/ {for(i=6; i<=NF; i++) { printf "%s ",$i } print "\n" }' | + while IFS=$"\n" read -r c; do + fwrule=$(echo "$c" | sed 's/reject/REDIRECT --to-ports 65535/') + if [ -n "$fwrule" ] && [ -z "$(iptables-save | grep zone_lan_prerouting | grep '${fwrule}')" ]; then + eval "$IP6TABLES -w -t nat -A zone_lan_prerouting ${fwrule} 2>&1 >/dev/null" + fi + done + fw3 -6 print 2>/dev/null | awk '/iptables/&&/zone_lan_forward/&&/tcp/&&/-t filter/&&/-j drop/ {for(i=6; i<=NF; i++) { printf "%s ",$i } print "\n" }' | + while IFS=$"\n" read -r c; do + fwrule=$(echo "$c" | sed 's/drop/REDIRECT --to-ports 65535/') + if [ -n "$fwrule" ] && [ -z "$(iptables-save | grep zone_lan_prerouting | grep '${fwrule}')" ]; then + eval "$IP6TABLES -t nat -A zone_lan_prerouting ${fwrule} 2>&1 >/dev/null" + fi + done +} + +xray_rules6_parse_args "$@" +xray_rules6_flush +xray_rules6_ipset_init +xray_rules6_iptchains_init +xray_rules6_fw_drop \ No newline at end of file diff --git a/xray-core/patches/001-fix-wireguard-go.patch b/xray-core/patches/001-fix-wireguard-go.patch new file mode 100644 index 000000000..421febce6 --- /dev/null +++ b/xray-core/patches/001-fix-wireguard-go.patch @@ -0,0 +1,97 @@ +diff -aurN xray-core-1.8.5.old/go.mod xray-core-1.8.5/go.mod +--- a/go.mod 2023-09-18 16:14:12.554956393 +0200 ++++ b/go.mod 2023-09-18 16:16:56.304259547 +0200 +@@ -12,13 +12,13 @@ + github.com/pires/go-proxyproto v0.7.0 + github.com/quic-go/quic-go v0.38.1 + github.com/refraction-networking/utls v1.4.3 +- github.com/sagernet/sing v0.2.9 ++ github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a + github.com/sagernet/sing-shadowsocks v0.2.4 +- github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c + github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb + github.com/stretchr/testify v1.8.4 + github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e + github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6 ++ github.com/xtls/wireguard-go v0.0.0-20230303120718-56f003b3a66e + go4.org/netipx v0.0.0-20230824141953-6213f710f925 + golang.org/x/crypto v0.12.0 + golang.org/x/net v0.14.0 +@@ -47,7 +47,7 @@ + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/quic-go/qtls-go1-20 v0.3.3 // indirect + github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect +- go.uber.org/atomic v1.11.0 // indirect ++ go.uber.org/atomic v1.10.0 // indirect + golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/text v0.12.0 // indirect +diff -aurN xray-core-1.8.5.old/go.sum xray-core-1.8.5/go.sum +--- a/go.sum 2023-09-18 16:14:12.554956393 +0200 ++++ b/go.sum 2023-09-18 16:16:56.304259547 +0200 +@@ -123,12 +123,10 @@ + github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= + github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= + github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +-github.com/sagernet/sing v0.2.9 h1:3wsTz+JG5Wzy65eZnh6AuCrD2QqcRF6Iq6f7ttmJsAo= +-github.com/sagernet/sing v0.2.9/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= ++github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a h1:b89t6Mjgk4rJ5lrNMnCzy1/J116XkhgdB3YNd9FHyF4= ++github.com/sagernet/sing v0.2.10-0.20230807080248-4db0062caa0a/go.mod h1:9uOZwWkhT2Z2WldolLxX34s+1svAX4i4vvz5hy8u1MA= + github.com/sagernet/sing-shadowsocks v0.2.4 h1:s/CqXlvFAZhlIoHWUwPw5CoNnQ9Ibki9pckjuugtVfY= + github.com/sagernet/sing-shadowsocks v0.2.4/go.mod h1:80fNKP0wnqlu85GZXV1H1vDPC/2t+dQbFggOw4XuFUM= +-github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= +-github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= + github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U= + github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= + github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +@@ -168,10 +166,12 @@ + github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= + github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6 h1:T+YCYGfFdzyaKTDCdZn/hEiKvsw6yUfd+e4hze0rCUw= + github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6/go.mod h1:rkuAY1S9F8eI8gDiPDYvACE8e2uwkyg8qoOTuwWov7Y= ++github.com/xtls/wireguard-go v0.0.0-20230303120718-56f003b3a66e h1:Y0CxNt+TeOhFUS2J/EF6osq9RukduvGYUNk2xPdKW60= ++github.com/xtls/wireguard-go v0.0.0-20230303120718-56f003b3a66e/go.mod h1:XFvPXP1gUqy/12j+KbdShku+YWiZJjaYLEAn4ZXaRGU= + github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= + go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +-go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +-go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= ++go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= ++go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= + go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= + go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ= + go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= +diff -aurN xray-core-1.8.5.old/proxy/wireguard/bind.go xray-core-1.8.5/proxy/wireguard/bind.go +--- a/proxy/wireguard/bind.go 2023-09-18 16:14:12.562956262 +0200 ++++ b/proxy/wireguard/bind.go 2023-09-18 16:15:43.597456179 +0200 +@@ -9,7 +9,7 @@ + "strconv" + "sync" + +- "github.com/sagernet/wireguard-go/conn" ++ "github.com/xtls/wireguard-go/conn" + xnet "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/features/dns" + "github.com/xtls/xray-core/transport/internet" +diff -aurN xray-core-1.8.5.old/proxy/wireguard/tun.go xray-core-1.8.5/proxy/wireguard/tun.go +--- a/proxy/wireguard/tun.go 2023-09-18 16:14:12.562956262 +0200 ++++ b/proxy/wireguard/tun.go 2023-09-18 16:15:52.413310983 +0200 +@@ -12,7 +12,7 @@ + "net/netip" + "os" + +- "github.com/sagernet/wireguard-go/tun" ++ "github.com/xtls/wireguard-go/tun" + "github.com/xtls/xray-core/features/dns" + "gvisor.dev/gvisor/pkg/buffer" + "gvisor.dev/gvisor/pkg/tcpip" +diff -aurN xray-core-1.8.5.old/proxy/wireguard/wireguard.go xray-core-1.8.5/proxy/wireguard/wireguard.go +--- a/proxy/wireguard/wireguard.go 2023-09-18 16:14:12.562956262 +0200 ++++ b/proxy/wireguard/wireguard.go 2023-09-18 16:16:01.109167878 +0200 +@@ -27,7 +27,7 @@ + "net/netip" + "strings" + +- "github.com/sagernet/wireguard-go/device" ++ "github.com/xtls/wireguard-go/device" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/log"