mirror of
https://github.com/asdlokj1qpi233/subconverter.git
synced 2026-11-04 02:34:06 +00:00
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:
6 files changed
+164
-30
No files matched your search
+7
-7
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, "");
|
||||
|
||||
Reference in new issue
Block a user