Enhancements

Fix support for Quantumult X style VMess subscription.
Fix plugin support for Shadowsocks Android clients.
Add support for exporting Shadowsocks Android subscription.
Add regular expression blacklist to prevent high CPU usage.
Move "rename_node" options to "[node_pref]" section.
Optimize codes.
This commit is contained in:
Tindy X committed 2020-01-14 13:18:43 +08:00
1 parent 764b0e68d4
commit b186d805f3
6 files changed
+164 -30

No files matched your search

+7 -7
View File
@@ -35,6 +35,13 @@ proxy_subscription=NONE
;Append a proxy type string ([SS] [SSR] [VMess]) to node remark.
append_proxy_type=false
[node_pref]
udp_flag=false
tcp_fast_open_flag=false
sort_flag=false
skip_cert_verify_flag=false
filter_deprecated_nodes=false
;Rename remarks with the following patterns. Supports regular expression.
;Format: Search_Pattern@Replace_Pattern
;rename_node=IPLC@专线
@@ -89,13 +96,6 @@ append_proxy_type=false
; times RE
rename_node=\(?((x|X)?(\d+)(\.?\d+)?)((\s?倍率?)|(x|X))\)?@$1x
[node_pref]
udp_flag=false
tcp_fast_open_flag=false
sort_flag=false
skip_cert_verify_flag=false
filter_deprecated_nodes=false
[managed_config]
;Append a '#!MANAGED-CONFIG' info to Surge configurations
write_managed_config=true
+23 -16
View File
@@ -47,6 +47,8 @@ std::string surge_ssr_path;
YAML::Node clash_base;
INIReader surge_base, mellow_base;
string_array regex_blacklist = {"(.*)*"};
#ifndef _WIN32
void SetConsoleTitle(std::string title)
{
@@ -176,11 +178,6 @@ void readConf()
proxy_ruleset = ini.Get("proxy_ruleset");
if(ini.ItemExist("proxy_subscription"))
proxy_subscription = ini.Get("proxy_subscription");
if(ini.ItemPrefixExist("rename_node"))
{
ini.GetAll("rename_node", renames_temp);
safe_set_renames(renames_temp);
}
if(ini.SectionExist("surge_external_proxy"))
{
@@ -202,6 +199,11 @@ void readConf()
scv_flag = ini.GetBool("skip_cert_verify_flag");
if(ini.ItemExist("filter_deprecated_nodes"))
filter_deprecated = ini.GetBool("filter_deprecated_nodes");
if(ini.ItemPrefixExist("rename_node"))
{
ini.GetAll("rename_node", renames_temp);
safe_set_renames(renames_temp);
}
}
ini.EnterSection("managed_config");
@@ -353,6 +355,9 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
std::vector<ruleset_content> rca;
extra_settings ext;
if(std::find(regex_blacklist.cbegin(), regex_blacklist.cend(), include) != regex_blacklist.cend() || std::find(regex_blacklist.cbegin(), regex_blacklist.cend(), exclude) != regex_blacklist.cend())
return "Invalid request!";
//for external configuration
std::string ext_clash_base = clash_rule_base, ext_surge_base = surge_rule_base, ext_mellow_base = mellow_rule_base, ext_surfboard_base = surfboard_rule_base;
@@ -516,7 +521,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(upload == "true")
uploadGist(target, upload_path, output_content, false);
return output_content;
}
else if(target == "surge")
{
@@ -534,7 +538,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(write_managed_config && managed_config_prefix.size() && !ext.nodelist)
output_content = "#!MANAGED-CONFIG " + managed_config_prefix + "/sub?" + argument + "\n\n" + output_content;
return output_content;
}
else if(target == "surfboard")
{
@@ -550,7 +553,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(write_managed_config && managed_config_prefix.size())
output_content = "#!MANAGED-CONFIG " + managed_config_prefix + "/sub?" + argument + "\n\n" + output_content;
return output_content;
}
else if(target == "mellow")
{
@@ -574,8 +576,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(upload == "true")
uploadGist("mellow", upload_path, output_content, true);
return output_content;
}
else if(target == "ss")
{
@@ -585,13 +585,19 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
uploadGist("ss", upload_path, output_content, false);
return output_content;
}
else if(target == "sssub")
{
std::cerr<<"SS Subscription"<<std::endl;
output_content = netchToSSSub(nodes, ext);
if(upload == "true")
uploadGist("sssub", upload_path, output_content, false);
}
else if(target == "ssr")
{
std::cerr<<"SSR"<<std::endl;
output_content = netchToSSR(nodes, ext);
if(upload == "true")
uploadGist("ssr", upload_path, output_content, false);
return output_content;
}
else if(target == "v2ray")
{
@@ -599,7 +605,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
output_content = netchToVMess(nodes, ext);
if(upload == "true")
uploadGist("v2ray", upload_path, output_content, false);
return output_content;
}
else if(target == "quan")
{
@@ -607,7 +612,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
output_content = netchToQuan(nodes, ext);
if(upload == "true")
uploadGist("quan", upload_path, output_content, false);
return output_content;
}
else if(target == "quanx")
{
@@ -615,7 +619,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
output_content = netchToQuanX(nodes, ext);
if(upload == "true")
uploadGist("quanx", upload_path, output_content, false);
return output_content;
}
else if(target == "ssd")
{
@@ -623,13 +626,12 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
output_content = netchToSSD(nodes, group, ext);
if(upload == "true")
uploadGist("ssd", upload_path, output_content, false);
return output_content;
}
else
{
std::cerr<<"Unspecified"<<std::endl;
return std::string();
}
return output_content;
}
std::string simpleToClashR(RESPONSE_CALLBACK_ARGS)
@@ -801,6 +803,11 @@ int main(int argc, char *argv[])
return subconverter(argument + "&target=ss", postdata);
});
append_response("GET", "/sssub", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
return subconverter(argument + "&target=sssub", postdata);
});
append_response("GET", "/ssr", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
return subconverter(argument + "&target=ssr", postdata);
+47 -6
View File
@@ -429,6 +429,7 @@ void explodeSSAndroid(std::string ss, bool libev, std::string custom_port, int l
Document json;
nodeInfo node;
std::string ps, password, method, server, port, group = SS_DEFAULT_GROUP;
std::string plugin, pluginopts;
int index = nodes.size();
//first add some extra data before parsing
ss = "{\"nodes\":" + ss + "}";
@@ -448,6 +449,8 @@ void explodeSSAndroid(std::string ss, bool libev, std::string custom_port, int l
ps = server + ":" + port;
json["nodes"][i]["password"] >> password;
json["nodes"][i]["method"] >> method;
plugin = GetMember(json["nodes"][i], "plugin");
pluginopts = GetMember(json["nodes"][i], "plugin_opts");
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.id = index;
@@ -455,7 +458,7 @@ void explodeSSAndroid(std::string ss, bool libev, std::string custom_port, int l
node.remarks = ps;
node.server = server;
node.port = to_int(port);
node.proxyStr = ssConstruct(server, port, password, method, "", "", ps, local_port, libev);
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, ps, local_port, libev);
nodes.push_back(node);
index++;
}
@@ -1245,13 +1248,11 @@ bool explodeSurge(std::string surge, std::string custom_port, int local_port, st
id = itemVal;
else if(itemName == "ws")
{
if(itemVal == "true")
net = "ws";
net = itemVal == "true" ? "ws" : "tcp";
}
else if(itemName == "tls")
{
if(itemVal == "true")
tls = "tls";
tls = itemVal == "true" ? "tls" : "";
}
else if(itemName == "ws-path")
path = itemVal;
@@ -1286,7 +1287,7 @@ bool explodeSurge(std::string surge, std::string custom_port, int local_port, st
}
node.proxyStr = httpConstruct(remarks, server, port, username, password);
}
else if(remarks == "shadowsocks") //quantumult style ss/ssr link
else if(remarks == "shadowsocks") //quantumult x style ss/ssr link
{
server = trim(configs[0].substr(0, configs[0].rfind(":")));
port = custom_port == "" ? trim(configs[0].substr(configs[0].rfind(":") + 1)) : custom_port;
@@ -1338,6 +1339,46 @@ bool explodeSurge(std::string surge, std::string custom_port, int local_port, st
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, remarks, local_port, libev);
}
}
else if(remarks == "vmess") //quantumult x style vmess link
{
server = trim(configs[0].substr(0, configs[0].rfind(":")));
port = custom_port == "" ? trim(configs[0].substr(configs[0].rfind(":") + 1)) : custom_port;
plugin = protocol = remarks = "";
net = "tcp";
for(i = 1; i < configs.size(); i++)
{
vArray = split(trim(configs[i]), "=");
if(vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
itemVal = trim(vArray[1]);
if(itemName == "method")
method = itemVal;
else if(itemName == "password")
id = itemVal;
else if(itemName == "tag")
remarks = itemVal;
else if(itemName == "obfs")
{
if(itemVal == "ws")
net = "ws";
}
else if(itemName == "obfs-host")
host = itemVal;
else if(itemName == "over-tls")
tls = itemVal == "true" ? "tls" : "";
}
if(remarks == "")
remarks = server + ":" + port;
if(host == "" && !isIPv4(server) && !isIPv6(server))
host = server;
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.group = V2RAY_DEFAULT_GROUP;
node.proxyStr = vmessConstruct(server, port, "", id, "0", net, method, path, host, tls, local_port);
}
else
continue;
+78
View File
@@ -1017,6 +1017,84 @@ std::string netchToSS(std::vector<nodeInfo> &nodes, extra_settings &ext)
return base64_encode(allLinks);
}
std::string netchToSSSub(std::vector<nodeInfo> &nodes, extra_settings &ext)
{
rapidjson::Document json;
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
std::string remark, hostname, password, method;
std::string plugin, pluginopts;
std::string protocol, obfs;
int port;
writer.StartArray();
std::for_each(nodes.begin(), nodes.end(), [ext](nodeInfo &x)
{
x.remarks = nodeRename(x.remarks);
if(ext.remove_emoji)
x.remarks = trim(removeEmoji(x.remarks));
if(ext.add_emoji)
x.remarks = addEmoji(x.remarks);
});
if(ext.sort_flag)
{
std::sort(nodes.begin(), nodes.end(), [](const nodeInfo &a, const nodeInfo &b)
{
return a.remarks < b.remarks;
});
}
for(nodeInfo &x : nodes)
{
json.Parse(x.proxyStr.data());
remark = x.remarks;
hostname = x.server;
port = (unsigned short)stoi(GetMember(json, "Port"));
password = GetMember(json, "Password");
method = GetMember(json, "EncryptMethod");
plugin = GetMember(json, "Plugin");
pluginopts = GetMember(json, "PluginOption");
protocol = GetMember(json, "Protocol");
obfs = GetMember(json, "OBFS");
switch(x.linkType)
{
case SPEEDTEST_MESSAGE_FOUNDSS:
if(plugin == "simple-obfs")
plugin = "obfs-local";
break;
case SPEEDTEST_MESSAGE_FOUNDSSR:
if(std::count(ss_ciphers.begin(), ss_ciphers.end(), method) > 0 && protocol == "origin" && obfs == "plain")
continue;
break;
default:
continue;
}
writer.StartObject();
writer.Key("server");
writer.String(hostname.data());
writer.Key("server_port");
writer.Int(port);
writer.Key("method");
writer.String(method.data());
writer.Key("password");
writer.String(password.data());
writer.Key("remarks");
writer.String(remark.data());
writer.Key("plugin");
writer.String(plugin.data());
writer.Key("plugin_opts");
writer.String(pluginopts.data());
writer.EndObject();
}
writer.EndArray();
return sb.GetString();
}
std::string netchToSSR(std::vector<nodeInfo> &nodes, extra_settings &ext)
{
rapidjson::Document json;
+1
View File
@@ -33,6 +33,7 @@ std::string netchToSurge(std::vector<nodeInfo> &nodes, std::string &base_conf, s
std::string netchToMellow(std::vector<nodeInfo> &nodes, std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext);
void netchToMellow(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext);
std::string netchToSS(std::vector<nodeInfo> &nodes, extra_settings &ext);
std::string netchToSSSub(std::vector<nodeInfo> &nodes, extra_settings &ext);
std::string netchToSSR(std::vector<nodeInfo> &nodes, extra_settings &ext);
std::string netchToVMess(std::vector<nodeInfo> &nodes, extra_settings &ext);
std::string netchToQuanX(std::vector<nodeInfo> &nodes, extra_settings &ext);
+8 -1
View File
@@ -68,6 +68,10 @@ void OnReq(evhttp_request *req, void *args)
{
const char *req_content_type = evhttp_find_header(req->input_headers, "Content-Type"), *req_ac_method = evhttp_find_header(req->input_headers, "Access-Control-Request-Method");
const char *req_method = req_ac_method == NULL ? EVBUFFER_LENGTH(req->input_buffer) == 0 ? "GET" : "POST" : "OPTIONS", *uri = req->uri;
char *client_ip;
u_short client_port;
evhttp_connection_get_peer(evhttp_request_get_connection(req), &client_ip, &client_port);
std::cerr<<"Accept connection from client "<<client_ip<<":"<<client_port<<"\n";
int retVal;
std::string postdata, content_type, return_data;
@@ -115,7 +119,10 @@ void OnReq(evhttp_request *req, void *args)
evhttp_send_reply(req, HTTP_OK, "", OutBuf);
break;
case -1: //not found
evhttp_send_error(req, HTTP_NOTFOUND, "Resource not found");
return_data = "File not found.";
evbuffer_add(OutBuf, return_data.data(), return_data.size());
evhttp_send_reply(req, HTTP_NOTFOUND, "", OutBuf);
//evhttp_send_error(req, HTTP_NOTFOUND, "Resource not found");
break;
default: //undefined behavior
evhttp_send_error(req, HTTP_INTERNAL, "");