🦄 Sync 2026-06-05 01:27:09

This commit is contained in:
github-actions[bot] committed 2026-06-05 01:27:09 +08:00
1 parent 4c613c876b
commit 23b721b464
63 files changed
+10393 -11351

No files matched your search

@@ -1,119 +1,457 @@
module YAML
class << self
alias_method :load, :unsafe_load if YAML.respond_to? :unsafe_load
alias_method :original_dump, :dump
alias_method :original_load_file, :load_file
end
class << self
alias_method :load, :unsafe_load if YAML.respond_to? :unsafe_load
alias_method :original_dump, :dump
alias_method :original_load_file, :load_file
end
def self.LOG(info)
puts Time.new.strftime("%Y-%m-%d %H:%M:%S") + " [Info] " + "#{info}"
end
def self.LOG(info)
puts Time.new.strftime("%Y-%m-%d %H:%M:%S") + " [Info] " + "#{info}"
end
def self.LOG_ERROR(info)
puts Time.new.strftime("%Y-%m-%d %H:%M:%S") + " [Error] " + "#{info}"
end
def self.LOG_ERROR(info)
puts Time.new.strftime("%Y-%m-%d %H:%M:%S") + " [Error] " + "#{info}"
end
def self.LOG_WARN(info)
puts Time.new.strftime("%Y-%m-%d %H:%M:%S") + " [Warning] " + "#{info}"
end
def self.LOG_WARN(info)
puts Time.new.strftime("%Y-%m-%d %H:%M:%S") + " [Warning] " + "#{info}"
end
def self.LOG_TIP(info)
puts Time.new.strftime("%Y-%m-%d %H:%M:%S") + " [Tip] " + "#{info}"
end
def self.LOG_TIP(info)
puts Time.new.strftime("%Y-%m-%d %H:%M:%S") + " [Tip] " + "#{info}"
end
# Keep `short-id` as string before YAML parsing so leading zeros are preserved.
# This is required for REALITY short-id values like `00000000`.
def self.load_file(filename, *args, **kwargs)
yaml_content = File.read(filename)
processed_content = fix_short_id_quotes(yaml_content)
def self.load_file(filename, *args, **kwargs)
yaml_content = File.read(filename)
processed_content = fix_short_id_quotes(yaml_content)
if kwargs.empty?
load(processed_content, *args)
else
load(processed_content, *args, **kwargs)
end
end
load(processed_content, *args, **kwargs)
end
def self.dump(obj, io = nil, **options)
begin
if io.nil?
yaml_content = original_dump(obj, **options)
fix_short_id_quotes(yaml_content)
elsif io.respond_to?(:write)
require 'stringio'
temp_io = StringIO.new
original_dump(obj, temp_io, **options)
yaml_content = temp_io.string
processed_content = fix_short_id_quotes(yaml_content)
io.write(processed_content)
io
else
yaml_content = original_dump(obj, io, **options)
fix_short_id_quotes(yaml_content)
end
rescue => e
LOG_ERROR("Write file failed:【%s】" % [e.message])
nil
end
end
def self.dump(obj, io = nil, **options)
begin
yaml_content = original_dump(obj, **options)
processed = fix_short_id_quotes(yaml_content)
if io.nil?
processed
elsif io.respond_to?(:write)
io.write(processed)
io
else
processed
end
rescue => e
LOG_ERROR("Write file failed:【%s】" % [e.message])
nil
end
end
private
private
SHORT_ID_REGEX = /^(\s*)short-id:\s*(.*)$/
LIST_ITEM_REGEX = /^(\s*)-\s*(.*)$/
KEY_REGEX = /^(\s*)([a-zA-Z0-9_-]+):\s*(.*)$/
QUOTED_VALUE_REGEX = /^["'].*["']$/
# fix_short_id_quotes:
# Purpose: ensure YAML `short-id` values are emitted with the intended
# representation when dumping or re-writing YAML files.
# Behavior:
# - Non-null, non-empty scalar `short-id` values (and items inside
# `short-id` sequences) are written as double-quoted strings.
# - Empty strings are preserved as empty quoted strings ("" remains "").
# - Null values are preserved and may be emitted explicitly (e.g. `! "").
# Lastly, use gsub to clean up any `! ""` tags
# Examples:
# Input: short-id: 00000000 -> Output: short-id: "00000000"
# Input: short-id: "" -> Output: short-id: ""
# Input: short-id: "abc123" -> Output: short-id: "abc123"
# Input: short-id: "1600e237" -> Output: short-id: "1600e237"
# Input: short-id: null -> Output: short-id: ""
# Inline map support, e.g. reality-opts: { ..., short-id: 00000000 }
INLINE_SHORT_ID_REGEX = /(short-id:\s*)(?!["'\[])([^\s,"'{}\[\]\n\r]+)(?=\s*(?:[,}\]\n\r]|$))/m.freeze
def self.fix_short_id_quotes(yaml_content)
return yaml_content unless yaml_content.include?('short-id:')
def self.fix_short_id_quotes(yaml_content)
return yaml_content unless yaml_content.include?('short-id:')
begin
stream = Psych.parse_stream(yaml_content)
begin
# First, normalize inline-map style unquoted short-id.
processed = yaml_content.gsub(INLINE_SHORT_ID_REGEX) do
"#{$1}\"#{$2}\""
end
traverse = lambda do |node|
case node
when Psych::Nodes::Mapping
children = node.children || []
i = 0
while i < children.length
key = children[i]
val = children[i + 1]
if key.is_a?(Psych::Nodes::Scalar) && key.value == 'short-id'
if val.is_a?(Psych::Nodes::Scalar)
is_null_scalar = (val.tag == 'tag:yaml.org,2002:null') || (val.tag == '!!null') || (val.value =~ /^\s*(~|null|NULL|Null)\s*$/)
unless is_null_scalar
val.tag = nil
val.style = defined?(Psych::Nodes::Scalar::DOUBLE_QUOTED) ? Psych::Nodes::Scalar::DOUBLE_QUOTED : 2
end
elsif val.is_a?(Psych::Nodes::Sequence)
val.children.each do |child|
if child.is_a?(Psych::Nodes::Scalar)
is_null_child = (child.tag == 'tag:yaml.org,2002:null') || (child.tag == '!!null') || (child.value =~ /^\s*(~|null|NULL|Null)\s*$/)
unless is_null_child
child.tag = nil
child.style = defined?(Psych::Nodes::Scalar::DOUBLE_QUOTED) ? Psych::Nodes::Scalar::DOUBLE_QUOTED : 2
end
end
end
end
else
traverse.call(key) if key.respond_to?(:children)
traverse.call(val) if val.respond_to?(:children)
end
i += 2
end
when Psych::Nodes::Sequence
(node.children || []).each { |c| traverse.call(c) }
when Psych::Nodes::Document, Psych::Nodes::Stream
(node.children || []).each { |c| traverse.call(c) }
else
if node.respond_to?(:children)
(node.children || []).each { |c| traverse.call(c) }
end
end
end
lines = processed.lines
short_id_indices = lines.each_index.select { |i| lines[i] =~ SHORT_ID_REGEX }
short_id_indices.each do |short_id_index|
line = lines[short_id_index]
if line =~ SHORT_ID_REGEX
indent = $1
value = $2.strip
if value.empty?
(short_id_index + 1...lines.size).each do |i|
line = lines[i]
next if line.strip.empty?
if line[/^\s*/].length <= short_id_indent_len
break
end
if line =~ LIST_ITEM_REGEX
indent = $1
value = $2.strip
if value =~ KEY_REGEX
break
end
if value !~ QUOTED_VALUE_REGEX
lines[i] = "#{indent}- \"#{value}\"\n"
end
elsif line =~ KEY_REGEX
break
end
end
else
if value !~ QUOTED_VALUE_REGEX
lines[short_id_index] = "#{indent}short-id: \"#{value}\"\n"
end
end
end
end
lines.join
rescue => e
LOG_ERROR("Fix short-id values type failed:【%s】" % [e.message])
yaml_content
end
end
end
stream.children.each do |doc_node|
if doc_node.is_a?(Psych::Nodes::Document)
traverse.call(doc_node.root) if doc_node.root
end
end
if stream.respond_to?(:to_yaml)
processed_yaml = stream.to_yaml
processed_yaml = processed_yaml.gsub(/^([ \t]*short-id:\s*)!\s*/, "\\1")
processed_yaml
else
yaml_content
end
rescue => e
LOG_ERROR("Fix short-id values type failed:【%s】" % [e.message])
yaml_content
end
end
def self.overwrite(base, override)
return override if base.nil?
return base if override.nil?
current_key = nil
current_operation = nil
begin
case override
when Hash
result = base.is_a?(Hash) ? base.dup : {}
override.each do |key, value|
current_key = key
processed_key, operation = parse_key(key)
current_operation = operation
applied = apply_operation(result[processed_key], value, operation)
if applied.equal?(DELETED_SENTINEL)
result.delete(processed_key)
else
result[processed_key] = applied
end
end
result
else
override
end
rescue => e
LOG_ERROR("YAML overwrite failed:【key: %s, operation: %s, error: %s】" % [current_key, current_operation, e.message])
base
end
end
private
def self.parse_key(key)
key_str = key.to_s
# +<key>
if key_str.start_with?('+<') && key_str.include?('>')
close_idx = key_str.index('>')
inner_key = key_str[2...close_idx]
return inner_key, :prepend_array
end
# <key>suffix
if key_str.start_with?('<') && key_str.include?('>')
close_idx = key_str.index('>')
inner_key = key_str[1...close_idx]
suffix = key_str[(close_idx + 1)..-1]
return inner_key, determine_operation(suffix)
end
# 前缀 +key
if key_str.start_with?('+')
return key_str[1..-1], :prepend_array
end
# 尾部(支持 +, !, *, -)
if key_str =~ /^(.*?)([+!*\-])$/
return Regexp.last_match(1), determine_operation(Regexp.last_match(2))
end
[key_str, :merge]
end
def self.determine_operation(suffix)
case suffix
when '+'
:append_array
when '-'
:delete
when '!'
:force_overwrite
when '*'
:batch_update
else
:merge
end
end
def self.match_value(target, condition)
return false if target.nil? || condition.nil?
begin
if condition.is_a?(String) && condition.start_with?('/') && condition.end_with?('/')
pattern = condition[1...-1]
regexp = Regexp.new(pattern)
if target.is_a?(Array)
target.any? { |item| item.to_s =~ regexp }
else
target.to_s =~ regexp
end
elsif condition.is_a?(Array) && target.is_a?(Array)
condition.all? { |c| target.include?(c) }
else
target == condition
end
rescue => e
LOG_ERROR("YAML overwrite failed:【(match value) => target: %s, condition: %s, error: %s】" % [target, condition, e.message])
false
end
end
def self.deep_dup(obj)
case obj
when Array
obj.map { |x| deep_dup(x) }
when Hash
obj.transform_values { |v| deep_dup(v) }
else
obj.dup rescue obj
end
end
def self.merge_hash(base, value, prepend: false)
if prepend
result = {}
value.each do |k, v|
if base.key?(k)
result[k] = apply_operation(base[k], v, :merge)
else
result[k] = deep_dup(v)
end
end
base.each do |k, v|
result[k] = deep_dup(v) unless result.key?(k)
end
result
else
result = deep_dup(base)
value.each do |k, v|
if result.key?(k)
result[k] = apply_operation(result[k], v, :merge)
else
result[k] = deep_dup(v)
end
end
result
end
end
def self.delete_from_hash(base, value)
result = deep_dup(base)
case value
when Array
value.each { |k| result.delete(k) }
when Hash
value.each do |k, v|
if v.nil? || v == true
result.delete(k)
elsif result[k].is_a?(Hash) && v.is_a?(Hash)
nested = apply_operation(result[k], v, :delete)
if nested.equal?(DELETED_SENTINEL)
result.delete(k)
else
result[k] = nested
end
else
result.delete(k)
end
end
else
result.delete(value)
end
result
end
DELETED_SENTINEL = Object.new.freeze
def self.apply_operation(base, value, operation)
case operation
when :delete
if base.is_a?(Array) && value.is_a?(Array)
base - value
elsif base.is_a?(Array) && !value.nil?
base - [value]
elsif base.is_a?(Hash)
delete_from_hash(base, value)
else
DELETED_SENTINEL
end
when :force_overwrite
deep_dup(value)
when :prepend_array
if base.is_a?(Array) && value.is_a?(Array)
(deep_dup(value) + base).uniq
elsif base.is_a?(Hash) && value.is_a?(Hash)
merge_hash(base, value, prepend: true)
else
deep_dup(value)
end
when :append_array
if base.is_a?(Array) && value.is_a?(Array)
base_dup = base.dup
deep_dup(value).each { |v| base_dup.delete(v) }
base_dup + deep_dup(value)
elsif base.is_a?(Hash) && value.is_a?(Hash)
merge_hash(base, value, prepend: false)
else
deep_dup(value)
end
when :batch_update
batch_update_items(base, value)
when :merge
if base.is_a?(Hash) && value.is_a?(Hash)
overwrite(base, value)
elsif value.nil?
base
else
deep_dup(value)
end
else
deep_dup(value)
end
end
def self.apply_set_fields(item, set_values)
keys_to_delete = []
set_values.each do |k, v|
processed_key, operation = parse_key(k)
result = apply_operation(item[processed_key], v, operation)
if result.equal?(DELETED_SENTINEL)
keys_to_delete << processed_key
else
item[processed_key] = result
end
end
keys_to_delete.each { |k| item.delete(k) }
end
def self.match_item(item, where_conditions, key = nil)
where_conditions.all? do |k, v|
if k == 'key' && !key.nil?
match_value(key, v)
elsif item.is_a?(Hash)
match_value(item[k] || item[k.to_s], v)
elsif item.is_a?(String) && k == 'value'
match_value(item, v)
else
false
end
end
end
def self.batch_update_items(collection, update_spec)
return collection unless update_spec.is_a?(Hash)
begin
where_conditions = update_spec['where'] || {}
set_values = update_spec['set'] || {}
if collection.is_a?(Array)
result = collection.dup
delete_indices = []
result.each_with_index do |item, index|
match = match_item(item, where_conditions)
if match
if item.is_a?(Hash)
apply_set_fields(item, set_values)
elsif item.is_a?(String) && set_values.key?('value')
new_value = set_values['value']
if new_value.nil?
delete_indices << index
else
result[index] = deep_dup(new_value)
end
end
end
end
delete_indices.reverse_each { |i| result.delete_at(i) }
result
elsif collection.is_a?(Hash)
if where_conditions.any? { |k, _| k != 'key' } &&
match_item(collection, where_conditions)
result = collection.dup
apply_set_fields(result, set_values)
result
else
result = collection.dup
keys_to_delete = []
result.each do |key, value|
next unless value.is_a?(Hash)
match = match_item(value, where_conditions, key)
if match
if set_values.key?('key-') || (set_values.key?('key') && set_values['key'].nil?)
keys_to_delete << key
else
apply_set_fields(value, set_values)
end
end
end
keys_to_delete.each { |k| result.delete(k) }
result
end
elsif collection.nil?
nil
else
collection
end
rescue => e
LOG_ERROR("YAML overwrite failed:【(batch update) => update_spec: %s, error: %s】" % [update_spec, e.message])
collection
end
end
end
@@ -86,17 +86,6 @@ DOWNLOAD_RESULT=$?
config_cus_up()
{
if [ -z "$CONFIG_PATH" ]; then
for file_name in /etc/openclash/config/*
do
if [ -f "$file_name" ]; then
CONFIG_PATH=$file_name
break
fi
done
uci -q set openclash.config.config_path="$CONFIG_PATH"
uci commit openclash
fi
if [ -z "$subscribe_url_param" ]; then
if [ -n "$key_match_param" ] || [ -n "$key_ex_match_param" ]; then
LOG_OUT "Config File【$name】Start Picking Nodes..."
@@ -192,6 +181,10 @@ config_su_check()
mv "$CFG_FILE" "$CONFIG_FILE" 2>/dev/null
LOG_OUT "Config File【$name】Update Successful!"
fi
if [ -z "$CONFIG_PATH" ]; then
uci -q set openclash.config.config_path="$CONFIG_FILE"
uci commit openclash
fi
if [ "$CONFIG_FILE" == "$CONFIG_PATH" ]; then
restart=1
fi
@@ -335,13 +328,13 @@ sub_info_get()
config_get "udp" "$section" "udp" ""
config_get "skip_cert_verify" "$section" "skip_cert_verify" ""
config_get "sort" "$section" "sort" ""
config_get "convert_address" "$section" "convert_address" ""
config_get "convert_address" "$section" "convert_address" "https://api.asailor.org/sub"
config_get "template" "$section" "template" ""
config_get "node_type" "$section" "node_type" ""
config_get "rule_provider" "$section" "rule_provider" ""
config_get "custom_template_url" "$section" "custom_template_url" ""
config_get "de_ex_keyword" "$section" "de_ex_keyword" ""
config_get "sub_ua" "$section" "sub_ua" "clash.meta"
config_get "sub_ua" "$section" "sub_ua" "clash-verge/v2.4.5"
CONFIG_FILE="/etc/openclash/config/$name.yaml"
CFG_FILE="/tmp/$name.yaml"
@@ -74,7 +74,7 @@ DOWNLOAD_FILE_CURL() {
fi
else
CURL_OUTPUT=$(curl -w "\n%{http_code}" -SsL --connect-timeout 30 -m 60 --speed-time 30 --speed-limit 1 --retry 2 -H "User-Agent: ${DOWNLOAD_UA}" "$DOWNLOAD_URL" -o "$DOWNLOAD_PATH" 2>&1)
EXIR_CODE=${PIPESTATUS[0]}
EXIR_CODE=$?
HTTP_CODE=$(echo "$CURL_OUTPUT" | tail -n1)
if [ "$EXIR_CODE" -ne 0 ] || [ "$HTTP_CODE" -ne 200 ]; then
@@ -0,0 +1,179 @@
#!/bin/bash
. /usr/share/openclash/openclash_ps.sh
. /usr/share/openclash/log.sh
. /usr/share/openclash/openclash_curl.sh
. /usr/share/openclash/uci.sh
set_lock() {
exec 888>"/tmp/lock/openclash_update_databases.lock" 2>/dev/null
flock -x 888 2>/dev/null
}
del_lock() {
flock -u 888 2>/dev/null
rm -rf "/tmp/lock/openclash_update_databases.lock" 2>/dev/null
}
set_lock
inc_job_counter
small_flash_memory=$(uci_get_config "small_flash_memory")
github_address_mod=$(uci_get_config "github_address_mod" || echo 0)
restart=0
update_one() {
local type="$1"
local custom_key="$2"
local tmpfile="$3"
local path="$4"
local name="$5"
local min_size=10240
local download_url=""
local custom_url=$(uci_get_config "$custom_key")
if [ "$small_flash_memory" != "1" ]; then
mkdir -p /etc/openclash
else
mkdir -p /tmp/etc/openclash
fi
case "$type" in
geoip)
if [ -z "$custom_url" ]; then
if [ "$github_address_mod" != "0" ]; then
if [ "$github_address_mod" == "https://cdn.jsdelivr.net/" ] || [ "$github_address_mod" == "https://fastly.jsdelivr.net/" ] || [ "$github_address_mod" == "https://testingcf.jsdelivr.net/" ]; then
download_url="${github_address_mod}gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat"
else
download_url="${github_address_mod}https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat"
fi
else
download_url="https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat"
fi
else
download_url="$custom_url"
fi
;;
geosite)
if [ -z "$custom_url" ]; then
if [ "$github_address_mod" != "0" ]; then
if [ "$github_address_mod" == "https://cdn.jsdelivr.net/" ] || [ "$github_address_mod" == "https://fastly.jsdelivr.net/" ] || [ "$github_address_mod" == "https://testingcf.jsdelivr.net/" ]; then
download_url="${github_address_mod}gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat"
else
download_url="${github_address_mod}https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
fi
else
download_url="https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
fi
else
download_url="$custom_url"
fi
;;
geoasn)
if [ -z "$custom_url" ]; then
if [ "$github_address_mod" != "0" ]; then
if [ "$github_address_mod" == "https://cdn.jsdelivr.net/" ] || [ "$github_address_mod" == "https://fastly.jsdelivr.net/" ] || [ "$github_address_mod" == "https://testingcf.jsdelivr.net/" ]; then
download_url="${github_address_mod}gh/xishang0128/geoip@release/GeoLite2-ASN.mmdb"
else
download_url="${github_address_mod}https://github.com/xishang0128/geoip/releases/latest/download/GeoLite2-ASN.mmdb"
fi
else
download_url="https://github.com/xishang0128/geoip/releases/latest/download/GeoLite2-ASN.mmdb"
fi
else
download_url="$custom_url"
fi
;;
ipdb)
if [ -z "$custom_url" ]; then
if [ "$github_address_mod" != "0" ]; then
if [ "$github_address_mod" == "https://cdn.jsdelivr.net/" ] || [ "$github_address_mod" == "https://fastly.jsdelivr.net/" ] || [ "$github_address_mod" == "https://testingcf.jsdelivr.net/" ]; then
download_url="${github_address_mod}gh/alecthw/mmdb_china_ip_list@release/lite/Country.mmdb"
else
download_url="${github_address_mod}https://raw.githubusercontent.com/alecthw/mmdb_china_ip_list/release/lite/Country.mmdb"
fi
else
download_url="https://raw.githubusercontent.com/alecthw/mmdb_china_ip_list/release/lite/Country.mmdb"
fi
else
download_url="$custom_url"
fi
;;
*)
return 1
;;
esac
LOG_OUT "【$name】Start Downloading Database..."
DOWNLOAD_FILE_CURL "$download_url" "/tmp/$tmpfile" "$path"
DOWNLOAD_RESULT=$?
if [ "$DOWNLOAD_RESULT" -eq 0 ] && [ -s "/tmp/$tmpfile" ]; then
if head -c 512 "/tmp/$tmpfile" | grep -qiE "<!doctype|<html|<head|<body"; then
LOG_OUT "【$name】Download Failed: HTML Response Detected, Abort Update..."
rm -rf "/tmp/$tmpfile"
elif [ $(du -b "/tmp/$tmpfile" 2>/dev/null | awk '{print $1}' || echo "$min_size") -lt "$min_size" ]; then
LOG_OUT "【$name】Download Failed: File Size Too Small, Abort Update..."
rm -rf "/tmp/$tmpfile"
else
LOG_OUT "【$name】Download Success, Check Updated..."
cmp -s "/tmp/$tmpfile" "$path"
if [ "$?" -ne 0 ]; then
LOG_OUT "【$name】Has Been Updated, Starting To Replace The Old Version..."
case "$type" in
geoip)
rm -rf "/etc/openclash/geoip.dat"
;;
geosite)
rm -rf "/etc/openclash/geosite.dat"
;;
geoasn)
rm -rf "/etc/openclash/GeoLite2-ASN.mmdb"
;;
esac
mv "/tmp/$tmpfile" "$path" >/dev/null 2>&1
LOG_OUT "【$name】Update Successful!"
restart=1
else
LOG_OUT "【$name】No Change, Do Nothing..."
fi
fi
elif [ "$DOWNLOAD_RESULT" -eq 2 ]; then
LOG_OUT "【$name】No Change, Do Nothing..."
else
LOG_OUT "【$name】Update Error, Please Try Again Later..."
fi
rm -rf "/tmp/$tmpfile" >/dev/null 2>&1
}
case "$1" in
all)
update_one geoip "geoip_custom_url" "GeoIP.dat" "/etc/openclash/GeoIP.dat" "GeoIP.dat"
update_one geosite "geosite_custom_url" "GeoSite.dat" "/etc/openclash/GeoSite.dat" "GeoSite.dat"
update_one geoasn "geoasn_custom_url" "GeoLite2-ASN.mmdb" "/etc/openclash/ASN.mmdb" "ASN.mmdb"
update_one ipdb "geo_custom_url" "Country.mmdb" "/etc/openclash/Country.mmdb" "Country.mmdb"
;;
geoip)
update_one geoip "geoip_custom_url" "GeoIP.dat" "/etc/openclash/GeoIP.dat" "GeoIP.dat"
;;
geosite)
update_one geosite "geosite_custom_url" "GeoSite.dat" "/etc/openclash/GeoSite.dat" "GeoSite.dat"
;;
geoasn)
update_one geoasn "geoasn_custom_url" "GeoLite2-ASN.mmdb" "/etc/openclash/ASN.mmdb" "ASN.mmdb"
;;
ipdb)
update_one ipdb "geo_custom_url" "Country.mmdb" "/etc/openclash/Country.mmdb" "Country.mmdb"
;;
*)
echo "Usage: $0 {all|geoip|geosite|geoasn|ipdb}"
del_lock
dec_job_counter_and_restart "$restart"
exit 1
;;
esac
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
@@ -1,70 +0,0 @@
#!/bin/bash
. /usr/share/openclash/openclash_ps.sh
. /usr/share/openclash/log.sh
. /usr/share/openclash/openclash_curl.sh
. /usr/share/openclash/uci.sh
set_lock() {
exec 874>"/tmp/lock/openclash_geoasn.lock" 2>/dev/null
flock -x 874 2>/dev/null
}
del_lock() {
flock -u 874 2>/dev/null
rm -rf "/tmp/lock/openclash_geoasn.lock" 2>/dev/null
}
set_lock
inc_job_counter
small_flash_memory=$(uci_get_config "small_flash_memory")
GEOASN_CUSTOM_URL=$(uci_get_config "geoasn_custom_url")
github_address_mod=$(uci_get_config "github_address_mod" || echo 0)
restart=0
if [ "$small_flash_memory" != "1" ]; then
geoasn_path="/etc/openclash/ASN.mmdb"
mkdir -p /etc/openclash
else
geoasn_path="/tmp/etc/openclash/ASN.mmdb"
mkdir -p /tmp/etc/openclash
fi
LOG_OUT "Start Downloading Geo ASN Database..."
if [ -z "$GEOASN_CUSTOM_URL" ]; then
if [ "$github_address_mod" != "0" ]; then
if [ "$github_address_mod" == "https://cdn.jsdelivr.net/" ] || [ "$github_address_mod" == "https://fastly.jsdelivr.net/" ] || [ "$github_address_mod" == "https://testingcf.jsdelivr.net/" ]; then
DOWNLOAD_URL="${github_address_mod}gh/xishang0128/geoip@release/GeoLite2-ASN.mmdb"
else
DOWNLOAD_URL="${github_address_mod}https://github.com/xishang0128/geoip/releases/latest/download/GeoLite2-ASN.mmdb"
fi
else
DOWNLOAD_URL="https://github.com/xishang0128/geoip/releases/latest/download/GeoLite2-ASN.mmdb"
fi
else
DOWNLOAD_URL=$GEOASN_CUSTOM_URL
fi
DOWNLOAD_FILE_CURL "$DOWNLOAD_URL" "/tmp/GeoLite2-ASN.mmdb" "$geoasn_path"
DOWNLOAD_RESULT=$?
if [ "$DOWNLOAD_RESULT" -eq 0 ] && [ -s "/tmp/GeoLite2-ASN.mmdb" ]; then
LOG_OUT "Geo ASN Database Download Success, Check Updated..."
cmp -s /tmp/GeoLite2-ASN.mmdb "$geoasn_path"
if [ "$?" -ne "0" ]; then
LOG_OUT "Geo ASN Database Has Been Updated, Starting To Replace The Old Version..."
rm -rf "/etc/openclash/GeoLite2-ASN.mmdb"
mv /tmp/GeoLite2-ASN.mmdb "$geoasn_path" >/dev/null 2>&1
LOG_OUT "Geo ASN Database Update Successful!"
restart=1
else
LOG_OUT "Updated Geo ASN Database No Change, Do Nothing..."
fi
elif [ "$DOWNLOAD_RESULT" -eq 2 ]; then
LOG_OUT "Updated Geo ASN Database No Change, Do Nothing..."
else
LOG_OUT "Geo ASN Database Update Error, Please Try Again Later..."
fi
rm -rf /tmp/GeoLite2-ASN.mmdb >/dev/null 2>&1
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
@@ -1,70 +0,0 @@
#!/bin/bash
. /usr/share/openclash/openclash_ps.sh
. /usr/share/openclash/log.sh
. /usr/share/openclash/openclash_curl.sh
. /usr/share/openclash/uci.sh
set_lock() {
exec 873>"/tmp/lock/openclash_geoip.lock" 2>/dev/null
flock -x 873 2>/dev/null
}
del_lock() {
flock -u 873 2>/dev/null
rm -rf "/tmp/lock/openclash_geoip.lock" 2>/dev/null
}
set_lock
inc_job_counter
small_flash_memory=$(uci_get_config "small_flash_memory")
GEOIP_CUSTOM_URL=$(uci_get_config "geoip_custom_url")
github_address_mod=$(uci_get_config "github_address_mod" || echo 0)
restart=0
if [ "$small_flash_memory" != "1" ]; then
geoip_path="/etc/openclash/GeoIP.dat"
mkdir -p /etc/openclash
else
geoip_path="/tmp/etc/openclash/GeoIP.dat"
mkdir -p /tmp/etc/openclash
fi
LOG_OUT "Start Downloading GeoIP Dat..."
if [ -z "$GEOIP_CUSTOM_URL" ]; then
if [ "$github_address_mod" != "0" ]; then
if [ "$github_address_mod" == "https://cdn.jsdelivr.net/" ] || [ "$github_address_mod" == "https://fastly.jsdelivr.net/" ] || [ "$github_address_mod" == "https://testingcf.jsdelivr.net/" ]; then
DOWNLOAD_URL="${github_address_mod}gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat"
else
DOWNLOAD_URL="${github_address_mod}https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat"
fi
else
DOWNLOAD_URL="https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat"
fi
else
DOWNLOAD_URL=$GEOIP_CUSTOM_URL
fi
DOWNLOAD_FILE_CURL "$DOWNLOAD_URL" "/tmp/GeoIP.dat" "$geoip_path"
DOWNLOAD_RESULT=$?
if [ "$DOWNLOAD_RESULT" -eq 0 ] && [ -s "/tmp/GeoIP.dat" ]; then
LOG_OUT "GeoIP Dat Download Success, Check Updated..."
cmp -s /tmp/GeoIP.dat "$geoip_path"
if [ "$?" -ne "0" ]; then
LOG_OUT "GeoIP Dat Has Been Updated, Starting To Replace The Old Version..."
rm -rf "/etc/openclash/geoip.dat"
mv /tmp/GeoIP.dat "$geoip_path" >/dev/null 2>&1
LOG_OUT "GeoIP Dat Update Successful!"
restart=1
else
LOG_OUT "Updated GeoIP Dat No Change, Do Nothing..."
fi
elif [ "$DOWNLOAD_RESULT" -eq 2 ]; then
LOG_OUT "Updated GeoIP Dat No Change, Do Nothing..."
else
LOG_OUT "GeoIP Dat Update Error, Please Try Again Later..."
fi
rm -rf /tmp/GeoIP.dat >/dev/null 2>&1
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
@@ -1,70 +0,0 @@
#!/bin/bash
. /usr/share/openclash/openclash_ps.sh
. /usr/share/openclash/log.sh
. /usr/share/openclash/openclash_curl.sh
. /usr/share/openclash/uci.sh
set_lock() {
exec 874>"/tmp/lock/openclash_geosite.lock" 2>/dev/null
flock -x 874 2>/dev/null
}
del_lock() {
flock -u 874 2>/dev/null
rm -rf "/tmp/lock/openclash_geosite.lock" 2>/dev/null
}
set_lock
inc_job_counter
small_flash_memory=$(uci_get_config "small_flash_memory")
GEOSITE_CUSTOM_URL=$(uci_get_config "geosite_custom_url")
github_address_mod=$(uci_get_config "github_address_mod" || echo 0)
restart=0
if [ "$small_flash_memory" != "1" ]; then
geosite_path="/etc/openclash/GeoSite.dat"
mkdir -p /etc/openclash
else
geosite_path="/tmp/etc/openclash/GeoSite.dat"
mkdir -p /tmp/etc/openclash
fi
LOG_OUT "Start Downloading GeoSite Database..."
if [ -z "$GEOSITE_CUSTOM_URL" ]; then
if [ "$github_address_mod" != "0" ]; then
if [ "$github_address_mod" == "https://cdn.jsdelivr.net/" ] || [ "$github_address_mod" == "https://fastly.jsdelivr.net/" ] || [ "$github_address_mod" == "https://testingcf.jsdelivr.net/" ]; then
DOWNLOAD_URL="${github_address_mod}gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat"
else
DOWNLOAD_URL="${github_address_mod}https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
fi
else
DOWNLOAD_URL="https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
fi
else
DOWNLOAD_URL=$GEOSITE_CUSTOM_URL
fi
DOWNLOAD_FILE_CURL "$DOWNLOAD_URL" "/tmp/GeoSite.dat" "$geosite_path"
DOWNLOAD_RESULT=$?
if [ "$DOWNLOAD_RESULT" -eq 0 ] && [ -s "/tmp/GeoSite.dat" ]; then
LOG_OUT "GeoSite Database Download Success, Check Updated..."
cmp -s /tmp/GeoSite.dat "$geosite_path"
if [ "$?" -ne "0" ]; then
LOG_OUT "GeoSite Database Has Been Updated, Starting To Replace The Old Version..."
rm -rf "/etc/openclash/geosite.dat"
mv /tmp/GeoSite.dat "$geosite_path" >/dev/null 2>&1
LOG_OUT "GeoSite Database Update Successful!"
restart=1
else
LOG_OUT "Updated GeoSite Database No Change, Do Nothing..."
fi
elif [ "$DOWNLOAD_RESULT" -eq 2 ]; then
LOG_OUT "Updated GeoSite Database No Change, Do Nothing..."
else
LOG_OUT "GeoSite Database Update Error, Please Try Again Later..."
fi
rm -rf /tmp/GeoSite.dat >/dev/null 2>&1
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
@@ -1,69 +0,0 @@
#!/bin/bash
. /usr/share/openclash/openclash_ps.sh
. /usr/share/openclash/log.sh
. /usr/share/openclash/openclash_curl.sh
. /usr/share/openclash/uci.sh
set_lock() {
exec 880>"/tmp/lock/openclash_ipdb.lock" 2>/dev/null
flock -x 880 2>/dev/null
}
del_lock() {
flock -u 880 2>/dev/null
rm -rf "/tmp/lock/openclash_ipdb.lock" 2>/dev/null
}
set_lock
inc_job_counter
small_flash_memory=$(uci_get_config "small_flash_memory")
GEOIP_CUSTOM_URL=$(uci_get_config "geo_custom_url")
github_address_mod=$(uci_get_config "github_address_mod" || echo 0)
restart=0
if [ "$small_flash_memory" != "1" ]; then
geoip_path="/etc/openclash/Country.mmdb"
mkdir -p /etc/openclash
else
geoip_path="/tmp/etc/openclash/Country.mmdb"
mkdir -p /tmp/etc/openclash
fi
LOG_OUT "Start Downloading Geoip Database..."
if [ -z "$GEOIP_CUSTOM_URL" ]; then
if [ "$github_address_mod" != "0" ]; then
if [ "$github_address_mod" == "https://cdn.jsdelivr.net/" ] || [ "$github_address_mod" == "https://fastly.jsdelivr.net/" ] || [ "$github_address_mod" == "https://testingcf.jsdelivr.net/" ]; then
DOWNLOAD_URL="${github_address_mod}gh/alecthw/mmdb_china_ip_list@release/lite/Country.mmdb"
else
DOWNLOAD_URL="${github_address_mod}https://raw.githubusercontent.com/alecthw/mmdb_china_ip_list/release/lite/Country.mmdb"
fi
else
DOWNLOAD_URL="https://raw.githubusercontent.com/alecthw/mmdb_china_ip_list/release/lite/Country.mmdb"
fi
else
DOWNLOAD_URL=$GEOIP_CUSTOM_URL
fi
DOWNLOAD_FILE_CURL "$DOWNLOAD_URL" "/tmp/Country.mmdb" "$geoip_path"
DOWNLOAD_RESULT=$?
if [ "$DOWNLOAD_RESULT" -eq 0 ] && [ -s "/tmp/Country.mmdb" ]; then
LOG_OUT "Geoip Database Download Success, Check Updated..."
cmp -s /tmp/Country.mmdb "$geoip_path"
if [ "$?" -ne 0 ]; then
LOG_OUT "Geoip Database Has Been Updated, Starting To Replace The Old Version..."
mv /tmp/Country.mmdb "$geoip_path" >/dev/null 2>&1
LOG_OUT "Geoip Database Update Successful!"
restart=1
else
LOG_OUT "Updated Geoip Database No Change, Do Nothing..."
fi
elif [ "$DOWNLOAD_RESULT" -eq 2 ]; then
LOG_OUT "Updated Geoip Database No Change, Do Nothing..."
else
LOG_OUT "Geoip Database Update Error, Please Try Again Later..."
fi
rm -rf /tmp/Country.mmdb >/dev/null 2>&1
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
@@ -46,7 +46,7 @@ function unlock_auto_select()
local no_old_region_unlock = {}
local full_support_list = {}
local fallback_select = {}
local gorup_i18 = "Group:"
local gorup_i18 = "【"..type.."】Group:"
local no_group_find = "failed to search based on keywords and automatically obtain the group, please confirm the validity of the regex!"
local full_support_no_area = "full support"
local full_support = "full support, area:"
@@ -119,6 +119,10 @@ function unlock_auto_select()
key_group = FS.uci_get_config("config", "stream_auto_select_group_key_google_not_cn") or "google|谷歌"
elseif type == "OpenAI" then
key_group = FS.uci_get_config("config", "stream_auto_select_group_key_openai") or "OpenAI|ChatGPT"
elseif type == "Claude" then
key_group = FS.uci_get_config("config", "stream_auto_select_group_key_claude") or "Claude"
elseif type == "Gemini" then
key_group = FS.uci_get_config("config", "stream_auto_select_group_key_gemini") or "Gemini"
end
if not key_group then key_group = type end
else
@@ -148,9 +152,9 @@ function unlock_auto_select()
--test now proxy
region = proxy_unlock_test()
if table_include(groups, now_name) then
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.."】"
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..group_show.."】"
else
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.." ➟ "..now_name.."】"
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..group_show.." ➟ "..now_name.."】"
end
if status ~= 2 and status ~= 4 then
region = proxy_unlock_test()
@@ -244,7 +248,7 @@ function unlock_auto_select()
end
end
if #(value.all) == 0 then
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..no_nodes_filter)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..no_nodes_filter)
break
end
--loop proxy test
@@ -274,16 +278,16 @@ function unlock_auto_select()
end
end
if #(proxies) == 0 then
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.."】"..no_nodes_filter)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..group_show.."】"..no_nodes_filter)
break
end
for p = 1, #(proxies) do
proxy = proxies[p]
if table_include(groups, proxy) then
group_now = get_group_now(info, proxy)
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.." ➟ "..group_now.."】"
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..group_show.." ➟ "..group_now.."】"
else
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.." ➟ "..proxy.."】"
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..group_show.." ➟ "..proxy.."】"
end
--skip tested proxy
while true do
@@ -303,7 +307,7 @@ function unlock_auto_select()
table.insert(full_support_list, {value.all[i], group_name, proxy, region})
if not all_test then
print(now..full_support.."【"..region.."】")
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..proxy.."】"..area_i18.."【"..region.."】")
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_success.."【"..proxy.."】"..area_i18.."【"..region.."】")
else
print(now..full_support.."【"..region.."】")
end
@@ -312,7 +316,7 @@ function unlock_auto_select()
table.insert(full_support_list, {value.all[i], group_name, proxy})
if not all_test then
print(now..full_support_no_area)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..proxy.."】")
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_success.."【"..proxy.."】")
else
print(now..full_support_no_area)
end
@@ -372,16 +376,16 @@ function unlock_auto_select()
end
region = proxy_unlock_test()
if table_include(groups, now_name) then
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.."】"
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..group_show.."】"
else
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..group_show.." ➟ "..now_name.."】"
now = os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..group_show.." ➟ "..now_name.."】"
end
if status == 2 then
if region ~= "" then
table.insert(full_support_list, {value.all[i], group_name, value.all[i], region})
if not all_test then
print(now..full_support.."【"..region.."】")
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, now_name).."】"..area_i18.."【"..region.."】")
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, now_name).."】"..area_i18.."【"..region.."】")
else
print(now..full_support.."【"..region.."】")
end
@@ -390,7 +394,7 @@ function unlock_auto_select()
table.insert(full_support_list, {value.all[i], group_name, value.all[i]})
if not all_test then
print(now..full_support_no_area)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, now_name).."】")
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, now_name).."】")
else
print(now..full_support_no_area)
end
@@ -469,24 +473,24 @@ function unlock_auto_select()
group_now = group_now .. area_i18 .. "【"..v[4].."】"
end
if #full_support_list > 0 then
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_all_full_support..group_now)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_all_full_support..group_now)
elseif #no_old_region_unlock > 0 then
if not all_test then
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success_no_old_region..group_now)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_success_no_old_region..group_now)
else
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_all_no_old_region..group_now)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_all_no_old_region..group_now)
end
elseif #other_region_unlock > 0 then
if not all_test then
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_faild_other_region..group_now)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_faild_other_region..group_now)
else
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_all_other_region..group_now)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_all_other_region..group_now)
end
else
if not all_test then
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_faild..group_now)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_faild..group_now)
else
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_all_faild..group_now)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_all_faild..group_now)
end
end
close_connections()
@@ -499,7 +503,7 @@ function unlock_auto_select()
else
group_now = value.name.." ➟ "..proxy_default
end
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_faild.."【"..group_now.."】")
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_faild.."【"..group_now.."】")
end
end
elseif #nodes_filter(get_group_now(info, value.name), info) ~= 0 then
@@ -509,12 +513,12 @@ function unlock_auto_select()
if not all_test then
print(now..full_support.."【"..region.."】")
end
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, value.name).."】"..area_i18.."【"..region.."】")
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, value.name).."】"..area_i18.."【"..region.."】")
else
if not all_test then
print(now..full_support_no_area)
end
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, value.name).."】")
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..value.name.."】"..select_success.."【"..get_group_now(info, value.name).."】")
end
if not all_test then
break
@@ -548,7 +552,7 @@ function unlock_auto_select()
if status == 2 and not all_test then break end
end
if not group_match and not auto_get_group then
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..type.." "..gorup_i18.."【"..key_group.."】"..no_group_find)
print(os.date("%Y-%m-%d %H:%M:%S").." [Info] "..gorup_i18.."【"..key_group.."】"..no_group_find)
end
--write cache
FS.writefile(unlock_cache_file, JSON.stringify(unlock_cache_info))
@@ -817,6 +821,10 @@ function nodes_filter(t, info)
regex = FS.uci_get_config("config", "stream_auto_select_node_key_google_not_cn") or ""
elseif type == "OpenAI" then
regex = FS.uci_get_config("config", "stream_auto_select_node_key_openai") or ""
elseif type == "Claude" then
regex = FS.uci_get_config("config", "stream_auto_select_node_key_claude") or ""
elseif type == "Gemini" then
regex = FS.uci_get_config("config", "stream_auto_select_node_key_gemini") or ""
end
if class_type(t) == "table" then
@@ -877,6 +885,10 @@ function proxy_unlock_test()
region = google_not_cn_test()
elseif type == "OpenAI" then
region = openai_unlock_test()
elseif type == "Claude" then
region = claude_unlock_test()
elseif type == "Gemini" then
region = gemini_unlock_test()
end
return region
end
@@ -908,6 +920,10 @@ function auto_get_policy_group(passwd, ip, port)
SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://timeline.google.com &')
elseif type == "OpenAI" then
SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://chatgpt.com/ &')
elseif type == "Claude" then
SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://claude.ai/ &')
elseif type == "Gemini" then
SYS.call('curl -sL -m 5 --limit-rate 1k -o /dev/null https://gemini.google.com/ &')
end
os.execute("sleep 1")
con = SYS.exec(string.format('curl -sL -m 5 --retry 2 -H "Content-Type: application/json" -H "Authorization: Bearer %s" -XGET http://%s:%s/connections', passwd, ip, port))
@@ -976,6 +992,16 @@ function auto_get_policy_group(passwd, ip, port)
auto_get_group = con.connections[i].chains[#(con.connections[i].chains)]
break
end
elseif type == "Claude" then
if string.match(con.connections[i].metadata.host, "claude%.ai") then
auto_get_group = con.connections[i].chains[#(con.connections[i].chains)]
break
end
elseif type == "Gemini" then
if string.match(con.connections[i].metadata.host, "gemini%.google%.com") then
auto_get_group = con.connections[i].chains[#(con.connections[i].chains)]
break
end
end
end
end
@@ -1571,6 +1597,82 @@ function openai_unlock_test()
return region
end
function claude_unlock_test()
status = 0
local url = "https://claude.ai/"
local region_url = "https://claude.ai/cdn-cgi/trace"
local regex = FS.uci_get_config("config", "stream_auto_select_region_key_claude") or ""
local region = ""
local old_region = get_old_region()
local old_regex = get_old_regex()
local data = SYS.exec(string.format("curl -sIL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' '%s'", UA, url))
if data then
if string.find(data, "App unavailable in region") then
status = 1
else
status = 2
local region_data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' '%s'", UA, region_url))
if region_data and string.match(region_data, "loc=%a+") then
region = string.upper(string.sub(string.match(region_data, "loc=%a+"), 5, -1))
if not datamatch(region, regex) then
status = 3
elseif old_regex ~= regex and not all_test then
status = 2
elseif old_region ~= "" and region ~= old_region and not all_test then
status = 4
end
if status == 2 and not all_test and region ~= old_region then
write_cache(type, "old_region", region)
end
if status == 2 and not all_test and regex ~= old_regex then
write_cache(type, "old_regex", regex)
end
end
end
end
return region
end
function gemini_unlock_test()
status = 0
local url = "https://gemini.google.com/"
local regex = FS.uci_get_config("config", "stream_auto_select_region_key_gemini") or ""
local region = ""
local old_region = get_old_region()
local old_regex = get_old_regex()
local data = SYS.exec(string.format("curl -sL --connect-timeout 5 -m 5 --speed-time 5 --speed-limit 1 --retry 2 -H 'Accept-Language: en' -H 'Content-Type: application/json' -H 'User-Agent: %s' '%s'", UA, url))
if not data or string.sub(data, 1, 4) == "curl" then
status = 1
return region
end
if string.find(data, "45631641,null,true", 1, true) then
local cc = string.match(data, ',2,1,200,"([A-Z][A-Z][A-Z])"')
if cc and cc ~= "" then
region = cc
end
status = 2
if region ~= "" then
if not datamatch(region, regex) then
status = 3
elseif old_regex ~= regex and not all_test then
status = 2
elseif old_region ~= "" and region ~= old_region and not all_test then
status = 4
end
if status == 2 and not all_test and region ~= old_region then
write_cache(type, "old_region", region)
end
if status == 2 and not all_test and regex ~= old_regex then
write_cache(type, "old_regex", regex)
end
end
else
status = 1
end
return region
end
function network_test()
local test_url1 = "https://www.gstatic.com/generate_204"
local test_url2 = "https://cp.cloudflare.com/generate_204"
@@ -16,7 +16,6 @@ del_lock() {
set_lock
inc_job_counter
restart=0
if [ -n "$1" ] && [ "$1" != "one_key_update" ]; then
/usr/share/openclash/openclash_version.sh "$1" 2>/dev/null
@@ -29,7 +28,7 @@ fi
if [ ! -f "/tmp/openclash_last_version" ]; then
LOG_ERROR "Failed to get version information, please try again later..."
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
dec_job_counter_and_restart "0"
del_lock
exit 0
fi
@@ -57,7 +56,7 @@ LAST_VER=$(sed -n 1p "$LAST_OPVER" 2>/dev/null |sed "s/^v//g" |tr -d "\n")
if [ -x "/bin/opkg" ]; then
OP_CV=$(rm -f /var/lock/opkg.lock && opkg status luci-app-openclash 2>/dev/null |grep 'Version' |awk -F 'Version: ' '{print $2}' 2>/dev/null)
elif [ -x "/usr/bin/apk" ]; then
OP_CV=$(apk list luci-app-openclash 2>/dev/null|grep 'installed' | grep -oE '[0-9]+(\.[0-9]+)*' | head -1 2>/dev/null)
OP_CV=$(apk list luci-app-openclash 2>/dev/null|grep "installed" | grep -oE '[0-9]+(\.[0-9]+)*' | head -1 2>/dev/null)
fi
OP_LV=$(sed -n 1p "$LAST_OPVER" 2>/dev/null |sed "s/^v//g" |tr -d "\n")
RELEASE_BRANCH=$(uci_get_config "release_branch" || echo "master")
@@ -69,14 +68,12 @@ if [ "$1" = "one_key_update" ]; then
LOG_TIP "If the download fails, try setting the CDN in Overwrite Settings - General Settings - Github Address Modify Options"
fi
if [ -n "$2" ]; then
/usr/share/openclash/openclash_core.sh "Meta" "$1" "$2" >/dev/null 2>&1 &
/usr/share/openclash/openclash_core.sh "Meta" "$1" "$2" >/dev/null 2>&1
github_address_mod="$2"
else
/usr/share/openclash/openclash_core.sh "Meta" "$1" >/dev/null 2>&1 &
/usr/share/openclash/openclash_core.sh "Meta" "$1" >/dev/null 2>&1
github_address_mod=0
fi
wait
else
if [ "$github_address_mod" = "0" ]; then
LOG_TIP "If the download fails, try setting the CDN in Overwrite Settings - General Settings - Github Address Modify Options"
@@ -175,9 +172,8 @@ if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [
elif [ -x "/usr/bin/apk" ]; then
LOG_ERROR "【OpenClash - v$LAST_VER】Pre update test failed after 3 attempts, the file is saved in /tmp/openclash.apk, please try to update manually with【apk add -q --force-overwrite --clean-protected --allow-untrusted /tmp/openclash.apk】"
fi
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
dec_job_counter_and_restart "0"
del_lock
exit 0
fi
@@ -191,8 +187,8 @@ if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [
LOG_ERROR "【OpenClash - v$LAST_VER】Download Failed after 3 attempts, please check the network or try again later!"
rm -rf /tmp/openclash.ipk >/dev/null 2>&1
rm -rf /tmp/openclash.apk >/dev/null 2>&1
dec_job_counter_and_restart "0"
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
del_lock
exit 0
fi
@@ -200,9 +196,8 @@ if [ -n "$OP_CV" ] && [ -n "$OP_LV" ] && version_compare "$OP_CV" "$OP_LV" && [
done
cat > /tmp/openclash_update.sh <<"EOF"
#!/bin/sh
START_LOG="/tmp/openclash_start.log"
LOG_FILE="/tmp/openclash.log"
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
. /usr/share/openclash/log.sh
. /usr/share/openclash/openclash_ps.sh
UPDATE_LOCK="/tmp/lock/openclash_update_install.lock"
mkdir -p /tmp/lock
@@ -224,27 +219,6 @@ fi
trap 'del_update_lock; exit' INT TERM EXIT
LOG_ERROR()
{
if [ -n "${1}" ]; then
echo -e "${1}" > $START_LOG
echo -e "${LOGTIME} [Error] ${1}" >> $LOG_FILE
fi
}
LOG_TIP()
{
if [ -n "${1}" ]; then
echo -e "${1}" > $START_LOG
echo -e "${LOGTIME} [Tip] ${1}" >> $LOG_FILE
fi
}
SLOG_CLEAN()
{
echo "" > $START_LOG
}
check_install_success()
{
local target_version="$1"
@@ -253,7 +227,7 @@ check_install_success()
if [ -x "/bin/opkg" ]; then
current_version=$(rm -f /var/lock/opkg.lock && opkg status luci-app-openclash 2>/dev/null |grep 'Version' |awk -F 'Version: ' '{print $2}' 2>/dev/null)
elif [ -x "/usr/bin/apk" ]; then
current_version=$(apk list luci-app-openclash 2>/dev/null|grep 'installed' | grep -oE '[0-9]+(\.[0-9]+)*' | head -1 2>/dev/null)
current_version=$(apk list luci-app-openclash 2>/dev/null |grep "installed" | grep -oE '[0-9]+(\.[0-9]+)*' | head -1 2>/dev/null)
fi
if [ -n "$current_version" ] && [ "$current_version" = "$target_version" ]; then
@@ -266,41 +240,39 @@ check_install_success()
install_missing_packages() {
local installed_before="$1"
if [ -x "/bin/opkg" ]; then
if [ -n "$installed_before" ]; then
for pkg in $installed_before; do
if ! opkg status "$pkg" >/dev/null 2>&1; then
local retry_count=0
local max_retries=3
while [ $retry_count -lt $max_retries ]; do
retry_count=$((retry_count + 1))
local retry_count=0
local max_retries=3
if [ -x "/bin/opkg" ]; then
if opkg status "$pkg" >/dev/null 2>&1; then
continue
fi
elif [ -x "/usr/bin/apk" ]; then
if apk list "$pkg" |grep "installed" >/dev/null 2>&1; then
continue
fi
fi
LOG_TIP "【$pkg】depended package reinstalling..."
while [ $retry_count -lt $max_retries ]; do
retry_count=$((retry_count + 1))
if [ -x "/bin/opkg" ]; then
opkg install "$pkg"
if [ $? -eq 0 ]; then
break
else
if [ $retry_count -lt $max_retries ]; then
sleep 2
fi
fi
done
fi
done
elif [ -x "/usr/bin/apk" ]; then
for pkg in $installed_before; do
if ! apk info "$pkg" >/dev/null 2>&1; then
local retry_count=0
local max_retries=3
while [ $retry_count -lt $max_retries ]; do
retry_count=$((retry_count + 1))
elif [ -x "/usr/bin/apk" ]; then
apk add "$pkg"
if [ $? -eq 0 ]; then
break
fi
if [ $? -eq 0 ]; then
break
else
if [ $retry_count -lt $max_retries ]; then
sleep 2
else
if [ $retry_count -lt $max_retries ]; then
sleep 2
fi
LOG_ERROR "【$pkg】failed to install, please try to install it manually..."
fi
done
fi
fi
done
done
fi
}
@@ -324,7 +296,7 @@ while [ $install_retry_count -lt $max_install_retries ]; do
opkg install /tmp/openclash.ipk
elif [ -x "/usr/bin/apk" ]; then
for pkg in $packages_to_check; do
if apk info "$pkg" >/dev/null 2>&1; then
if apk list "$pkg" |grep "installed" >/dev/null 2>&1; then
installed_before="$installed_before $pkg"
fi
done
@@ -357,9 +329,9 @@ else
elif [ -x "/usr/bin/apk" ]; then
LOG_ERROR "OpenClash update failed after 3 attempts, the file is saved in /tmp/openclash.apk, please try to update manually with【apk add -q --force-overwrite --clean-protected --allow-untrusted /tmp/openclash.apk】"
fi
SLOG_CLEAN
fi
dec_job_counter_and_restart "0"
SLOG_CLEAN
del_update_lock
EOF
chmod 4755 /tmp/openclash_update.sh
@@ -367,6 +339,8 @@ EOF
if [ ! -f "/tmp/openclash_update.sh" ] || [ ! -s "/tmp/openclash_update.sh" ] || [ ! -x "/tmp/openclash_update.sh" ]; then
LOG_ERROR "Failed to create update script!"
rm -rf /tmp/openclash_update.sh
dec_job_counter_and_restart "0"
SLOG_CLEAN
del_lock
exit 1
fi
@@ -405,8 +379,8 @@ else
else
LOG_TIP "OpenClash has not been updated, stop continuing!"
fi
dec_job_counter_and_restart "0"
SLOG_CLEAN
dec_job_counter_and_restart "$restart"
fi
del_lock
@@ -5,20 +5,10 @@
. /usr/share/openclash/uci.sh
LOG_FILE="/tmp/openclash.log"
CONFIG_FILE="/etc/openclash/$(uci_get_config "config_path" |awk -F '/' '{print $5}' 2>/dev/null)"
ipv6_enable=$(uci_get_config "ipv6_enable" || echo 0)
enable_redirect_dns=$(uci_get_config "enable_redirect_dns")
dns_port=$(uci_get_config "dns_port")
disable_masq_cache=$(uci_get_config "disable_masq_cache")
cfg_update_interval=$(uci_get_config "config_update_interval" || echo 60)
log_size=$(uci_get_config "log_size" || echo 1024)
router_self_proxy=$(uci_get_config "router_self_proxy" || echo 1)
stream_auto_select_interval=$(uci_get_config "stream_auto_select_interval" || echo 30)
skip_proxy_address=$(uci_get_config "skip_proxy_address" || echo 0)
CFG_UPDATE_INT=1
CFG_UPDATE_INT=0
SKIP_PROXY_ADDRESS=1
SKIP_PROXY_ADDRESS_INTERVAL=30
STREAM_AUTO_SELECT=1
STREAM_AUTO_SELECT=0
FIREWALL_RELOAD=0
MAX_FIREWALL_RELOAD=3
FW4=$(command -v fw4)
@@ -155,11 +145,20 @@ end" 2>/dev/null >> $LOG_FILE
while :;
do
CONFIG_FILE="/etc/openclash/$(uci_get_config "config_path" |awk -F '/' '{print $5}' 2>/dev/null)"
ipv6_enable=$(uci_get_config "ipv6_enable" || echo 0)
enable_redirect_dns=$(uci_get_config "enable_redirect_dns")
dns_port=$(uci_get_config "dns_port")
disable_masq_cache=$(uci_get_config "disable_masq_cache")
log_size=$(uci_get_config "log_size" || echo 1024)
router_self_proxy=$(uci_get_config "router_self_proxy" || echo 1)
skip_proxy_address=$(uci_get_config "skip_proxy_address" || echo 0)
cfg_update=$(uci_get_config "auto_update")
cfg_update_mode=$(uci_get_config "config_auto_update_mode")
cfg_update_interval_now=$(uci_get_config "config_update_interval" || echo 60)
cfg_update_interval=$(uci_get_config "config_update_interval" || echo 60)
stream_auto_select=$(uci_get_config "stream_auto_select" || echo 0)
stream_auto_select_interval_now=$(uci_get_config "stream_auto_select_interval" || echo 30)
stream_auto_select_interval=$(uci_get_config "stream_auto_select_interval" || echo 30)
stream_auto_select_netflix=$(uci_get_config "stream_auto_select_netflix" || echo 0)
stream_auto_select_disney=$(uci_get_config "stream_auto_select_disney" || echo 0)
stream_auto_select_hbo_max=$(uci_get_config "stream_auto_select_hbo_max" || echo 0)
@@ -172,6 +171,8 @@ do
stream_auto_select_bilibili=$(uci_get_config "stream_auto_select_bilibili" || echo 0)
stream_auto_select_google_not_cn=$(uci_get_config "stream_auto_select_google_not_cn" || echo 0)
stream_auto_select_openai=$(uci_get_config "stream_auto_select_openai" || echo 0)
stream_auto_select_claude=$(uci_get_config "stream_auto_select_claude" || echo 0)
stream_auto_select_gemini=$(uci_get_config "stream_auto_select_gemini" || echo 0)
upnp_lease_file=$(uci -q get upnpd.config.upnp_lease_file)
#wait for core start complete
@@ -362,66 +363,72 @@ fi
## 配置文件循环更新
if [ "$cfg_update" -eq 1 ] && [ "$cfg_update_mode" -eq 1 ]; then
[ "$cfg_update_interval" -ne "$cfg_update_interval_now" ] && CFG_UPDATE_INT=0 && cfg_update_interval="$cfg_update_interval_now"
if [ "$CFG_UPDATE_INT" -ne 0 ]; then
[ "$(expr "$CFG_UPDATE_INT" % "$cfg_update_interval_now")" -eq 0 ] && /usr/share/openclash/openclash.sh
[ "$(expr "$CFG_UPDATE_INT" % "$cfg_update_interval")" -eq 0 ] && /usr/share/openclash/openclash.sh
fi
CFG_UPDATE_INT=$(expr "$CFG_UPDATE_INT" + 1)
fi
##STREAMING_UNLOCK_CHECK
if [ "$stream_auto_select" -eq 1 ] && [ "$router_self_proxy" -eq 1 ]; then
[ "$stream_auto_select_interval" -ne "$stream_auto_select_interval_now" ] && STREAM_AUTO_SELECT=1 && stream_auto_select_interval="$stream_auto_select_interval_now"
if [ "$STREAM_AUTO_SELECT" -ne 0 ]; then
if [ "$(expr "$STREAM_AUTO_SELECT" % "$stream_auto_select_interval_now")" -eq 0 ] || [ "$STREAM_AUTO_SELECT" -eq 1 ]; then
if [ "$(expr "$STREAM_AUTO_SELECT" % "$stream_auto_select_interval")" -eq 0 ] || [ "$STREAM_AUTO_SELECT" -eq 1 ]; then
if [ "$stream_auto_select_netflix" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For Netflix Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【Netflix】..."
/usr/share/openclash/openclash_streaming_unlock.lua "Netflix" >> $LOG_FILE
fi
if [ "$stream_auto_select_disney" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For Disney Plus Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【Disney Plus】..."
/usr/share/openclash/openclash_streaming_unlock.lua "Disney Plus" >> $LOG_FILE
fi
if [ "$stream_auto_select_google_not_cn" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For Google Not CN Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【Google Not CN】..."
/usr/share/openclash/openclash_streaming_unlock.lua "Google" >> $LOG_FILE
fi
if [ "$stream_auto_select_ytb" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For YouTube Premium Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【YouTube Premium】..."
/usr/share/openclash/openclash_streaming_unlock.lua "YouTube Premium" >> $LOG_FILE
fi
if [ "$stream_auto_select_prime_video" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For Amazon Prime Video Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【Amazon Prime Video】..."
/usr/share/openclash/openclash_streaming_unlock.lua "Amazon Prime Video" >> $LOG_FILE
fi
if [ "$stream_auto_select_hbo_max" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For HBO Max Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【HBO Max】..."
/usr/share/openclash/openclash_streaming_unlock.lua "HBO Max" >> $LOG_FILE
fi
if [ "$stream_auto_select_tvb_anywhere" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For TVB Anywhere+ Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【TVB Anywhere+】..."
/usr/share/openclash/openclash_streaming_unlock.lua "TVB Anywhere+" >> $LOG_FILE
fi
if [ "$stream_auto_select_dazn" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For DAZN Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【DAZN】..."
/usr/share/openclash/openclash_streaming_unlock.lua "DAZN" >> $LOG_FILE
fi
if [ "$stream_auto_select_paramount_plus" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For Paramount Plus Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【Paramount Plus】..."
/usr/share/openclash/openclash_streaming_unlock.lua "Paramount Plus" >> $LOG_FILE
fi
if [ "$stream_auto_select_discovery_plus" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For Discovery Plus Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【Discovery Plus】..."
/usr/share/openclash/openclash_streaming_unlock.lua "Discovery Plus" >> $LOG_FILE
fi
if [ "$stream_auto_select_bilibili" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For Bilibili Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【Bilibili】..."
/usr/share/openclash/openclash_streaming_unlock.lua "Bilibili" >> $LOG_FILE
fi
if [ "$stream_auto_select_openai" -eq 1 ]; then
LOG_TIP "Start Auto Select Proxy For OpenAI Unlock..."
LOG_TIP "Start Auto Select Unlock Proxy For【OpenAI】..."
/usr/share/openclash/openclash_streaming_unlock.lua "OpenAI" >> $LOG_FILE
fi
if [ "$stream_auto_select_claude" -eq 1 ]; then
LOG_TIP "Start Auto Select Unlock Proxy For【Claude】..."
/usr/share/openclash/openclash_streaming_unlock.lua "Claude" >> $LOG_FILE
fi
if [ "$stream_auto_select_gemini" -eq 1 ]; then
LOG_TIP "Start Auto Select Unlock Proxy For【Gemini】..."
/usr/share/openclash/openclash_streaming_unlock.lua "Gemini" >> $LOG_FILE
fi
fi
fi
STREAM_AUTO_SELECT=$(expr "$STREAM_AUTO_SELECT" + 1)
@@ -672,6 +672,7 @@ proxies: # socks5
grpc-opts:
grpc-service-name: "example"
# grpc-user-agent: "grpc-go/1.36.0"
# ping-interval: 0 # 默认关闭,单位为秒
# ip-version: ipv4
# vless
@@ -762,6 +763,7 @@ proxies: # socks5
grpc-opts:
grpc-service-name: "grpc"
# grpc-user-agent: "grpc-go/1.36.0"
# ping-interval: 0 # 默认关闭,单位为秒
reality-opts:
public-key: CrrQSjAG_YkHLwvM2M-7XkKJilgL5upBKCp0od0tLhE
@@ -790,6 +792,69 @@ proxies: # socks5
# v2ray-http-upgrade: false
# v2ray-http-upgrade-fast-open: false
- name: "vless-xhttp"
type: vless
server: server
port: 443
uuid: uuid
udp: true
tls: true
network: xhttp
alpn:
- h2
# ech-opts: ...
# reality-opts: ...
# skip-cert-verify: false
# fingerprint: ...
# certificate: ...
# private-key: ...
servername: xxx.com
client-fingerprint: chrome
encryption: ""
xhttp-opts:
path: "/"
host: xxx.com
# mode: "stream-one" # Available: "stream-one", "stream-up" or "packet-up"
# headers:
# X-Forwarded-For: ""
# no-grpc-header: false
# x-padding-bytes: "100-1000"
# reuse-settings: # aka XMUX
# max-connections: "16-32"
# max-concurrency: "0"
# c-max-reuse-times: "0"
# h-max-request-times: "600-900"
# h-max-reusable-secs: "1800-3000"
# download-settings:
# ## xhttp part
# path: "/"
# host: xxx.com
# headers:
# X-Forwarded-For: ""
# no-grpc-header: false
# x-padding-bytes: "100-1000"
# reuse-settings: # aka XMUX
# max-connections: "16-32"
# max-concurrency: "0"
# c-max-reuse-times: "0"
# h-max-request-times: "600-900"
# h-max-reusable-secs: "1800-3000"
# ## proxy part
# server: server
# port: 443
# tls: true
# alpn:
# - h2
# ech-opts: ...
# reality-opts: ...
# skip-cert-verify: false
# fingerprint: ...
# certificate: ...
# private-key: ...
# servername: xxx.com
# client-fingerprint: chrome
# Trojan
- name: "trojan"
type: trojan
@@ -833,6 +898,7 @@ proxies: # socks5
grpc-opts:
grpc-service-name: "example"
# grpc-user-agent: "grpc-go/1.36.0"
# ping-interval: 0 # 默认关闭,单位为秒
- name: trojan-ws
server: server
@@ -1080,6 +1146,8 @@ proxies: # socks5
# multiplexing: MULTIPLEXING_LOW
# 如果想开启 0-RTT 握手,请设置为 HANDSHAKE_NO_WAIT,否则请设置为 HANDSHAKE_STANDARD。默认值为 HANDSHAKE_STANDARD
# handshake-mode: HANDSHAKE_STANDARD
# 一个 base64 字符串用于微调网络行为
# traffic-pattern: ""
# sudoku
- name: sudoku
@@ -1090,15 +1158,17 @@ proxies: # socks5
aead-method: chacha20-poly1305 # 可选:chacha20-poly1305、aes-128-gcm、none(不建议;且 enable-pure-downlink=false 时不可用)
padding-min: 2 # 最小填充率(0-100)
padding-max: 7 # 最大填充率(0-100,必须 >= padding-min)
table-type: prefer_ascii # 可选值:prefer_ascii、prefer_entropy 前者全ascii映射,后者保证熵值(汉明1)低于3
# custom-table: xpxvvpvv # 可选,自定义字节布局,必须包含2个x、2个p、4个v,可随意组合。启用此处则需配置`table-type`为`prefer_entropy`
# custom-tables: ["xpxvvpvv", "vxpvxvvp"] # 可选,自定义字节布局列表(x/v/p),用于 xvp 模式轮换;非空时覆盖 custom-table
http-mask: true # 是否启用http掩码
# http-mask-mode: legacy # 可选:legacy(默认)、stream(split-stream)、poll、auto(先 stream 再 poll);stream/poll/auto 支持走 CDN/反代
# http-mask-tls: true # 可选:仅在 http-mask-mode 为 stream/poll/auto 时生效;true 强制 https;false 强制 http(不会根据端口自动推断)
# http-mask-host: "" # 可选:覆盖 Host/SNI(支持 example.com 或 example.com:443);仅在 http-mask-mode 为 stream/poll/auto 时生效
# path-root: "" # 可选:HTTP 隧道端点一级路径前缀(双方需一致),例如 "aabbcc" 或 "/aabbcc/" => /aabbcc/session、/aabbcc/stream、/aabbcc/api/v1/upload
# http-mask-multiplex: off # 可选:off(默认)、auto(复用底层 HTTP 连接,减少建链 RTT)、on(Sudoku mux 单隧道多目标;仅在 http-mask-mode=stream/poll/auto 生效)
table-type: prefer_ascii # 可选值:prefer_ascii、prefer_entropy、up_ascii_down_entropy、up_entropy_down_ascii
# custom-table: xpxvvpvv # 可选,自定义字节布局,必须包含2个x、2个p、4个v,可随意组合;只对 entropy 方向生效
# custom-tables: ["xpxvvpvv", "vxpvxvvp"] # 可选,自定义字节布局列表(x/v/p),非空时覆盖 custom-table
# 推荐:使用 httpmask 对象统一管理 HTTPMask 相关字段:
httpmask:
disable: false # true 禁用所有 HTTP 伪装/隧道
mode: legacy # 可选:legacy(默认)、stream(split-stream)、poll、auto(先 stream 再 poll)、ws(WebSocket 隧道)
# tls: true # 可选:按需开启 HTTPS/WSS
# host: "" # 可选:覆盖 Host/SNI(支持 example.com 或 example.com:443);仅在 mode 为 stream/poll/auto/ws 时生效
# path-root: "" # 可选:HTTP 隧道端点一级路径前缀(双方需一致),例如 "aabbcc" 或 "/aabbcc/" => /aabbcc/session、/aabbcc/stream、/aabbcc/api/v1/upload、/aabbcc/ws
# multiplex: "off" # 可选字符串:off(默认)、auto(复用底层 HTTP 连接,减少建链 RTT)、on(Sudoku mux 单隧道多目标;仅在 mode=stream/poll/auto 生效;ws 强制 off)
enable-pure-downlink: false # 可选:false=带宽优化下行(更快,要求 aead-method != none);true=纯 Sudoku 下行
# anytls
@@ -1118,6 +1188,23 @@ proxies: # socks5
# - http/1.1
# skip-cert-verify: true
# trusttunnel
- name: trusttunnel
type: trusttunnel
server: 1.2.3.4
port: 443
username: username
password: password
# client-fingerprint: chrome
health-check: true
udp: true
# sni: "example.com"
# alpn:
# - h2
# skip-cert-verify: true
# quic: true # 默认为false
# congestion-controller: bbr
# dns 出站会将请求劫持到内部 dns 模块,所有请求均在内部处理
- name: "dns-out"
type: dns
@@ -1555,6 +1642,12 @@ listeners:
flow: xtls-rprx-vision
# ws-path: "/" # 如果不为空则开启 websocket 传输层
# grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层
# xhttp-config: # 如果不为空则开启 xhttp 传输层
# path: "/"
# host: ""
# mode: auto # Available: "stream-one", "stream-up" or "packet-up"
# no-sse-header: false
# sc-stream-up-server-secs: "20-80"
# -------------------------
# vless encryption服务端配置:
# (原生外观 / 只 XOR 公钥 / 全随机数。1-RTT 每次下发随机 300 到 600 秒的 ticket 以便 0-RTT 复用 / 只允许 1-RTT)
@@ -1631,6 +1724,8 @@ listeners:
users:
username1: password1
username2: password2
# 一个 base64 字符串用于微调网络行为
# traffic-pattern: ""
- name: sudoku-in-1
type: sudoku
@@ -1640,14 +1735,18 @@ listeners:
aead-method: chacha20-poly1305 # 可选:chacha20-poly1305、aes-128-gcm、none(不建议;且 enable-pure-downlink=false 时不可用)
padding-min: 1 # 最小填充率(0-100)
padding-max: 15 # 最大填充率(0-100,必须 >= padding-min)
table-type: prefer_ascii # 可选值:prefer_ascii、prefer_entropy 前者全ascii映射,后者保证熵值(汉明1)低于3
# custom-table: xpxvvpvv # 可选,自定义字节布局,必须包含2个x、2个p、4个v,可随意组合。启用此处则需配置`table-type`为`prefer_entropy`
# custom-tables: ["xpxvvpvv", "vxpvxvvp"] # 可选,自定义字节布局列表(x/v/p),用于 xvp 模式轮换;非空时覆盖 custom-table
table-type: prefer_ascii # 可选值:prefer_ascii、prefer_entropy、up_ascii_down_entropy、up_entropy_down_ascii
# custom-table: xpxvvpvv # 可选,自定义字节布局,必须包含2个x、2个p、4个v,可随意组合;只对 entropy 方向生效
# custom-tables: ["xpxvvpvv", "vxpvxvvp"] # 可选,自定义字节布局列表(x/v/p),用于多表轮换;非空时覆盖 custom-table
handshake-timeout: 5 # 可选(秒)
enable-pure-downlink: false # 可选:false=带宽优化下行(更快,要求 aead-method != none);true=纯 Sudoku 下行
disable-http-mask: false # 可选:禁用 http 掩码/隧道(默认为 false)
# http-mask-mode: legacy # 可选:legacy(默认)、stream(split-stream)、poll、auto(先 stream 再 poll);stream/poll/auto 支持走 CDN/反代
# path-root: "" # 可选:HTTP 隧道端点一级路径前缀(双方需一致),例如 "aabbcc" 或 "/aabbcc/" => /aabbcc/session、/aabbcc/stream、/aabbcc/api/v1/upload
# 推荐:使用 httpmask 对象统一管理 HTTPMask 相关字段:
httpmask:
disable: false # true 禁用所有 HTTP 伪装/隧道
mode: legacy # 可选:legacy(默认)、stream(split-stream)、poll、auto(先 stream 再 poll)、ws(WebSocket 隧道)
# path-root: "" # 可选:HTTP 隧道端点一级路径前缀(双方需一致),例如 "aabbcc" 或 "/aabbcc/" => /aabbcc/session、/aabbcc/stream、/aabbcc/api/v1/upload、/aabbcc/ws
#
# fallback: "127.0.0.1:80" # 可选:用于可连接请求的回落转发,可与其他服务共端口
@@ -1734,6 +1833,30 @@ listeners:
# masquerade: http://127.0.0.1:8080 #作为反向代理
# masquerade: https://127.0.0.1:8080 #作为反向代理
- name: trusttunnel-in-1
type: trusttunnel
port: 10821 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
listen: 0.0.0.0
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
users:
- username: 1
password: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
certificate: ./server.crt # 证书 PEM 格式,或者 证书的路径
private-key: ./server.key # 证书对应的私钥 PEM 格式,或者私钥路径
network: ["tcp", "udp"] # http2+http3
congestion-controller: bbr
# 下面两项为mTLS配置项,如果client-auth-type设置为 "verify-if-given" 或 "require-and-verify" 则client-auth-cert必须不为空
# client-auth-type: "" # 可选值:""、"request"、"require-any"、"verify-if-given"、"require-and-verify"
# client-auth-cert: string # 证书 PEM 格式,或者 证书的路径
# 如果填写则开启ech(可由 mihomo generate ech-keypair <明文域名> 生成)
# ech-key: |
# -----BEGIN ECH KEYS-----
# ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK
# madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz
# dC5jb20AAA==
# -----END ECH KEYS-----
# 注意,listeners中的tun仅提供给高级用户使用,普通用户应使用顶层配置中的tun
- name: tun-in-1
type: tun
@@ -41,7 +41,7 @@ if [ "$1" = "fake-ip" ] && [ "$enable_redirect_dns" != "2" ]; then
awk -v mode="$fake_ip_filter_mode" '
!/^$/ && !/^#/ {
# 跳过IPv4和IPv6地址
if ($0 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ || $0 ~ /:/) {
if ($0 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(\/[0-9]+)?$/ || $0 ~ /:/) {
next
}
if (mode == "blacklist") {
@@ -185,7 +185,7 @@ set_disable_qtype()
yml_dns_get()
{
local section="$1" regex='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$'
local enabled port type ip group dns_type dns_address interface specific_group node_resolve http3 ecs_subnet ecs_override disable_qtype_param
local enabled port type ip group dns_type dns_address interface specific_group node_resolve direct_nameserver http3 skip_cert_verify ecs_subnet ecs_override disable_qtype_param disable_qtype disable_ipv4 disable_ipv6 disable_reuse
config_get_bool "enabled" "$section" "enabled" "1"
[ "$enabled" = "0" ] && return
@@ -378,6 +378,8 @@ begin
smart_collect = '${44}' == '1'
smart_collect_size = '${45}'
fake_ip_range6 = '${46}'
fake_ip_range6_enable = '${47}' == '1'
global_ua = '${48}'
default_dashboard = '$default_dashboard'
yacd_type = '$yacd_type'
dashboard_type = '$dashboard_type'
@@ -408,6 +410,7 @@ begin
Value['external-controller'] = '0.0.0.0:' + controller_port
Value['secret'] = secret
Value['bind-address'] = '*'
Value['global-ua'] = global_ua if global_ua != '0'
Value['external-ui'] = '/usr/share/openclash/ui'
Value['external-ui-name'] = default_dashboard
case default_dashboard
@@ -480,7 +483,7 @@ begin
else
Value['dns']['enhanced-mode'] = 'fake-ip'
Value['dns']['fake-ip-range'] = fake_ip_range
if Value['dns']['ipv6']
if Value['dns']['ipv6'] and fake_ip_range6_enable
Value['dns']['fake-ip-range6'] = fake_ip_range6
end
end
@@ -657,14 +660,13 @@ begin
end
if fake_ip_mode == 'fake-ip' && (china_ip_route || china_ip6_route)
filter_mode = Value.dig('dns', 'fake-ip-filter-mode')
filters = Value.dig('dns', 'fake-ip-filter') || []
deleted_filters = filters.select { |f| f =~ /(geosite:?|rule-set:?).*(@cn|:cn|,cn|:china)/i }
if filter_mode == 'blacklist' || filter_mode.nil?
if !deleted_filters.any?
(Value['dns']['fake-ip-filter'] ||= []) << 'geosite:cn'
YAML.LOG_TIP('Because Need Ensure Bypassing IP Option Work, Added The Fake-IP-Filter Rule【geosite:cn】...')
end
else
filter_rule = 'rule-set:oc-cn-domain'
(Value['dns']['fake-ip-filter'] ||= []) << filter_rule
end
if filter_mode == 'whitelist'
filters = Value.dig('dns', 'fake-ip-filter') || []
deleted_filters = filters.select { |f| f =~ /(geosite:?|rule-set:?).*(@cn|:cn|,cn|:china)/i }
if deleted_filters.any?
Value['dns']['fake-ip-filter'] -= deleted_filters
deleted_filters.each do |f|
@@ -673,8 +675,14 @@ begin
end
end
if filter_mode == 'rule'
(Value['dns']['fake-ip-filter'] ||= []).unshift('GEOSITE,cn,real-ip')
YAML.LOG_TIP('Because Need Ensure Bypassing IP Option Work, Added The Fake-IP-Filter Rule【GEOSITE,cn,real-ip】...')
filter_rule = 'RULE-SET,oc-cn-domain,real-ip'
(Value['dns']['fake-ip-filter'] ||= []).unshift(filter_rule)
end
Value['dns']['fake-ip-filter'].uniq!
if filter_mode != 'whitelist'
rule_set_hash = {'rule-providers+'=>{'oc-cn-domain'=>{'type'=>'http', 'interval'=>43200, 'behavior'=>'domain', 'format'=>'mrs', 'url'=>'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geosite/cn.mrs', 'path'=> './rule_provider/oc-cn-domain.mrs'}}}
Value = YAML.overwrite(Value, rule_set_hash)
YAML.LOG_TIP('Because Need Ensure Bypassing IP Option Work, Added The Fake-IP-Filter Rule【%s】...' % [filter_rule])
end
end
rescue Exception => e
@@ -592,7 +592,7 @@ ruby -ryaml -rYAML -I "/usr/share/openclash" -E UTF-8 -e "
};
end;
#Mieru
#Mieru
if x['type'] == 'mieru' then
threads << Thread.new{
#port-range
@@ -1138,6 +1138,19 @@ ruby -ryaml -rYAML -I "/usr/share/openclash" -E UTF-8 -e "
uci_commands << uci_set + 'reality_short_id=\"' + x['reality-opts']['short-id'].to_s + '\"'
end
end
if x.key?('encryption') then
uci_commands << uci_set + 'vless_encryption=\"' + x['encryption'].to_s + '\"'
end
elsif x['network'].to_s == 'xhttp'
uci_commands << uci_set + 'obfs_vless=xhttp'
if x.key?('xhttp-opts') then
if x['xhttp-opts'].key?('path') then
uci_commands << uci_set + 'xhttp_opts_path=\"' + x['xhttp-opts']['path'].to_s + '\"'
end
if x['xhttp-opts'].key?('host') then
uci_commands << uci_set + 'xhttp_opts_host=\"' + x['xhttp-opts']['host'].to_s + '\"'
end
end
end
end
};
@@ -1424,6 +1437,132 @@ ruby -ryaml -rYAML -I "/usr/share/openclash" -E UTF-8 -e "
};
end;
if x['type'] == 'masque' then
threads << Thread.new{
#private-key
if x.key?('private-key') then
uci_commands << uci_set + 'masque_private_key=\"' + x['private-key'].to_s + '\"'
end
};
threads << Thread.new{
#public-key
if x.key?('public-key') then
uci_commands << uci_set + 'masque_public_key=\"' + x['public-key'].to_s + '\"'
end
};
threads << Thread.new{
#ip
if x.key?('ip') then
uci_commands << uci_set + 'masque_ip=\"' + x['ip'].to_s + '\"'
end
};
threads << Thread.new{
#ipv6
if x.key?('ipv6') then
uci_commands << uci_set + 'masque_ipv6=\"' + x['ipv6'].to_s + '\"'
end
};
threads << Thread.new{
#mtu
if x.key?('mtu') then
uci_commands << uci_set + 'masque_mtu=\"' + x['mtu'].to_s + '\"'
end
};
threads << Thread.new{
#remote-dns-resolve
if x.key?('remote-dns-resolve') then
uci_commands << uci_set + 'masque_remote_dns_resolve=\"' + x['remote-dns-resolve'].to_s + '\"'
end
};
threads << Thread.new{
#dns
if x.key?('dns') then
dns = uci_del + 'dns >/dev/null 2>&1'
system(dns)
x['dns'].each{
|x|
uci_commands << uci_add + 'masque_dns=\"' + x.to_s + '\"'
}
end
};
end;
if x['type'] == 'trusttunnel' then
threads << Thread.new{
#username
if x.key?('username') then
uci_commands << uci_set + 'trusttunnel_username=\"' + x['username'].to_s + '\"'
end
};
threads << Thread.new{
#password
if x.key?('password') then
uci_commands << uci_set + 'trusttunnel_password=\"' + x['password'].to_s + '\"'
end
};
threads << Thread.new{
#health-check
if x.key?('health-check') then
uci_commands << uci_set + 'trusttunnel_health_check=\"' + x['health-check'].to_s + '\"'
end
};
threads << Thread.new{
#quic
if x.key?('quic') then
uci_commands << uci_set + 'trusttunnel_quic=\"' + x['quic'].to_s + '\"'
end
};
threads << Thread.new{
#congestion-controller
if x.key?('congestion-controller') then
uci_commands << uci_set + 'trusttunnel_congestion_controller=\"' + x['congestion-controller'].to_s + '\"'
end
};
#alpn
threads << Thread.new{
if x.key?('alpn') then
alpn = uci_del + 'alpn >/dev/null 2>&1'
system(alpn)
x['alpn'].each{
|x|
uci_commands << uci_add + 'alpn=\"' + x.to_s + '\"'
}
end
};
#sni
threads << Thread.new{
if x.key?('sni') then
uci_commands << uci_set + 'sni=\"' + x['sni'].to_s + '\"'
end
};
#skip-cert-verify
threads << Thread.new{
if x.key?('skip-cert-verify') then
uci_commands << uci_set + 'skip_cert_verify=\"' + x['skip-cert-verify'].to_s + '\"'
end
};
#client_fingerprint
threads << Thread.new{
if x.key?('client-fingerprint') then
uci_commands << uci_set + 'client_fingerprint=\"' + x['client-fingerprint'].to_s + '\"'
end
};
end;
#加入策略组
threads << Thread.new{
#加入策略组
@@ -186,135 +186,6 @@ yml_servers_set()
config_get "name" "$section" "name" ""
config_get "server" "$section" "server" ""
config_get "port" "$section" "port" ""
config_get "dialer_proxy" "$section" "dialer_proxy" ""
config_get "cipher" "$section" "cipher" ""
config_get "cipher_ssr" "$section" "cipher_ssr" ""
config_get "password" "$section" "password" ""
config_get "securitys" "$section" "securitys" ""
config_get "udp" "$section" "udp" ""
config_get "obfs" "$section" "obfs" ""
config_get "obfs_ssr" "$section" "obfs_ssr" ""
config_get "obfs_param" "$section" "obfs_param" ""
config_get "obfs_vmess" "$section" "obfs_vmess" ""
config_get "obfs_trojan" "$section" "obfs_trojan" ""
config_get "protocol" "$section" "protocol" ""
config_get "protocol_param" "$section" "protocol_param" ""
config_get "host" "$section" "host" ""
config_get "mux" "$section" "mux" ""
config_get "custom" "$section" "custom" ""
config_get "tls" "$section" "tls" ""
config_get "skip_cert_verify" "$section" "skip_cert_verify" ""
config_get "path" "$section" "path" ""
config_get "alterId" "$section" "alterId" ""
config_get "uuid" "$section" "uuid" ""
config_get "auth_name" "$section" "auth_name" ""
config_get "auth_pass" "$section" "auth_pass" ""
config_get "psk" "$section" "psk" ""
config_get "obfs_snell" "$section" "obfs_snell" ""
config_get "snell_version" "$section" "snell_version" ""
config_get "sni" "$section" "sni" ""
config_get "alpn" "$section" "alpn" ""
config_get "http_path" "$section" "http_path" ""
config_get "keep_alive" "$section" "keep_alive" ""
config_get "servername" "$section" "servername" ""
config_get "h2_path" "$section" "h2_path" ""
config_get "h2_host" "$section" "h2_host" ""
config_get "grpc_service_name" "$section" "grpc_service_name" ""
config_get "ws_opts_path" "$section" "ws_opts_path" ""
config_get "ws_opts_headers" "$section" "ws_opts_headers" ""
config_get "max_early_data" "$section" "max_early_data" ""
config_get "early_data_header_name" "$section" "early_data_header_name" ""
config_get "trojan_ws_path" "$section" "trojan_ws_path" ""
config_get "trojan_ws_headers" "$section" "trojan_ws_headers" ""
config_get "interface_name" "$section" "interface_name" ""
config_get "routing_mark" "$section" "routing_mark" ""
config_get "obfs_vless" "$section" "obfs_vless" ""
config_get "vless_flow" "$section" "vless_flow" ""
config_get "http_headers" "$section" "http_headers" ""
config_get "hysteria_protocol" "$section" "hysteria_protocol" ""
config_get "hysteria2_protocol" "$section" "hysteria2_protocol" ""
config_get "hysteria_up" "$section" "hysteria_up" ""
config_get "hysteria_down" "$section" "hysteria_down" ""
config_get "hysteria_alpn" "$section" "hysteria_alpn" ""
config_get "hysteria_obfs" "$section" "hysteria_obfs" ""
config_get "hysteria_auth" "$section" "hysteria_auth" ""
config_get "hysteria_auth_str" "$section" "hysteria_auth_str" ""
config_get "hysteria_ca" "$section" "hysteria_ca" ""
config_get "hysteria_ca_str" "$section" "hysteria_ca_str" ""
config_get "recv_window_conn" "$section" "recv_window_conn" ""
config_get "recv_window" "$section" "recv_window" ""
config_get "disable_mtu_discovery" "$section" "disable_mtu_discovery" ""
config_get "initial_stream_receive_window" "$section" "initial_stream_receive_window" ""
config_get "max_stream_receive_window" "$section" "max_stream_receive_window" ""
config_get "initial_connection_receive_window" "$section" "initial_connection_receive_window" ""
config_get "max_connection_receive_window" "$section" "max_connection_receive_window" ""
config_get "xudp" "$section" "xudp" ""
config_get "packet_encoding" "$section" "packet_encoding" ""
config_get "global_padding" "$section" "global_padding" ""
config_get "authenticated_length" "$section" "authenticated_length" ""
config_get "wg_ip" "$section" "wg_ip" ""
config_get "wg_ipv6" "$section" "wg_ipv6" ""
config_get "private_key" "$section" "private_key" ""
config_get "public_key" "$section" "public_key" ""
config_get "preshared_key" "$section" "preshared_key" ""
config_get "wg_dns" "$section" "wg_dns" ""
config_get "public_key" "$section" "public_key" ""
config_get "preshared_key" "$section" "preshared_key" ""
config_get "wg_mtu" "$section" "wg_mtu" ""
config_get "tc_ip" "$section" "tc_ip" ""
config_get "tc_token" "$section" "tc_token" ""
config_get "tc_uuid" "$section" "tc_uuid" ""
config_get "tc_password" "$section" "tc_password" ""
config_get "udp_relay_mode" "$section" "udp_relay_mode" ""
config_get "congestion_controller" "$section" "congestion_controller" ""
config_get "tc_alpn" "$section" "tc_alpn" ""
config_get "disable_sni" "$section" "disable_sni" ""
config_get "reduce_rtt" "$section" "reduce_rtt" ""
config_get "heartbeat_interval" "$section" "heartbeat_interval" ""
config_get "request_timeout" "$section" "request_timeout" ""
config_get "max_udp_relay_packet_size" "$section" "max_udp_relay_packet_size" ""
config_get "fast_open" "$section" "fast_open" ""
config_get "fingerprint" "$section" "fingerprint" ""
config_get "ports" "$section" "ports" ""
config_get "hop_interval" "$section" "hop_interval" ""
config_get "max_open_streams" "$section" "max_open_streams" ""
config_get "obfs_password" "$section" "obfs_password" ""
config_get "packet_addr" "$section" "packet_addr" ""
config_get "client_fingerprint" "$section" "client_fingerprint" ""
config_get "ip_version" "$section" "ip_version" ""
config_get "tfo" "$section" "tfo" ""
config_get "udp_over_tcp" "$section" "udp_over_tcp" ""
config_get "reality_public_key" "$section" "reality_public_key" ""
config_get "reality_short_id" "$section" "reality_short_id" ""
config_get "obfs_version_hint" "$section" "obfs_version_hint" ""
config_get "obfs_restls_script" "$section" "obfs_restls_script" ""
config_get "multiplex" "$section" "multiplex" ""
config_get "multiplex_protocol" "$section" "multiplex_protocol" ""
config_get "multiplex_max_connections" "$section" "multiplex_max_connections" ""
config_get "multiplex_min_streams" "$section" "multiplex_min_streams" ""
config_get "multiplex_max_streams" "$section" "multiplex_max_streams" ""
config_get "multiplex_padding" "$section" "multiplex_padding" ""
config_get "multiplex_statistic" "$section" "multiplex_statistic" ""
config_get "multiplex_only_tcp" "$section" "multiplex_only_tcp" ""
config_get "other_parameters" "$section" "other_parameters" ""
config_get "hysteria_obfs_password" "$section" "hysteria_obfs_password" ""
config_get "port_range" "$section" "port_range" ""
config_get "username" "$section" "username" ""
config_get "transport" "$section" "transport" "TCP"
config_get "multiplexing" "$section" "multiplexing" "MULTIPLEXING_LOW"
config_get "private_key" "$section" "private_key" ""
config_get "private_key_passphrase" "$section" "private_key_passphrase" ""
config_get "host_key" "$section" "host_key" ""
config_get "host_key_algorithms" "$section" "host_key_algorithms" ""
config_get "idle_session_check_interval" "$section" "idle_session_check_interval" ""
config_get "idle_session_timeout" "$section" "idle_session_timeout" ""
config_get "min_idle_session" "$section" "min_idle_session" ""
config_get "sudoku_key" "$section" "sudoku_key" ""
config_get "aead_method" "$section" "aead_method" "none"
config_get "padding_min" "$section" "padding_min" ""
config_get "padding_max" "$section" "padding_max" ""
config_get "table_type" "$section" "table_type" "prefer_ascii"
config_get "http_mask" "$section" "http_mask" "true"
if [ "$enabled" = "0" ]; then
return
@@ -336,11 +207,12 @@ yml_servers_set()
return
fi
if [ -z "$password" ]; then
if [ "$type" = "ss" ] || [ "$type" = "trojan" ] || [ "$type" = "ssr" ]; then
return
fi
fi
if [ "$type" = "ss" ] || [ "$type" = "trojan" ] || [ "$type" = "ssr" ]; then
config_get "password" "$section" "password" ""
if [ -z "$password" ]; then
return
fi
fi
if [ ! -z "$config" ] && [ "$config" != "$CONFIG_NAME" ] && [ "$config" != "all" ]; then
return
@@ -364,59 +236,27 @@ yml_servers_set()
fi
LOG_OUT "Start Writing【$CONFIG_NAME - $type - $name】Proxy To Config File..."
if [ "$obfs" != "none" ] && [ -n "$obfs" ]; then
if [ "$obfs" = "websocket" ]; then
obfss="plugin: v2ray-plugin"
elif [ "$obfs" = "shadow-tls" ]; then
obfss="plugin: shadow-tls"
elif [ "$obfs" = "restls" ]; then
obfss="plugin: restls"
else
obfss="plugin: obfs"
fi
else
obfss=""
fi
if [ "$obfs_vless" = "ws" ]; then
obfs_vless="network: ws"
fi
if [ "$obfs_vless" = "grpc" ]; then
obfs_vless="network: grpc"
fi
if [ "$obfs_vless" = "tcp" ]; then
obfs_vless="network: tcp"
fi
if [ "$obfs_vmess" = "websocket" ]; then
obfs_vmess="network: ws"
fi
if [ "$obfs_vmess" = "http" ]; then
obfs_vmess="network: http"
fi
if [ "$obfs_vmess" = "h2" ]; then
obfs_vmess="network: h2"
fi
if [ "$obfs_vmess" = "grpc" ]; then
obfs_vmess="network: grpc"
fi
if [ ! -z "$custom" ] && [ "$type" = "vmess" ]; then
custom="Host: \"$custom\""
fi
if [ ! -z "$path" ]; then
if [ "$type" != "vmess" ]; then
path="path: \"$path\""
elif [ "$obfs_vmess" = "network: ws" ]; then
path="ws-path: \"$path\""
fi
fi
config_get "dialer_proxy" "$section" "dialer_proxy" ""
config_get "udp" "$section" "udp" ""
config_get "skip_cert_verify" "$section" "skip_cert_verify" ""
config_get "tls" "$section" "tls" ""
config_get "sni" "$section" "sni" ""
config_get "alpn" "$section" "alpn" ""
config_get "fingerprint" "$section" "fingerprint" ""
config_get "client_fingerprint" "$section" "client_fingerprint" ""
config_get "ip_version" "$section" "ip_version" ""
config_get "tfo" "$section" "tfo" ""
config_get "multiplex" "$section" "multiplex" ""
config_get "multiplex_protocol" "$section" "multiplex_protocol" ""
config_get "multiplex_max_connections" "$section" "multiplex_max_connections" ""
config_get "multiplex_min_streams" "$section" "multiplex_min_streams" ""
config_get "multiplex_max_streams" "$section" "multiplex_max_streams" ""
config_get "multiplex_padding" "$section" "multiplex_padding" ""
config_get "multiplex_statistic" "$section" "multiplex_statistic" ""
config_get "multiplex_only_tcp" "$section" "multiplex_only_tcp" ""
config_get "interface_name" "$section" "interface_name" ""
config_get "routing_mark" "$section" "routing_mark" ""
config_get "other_parameters" "$section" "other_parameters" ""
if [ "$client_fingerprint" = "none" ]; then
client_fingerprint=""
@@ -428,6 +268,35 @@ yml_servers_set()
#ss
if [ "$type" = "ss" ]; then
config_get "cipher" "$section" "cipher" ""
config_get "obfs" "$section" "obfs" ""
config_get "host" "$section" "host" ""
config_get "mux" "$section" "mux" ""
config_get "custom" "$section" "custom" ""
config_get "path" "$section" "path" ""
config_get "obfs_password" "$section" "obfs_password" ""
config_get "obfs_version_hint" "$section" "obfs_version_hint" ""
config_get "obfs_restls_script" "$section" "obfs_restls_script" ""
config_get "udp_over_tcp" "$section" "udp_over_tcp" ""
if [ "$obfs" != "none" ] && [ -n "$obfs" ]; then
if [ "$obfs" = "websocket" ]; then
obfss="plugin: v2ray-plugin"
elif [ "$obfs" = "shadow-tls" ]; then
obfss="plugin: shadow-tls"
elif [ "$obfs" = "restls" ]; then
obfss="plugin: restls"
else
obfss="plugin: obfs"
fi
else
obfss=""
fi
if [ ! -z "$path" ]; then
path="path: \"$path\""
fi
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -528,6 +397,12 @@ fi
#ssr
if [ "$type" = "ssr" ]; then
config_get "cipher_ssr" "$section" "cipher_ssr" ""
config_get "obfs_ssr" "$section" "obfs_ssr" ""
config_get "protocol" "$section" "protocol" ""
config_get "obfs_param" "$section" "obfs_param" ""
config_get "protocol_param" "$section" "protocol_param" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -557,6 +432,48 @@ fi
#vmess
if [ "$type" = "vmess" ]; then
config_get "uuid" "$section" "uuid" ""
config_get "alterId" "$section" "alterId" ""
config_get "securitys" "$section" "securitys" ""
config_get "xudp" "$section" "xudp" ""
config_get "packet_encoding" "$section" "packet_encoding" ""
config_get "global_padding" "$section" "global_padding" ""
config_get "authenticated_length" "$section" "authenticated_length" ""
config_get "servername" "$section" "servername" ""
config_get "obfs_vmess" "$section" "obfs_vmess" ""
config_get "custom" "$section" "custom" ""
config_get "path" "$section" "path" ""
config_get "ws_opts_path" "$section" "ws_opts_path" ""
config_get "ws_opts_headers" "$section" "ws_opts_headers" ""
config_get "max_early_data" "$section" "max_early_data" ""
config_get "early_data_header_name" "$section" "early_data_header_name" ""
config_get "http_path" "$section" "http_path" ""
config_get "keep_alive" "$section" "keep_alive" ""
config_get "h2_path" "$section" "h2_path" ""
config_get "h2_host" "$section" "h2_host" ""
config_get "grpc_service_name" "$section" "grpc_service_name" ""
if [ "$obfs_vmess" = "websocket" ]; then
obfs_vmess="network: ws"
fi
if [ "$obfs_vmess" = "http" ]; then
obfs_vmess="network: http"
fi
if [ "$obfs_vmess" = "h2" ]; then
obfs_vmess="network: h2"
fi
if [ "$obfs_vmess" = "grpc" ]; then
obfs_vmess="network: grpc"
fi
if [ ! -z "$custom" ]; then
custom="Host: \"$custom\""
fi
if [ ! -z "$path" ] && [ "$obfs_vmess" = "network: ws" ]; then
path="ws-path: \"$path\""
fi
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -702,6 +619,11 @@ fi
#anytls
if [ "$type" = "anytls" ]; then
config_get "password" "$section" "password" ""
config_get "idle_session_check_interval" "$section" "idle_session_check_interval" ""
config_get "idle_session_timeout" "$section" "idle_session_timeout" ""
config_get "min_idle_session" "$section" "min_idle_session" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -758,6 +680,11 @@ fi
#Mieru
if [ "$type" = "mieru" ]; then
config_get "port_range" "$section" "port_range" ""
config_get "username" "$section" "username" ""
config_get "transport" "$section" "transport" "TCP"
config_get "multiplexing" "$section" "multiplexing" "MULTIPLEXING_LOW"
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -788,6 +715,21 @@ fi
#Tuic
if [ "$type" = "tuic" ]; then
config_get "tc_ip" "$section" "tc_ip" ""
config_get "tc_token" "$section" "tc_token" ""
config_get "tc_uuid" "$section" "tc_uuid" ""
config_get "tc_password" "$section" "tc_password" ""
config_get "udp_relay_mode" "$section" "udp_relay_mode" ""
config_get "congestion_controller" "$section" "congestion_controller" ""
config_get "tc_alpn" "$section" "tc_alpn" ""
config_get "disable_sni" "$section" "disable_sni" ""
config_get "reduce_rtt" "$section" "reduce_rtt" ""
config_get "fast_open" "$section" "fast_open" ""
config_get "heartbeat_interval" "$section" "heartbeat_interval" ""
config_get "request_timeout" "$section" "request_timeout" ""
config_get "max_udp_relay_packet_size" "$section" "max_udp_relay_packet_size" ""
config_get "max_open_streams" "$section" "max_open_streams" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -874,6 +816,14 @@ fi
#WireGuard
if [ "$type" = "wireguard" ]; then
config_get "wg_ip" "$section" "wg_ip" ""
config_get "wg_ipv6" "$section" "wg_ipv6" ""
config_get "private_key" "$section" "private_key" ""
config_get "public_key" "$section" "public_key" ""
config_get "preshared_key" "$section" "preshared_key" ""
config_get "wg_dns" "$section" "wg_dns" ""
config_get "wg_mtu" "$section" "wg_mtu" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -905,7 +855,7 @@ cat >> "$SERVER_FILE" <<-EOF
preshared-key: "$preshared_key"
EOF
fi
if [ -n "$preshared_key" ]; then
if [ -n "$wg_dns" ]; then
cat >> "$SERVER_FILE" <<-EOF
dns:
EOF
@@ -925,6 +875,22 @@ fi
#hysteria
if [ "$type" = "hysteria" ]; then
config_get "hysteria_protocol" "$section" "hysteria_protocol" ""
config_get "hysteria_up" "$section" "hysteria_up" ""
config_get "hysteria_down" "$section" "hysteria_down" ""
config_get "hysteria_alpn" "$section" "hysteria_alpn" ""
config_get "hysteria_obfs" "$section" "hysteria_obfs" ""
config_get "hysteria_auth" "$section" "hysteria_auth" ""
config_get "hysteria_auth_str" "$section" "hysteria_auth_str" ""
config_get "hysteria_ca" "$section" "hysteria_ca" ""
config_get "hysteria_ca_str" "$section" "hysteria_ca_str" ""
config_get "recv_window_conn" "$section" "recv_window_conn" ""
config_get "recv_window" "$section" "recv_window" ""
config_get "disable_mtu_discovery" "$section" "disable_mtu_discovery" ""
config_get "fast_open" "$section" "fast_open" ""
config_get "ports" "$section" "ports" ""
config_get "hop_interval" "$section" "hop_interval" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -1029,6 +995,22 @@ fi
#hysteria2
if [ "$type" = "hysteria2" ]; then
config_get "password" "$section" "password" ""
config_get "hysteria_up" "$section" "hysteria_up" ""
config_get "hysteria_down" "$section" "hysteria_down" ""
config_get "hysteria_alpn" "$section" "hysteria_alpn" ""
config_get "hysteria_obfs" "$section" "hysteria_obfs" ""
config_get "hysteria_obfs_password" "$section" "hysteria_obfs_password" ""
config_get "hysteria_ca" "$section" "hysteria_ca" ""
config_get "hysteria_ca_str" "$section" "hysteria_ca_str" ""
config_get "initial_stream_receive_window" "$section" "initial_stream_receive_window" ""
config_get "max_stream_receive_window" "$section" "max_stream_receive_window" ""
config_get "initial_connection_receive_window" "$section" "initial_connection_receive_window" ""
config_get "max_connection_receive_window" "$section" "max_connection_receive_window" ""
config_get "ports" "$section" "ports" ""
config_get "hysteria2_protocol" "$section" "hysteria2_protocol" ""
config_get "hop_interval" "$section" "hop_interval" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -1096,7 +1078,7 @@ EOF
fi
if [ -n "$max_stream_receive_window" ]; then
cat >> "$SERVER_FILE" <<-EOF
max_stream_receive_window: "$max_stream_receive_window"
max-stream-receive-window: "$max_stream_receive_window"
EOF
fi
if [ -n "$initial_connection_receive_window" ]; then
@@ -1133,6 +1115,35 @@ fi
#vless
if [ "$type" = "vless" ]; then
config_get "uuid" "$section" "uuid" ""
config_get "xudp" "$section" "xudp" ""
config_get "packet_addr" "$section" "packet_addr" ""
config_get "packet_encoding" "$section" "packet_encoding" ""
config_get "servername" "$section" "servername" ""
config_get "obfs_vless" "$section" "obfs_vless" ""
config_get "ws_opts_path" "$section" "ws_opts_path" ""
config_get "ws_opts_headers" "$section" "ws_opts_headers" ""
config_get "grpc_service_name" "$section" "grpc_service_name" ""
config_get "reality_public_key" "$section" "reality_public_key" ""
config_get "reality_short_id" "$section" "reality_short_id" ""
config_get "vless_flow" "$section" "vless_flow" ""
config_get "xhttp_opts_path" "$section" "xhttp_opts_path" ""
config_get "xhttp_opts_host" "$section" "xhttp_opts_host" ""
config_get "vless_encryption" "$section" "vless_encryption" ""
if [ "$obfs_vless" = "ws" ]; then
obfs_vless="network: ws"
fi
if [ "$obfs_vless" = "grpc" ]; then
obfs_vless="network: grpc"
fi
if [ "$obfs_vless" = "tcp" ]; then
obfs_vless="network: tcp"
fi
if [ "$obfs_vless" = "xhttp" ]; then
obfs_vless="network: xhttp"
fi
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -1232,6 +1243,11 @@ EOF
if [ ! -z "$vless_flow" ]; then
cat >> "$SERVER_FILE" <<-EOF
flow: "$vless_flow"
EOF
fi
if [ -n "$vless_encryption" ]; then
cat >> "$SERVER_FILE" <<-EOF
encryption: "$vless_encryption"
EOF
fi
if [ -n "$reality_public_key" ] || [ -n "$reality_short_id" ]; then
@@ -1247,6 +1263,21 @@ EOF
if [ -n "$reality_short_id" ]; then
cat >> "$SERVER_FILE" <<-EOF
short-id: "$reality_short_id"
EOF
fi
fi
if [ "$obfs_vless" = "network: xhttp" ]; then
cat >> "$SERVER_FILE" <<-EOF
xhttp-opts:
EOF
if [ -n "$xhttp_opts_path" ]; then
cat >> "$SERVER_FILE" <<-EOF
path: "$xhttp_opts_path"
EOF
fi
if [ -n "$xhttp_opts_host" ]; then
cat >> "$SERVER_FILE" <<-EOF
host: "$xhttp_opts_host"
EOF
fi
fi
@@ -1276,6 +1307,13 @@ fi
#ssh
if [ "$type" = "ssh" ]; then
config_get "auth_name" "$section" "auth_name" ""
config_get "auth_pass" "$section" "auth_pass" ""
config_get "private_key" "$section" "private_key" ""
config_get "private_key_passphrase" "$section" "private_key_passphrase" ""
config_get "host_key" "$section" "host_key" ""
config_get "host_key_algorithms" "$section" "host_key_algorithms" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -1318,6 +1356,9 @@ fi
#socks5
if [ "$type" = "socks5" ]; then
config_get "auth_name" "$section" "auth_name" ""
config_get "auth_pass" "$section" "auth_pass" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -1358,6 +1399,10 @@ fi
#http
if [ "$type" = "http" ]; then
config_get "auth_name" "$section" "auth_name" ""
config_get "auth_pass" "$section" "auth_pass" ""
config_get "http_headers" "$section" "http_headers" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -1399,6 +1444,11 @@ fi
#trojan
if [ "$type" = "trojan" ]; then
config_get "grpc_service_name" "$section" "grpc_service_name" ""
config_get "obfs_trojan" "$section" "obfs_trojan" ""
config_get "trojan_ws_path" "$section" "trojan_ws_path" ""
config_get "trojan_ws_headers" "$section" "trojan_ws_headers" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -1467,6 +1517,11 @@ fi
#snell
if [ "$type" = "snell" ]; then
config_get "psk" "$section" "psk" ""
config_get "snell_version" "$section" "snell_version" ""
config_get "obfs_snell" "$section" "obfs_snell" ""
config_get "host" "$section" "host" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -1490,6 +1545,13 @@ fi
#Sudoku
if [ "$type" = "sudoku" ]; then
config_get "sudoku_key" "$section" "sudoku_key" ""
config_get "aead_method" "$section" "aead_method" "none"
config_get "padding_min" "$section" "padding_min" ""
config_get "padding_max" "$section" "padding_max" ""
config_get "table_type" "$section" "table_type" "prefer_ascii"
config_get "http_mask" "$section" "http_mask" "true"
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
@@ -1528,6 +1590,132 @@ EOF
fi
fi
#MASQUE
if [ "$type" = "masque" ]; then
config_get "masque_private_key" "$section" "masque_private_key" ""
config_get "masque_public_key" "$section" "masque_public_key" ""
config_get "masque_ip" "$section" "masque_ip" ""
config_get "masque_ipv6" "$section" "masque_ipv6" ""
config_get "masque_mtu" "$section" "masque_mtu" ""
config_get "masque_remote_dns_resolve" "$section" "masque_remote_dns_resolve" ""
config_get "masque_dns" "$section" "masque_dns" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
server: "$server"
port: $port
EOF
if [ -n "$masque_private_key" ]; then
cat >> "$SERVER_FILE" <<-EOF
private-key: "$masque_private_key"
EOF
fi
if [ -n "$masque_public_key" ]; then
cat >> "$SERVER_FILE" <<-EOF
public-key: "$masque_public_key"
EOF
fi
if [ -n "$masque_ip" ]; then
cat >> "$SERVER_FILE" <<-EOF
ip: "$masque_ip"
EOF
fi
if [ -n "$masque_ipv6" ]; then
cat >> "$SERVER_FILE" <<-EOF
ipv6: "$masque_ipv6"
EOF
fi
if [ -n "$masque_mtu" ]; then
cat >> "$SERVER_FILE" <<-EOF
mtu: $masque_mtu
EOF
fi
if [ -n "$masque_remote_dns_resolve" ]; then
cat >> "$SERVER_FILE" <<-EOF
remote-dns-resolve: $masque_remote_dns_resolve
EOF
fi
if [ ! -z "$udp" ]; then
cat >> "$SERVER_FILE" <<-EOF
udp: $udp
EOF
fi
if [ ! -z "$masque_dns" ]; then
cat >> "$SERVER_FILE" <<-EOF
dns:
EOF
config_list_foreach "$section" "masque_dns" set_alpn
fi
fi
#TrustTunnel
if [ "$type" = "trusttunnel" ]; then
config_get "trusttunnel_username" "$section" "trusttunnel_username" ""
config_get "trusttunnel_password" "$section" "trusttunnel_password" ""
config_get "trusttunnel_health_check" "$section" "trusttunnel_health_check" ""
config_get "trusttunnel_quic" "$section" "trusttunnel_quic" ""
config_get "trusttunnel_congestion_controller" "$section" "trusttunnel_congestion_controller" ""
cat >> "$SERVER_FILE" <<-EOF
- name: "$name"
type: $type
server: "$server"
port: $port
EOF
if [ -n "$trusttunnel_username" ]; then
cat >> "$SERVER_FILE" <<-EOF
username: "$trusttunnel_username"
EOF
fi
if [ -n "$trusttunnel_password" ]; then
cat >> "$SERVER_FILE" <<-EOF
password: "$trusttunnel_password"
EOF
fi
if [ -n "$trusttunnel_health_check" ]; then
cat >> "$SERVER_FILE" <<-EOF
health-check: $trusttunnel_health_check
EOF
fi
if [ -n "$trusttunnel_quic" ]; then
cat >> "$SERVER_FILE" <<-EOF
quic: $trusttunnel_quic
EOF
fi
if [ -n "$trusttunnel_congestion_controller" ]; then
cat >> "$SERVER_FILE" <<-EOF
congestion-controller: "$trusttunnel_congestion_controller"
EOF
fi
if [ -n "$client_fingerprint" ]; then
cat >> "$SERVER_FILE" <<-EOF
client-fingerprint: "$client_fingerprint"
EOF
fi
if [ -n "$udp" ]; then
cat >> "$SERVER_FILE" <<-EOF
udp: $udp
EOF
fi
if [ -n "$sni" ]; then
cat >> "$SERVER_FILE" <<-EOF
sni: "$sni"
EOF
fi
if [ ! -z "$alpn" ]; then
cat >> "$SERVER_FILE" <<-EOF
alpn:
EOF
config_list_foreach "$section" "alpn" set_alpn
fi
if [ ! -z "$skip_cert_verify" ]; then
cat >> "$SERVER_FILE" <<-EOF
skip-cert-verify: $skip_cert_verify
EOF
fi
fi
#ip_version
if [ ! -z "$ip_version" ]; then
cat >> "$SERVER_FILE" <<-EOF
@@ -21,6 +21,9 @@ yml_other_set()
begin
thread_pool = [];
# GEOIP replace
geoip_pattern = /GEOIP,([A-Za-z]{2}),([^,]+)(,.*)?/;
match_pattern = /(^MATCH.*|^FINAL.*)/;
thread_pool << Thread.new{
#BT/P2P DIRECT Rules
begin
@@ -102,10 +105,6 @@ yml_other_set()
end;
end;
# GEOIP replace
geoip_pattern = /GEOIP,([A-Za-z]{2}),([^,]+)(,.*)?/;
match_pattern = /(^MATCH.*|^FINAL.*)/;
Value['rules'].to_a.collect!{|x|
x.to_s.gsub(geoip_pattern, 'GEOIP,\1,DIRECT\3').gsub(match_pattern, 'MATCH,DIRECT')
};
@@ -136,6 +135,18 @@ yml_other_set()
{ file: '/etc/openclash/custom/openclash_custom_rules_2.list', position: 'bottom' }
];
# 在开始对 custom_files 进行插入之前,记录当前 rules 中 GEOIP、MATCH、DST-PORT,80 的原始规则文本(锚点)
anchors_orig = {}
if Value.has_key?('rules') and not Value['rules'].to_a.empty? then
anchors_orig[:dst80_rule] = Value['rules'].grep(/DST-PORT,80/).last
anchors_orig[:match_rule] = Value['rules'].grep(match_pattern).first
anchors_orig[:geo_rule] = Value['rules'].grep(geoip_pattern).first
else
anchors_orig[:dst80_rule] = nil
anchors_orig[:match_rule] = nil
anchors_orig[:geo_rule] = nil
end;
custom_files.each{|file_info|
if File::exist?(file_info[:file]) then
custom_data = YAML.load_file(file_info[:file]);
@@ -150,6 +161,13 @@ yml_other_set()
[]
end;
rule_providers_array = case custom_data.class.to_s
when 'Hash'
custom_data['rule-providers'].to_a if custom_data['rule-providers'].class.to_s == 'Hash'
else
[]
end;
next unless rules_array;
ipv4_regex = /^(\d{1,3}\.){3}\d{1,3}$/;
@@ -157,9 +175,10 @@ yml_other_set()
cidr_regex = /\/\d+$/;
rule_suffix_regex = /^no-resolve$|^src$/;
ip_rule_types = ['IP-CIDR', 'IP-CIDR6', 'SRC-IP-CIDR', 'SRC-IP-CIDR6', 'IP-SUFFIX', 'SRC-IP-SUFFIX'];
transformed_rules = rules_array.map{|x|
parts = x.split(',');
if parts.length >= 2 then
if parts.length >= 2 && ip_rule_types.include?(parts[0].strip.upcase) then
ip_part = parts[1].strip;
if ip_part !~ cidr_regex then
# IPv4
@@ -194,11 +213,15 @@ yml_other_set()
if file_info[:position] == 'top' then
valid_rules.reverse.each{|x| Value['rules'].insert(0,x)};
else
if Value['rules'].grep(/GEOIP/)[0].nil? or Value['rules'].grep(/GEOIP/)[0].empty? then
ruby_add_index = Value['rules'].index(Value['rules'].grep(/DST-PORT,80/).last);
ruby_add_index ||= Value['rules'].index(Value['rules'].grep(/(MATCH|FINAL)/).first);
else
ruby_add_index = Value['rules'].index(Value['rules'].grep(/GEOIP/).first);
ruby_add_index = nil;
if anchors_orig[:dst80_rule]
ruby_add_index = Value['rules'].rindex(anchors_orig[:dst80_rule])
end;
if ruby_add_index.nil? and anchors_orig[:match_rule]
ruby_add_index = Value['rules'].rindex(anchors_orig[:match_rule])
end;
if ruby_add_index.nil? and anchors_orig[:geo_rule]
ruby_add_index = Value['rules'].rindex(anchors_orig[:geo_rule])
end;
ruby_add_index ||= -1;
@@ -209,6 +232,11 @@ yml_other_set()
else
Value['rules'] = valid_rules.uniq;
end;
if rule_providers_array and not rule_providers_array.empty? then
Value['rule-providers'] ||= {};
Value['rule-providers'] = Value['rule-providers'].merge!(custom_data['rule-providers']);
end;
end;
};
@@ -256,19 +284,13 @@ yml_other_set()
thread_pool << Thread.new{
threads = [];
#provider path
#provider CDN
begin
provider_configs = {'proxy-providers' => 'proxy_provider', 'rule-providers' => 'rule_provider'};
provider_configs.each do |provider_type, path_prefix|
if Value.key?(provider_type) then
if Value.key?(provider_type) && Value[provider_type].is_a?(Hash) then
Value[provider_type].each{|name, config|
threads << Thread.new {
if config['path'] and not config['path'] =~ /.\/#{path_prefix}\/*/ then
config['path'] = './'+path_prefix+'/'+File.basename(config['path']);
elsif not config['path'] and config['type'] == 'http' then
config['path'] = './'+path_prefix+'/'+name;
end;
# CDN
if '$github_address_mod' != '0' and config['url'] then
if config['url'] =~ /^https:\/\/raw.githubusercontent.com/ then
@@ -293,12 +315,12 @@ yml_other_set()
end;
end;
rescue Exception => e
YAML.LOG_ERROR('Edit Provider Path Failed,【' + e.message + '】');
YAML.LOG_ERROR('Edit Provider CDN Failed,【' + e.message + '】');
end;
# tolerance
begin
if '$tolerance' != '0' and Value.key?('proxy-groups') then
if '$tolerance' != '0' and Value.key?('proxy-groups') and Value['proxy-groups'].is_a?(Array) then
Value['proxy-groups'].each{|group|
threads << Thread.new {
if group['type'] == 'url-test' then
@@ -314,7 +336,7 @@ yml_other_set()
# URL-Test interval
begin
if '$urltest_interval_mod' != '0' then
if Value.key?('proxy-groups') then
if Value.key?('proxy-groups') and Value['proxy-groups'].is_a?(Array) then
Value['proxy-groups'].each{|group|
threads << Thread.new {
if ['url-test', 'fallback', 'load-balance', 'smart'].include?(group['type']) then
@@ -349,7 +371,7 @@ yml_other_set()
};
};
end;
if Value.key?('proxy-groups') then
if Value.key?('proxy-groups') and Value['proxy-groups'].is_a?(Array) then
Value['proxy-groups'].each{|group|
threads << Thread.new {
if ['url-test', 'fallback', 'load-balance', 'smart'].include?(group['type']) then
@@ -365,7 +387,7 @@ yml_other_set()
# smart auto switch
begin
if ('${8}' == '1' or '${9}' == '1' or '${11}' != '0' or '${12}' != '0' or '${12}' == '1' or '${13}' == '1') and Value.key?('proxy-groups') then
if ('${8}' == '1' or '${9}' == '1' or '${11}' != '0' or '${12}' != '0' or '${12}' == '1' or '${13}' == '1') and Value.key?('proxy-groups') and Value['proxy-groups'].is_a?(Array) then
Value['proxy-groups'].each{|group|
threads << Thread.new {
if '${8}' == '1' and ['url-test', 'load-balance'].include?(group['type']) then