diff --git a/base/pref-new.yml b/base/pref-new.yml index 1f9661d..099a73b 100644 --- a/base/pref-new.yml +++ b/base/pref-new.yml @@ -34,10 +34,10 @@ userinfo: - {match: "^.*?流量:(?:.*?) 剩:(.*?)天$", replace: "left=$1d"} node_pref: - udp_flag: false - tcp_fast_open_flag: false +# udp_flag: false +# tcp_fast_open_flag: false sort_flag: false - skip_cert_verify_flag: false +# skip_cert_verify_flag: false filter_deprecated_nodes: false append_sub_userinfo: true clash_use_new_field_name: false diff --git a/base/pref.ini b/base/pref.ini index 0516a8b..6dcad40 100644 --- a/base/pref.ini +++ b/base/pref.ini @@ -76,10 +76,10 @@ time_rule=^Smart Access expire: (\d+)/(\d+)/(\d+)$|$1:$2:$3:0:0:0 time_rule=^.*?流量:(?:.*?) 剩:(.*)$|left=$1d [node_pref] -udp_flag=false -tcp_fast_open_flag=false +;udp_flag=false +;tcp_fast_open_flag=false sort_flag=false -skip_cert_verify_flag=false +;skip_cert_verify_flag=false filter_deprecated_nodes=false append_sub_userinfo=true clash_use_new_field_name=false diff --git a/src/interfaces.cpp b/src/interfaces.cpp index 3572414..5978628 100644 --- a/src/interfaces.cpp +++ b/src/interfaces.cpp @@ -47,7 +47,8 @@ std::mutex on_configuring; //preferences string_array renames, emojis; bool add_emoji = false, remove_old_emoji = false, append_proxy_type = false, filter_deprecated = true; -bool udp_flag = false, tfo_flag = false, scv_flag = false, do_sort = false, config_update_strict = false; +tribool udp_flag, tfo_flag, scv_flag; +bool do_sort = false, config_update_strict = false; bool clash_use_new_field_name = false; std::string proxy_config, proxy_ruleset, proxy_subscription; int config_update_interval = 0; @@ -153,7 +154,7 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS) switch(hash_(vArray[0])) { case "DOMAIN-SUFFIX"_hash: - strLine = " - '." + vArray[1] + "'"; + strLine = " - '." + vArray[1] + "'\n - '" + vArray[1] + "'"; break; case "DOMAIN"_hash: strLine = " - '" + vArray[1] + "'"; @@ -536,10 +537,15 @@ void readYAMLConf(YAML::Node &node) if(node["node_pref"].IsDefined()) { section = node["node_pref"]; + /* section["udp_flag"] >> udp_flag; section["tcp_fast_open_flag"] >> tfo_flag; - section["sort_flag"] >> do_sort; section["skip_cert_verify_flag"] >> scv_flag; + */ + udp_flag.set(safe_as(section["udp_flag"])); + tfo_flag.set(safe_as(section["tcp_fast_open_flag"])); + scv_flag.set(safe_as(section["skip_cert_verify_flag"])); + section["sort_flag"] >> do_sort; section["filter_deprecated_nodes"] >> filter_deprecated; section["append_sub_userinfo"] >> append_userinfo; section["clash_use_new_field_name"] >> clash_use_new_field_name; @@ -759,10 +765,15 @@ void readConf() if(ini.SectionExist("node_pref")) { ini.EnterSection("node_pref"); + /* ini.GetBoolIfExist("udp_flag", udp_flag); ini.GetBoolIfExist("tcp_fast_open_flag", tfo_flag); - ini.GetBoolIfExist("sort_flag", do_sort); ini.GetBoolIfExist("skip_cert_verify_flag", scv_flag); + */ + udp_flag.set(ini.Get("udp_flag")); + tfo_flag.set(ini.Get("tcp_fast_open_flag")); + scv_flag.set(ini.Get("skip_cert_verify_flag")); + ini.GetBoolIfExist("sort_flag", do_sort); ini.GetBoolIfExist("filter_deprecated_nodes", filter_deprecated); ini.GetBoolIfExist("append_sub_userinfo", append_userinfo); ini.GetBoolIfExist("clash_use_new_field_name", clash_use_new_field_name); @@ -1191,21 +1202,33 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS) ext.remove_emoji = remove_old_emoji; } ext.append_proxy_type = append_type.get(append_proxy_type); + if(target == "clash" || target == "clashr") + expand.define(true); + /// read preference from argument ext.tfo = tfo; ext.udp = udp; ext.skip_cert_verify = scv; + /// assign global var if not in argument + ext.tfo.define(tfo_flag); + ext.udp.define(udp_flag); + ext.skip_cert_verify.define(scv_flag); + ext.sort_flag = sort_flag.get(do_sort); ext.filter_deprecated = fdn.get(filter_deprecated); ext.clash_new_field_name = clash_new_field.get(clash_use_new_field_name); ext.clash_script = clash_script.get(); + if(!expand) + ext.clash_new_field_name = true; + else + ext.clash_script = false; ext.nodelist = nodelist; ext.surge_ssr_path = surge_ssr_path; ext.quanx_dev_id = dev_id.size() ? dev_id : quanx_script_id; ext.enable_rule_generator = enable_rule_generator; ext.overwrite_original_rules = overwrite_original_rules; - if(expand) + if(!expand) ext.managed_config_prefix = managed_config_prefix; //load external configuration diff --git a/src/subexport.cpp b/src/subexport.cpp index 7a05388..051c935 100644 --- a/src/subexport.cpp +++ b/src/subexport.cpp @@ -1284,9 +1284,11 @@ std::string netchToClash(std::vector &nodes, std::string &base_conf, s if(!ext.enable_rule_generator) return YAML::Dump(yamlnode); - if(ext.clash_script) + if(ext.managed_config_prefix.size() || ext.clash_script) { - renderClashScript(yamlnode, ruleset_content_array, ext.managed_config_prefix); + if(yamlnode["mode"].IsDefined()) + yamlnode["mode"] = ext.clash_script ? "Script" : "Rule"; + renderClashScript(yamlnode, ruleset_content_array, ext.managed_config_prefix, ext.clash_script, ext.overwrite_original_rules); return YAML::Dump(yamlnode); } diff --git a/src/templates.cpp b/src/templates.cpp index 373e14e..e3f02eb 100644 --- a/src/templates.cpp +++ b/src/templates.cpp @@ -140,21 +140,24 @@ const std::string clash_script_template = R"(def main(ctx, md): {% if not rule.keyword == "" %}{% include "keyword_template" %}{% endif %} {% endfor %} - ip = ctx.resolve_ip(md.host) - if ip == "": - return "{{ match_group }}" geoips = { {{ geoips }} } + ip = md.dst_ip + if ip == "": + ip = ctx.resolve_ip(md.host) + if ip == "": + ctx.log('[Script] dns lookup error use {{ match_group }}') + return "{{ match_group }}" for key in geoips: if ctx.geoip(ip) == key: return geoips[key] return "{{ match_group }}")"; -const std::string clash_script_group_template = R"( if ctx.rule_providers["{{ rule.group }}_domain"].match(md): +const std::string clash_script_group_template = R"({% if rule.has_domain == "true" %} if ctx.rule_providers["{{ rule.name }}_domain"].match(md): ctx.log('[Script] matched {{ rule.group }} DOMAIN rule') - return "{{ rule.group }}" - if ctx.rule_providers["{{ rule.group }}_ipcidr"].match(md): + return "{{ rule.group }}"{% endif %} +{% if rule.has_ipcidr == "true" %} if ctx.rule_providers["{{ rule.name }}_ipcidr"].match(md): ctx.log('[Script] matched {{ rule.group }} IP rule') - return "{{ rule.group }}")"; + return "{{ rule.group }}"{% endif %})"; const std::string clash_script_keyword_template = R"( keywords = [{{ rule.keyword }}] for keyword in keywords: @@ -162,14 +165,20 @@ const std::string clash_script_keyword_template = R"( keywords = [{{ rule.keywo ctx.log('[Script] matched {{ rule.group }} DOMAIN-KEYWORD rule') return "{{ rule.group }}")"; -int renderClashScript(YAML::Node &base_rule, std::vector &ruleset_content_array, std::string remote_path_prefix) +int renderClashScript(YAML::Node &base_rule, std::vector &ruleset_content_array, std::string remote_path_prefix, bool script, bool overwrite_original_rules) { nlohmann::json data; - std::string match_group, geoips, retrieved_rules; - std::string strLine, rule_group, rule_path; + std::string match_group, geoips, retrieved_rules; + std::string strLine, rule_group, rule_path, rule_name; std::stringstream strStrm; string_array vArray, groups; - string_map keywords, urls; + string_map keywords, urls, names; + std::map has_domain, has_ipcidr; + YAML::Node rules; + + if(!overwrite_original_rules && base_rule["rules"].IsDefined()) + rules = base_rule["rules"]; + for(ruleset_content &x : ruleset_content_array) { rule_group = x.rule_group; @@ -177,28 +186,40 @@ int renderClashScript(YAML::Node &base_rule, std::vector &rules if(rule_path.empty()) { strLine = x.rule_content.get().substr(2); - if(startsWith(strLine, "MATCH") || startsWith(strLine, "FINAL")) - match_group = rule_group; - else if(startsWith(strLine, "GEOIP")) + if(script) { - vArray = split(strLine, ","); - if(vArray.size() < 2) - continue; - geoips += "\"" + vArray[1] + "\": \"" + rule_group + "\","; + if(startsWith(strLine, "MATCH") || startsWith(strLine, "FINAL")) + match_group = rule_group; + else if(startsWith(strLine, "GEOIP")) + { + vArray = split(strLine, ","); + if(vArray.size() < 2) + continue; + geoips += "\"" + vArray[1] + "\": \"" + rule_group + "\","; + } + continue; } + if(strLine.find("FINAL") == 0) + strLine.replace(0, 5, "MATCH"); + strLine += "," + rule_group; + if(std::count(strLine.begin(), strLine.end(), ',') > 2) + strLine = regReplace(strLine, "^(.*?,.*?)(,.*)(,.*)$", "$1$3$2"); + rules.push_back(strLine); continue; } else { - if(fileExist(rule_path) || startsWith(rule_path, "https://") || startsWith(rule_path, "http://") || startsWith(rule_path, "data:")) + if(remote_path_prefix.size()) { - if(urls.find(rule_group) == urls.end()) - urls[rule_group] = rule_path; + if(fileExist(rule_path) || startsWith(rule_path, "https://") || startsWith(rule_path, "http://") || startsWith(rule_path, "data:")) + { + rule_name = std::to_string(hash_(rule_path)); + names[rule_name] = rule_group; + urls[rule_name] = rule_path; + } else - urls[rule_group] += "|" + rule_path; + continue; } - else - continue; retrieved_rules = x.rule_content.get(); if(retrieved_rules.empty()) @@ -225,53 +246,89 @@ int renderClashScript(YAML::Node &base_rule, std::vector &rules if(startsWith(strLine, "DOMAIN-KEYWORD,")) { - vArray = split(strLine, ","); - if(vArray.size() < 2) - continue; - if(keywords.find(rule_group) == keywords.end()) - keywords[rule_group] = "\"" + vArray[1] + "\""; + if(script) + { + vArray = split(strLine, ","); + if(vArray.size() < 2) + continue; + if(keywords.find(rule_name) == keywords.end()) + keywords[rule_name] = "\"" + vArray[1] + "\""; + else + keywords[rule_name] += ",\"" + vArray[1] + "\""; + } else - keywords[rule_group] += ",\"" + vArray[1] + "\""; + { + strLine += "," + rule_group; + if(std::count(strLine.begin(), strLine.end(), ',') > 2) + strLine = regReplace(strLine, "^(.*?,.*?)(,.*)(,.*)$", "$1$3$2"); + rules.push_back(strLine); + } } + else if(startsWith(strLine, "DOMAIN,") || startsWith(strLine, "DOMAIN-SUFFIX,")) + has_domain[rule_name] = true; + else if(startsWith(strLine, "IP-CIDR,") || startsWith(strLine, "IP-CIDR6,")) + has_ipcidr[rule_name] = true; } - if(std::find(groups.begin(), groups.end(), rule_group) == groups.end()) - groups.emplace_back(rule_group); + if(has_domain[rule_name] && !script) + rules.push_back("RULE-SET," + rule_group + "_" + rule_name + "_domain," + rule_group); + if(has_ipcidr[rule_name] && !script) + rules.push_back("RULE-SET," + rule_group + "_" + rule_name + "_ipcidr," + rule_group); + if(std::find(groups.begin(), groups.end(), rule_name) == groups.end()) + groups.emplace_back(rule_name); } } int index = 0; for(std::string &x : groups) { std::string json_path = "rules." + std::to_string(index) + "."; - std::string url = urls[x], keyword = keywords[x]; - base_rule["rule-providers"][x + "_domain"]["type"] = "http"; - base_rule["rule-providers"][x + "_domain"]["behavior"] = "domain"; - base_rule["rule-providers"][x + "_domain"]["url"] = remote_path_prefix + "/getruleset?type=3&url=" + urlsafe_base64_encode(url); - base_rule["rule-providers"][x + "_domain"]["path"] = "./rule-providers_" + getMD5(url + x + "domain") + ".yaml"; - base_rule["rule-providers"][x + "_ipcidr"]["type"] = "http"; - base_rule["rule-providers"][x + "_ipcidr"]["behavior"] = "ipcidr"; - base_rule["rule-providers"][x + "_ipcidr"]["url"] = remote_path_prefix + "/getruleset?type=4&url=" + urlsafe_base64_encode(url); - base_rule["rule-providers"][x + "_ipcidr"]["path"] = "./rule-providers_" + getMD5(url + x + "ipcidr") + ".yaml"; - parse_json_pointer(data, json_path + "group", x); - parse_json_pointer(data, json_path + "set", "true"); - parse_json_pointer(data, json_path + "keyword", keyword); + std::string url = urls[x], keyword = keywords[x], name = names[x]; + bool group_has_domain = has_domain[x], group_has_ipcidr = has_ipcidr[x]; + if(group_has_domain) + { + base_rule["rule-providers"][name + "_" + x + "_domain"]["type"] = "http"; + base_rule["rule-providers"][name + "_" + x + "_domain"]["behavior"] = "domain"; + base_rule["rule-providers"][name + "_" + x + "_domain"]["url"] = remote_path_prefix + "/getruleset?type=3&url=" + urlsafe_base64_encode(url); + base_rule["rule-providers"][name + "_" + x + "_domain"]["path"] = "./rule-providers_" + x + "_domain.yaml"; + } + if(group_has_ipcidr) + { + base_rule["rule-providers"][name + "_" + x + "_ipcidr"]["type"] = "http"; + base_rule["rule-providers"][name + "_" + x + "_ipcidr"]["behavior"] = "ipcidr"; + base_rule["rule-providers"][name + "_" + x + "_ipcidr"]["url"] = remote_path_prefix + "/getruleset?type=4&url=" + urlsafe_base64_encode(url); + base_rule["rule-providers"][name + "_" + x + "_ipcidr"]["path"] = "./rule-providers_" + x + "_ipcidr.yaml"; + } + if(script) + { + parse_json_pointer(data, json_path + "has_domain", group_has_domain ? "true" : "false"); + parse_json_pointer(data, json_path + "has_ipcidr", group_has_ipcidr ? "true" : "false"); + parse_json_pointer(data, json_path + "name", name + "_" + x); + parse_json_pointer(data, json_path + "group", name); + parse_json_pointer(data, json_path + "set", "true"); + parse_json_pointer(data, json_path + "keyword", keyword); + } index++; } - parse_json_pointer(data, "geoips", geoips.erase(geoips.size() - 1)); - parse_json_pointer(data, "match_group", match_group); - - inja::Environment env; - env.include_template("group_template", env.parse(clash_script_group_template)); - env.include_template("keyword_template", env.parse(clash_script_keyword_template)); - inja::Template tmpl = env.parse(clash_script_template); - - try + if(script) { - std::string output_content = env.render(tmpl, data); - base_rule["script"]["code"] = output_content; - } - catch (std::exception&) - { - return -1; + parse_json_pointer(data, "geoips", geoips.erase(geoips.size() - 1)); + parse_json_pointer(data, "match_group", match_group); + + inja::Environment env; + env.include_template("group_template", env.parse(clash_script_group_template)); + env.include_template("keyword_template", env.parse(clash_script_keyword_template)); + inja::Template tmpl = env.parse(clash_script_template); + + try + { + std::string output_content = env.render(tmpl, data); + base_rule["script"]["code"] = output_content; + } + catch (std::exception&) + { + return -1; + } } + else + base_rule["rules"] = rules; return 0; } diff --git a/src/templates.h b/src/templates.h index 4232e72..9188f4c 100644 --- a/src/templates.h +++ b/src/templates.h @@ -4,6 +4,8 @@ #include #include +#include "subexport.h" + typedef std::map string_map; struct template_args @@ -14,6 +16,6 @@ struct template_args }; int render_template(const std::string &content, const template_args &vars, std::string &output, const std::string &include_scope = "template"); -int renderClashScript(YAML::Node &base_rule, std::vector &ruleset_content_array, std::string remote_path_prefix); +int renderClashScript(YAML::Node &base_rule, std::vector &ruleset_content_array, std::string remote_path_prefix, bool script, bool overwrite_original_rules); #endif // TEMPLATES_H_INCLUDED