From 6533df2792e7858e45ddd4f61bb0280afdd207ca Mon Sep 17 00:00:00 2001 From: Tindy X <49061470+tindy2013@users.noreply.github.com> Date: Fri, 8 Nov 2019 14:50:28 +0800 Subject: [PATCH] Enhancements Fix a potential bug which will cause SSD subscription to be ignored. Add experimental Surge subscription output. Change the default generate options. Update build scripts. --- build.alpine.release.sh | 8 +- build.macos.release.sh | 6 +- main.cpp | 35 ++++++- pref.ini | 51 +++++----- speedtestutil.cpp | 10 +- subexport.cpp | 213 ++++++++++++++++++++++++++++++++++++++++ surge.conf | 7 ++ webget.cpp | 63 ++++++++++++ 8 files changed, 358 insertions(+), 35 deletions(-) create mode 100644 surge.conf diff --git a/build.alpine.release.sh b/build.alpine.release.sh index 3ddc844..fe1d831 100644 --- a/build.alpine.release.sh +++ b/build.alpine.release.sh @@ -9,18 +9,18 @@ git clone https://github.com/curl/curl cd curl ./buildconf ./configure --with-ssl --disable-ldap --disable-ldaps --disable-rtsp --without-libidn2 > /dev/null -make install -j4 +make install -j4 > /dev/null cd .. git clone https://github.com/jbeder/yaml-cpp cd yaml-cpp -cmake . -make install -j4 +cmake . > /dev/null +make install -j4 > /dev/null cd .. git clone git://sourceware.org/git/bzip2.git cd bzip2 -make install -j4 +make install -j4 > /dev/null cd .. g++ -Wall -std=c++17 -fexceptions -DCURL_STATICLIB -c logger.cpp -o obj\logger.o diff --git a/build.macos.release.sh b/build.macos.release.sh index 8cf9eae..24bd7c5 100644 --- a/build.macos.release.sh +++ b/build.macos.release.sh @@ -6,15 +6,15 @@ brew reinstall yaml-cpp rapidjson libevent zlib git clone https://github.com/curl/curl cd curl -./buildconf +./buildconf > /dev/null ./configure --with-ssl=/usr/local/opt/openssl@1.1 --without-mbedtls --disable-ldap --disable-ldaps --disable-rtsp --without-libidn2 > /dev/null -make -j8 +make -j8 > /dev/null cd .. curl -L -o bzip2-1.0.6.tar.gz https://sourceforge.net/projects/bzip2/files/bzip2-1.0.6.tar.gz/download tar xvf bzip2-1.0.6.tar.gz cd bzip2-1.0.6 -make -j8 +make -j8 > /dev/null cd .. cp /usr/local/lib/libevent.a . diff --git a/main.cpp b/main.cpp index 3556cf3..e0eba75 100644 --- a/main.cpp +++ b/main.cpp @@ -150,7 +150,7 @@ void readConf() } std::string netchToClash(std::vector &nodes, std::string &baseConf, std::vector &ruleset_content_array, string_array &extra_proxy_group, bool clashR); -std::string netchToSurge(std::vector &nodes, std::string &base_conf, std::vector &ruleset_content_array, string_array &extra_proxy_group, int surge_ver); +std::string netchToSurge(std::vector &nodes, std::string &base_conf, string_array &ruleset_array, string_array &extra_proxy_group, int surge_ver); int main() { @@ -239,6 +239,39 @@ int main() return netchToClash(nodes, clash_base_content, ruleset_content_array, clash_extra_group, true); }); + append_response("GET", "/surge", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string + { + if(!api_mode) + readConf(); + std::string surge_base_content; + std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")), group = UrlDecode(getUrlArg(argument, "group")); + int surge_ver = stoi(getUrlArg(argument, "ver")); + if(!url.size()) url = default_url; + string_array urls = split(url, "|"); + std::vector nodes; + if(include.size()) + { + eraseElements(def_include_remarks); + def_include_remarks.emplace_back(include); + } + if(group.size()) custom_group = group; + for(std::string &x : urls) + { + std::cerr<<"Fetching node data from url '"< std::string diff --git a/pref.ini b/pref.ini index 83b239c..9bf465d 100644 --- a/pref.ini +++ b/pref.ini @@ -3,7 +3,7 @@ default_url= ;Exclude nodes which remarks match the following patterns. Supports regular expression. -exclude_remarks=(剩余流量|到期时间|过期时间|官网地址|产品名称) +exclude_remarks=(流量|时间|官网|产品) ;exclude_remarks=(other rule) ;Only include nodes which remarks match the following patterns. Supports regular expression. @@ -12,6 +12,9 @@ exclude_remarks=(剩余流量|到期时间|过期时间|官网地址|产品名 ;Clash config base used by the generator, supports local files/URL clash_rule_base=simple_base.yml +;Surge config base used by the generator, supports local files/URL +surge_rule_base=surge.conf + ;Rename remarks with the following patterns. Supports regular expression. ;Format: Search_Pattern@Replace_Pattern ;rename_node=IPLC@专线 @@ -76,19 +79,19 @@ update_ruleset_on_request=false ; Group name,[]Rule surge_ruleset=DIRECT,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Unbreak.list -surge_ruleset=Advertising,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Advertising.list -surge_ruleset=Hijacking,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Hijacking.list -surge_ruleset=YouTube,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/YouTube.list -surge_ruleset=Netflix,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/Netflix.list +surge_ruleset=⛔️ 广告拦截,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Advertising.list +surge_ruleset=🚫 运营劫持,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Hijacking.list +surge_ruleset=🌌 YouTube,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/YouTube.list +surge_ruleset=🎥 NETFLIX,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/Netflix.list ;surge_ruleset=HBO,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/HBO.list ;surge_ruleset=Fox,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/Fox.list -surge_ruleset=GlobalMedia,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/GlobalMedia.list -surge_ruleset=HKMTMedia,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/HKMTMedia.list -surge_ruleset=Telegram,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Telegram.list -surge_ruleset=PROXY,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Global.list -surge_ruleset=Apple,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Apple.list +surge_ruleset=🌍 国外媒体,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/GlobalMedia.list +surge_ruleset=🌏 港台媒体,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/HKMTMedia.list +surge_ruleset=📲 电报信息,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Telegram.list +surge_ruleset=🔰 节点选择,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Global.list +surge_ruleset=🍎 苹果服务,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Apple.list surge_ruleset=DIRECT,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/China.list -surge_ruleset=Final,[]MATCH +surge_ruleset=💕 规则自选,[]MATCH [clash_proxy_group] ;Generate Clash Proxy Group with the following patterns. Node filterting rule supports regular expression. @@ -101,7 +104,8 @@ surge_ruleset=Final,[]MATCH ;custom_proxy_group=LoadBalance`load-balance`.*`http://www.gstatic.com/generate_204`300 ;for forcerule.yml -;custom_proxy_group=Proxy`select`.* +;custom_proxy_group=Proxy`select`.*[]AUTO`[]DIRECT`.* +;custom_proxy_group=AUTO`url-test`.*`http://www.gstatic.com/generate_204`300 ;custom_proxy_group=google`select`.* ;custom_proxy_group=netflix`select`.* ;custom_proxy_group=动画疯`select`(深台|彰化|新北|台) @@ -116,16 +120,19 @@ surge_ruleset=Final,[]MATCH ;custom_proxy_group=Telegram`select`新加坡`[]Proxy ;for DivineEngine_Profiles -custom_proxy_group=PROXY`select`.* -custom_proxy_group=YouTube`select`.* -custom_proxy_group=Netflix`select`.* -custom_proxy_group=Advertising`select`[]REJECT`[]DIRECT`[]PROXY -custom_proxy_group=Hijacking`select`[]REJECT`[]DIRECT`[]PROXY -custom_proxy_group=GlobalMedia`select`.* -custom_proxy_group=HKMTMedia`select`(HGC|HKBN|PCCW|HKT|深台|彰化|新北|台|新加坡|sg|hk|tw) -custom_proxy_group=Telegram`select`新加坡`[]PROXY -custom_proxy_group=Apple`select`[]DIRECT`[]PROXY -custom_proxy_group=Final`select`[]PROXY`[]DIRECT +custom_proxy_group=🔰 节点选择`select`[]♻️ 自动选择`[]🎯 全球直连`.* +custom_proxy_group=♻️ 自动选择`url-test`.*`http://www.gstatic.com/generate_204`300 +custom_proxy_group=🌌 YouTube`select`[]🔰 节点选择`[]♻️ 自动选择`[]🎯 全球直连`.* +custom_proxy_group=🎥 NETFLIX`select`[]🔰 节点选择`[]♻️ 自动选择`[]🎯 全球直连`.* +custom_proxy_group=⛔️ 广告拦截`select`[]🛑 全球拦截`[]🎯 全球直连`[]🔰 节点选择 +custom_proxy_group=🚫 运营劫持`select`[]🛑 全球拦截`[]🎯 全球直连`[]🔰 节点选择 +custom_proxy_group=🌍 国外媒体`select`[]♻️ 自动选择`[]🎯 全球直连`.* +custom_proxy_group=🌏 港台媒体`select`(HGC|HKBN|PCCW|HKT|深台|彰化|新北|台|hk|香港|tw) +custom_proxy_group=📲 电报信息`select`[]🔰 节点选择`[]♻️ 自动选择`[]DIRECT`(美|新加坡|sg|us)`.* +custom_proxy_group=🍎 苹果服务`select`[]🔰 节点选择`[]🎯 全球直连`[]♻️ 自动选择`.* +custom_proxy_group=💕 规则自选`select`[]🔰 节点选择`[]🎯 全球直连`[]♻️ 自动选择`.* +custom_proxy_group=🎯 全球直连`select`[]DIRECT +custom_proxy_group=🛑 全球拦截`select`[]REJECT [server] ;Address to bind on for Web Server diff --git a/speedtestutil.cpp b/speedtestutil.cpp index 0e2cc4a..fc601f2 100644 --- a/speedtestutil.cpp +++ b/speedtestutil.cpp @@ -1486,9 +1486,10 @@ void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom std::string strLink; nodeInfo node; - //try to parse as surge configuration - if(explodeSurge(sub, custom_port, local_port, nodes, sslibev)) + //try to parse as SSD configuration + if(strFind(sub, "ssd://")) { + explodeSSD(sub, sslibev, custom_port, local_port, nodes); return; } @@ -1510,10 +1511,9 @@ void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom //ignore } - //try to parse as SSD configuration - if(strFind(sub, "ssd://")) + //try to parse as surge configuration + if(explodeSurge(sub, custom_port, local_port, nodes, sslibev)) { - explodeSSD(sub, sslibev, custom_port, local_port, nodes); return; } diff --git a/subexport.cpp b/subexport.cpp index 9f82999..9573272 100644 --- a/subexport.cpp +++ b/subexport.cpp @@ -14,6 +14,7 @@ extern bool overwrite_original_rules; extern string_array renames, emojis; extern bool add_emoji, remove_old_emoji; +extern bool api_mode; std::string vmessConstruct(std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string tls, int local_port) { @@ -216,6 +217,67 @@ void rulesetToClash(YAML::Node &base_rule, std::vector &ruleset base_rule["Rule"] = Rules; } +void rulesetToSurge(INIReader &base_rule, string_array &ruleset_array, int surge_ver) +{ + string_array allRules, vArray; + std::string rule_group, rule_path, retrived_rules, strLine; + std::stringstream strStrm; + + base_rule.SetCurrentSection("Rule"); + + if(overwrite_original_rules) + base_rule.EraseSection(); + + for(std::string &x : ruleset_array) + { + vArray = split(x, ","); + if(vArray.size() != 2) + continue; + rule_group = trim(vArray[0]); + rule_path = trim(vArray[1]); + if(rule_path.find("[]") == 0) + { + strLine = rule_path.substr(2); + if(strLine == "MATCH") + strLine = "FINAL"; + allRules.emplace_back(strLine + "," + rule_group); + continue; + } + else + { + if(fileExist(rule_path)) + { + if(api_mode) + continue; + retrived_rules = fileGet(rule_path, false); + char delimiter = count(retrived_rules.begin(), retrived_rules.end(), '\n') <= 1 ? '\r' : '\n'; + + strStrm.clear(); + strStrm< &nodes, std::string &baseConf, std::vector &ruleset_content_array, string_array &extra_proxy_group, bool clashR) { YAML::Node yamlnode, proxies, singleproxy, singlegroup, original_groups; @@ -413,3 +475,154 @@ std::string netchToClash(std::vector &nodes, std::string &baseConf, st return to_string(yamlnode); } + +std::string netchToSurge(std::vector &nodes, std::string &base_conf, string_array &ruleset_array, string_array &extra_proxy_group, int surge_ver) +{ + rapidjson::Document json; + INIReader ini; + std::string proxy; + std::string type, remark, hostname, port, username, password, method; + std::string plugin, pluginopts; + std::string id, aid, transproto, faketype, host, path, quicsecure, quicsecret; + std::string url; + std::vector nodelist; + bool tlssecure; + string_array vArray, filtered_nodelist; + + ini.store_any_line = true; + if(ini.Parse(base_conf) != 0) + return std::string(); + + ini.SetCurrentSection("Proxy"); + ini.EraseSection(); + ini.Set("DIRECT", "direct"); + for(nodeInfo &x : nodes) + { + json.Parse(x.proxyStr.data()); + type = GetMember(json, "Type"); + remark = addEmoji(trim(removeEmoji(nodeRename(x.remarks)))); + hostname = GetMember(json, "Hostname"); + port = std::__cxx11::to_string((unsigned short)stoi(GetMember(json, "Port"))); + username = GetMember(json, "Username"); + password = GetMember(json, "Password"); + method = GetMember(json, "EncryptMethod"); + proxy = ""; + + if(type == "SS") + { + if(surge_ver >= 3) + { + plugin = GetMember(json, "Plugin"); + pluginopts = GetMember(json, "PluginOption"); + proxy = "ss," + hostname + "," + port + ",encrypt-method=" + method + ",password=" + password; + if(plugin.size() && pluginopts.size()) + { + proxy += "," + replace_all_distinct(pluginopts, ";", ","); + } + } + else + { + if(GetMember(json, "Plugin") == "") + { + proxy = "custom," + hostname + "," + port + "," + method + "," + password + ",https://github.com/ConnersHua/SSEncrypt/raw/master/SSEncrypt.module"; + } + else + continue; + } + + } + else if(type == "VMess") + { + if(surge_ver < 4) + continue; + id = GetMember(json, "UserID"); + aid = GetMember(json, "AlterID"); + transproto = GetMember(json, "TransferProtocol"); + host = GetMember(json, "Host"); + path = GetMember(json, "Path"); + tlssecure = GetMember(json, "TLSSecure") == "true"; + proxy = "vmess," + hostname + "," + port + "," + method + ",username=" + id + ",tls=" + (tlssecure ? "true" : "false"); + if(transproto == "ws") + { + proxy += ",ws=true,ws-path=" + path; + } + else if(transproto == "kcp" || transproto == "h2" || transproto == "quic") + continue; + } + else if(type == "Socks5") + { + proxy = "socks5," + hostname + "," + port; + if(username != "" && password != "") + proxy += "," + username + "," + password; + } + else if(type == "HTTP" || type == "HTTPS") + { + proxy = "http," + hostname + "," + port; + if(username != "" && password != "") + proxy += "," + username + "," + password; + proxy += std::string(",tls=") + (type == "HTTPS" ? "true" : "false"); + } + else + continue; + ini.Set(remark, proxy); + nodelist.emplace_back(remark); + } + + ini.SetCurrentSection("Proxy Group"); + for(std::string &x : extra_proxy_group) + { + eraseElements(filtered_nodelist); + unsigned int rules_upper_bound = 0; + url = ""; + proxy = ""; + + vArray = split(x, "`"); + if(vArray.size() < 3) + continue; + + if(vArray[1] == "select") + { + rules_upper_bound = vArray.size(); + } + else if(vArray[1] == "url-test" || vArray[1] == "fallback" || vArray[1] == "load-balance") + { + if(vArray.size() < 5) + continue; + rules_upper_bound = vArray.size() - 2; + url = vArray[vArray.size() - 2]; + } + else + continue; + + for(unsigned int i = 2; i < rules_upper_bound; i++) + { + if(vArray[i].find("[]") == 0) + { + filtered_nodelist.emplace_back(vArray[i].substr(2)); + } + else + { + for(std::string &y : nodelist) + { + if(regFind(y, vArray[i]) && std::find(filtered_nodelist.begin(), filtered_nodelist.end(), y) == filtered_nodelist.end()) + filtered_nodelist.emplace_back(y); + } + } + } + + if(!filtered_nodelist.size()) + filtered_nodelist.emplace_back("DIRECT"); + + proxy = vArray[1]; + for(std::string &x : filtered_nodelist) + proxy += "," + x; + if(vArray[1] == "url-test" || vArray[1] == "fallback" || vArray[1] == "load-balance") + proxy += ",url=" + url; + + ini.Set("{NONAME}", vArray[0] + " = " + proxy); //insert order + } + + rulesetToSurge(ini, ruleset_array, surge_ver); + + return ini.ToString(); +} diff --git a/surge.conf b/surge.conf new file mode 100644 index 0000000..3098f33 --- /dev/null +++ b/surge.conf @@ -0,0 +1,7 @@ +[General] +loglevel = notify +bypass-system = true +skip-proxy = 127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,100.64.0.0/10,localhost,*.local,e.crashlytics.com,captive.apple.com,::ffff:0:0:0:0/1,::ffff:128:0:0:0/1 +#DNS设置或根据自己网络情况进行相应设置 +bypass-tun = 192.168.0.0/16,10.0.0.0/8,172.16.0.0/12 +dns-server = 119.29.29.29,223.5.5.5 \ No newline at end of file diff --git a/webget.cpp b/webget.cpp index 91bfb2c..11a1a02 100644 --- a/webget.cpp +++ b/webget.cpp @@ -55,3 +55,66 @@ std::string webGet(std::string url, std::string proxy) { return curlGet(url, proxy); } + +long curlPost(std::string url, std::string data, std::string proxy) +{ + CURL *curl_handle; + double retVal = 0.0; + + CURLcode res = curl_global_init(CURL_GLOBAL_ALL); + + curl_handle = curl_easy_init(); + + curl_easy_setopt(curl_handle, CURLOPT_URL, url.data()); + curl_easy_setopt(curl_handle, CURLOPT_HEADER, 0L); + curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt(curl_handle, CURLOPT_POST, 1L); + curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, data.data()); + curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, data.size()); + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 10L); + curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L); + if(proxy != "") + curl_easy_setopt(curl_handle, CURLOPT_PROXY, proxy.data()); + + res = curl_easy_perform(curl_handle); + + if(res == CURLE_OK) + { + res = curl_easy_getinfo(curl_handle, CURLINFO_SPEED_UPLOAD, &retVal); + } + + curl_easy_cleanup(curl_handle); + curl_global_cleanup(); + return retVal; +} + +int curlPatch(std::string url, std::string data, std::string proxy) +{ + CURL *curl_handle; + int retVal = 0; + + CURLcode res = curl_global_init(CURL_GLOBAL_ALL); + + curl_handle = curl_easy_init(); + + curl_easy_setopt(curl_handle, CURLOPT_URL, url.data()); + curl_easy_setopt(curl_handle, CURLOPT_HEADER, 0L); + curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "PATCH"); + curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, data.data()); + curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, data.size()); + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 10L); + curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L); + if(proxy != "") + curl_easy_setopt(curl_handle, CURLOPT_PROXY, proxy.data()); + + res = curl_easy_perform(curl_handle); + if(res != CURLE_OK) + retVal = -1; + + curl_easy_cleanup(curl_handle); + curl_global_cleanup(); + return retVal; +}