mirror of
https://github.com/asdlokj1qpi233/subconverter.git
synced 2025-10-28 04:12:49 +00:00
Enhancements
Fix error when parsing some Surge configurations. Fix not filtering USER-AGENT and URL-REGEX rules for Surfboard configurations. (Issue [#127](https://github.com/tindy2013/subconverter/issues/127)). Add support for specifying tolerance and timeout for some proxy groups. (Issue [#121](https://github.com/tindy2013/subconverter/issues/121)). Add keeping comments and blank lines for more sections in Quantumult X configurations. Add rule type match to filter rules. Optimize codes.
This commit is contained in:
@@ -71,7 +71,7 @@ ruleset:
|
||||
|
||||
proxy_group:
|
||||
custom_proxy_group:
|
||||
# - {name: UrlTest, type: url-test, rule: [".*"], url: http://www.gstatic.com/generate_204, interval: 300}
|
||||
# - {name: UrlTest, type: url-test, rule: [".*"], url: http://www.gstatic.com/generate_204, interval: 300, tolerance: 100, timeout: 5}
|
||||
# - {name: Proxy, type: select, rule: [".*"]}
|
||||
# - {name: group1, type: select, rule: ["!!GROUPID=0"]}
|
||||
# - {name: v2ray, type: select, rule: ["!!GROUP=V2RayProvider"]}
|
||||
|
||||
@@ -142,13 +142,13 @@ surge_ruleset=!!import:snippets/rulesets.txt
|
||||
[clash_proxy_group]
|
||||
;Generate Clash Proxy Group with the following patterns. Node filterting rule supports regular expression.
|
||||
;Format: Group_Name`select`Rule_1`Rule_2`...
|
||||
; Group_Name`url-test|fallback|load-balance`Rule_1`Rule_2`...`test_url`interval
|
||||
; Group_Name`url-test|fallback|load-balance`Rule_1`Rule_2`...`test_url`interval[,timeout][,tolerance]
|
||||
;Rule with "[]" prefix will be added directly.
|
||||
|
||||
;custom_proxy_group=Proxy`select`.*`[]AUTO`[]DIRECT`.*
|
||||
;custom_proxy_group=UrlTest`url-test`.*`http://www.gstatic.com/generate_204`300
|
||||
;custom_proxy_group=FallBack`fallback`.*`http://www.gstatic.com/generate_204`300
|
||||
;custom_proxy_group=LoadBalance`load-balance`.*`http://www.gstatic.com/generate_204`300
|
||||
;custom_proxy_group=UrlTest`url-test`.*`http://www.gstatic.com/generate_204`300,5,100
|
||||
;custom_proxy_group=FallBack`fallback`.*`http://www.gstatic.com/generate_204`300,5
|
||||
;custom_proxy_group=LoadBalance`load-balance`.*`http://www.gstatic.com/generate_204`300,,100
|
||||
;custom_proxy_group=SSID`ssid`default_group`celluar=group0,ssid1=group1,ssid2=group2
|
||||
|
||||
;custom_proxy_group=g1`select`!!GROUPID=0
|
||||
|
||||
@@ -88,12 +88,17 @@ std::string parseProxy(const std::string &source)
|
||||
return proxy;
|
||||
}
|
||||
|
||||
#define basic_types "DOMAIN", "DOMAIN-SUFFIX", "DOMAIN-KEYWORD", "IP-CIDR", "SRC-IP-CIDR", "GEOIP", "MATCH", "FINAL"
|
||||
const string_array surge_rule_type = {basic_types, "IP-CIDR6", "USER-AGENT", "URL-REGEX", "AND", "OR", "NOT", "PROCESS-NAME", "IN-PORT", "DEST-PORT", "SRC-IP"};
|
||||
const string_array quanx_rule_type = {basic_types, "USER-AGENT", "URL-REGEX", "PROCESS-NAME", "HOST", "HOST-SUFFIX", "HOST-KEYWORD"};
|
||||
|
||||
std::string getRuleset(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string url = urlsafe_base64_decode(getUrlArg(argument, "url")), type = getUrlArg(argument, "type"), group = urlsafe_base64_decode(getUrlArg(argument, "group"));
|
||||
std::string output_content, dummy;
|
||||
int type_int = to_int(type, 0);
|
||||
|
||||
if(!url.size() || !type.size() || (type == "2" && !group.size()) || (type != "1" && type != "2"))
|
||||
if(!url.size() || !type.size() || (type_int == 2 && !group.size()) || (type_int != 1 && type_int != 2))
|
||||
{
|
||||
*status_code = 400;
|
||||
return "Invalid request!";
|
||||
@@ -108,42 +113,43 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
|
||||
return "Invalid request!";
|
||||
}
|
||||
|
||||
if(type == "2")
|
||||
std::string strLine;
|
||||
std::stringstream ss;
|
||||
const std::string rule_match_regex = "^(.*?,.*?)(,.*)(,.*)$";
|
||||
|
||||
ss << output_content;
|
||||
char delimiter = count(output_content.begin(), output_content.end(), '\n') < 1 ? '\r' : '\n';
|
||||
std::string::size_type lineSize;
|
||||
|
||||
output_content.clear();
|
||||
|
||||
while(getline(ss, strLine, delimiter))
|
||||
{
|
||||
std::string strLine;
|
||||
std::stringstream ss;
|
||||
const std::string rule_match_regex = "^(.*?,.*?)(,.*)(,.*)$";
|
||||
if(type_int == 2 && !std::any_of(quanx_rule_type.begin(), quanx_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
else if(!std::any_of(surge_rule_type.begin(), surge_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
|
||||
ss << output_content;
|
||||
char delimiter = count(output_content.begin(), output_content.end(), '\n') < 1 ? '\r' : '\n';
|
||||
std::string::size_type lineSize;
|
||||
|
||||
output_content.clear();
|
||||
|
||||
while(getline(ss, strLine, delimiter))
|
||||
lineSize = strLine.size();
|
||||
if(lineSize && strLine[lineSize - 1] == '\r') //remove line break
|
||||
{
|
||||
if(strLine.find("IP-CIDR6") == 0 || strLine.find("URL-REGEX") == 0 || strLine.find("PROCESS-NAME") == 0 || strLine.find("AND") == 0 || strLine.find("OR") == 0) //remove unsupported types
|
||||
continue;
|
||||
strLine.erase(lineSize - 1);
|
||||
lineSize--;
|
||||
}
|
||||
|
||||
lineSize = strLine.size();
|
||||
if(lineSize && strLine[lineSize - 1] == '\r') //remove line break
|
||||
{
|
||||
strLine.erase(lineSize - 1);
|
||||
lineSize--;
|
||||
}
|
||||
|
||||
if(!strLine.empty() && (strLine[0] != ';' && strLine[0] != '#' && !(lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')))
|
||||
if(!strLine.empty() && (strLine[0] != ';' && strLine[0] != '#' && !(lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')))
|
||||
{
|
||||
if(type_int == 2)
|
||||
{
|
||||
strLine += "," + group;
|
||||
|
||||
if(std::count(strLine.begin(), strLine.end(), ',') > 2 && regReplace(strLine, rule_match_regex, "$2") == ",no-resolve")
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3$2");
|
||||
else
|
||||
strLine = regReplace(strLine, rule_match_regex, "$1$3");
|
||||
}
|
||||
|
||||
output_content.append(strLine + "\n");
|
||||
}
|
||||
|
||||
output_content.append(strLine + "\n");
|
||||
}
|
||||
|
||||
return output_content;
|
||||
@@ -251,13 +257,13 @@ void readEmoji(YAML::Node node, string_array &dest, bool scope_limit = true)
|
||||
|
||||
void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true)
|
||||
{
|
||||
std::string strLine, name, type, url, interval;
|
||||
string_array tempArray;
|
||||
YAML::Node object;
|
||||
unsigned int i, j;
|
||||
|
||||
for(i = 0; i < node.size(); i++)
|
||||
{
|
||||
std::string strLine, name, type;
|
||||
name.clear();
|
||||
eraseElements(tempArray);
|
||||
object = node[i];
|
||||
@@ -268,19 +274,21 @@ void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true)
|
||||
dest.emplace_back(name);
|
||||
continue;
|
||||
}
|
||||
url = "http://www.gstatic.com/generate_204", interval = "300";
|
||||
std::string url = "http://www.gstatic.com/generate_204", interval = "300", tolerance, timeout;
|
||||
object["name"] >> name;
|
||||
object["type"] >> type;
|
||||
tempArray.emplace_back(name);
|
||||
tempArray.emplace_back(type);
|
||||
object["url"] >> url;
|
||||
object["interval"] >> interval;
|
||||
object["tolerance"] >> tolerance;
|
||||
object["timeout"] >> timeout;
|
||||
for(j = 0; j < object["rule"].size(); j++)
|
||||
tempArray.emplace_back(safe_as<std::string>(object["rule"][j]));
|
||||
if(type != "select" && type != "ssid")
|
||||
{
|
||||
tempArray.emplace_back(url);
|
||||
tempArray.emplace_back(interval);
|
||||
tempArray.emplace_back(interval + "," + timeout + "," + tolerance);
|
||||
}
|
||||
|
||||
if((type == "select" && tempArray.size() < 3) || (type == "ssid" && tempArray.size() < 4) || (type != "select" && type != "ssid" && tempArray.size() < 5))
|
||||
|
||||
@@ -1207,9 +1207,11 @@ bool explodeSurge(std::string surge, const std::string &custom_port, int local_p
|
||||
|
||||
ini.store_isolated_line = true;
|
||||
ini.keep_empty_section = false;
|
||||
ini.allow_dup_section_titles = true;
|
||||
ini.SetIsolatedItemsSection("Proxy");
|
||||
ini.IncludeSection("Proxy");
|
||||
ini.AddDirectSaveSection("Proxy");
|
||||
surge = regReplace(surge, "^#!.*$\\r?\\n", "");
|
||||
ini.Parse(surge);
|
||||
|
||||
if(!ini.SectionExist("Proxy"))
|
||||
|
||||
@@ -22,8 +22,15 @@
|
||||
extern bool api_mode;
|
||||
extern string_array ss_ciphers, ssr_ciphers;
|
||||
|
||||
string_array clashr_protocols = {"auth_aes128_md5", "auth_aes128_sha1"};
|
||||
string_array clashr_obfs = {"plain", "http_simple", "http_post", "tls1.2_ticket_auth"};
|
||||
const string_array clashr_protocols = {"auth_aes128_md5", "auth_aes128_sha1"};
|
||||
const string_array clashr_obfs = {"plain", "http_simple", "http_post", "tls1.2_ticket_auth"};
|
||||
|
||||
/// rule type lists
|
||||
#define basic_types "DOMAIN", "DOMAIN-SUFFIX", "DOMAIN-KEYWORD", "IP-CIDR", "SRC-IP-CIDR", "GEOIP", "MATCH", "FINAL"
|
||||
const string_array clash_rule_type = {basic_types, "IP-CIDR6", "SRC-PORT", "DST-PORT"};
|
||||
const string_array surge_rule_type = {basic_types, "IP-CIDR6", "USER-AGENT", "URL-REGEX", "AND", "OR", "NOT", "PROCESS-NAME", "IN-PORT", "DEST-PORT", "SRC-IP"};
|
||||
const string_array quanx_rule_type = {basic_types, "USER-AGENT", "URL-REGEX", "PROCESS-NAME", "HOST", "HOST-SUFFIX", "HOST-KEYWORD"};
|
||||
const string_array surfb_rule_type = {basic_types, "IP-CIDR6", "PROCESS-NAME", "IN-PORT", "DEST-PORT", "SRC-IP"};
|
||||
|
||||
template <typename T> T safe_as (const YAML::Node& node)
|
||||
{
|
||||
@@ -451,8 +458,12 @@ void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset
|
||||
}
|
||||
if(!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored
|
||||
continue;
|
||||
/*
|
||||
if(strLine.find("USER-AGENT") == 0 || strLine.find("URL-REGEX") == 0 || strLine.find("PROCESS-NAME") == 0 || strLine.find("AND") == 0 || strLine.find("OR") == 0) //remove unsupported types
|
||||
continue;
|
||||
*/
|
||||
if(!std::any_of(clash_rule_type.begin(), clash_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
/*
|
||||
if(strLine.find("IP-CIDR") == 0)
|
||||
strLine = replace_all_distinct(strLine, ",no-resolve", "");
|
||||
@@ -525,7 +536,7 @@ std::string rulesetToClashStr(YAML::Node &base_rule, std::vector<ruleset_content
|
||||
}
|
||||
if(!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored
|
||||
continue;
|
||||
if(strLine.find("USER-AGENT") == 0 || strLine.find("URL-REGEX") == 0 || strLine.find("PROCESS-NAME") == 0 || strLine.find("AND") == 0 || strLine.find("OR") == 0) //remove unsupported types
|
||||
if(!std::any_of(clash_rule_type.begin(), clash_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
strLine += "," + rule_group;
|
||||
if(std::count(strLine.begin(), strLine.end(), ',') > 2)
|
||||
@@ -558,7 +569,19 @@ void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_
|
||||
}
|
||||
|
||||
if(overwrite_original_rules)
|
||||
{
|
||||
base_rule.EraseSection();
|
||||
switch(surge_ver)
|
||||
{
|
||||
case -1:
|
||||
base_rule.EraseSection("filter_remote");
|
||||
break;
|
||||
case -4:
|
||||
base_rule.EraseSection("Remote Rule");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const std::string rule_match_regex = "^(.*?,.*?)(,.*)(,.*)$";
|
||||
|
||||
@@ -663,7 +686,13 @@ void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_
|
||||
}
|
||||
if(!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored
|
||||
continue;
|
||||
if((surge_ver == -1 || surge_ver == -2) && (strLine.find("IP-CIDR6") == 0 || strLine.find("URL-REGEX") == 0 || strLine.find("PROCESS-NAME") == 0 || strLine.find("AND") == 0 || strLine.find("OR") == 0)) //remove unsupported types
|
||||
|
||||
/// remove unsupported types
|
||||
if((surge_ver == -1 || surge_ver == -2) && !std::any_of(quanx_rule_type.begin(), quanx_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
else if(surge_ver == -3 && !std::any_of(surfb_rule_type.begin(), surfb_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
else if(!std::any_of(surge_rule_type.begin(), surge_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
|
||||
strLine += "," + rule_group;
|
||||
@@ -690,6 +719,28 @@ void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_
|
||||
}
|
||||
}
|
||||
|
||||
void parseGroupTimes(const std::string &src, int *interval, int *tolerance, int *timeout)
|
||||
{
|
||||
std::vector<int*> ptrs;
|
||||
ptrs.push_back(interval);
|
||||
ptrs.push_back(timeout);
|
||||
ptrs.push_back(tolerance);
|
||||
string_size bpos = 0, epos = src.find(",");
|
||||
for(int *x : ptrs)
|
||||
{
|
||||
if(x != NULL)
|
||||
*x = to_int(src.substr(bpos, epos - bpos), 0);
|
||||
if(epos != src.npos)
|
||||
{
|
||||
bpos = epos + 1;
|
||||
epos = src.find(",", bpos);
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void groupGenerate(std::string &rule, std::vector<nodeInfo> &nodelist, std::vector<std::string> &filtered_nodelist, bool add_direct)
|
||||
{
|
||||
std::string group;
|
||||
@@ -980,6 +1031,7 @@ void netchToClash(std::vector<nodeInfo> &nodes, YAML::Node &yamlnode, string_arr
|
||||
singlegroup["name"] = vArray[0];
|
||||
singlegroup["type"] = vArray[1];
|
||||
|
||||
int interval = -1;
|
||||
rules_upper_bound = vArray.size();
|
||||
switch(hash_(vArray[1]))
|
||||
{
|
||||
@@ -992,7 +1044,8 @@ void netchToClash(std::vector<nodeInfo> &nodes, YAML::Node &yamlnode, string_arr
|
||||
continue;
|
||||
rules_upper_bound -= 2;
|
||||
singlegroup["url"] = vArray[rules_upper_bound];
|
||||
singlegroup["interval"] = to_int(vArray[rules_upper_bound + 1]);
|
||||
parseGroupTimes(vArray[rules_upper_bound + 1], &interval, NULL, NULL);
|
||||
singlegroup["interval"] = interval;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
@@ -1072,10 +1125,6 @@ std::string netchToSurge(std::vector<nodeInfo> &nodes, std::string &base_conf, s
|
||||
std::vector<nodeInfo> nodelist;
|
||||
unsigned short local_port = 1080;
|
||||
bool tlssecure;
|
||||
//group pref
|
||||
std::string url;
|
||||
int interval = 0;
|
||||
std::string ssid_default;
|
||||
|
||||
string_array vArray, remarks_list, filtered_nodelist, args;
|
||||
|
||||
@@ -1231,6 +1280,10 @@ std::string netchToSurge(std::vector<nodeInfo> &nodes, std::string &base_conf, s
|
||||
ini.EraseSection();
|
||||
for(std::string &x : extra_proxy_group)
|
||||
{
|
||||
//group pref
|
||||
std::string url;
|
||||
int interval = 0, tolerance = 0, timeout = 0;
|
||||
std::string ssid_default;
|
||||
eraseElements(filtered_nodelist);
|
||||
unsigned int rules_upper_bound = 0;
|
||||
url.clear();
|
||||
@@ -1252,7 +1305,7 @@ std::string netchToSurge(std::vector<nodeInfo> &nodes, std::string &base_conf, s
|
||||
continue;
|
||||
rules_upper_bound -= 2;
|
||||
url = vArray[rules_upper_bound];
|
||||
interval = to_int(vArray[rules_upper_bound + 1]);
|
||||
parseGroupTimes(vArray[rules_upper_bound + 1], &interval, &tolerance, &timeout);
|
||||
break;
|
||||
case "ssid"_hash:
|
||||
if(rules_upper_bound < 4)
|
||||
@@ -1284,7 +1337,13 @@ std::string netchToSurge(std::vector<nodeInfo> &nodes, std::string &base_conf, s
|
||||
return std::move(a) + "," + std::move(b);
|
||||
});
|
||||
if(vArray[1] == "url-test" || vArray[1] == "fallback")
|
||||
{
|
||||
proxy += ",url=" + url + ",interval=" + std::to_string(interval);
|
||||
if(tolerance > 0)
|
||||
proxy += ",tolerance=" + std::to_string(tolerance);
|
||||
if(timeout > 0)
|
||||
proxy += ",timeout=" + std::to_string(timeout);
|
||||
}
|
||||
else if(vArray[1] == "load-balance")
|
||||
proxy += ",url=" + url;
|
||||
|
||||
@@ -1853,7 +1912,12 @@ std::string netchToQuanX(std::vector<nodeInfo> &nodes, std::string &base_conf, s
|
||||
{
|
||||
INIReader ini;
|
||||
ini.store_any_line = true;
|
||||
ini.AddDirectSaveSection("general");
|
||||
ini.AddDirectSaveSection("dns");
|
||||
ini.AddDirectSaveSection("rewrite_remote");
|
||||
ini.AddDirectSaveSection("rewrite_local");
|
||||
ini.AddDirectSaveSection("task_local");
|
||||
ini.AddDirectSaveSection("mitm");
|
||||
if(!ext.nodelist && ini.Parse(base_conf) != 0)
|
||||
return std::string();
|
||||
|
||||
@@ -2548,7 +2612,7 @@ std::string netchToLoon(std::vector<nodeInfo> &nodes, std::string &base_conf, st
|
||||
continue;
|
||||
rules_upper_bound -= 2;
|
||||
url = vArray[rules_upper_bound];
|
||||
interval = to_int(vArray[rules_upper_bound + 1]);
|
||||
parseGroupTimes(vArray[rules_upper_bound + 1], &interval, NULL, NULL);
|
||||
break;
|
||||
case "ssid"_hash:
|
||||
if(vArray.size() < 4)
|
||||
|
||||
Reference in New Issue
Block a user