refactor: Refactoring configuration of local domain lists

This commit is contained in:
Andrey Petelin
2025-09-05 14:50:43 +05:00
parent 17c1d09aa8
commit acfc95e86d
4 changed files with 115 additions and 37 deletions

View File

@@ -32,6 +32,7 @@ TEST_DOMAIN="fakeip.podkop.fyi"
INTERFACES_LIST="" INTERFACES_LIST=""
SRC_INTERFACE="" SRC_INTERFACE=""
RESOLV_CONF="/etc/resolv.conf" RESOLV_CONF="/etc/resolv.conf"
TMP_FOLDER="/tmp/podkop"
# Endpoints https://github.com/ampetelin/warp-endpoint-checker # Endpoints https://github.com/ampetelin/warp-endpoint-checker
CLOUDFLARE_OCTETS="8.47 162.159 188.114" CLOUDFLARE_OCTETS="8.47 162.159 188.114"
@@ -62,9 +63,9 @@ nolog() {
echolog() { echolog() {
local message="$1" local message="$1"
local module="$2" local level="$2"
log "$message" "$module" log "$message" "$level"
nolog "$message" nolog "$message"
} }
@@ -826,7 +827,6 @@ sing_box_configure_route() {
config_foreach include_source_ips_in_routing_handler config_foreach include_source_ips_in_routing_handler
# TODO(ampetelin): Add block rules # TODO(ampetelin): Add block rules
config_foreach
local exclude_from_ip_enabled local exclude_from_ip_enabled
config_get_bool exclude_from_ip_enabled "main" "exclude_from_ip_enabled" 0 config_get_bool exclude_from_ip_enabled "main" "exclude_from_ip_enabled" 0
@@ -906,12 +906,11 @@ configure_routing_for_section_lists() {
if [ "$local_domains_list_enabled" -eq 1 ]; then if [ "$local_domains_list_enabled" -eq 1 ]; then
log "Processing local domains routing rules for $section section" log "Processing local domains routing rules for $section section"
# TODO(ampetelin): it is necessary to implement configure_local_domains_lists "$section" "$route_rule_tag"
# configure_local_domains_list_handler "$section" "$route_rule_tag"
fi fi
if [ "$remote_domains_list_enabled" -eq 1 ]; then if [ "$remote_domains_list_enabled" -eq 1 ]; then
log "Processing local domains routing rules for $section section" log "Processing remote domains routing rules for $section section"
config_list_foreach "$section" "remote_domains_list" configure_remote_domains_or_subnets_list_handler \ config_list_foreach "$section" "remote_domains_list" configure_remote_domains_or_subnets_list_handler \
"domains" "$section" "$route_rule_tag" "domains" "$section" "$route_rule_tag"
fi fi
@@ -934,16 +933,16 @@ configure_community_list_handler() {
local section="$2" local section="$2"
local route_rule_tag="$3" local route_rule_tag="$3"
local rule_set_tag format url update_interval detour local ruleset_tag format url update_interval detour
rule_set_tag="$(get_rule_set_tag "$section" "$tag" "community")" ruleset_tag="$(get_ruleset_tag "$section" "$tag" "community")"
format="binary" format="binary"
url="$SRS_MAIN_URL/$tag.srs" url="$SRS_MAIN_URL/$tag.srs"
detour="$(_get_download_detour_tag)" detour="$(_get_download_detour_tag)"
config_get update_interval "main" "update_interval" "1d" config_get update_interval "main" "update_interval" "1d"
config=$(sing_box_cm_add_remote_ruleset "$config" "$rule_set_tag" "$format" "$url" "$detour" "$update_interval") config=$(sing_box_cm_add_remote_ruleset "$config" "$ruleset_tag" "$format" "$url" "$detour" "$update_interval")
_add_rule_set_to_dns_rules "$rule_set_tag" _add_ruleset_to_dns_rules "$ruleset_tag"
config=$(sing_box_cm_patch_route_rule "$config" "$route_rule_tag" "rule_set" "$rule_set_tag") config=$(sing_box_cm_patch_route_rule "$config" "$route_rule_tag" "rule_set" "$ruleset_tag")
} }
configure_user_domains_list_handler() { configure_user_domains_list_handler() {
@@ -951,9 +950,58 @@ configure_user_domains_list_handler() {
# TODO(ampetelin): it is necessary to implement # TODO(ampetelin): it is necessary to implement
} }
configure_local_domains_list_handler() { configure_local_domains_lists() {
local section="$1" local section="$1"
# TODO(ampetelin): it is necessary to implement local route_rule_tag="$2"
local ruleset_tag ruleset_filename ruleset_filepath
ruleset_tag="$(get_ruleset_tag "$section" "local" "domains")"
ruleset_filename="$ruleset_tag.json"
ruleset_filepath="$TMP_FOLDER/$ruleset_filename"
sing_box_cm_create_local_source_ruleset "$ruleset_filepath"
config=$(sing_box_cm_add_local_ruleset "$config" "$ruleset_tag" "source" "$ruleset_filepath")
config=$(sing_box_cm_patch_route_rule "$config" "$route_rule_tag" "rule_set" "$ruleset_tag")
_add_ruleset_to_dns_rules "$ruleset_tag" "$route_rule_tag"
config_list_foreach "$section" "local_domains_list" import_local_domains_ruleset "$section" "$ruleset_filepath"
}
import_local_domains_ruleset() {
local filepath="$1"
local section="$2"
local ruleset_filepath="$3"
if ! file_exists "$filepath"; then
log "File $filepath not found" "warn"
return 1
fi
local domains=""
while IFS= read -r domain; do
if [ -z "$domain" ]; then
continue
fi
if ! is_domain "$domain"; then
log "$domain is not domain" "debug"
continue
fi
if [ -z "$domains" ]; then
domains="$domain"
else
domains="$domains,$domain"
fi
done < "$filepath"
if [ -z "$domains" ]; then
log "No valid domains found in $filepath"
return 0
fi
domains="$(comma_string_to_json_array "$domains")"
sing_box_cm_patch_local_source_ruleset_rules "$ruleset_filepath" "domain_suffix" "$domains"
} }
configure_remote_domains_or_subnets_list_handler() { configure_remote_domains_or_subnets_list_handler() {
@@ -968,24 +1016,24 @@ configure_remote_domains_or_subnets_list_handler() {
json|srs) json|srs)
log "Detected file extension: .$file_extension → proceeding with processing" "debug" log "Detected file extension: .$file_extension → proceeding with processing" "debug"
local basename rule_set_tag format detour update_interval local basename ruleset_tag format detour update_interval
basename=$(url_get_basename "$url") basename=$(url_get_basename "$url")
rule_set_tag=$(get_rule_set_tag "$section" "$basename" "remote-$type") ruleset_tag=$(get_ruleset_tag "$section" "$basename" "remote-$type")
format="$(get_rule_set_format_by_file_extension "$file_extension")" format="$(get_ruleset_format_by_file_extension "$file_extension")"
detour="$(_get_download_detour_tag)" detour="$(_get_download_detour_tag)"
config_get update_interval "main" "update_interval" "1d" config_get update_interval "main" "update_interval" "1d"
config=$(sing_box_cm_add_remote_ruleset "$config" "$rule_set_tag" "$format" "$url" "$detour" "$update_interval") config=$(sing_box_cm_add_remote_ruleset "$config" "$ruleset_tag" "$format" "$url" "$detour" "$update_interval")
config=$(sing_box_cm_patch_route_rule "$config" "$route_rule_tag" "rule_set" "$rule_set_tag") config=$(sing_box_cm_patch_route_rule "$config" "$route_rule_tag" "rule_set" "$ruleset_tag")
case "$type" in case "$type" in
domains) _add_rule_set_to_dns_rules "$rule_set_tag" "$route_rule_tag" ;; domains) _add_ruleset_to_dns_rules "$ruleset_tag" "$route_rule_tag" ;;
subnets) ;; subnets) ;;
*) log "Unsupported remote rule set type: $type" "warn" ;; *) log "Unsupported remote rule set type: $type" "warn" ;;
esac esac
;; ;;
*) *)
log "Detected file extension: .$file_extension → no processing needed, managed on list_update" log "Detected file extension: .$file_extension → no processing needed, managed on list_update" "debug"
;; ;;
esac esac
} }
@@ -1004,14 +1052,14 @@ _get_download_detour_tag() {
fi fi
} }
_add_rule_set_to_dns_rules() { _add_ruleset_to_dns_rules() {
local rule_set_tag="$1" local ruleset_tag="$1"
config=$(sing_box_cm_patch_dns_route_rule "$config" "$SB_FAKEIP_DNS_RULE_TAG" "rule_set" "$rule_set_tag") config=$(sing_box_cm_patch_dns_route_rule "$config" "$SB_FAKEIP_DNS_RULE_TAG" "rule_set" "$ruleset_tag")
local split_dns_enabled final_dns_server local split_dns_enabled final_dns_server
config_get_bool split_dns_enabled "main" "split_dns_enabled" 0 config_get_bool split_dns_enabled "main" "split_dns_enabled" 0
if [ "$split_dns_enabled" -eq 1 ]; then if [ "$split_dns_enabled" -eq 1 ]; then
config=$(sing_box_cm_patch_dns_route_rule "$config" "$SB_INVERT_FAKEIP_DNS_RULE_TAG" "rule_set" "$rule_set_tag") config=$(sing_box_cm_patch_dns_route_rule "$config" "$SB_INVERT_FAKEIP_DNS_RULE_TAG" "rule_set" "$ruleset_tag")
fi fi
} }
@@ -1290,6 +1338,7 @@ import_subnets_from_remote_srs_file() {
rm -f "$binary_filepath" "$json_filepath" rm -f "$binary_filepath" "$json_filepath"
} }
## Support functions
# Decompiles a sing-box SRS binary file into a JSON ruleset file # Decompiles a sing-box SRS binary file into a JSON ruleset file
decompile_srs_file() { decompile_srs_file() {
local binary_filepath="$1" local binary_filepath="$1"

View File

@@ -1,6 +1,6 @@
SB_CONFIG="/etc/sing-box/config.json" SB_CONFIG="/etc/sing-box/config.json"
# Log # Log
SB_DEFAULT_LOG_LEVEL="warn" SB_DEFAULT_LOG_LEVEL="info"
# DNS # DNS
SB_DNS_SERVER_TAG="dns-server" SB_DNS_SERVER_TAG="dns-server"
SB_SPLIT_DNS_SERVER_TAG="split-dns-server" SB_SPLIT_DNS_SERVER_TAG="split-dns-server"

View File

@@ -2,21 +2,28 @@
is_ipv4() { is_ipv4() {
local ip="$1" local ip="$1"
local regex="^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$" local regex="^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$"
[[ $ip =~ $regex ]] [[ "$ip" =~ $regex ]]
} }
# Check if string is valid IPv4 with CIDR mask # Check if string is valid IPv4 with CIDR mask
is_ipv4_cidr() { is_ipv4_cidr() {
local ip="$1" local ip="$1"
local regex="^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}(\/(3[0-2]|2[0-9]|1[0-9]|[0-9]))$" local regex="^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}(\/(3[0-2]|2[0-9]|1[0-9]|[0-9]))$"
[[ $ip =~ $regex ]] [[ "$ip" =~ $regex ]]
}
is_domain() {
local str="$1"
#local regex="^(?=.{1,253}(?:\/|$))(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)\.)+(?:[a-zA-Z]{2,}|xn--[a-zA-Z0-9-]{1,59}[a-zA-Z0-9])(?:\/[^\s]*)?$"
echo "$str" | grep -Eq '^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9]))+$'
#[[ $str =~ $regex ]]
} }
# Checks if the given string is a valid base64-encoded sequence # Checks if the given string is a valid base64-encoded sequence
is_base64() { is_base64() {
local str="$1" local str="$1"
if echo "$str" | base64 -d > /dev/null 2>&1; then if echo "$str" | base64 -d >/dev/null 2>&1; then
return 0 return 0
fi fi
return 1 return 1
@@ -58,10 +65,10 @@ get_outbound_tag_by_section() {
} }
# Constructs and returns a ruleset tag using section, name, optional type, and a fixed postfix # Constructs and returns a ruleset tag using section, name, optional type, and a fixed postfix
get_rule_set_tag() { get_ruleset_tag() {
local section="$1" local section="$1"
local name="$2" local name="$2"
local type="${3:-}" local type="$3"
local postfix="ruleset" local postfix="ruleset"
if [ -n "$type" ]; then if [ -n "$type" ]; then
@@ -72,7 +79,7 @@ get_rule_set_tag() {
} }
# Determines the ruleset format based on the file extension (json → source, srs → binary) # Determines the ruleset format based on the file extension (json → source, srs → binary)
get_rule_set_format_by_file_extension() { get_ruleset_format_by_file_extension() {
local file_extension="$1" local file_extension="$1"
local format local format
@@ -154,7 +161,7 @@ base64_decode() {
local str="$1" local str="$1"
local decoded_url local decoded_url
decoded_url="$(echo "$str" | base64 -d 2> /dev/null)" decoded_url="$(echo "$str" | base64 -d 2>/dev/null)"
echo "$decoded_url" echo "$decoded_url"
} }
@@ -180,7 +187,7 @@ migrate_config_key() {
# Download URL content directly # Download URL content directly
download_to_stream() { download_to_stream() {
local url="$1" local url="$1"
local http_proxy_url="${2}" local http_proxy_url="$2"
if [ -n "$http_proxy_url" ]; then if [ -n "$http_proxy_url" ]; then
http_proxy="$http_proxy_url" https_proxy="$http_proxy_url" wget -qO- "$url" | sed 's/\r$//' http_proxy="$http_proxy_url" https_proxy="$http_proxy_url" wget -qO- "$url" | sed 's/\r$//'
@@ -193,7 +200,7 @@ download_to_stream() {
download_to_tempfile() { download_to_tempfile() {
local url="$1" local url="$1"
local filepath="$2" local filepath="$2"
local http_proxy_url="${3}" local http_proxy_url="$3"
if [ -n "$http_proxy_url" ]; then if [ -n "$http_proxy_url" ]; then
http_proxy="$http_proxy_url" https_proxy="$http_proxy_url" wget -O "$filepath" "$url" http_proxy="$http_proxy_url" https_proxy="$http_proxy_url" wget -O "$filepath" "$url"

View File

@@ -1227,6 +1227,28 @@ sing_box_cm_configure_clash_api() {
}' }'
} }
sing_box_cm_create_local_source_ruleset() {
local filepath="$1"
jq -n '{version: 3, rules: []}' > "$filepath"
}
sing_box_cm_patch_local_source_ruleset_rules() {
local filepath="$1"
local key="$2"
local value="$3"
value=$(_normalize_arg "$value")
local content
content="$(cat "$filepath")"
echo "$content" | jq \
--arg key "$key" \
--argjson value "$value" \
'.rules += [{($key): $value}]' > "$filepath"
}
####################################### #######################################
# Save a sing-box JSON configuration to a file, removing service-specific tags. # Save a sing-box JSON configuration to a file, removing service-specific tags.
# Arguments: # Arguments:
@@ -1239,12 +1261,12 @@ sing_box_cm_configure_clash_api() {
####################################### #######################################
sing_box_cm_save_config_to_file() { sing_box_cm_save_config_to_file() {
local config="$1" local config="$1"
local file_path="$2" local filepath="$2"
echo "$config" | jq \ echo "$config" | jq \
--arg tag "$SERVICE_TAG" \ --arg tag "$SERVICE_TAG" \
'walk(if type == "object" then del(.[$tag]) else . end)' \ 'walk(if type == "object" then del(.[$tag]) else . end)' \
> "$file_path" > "$filepath"
} }
_normalize_arg() { _normalize_arg() {