Merge branch 'master' into actions-msys-test

This commit is contained in:
Tindy X
2020-09-18 13:43:07 +08:00
9 changed files with 186 additions and 50 deletions

View File

@@ -3,13 +3,13 @@ set -xe
git clone https://github.com/curl/curl --depth=1
cd curl
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_USE_LIBSSH2=OFF -DHTTP_ONLY=ON -DCMAKE_USE_SCHANNEL=ON -DBUILD_SHARED_LIBS=OFF -DBUILD_CURL_EXE=OFF -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX -G "Unix Makefiles" .
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_USE_LIBSSH2=OFF -DHTTP_ONLY=ON -DCMAKE_USE_SCHANNEL=ON -DBUILD_SHARED_LIBS=OFF -DBUILD_CURL_EXE=OFF -DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" -G "Unix Makefiles" .
make install -j4
cd ..
git clone https://github.com/jbeder/yaml-cpp --depth=1
cd yaml-cpp
cmake -DCMAKE_BUILD_TYPE=Release -DYAML_CPP_BUILD_TESTS=OFF -DYAML_CPP_BUILD_TOOLS=OFF -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX -G "Unix Makefiles" .
cmake -DCMAKE_BUILD_TYPE=Release -DYAML_CPP_BUILD_TESTS=OFF -DYAML_CPP_BUILD_TOOLS=OFF -DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" -G "Unix Makefiles" .
make install -j4
cd ..
@@ -23,14 +23,14 @@ gcc -c -O3 -o duktape.o duktape.c
gcc -c -O3 -o duk_module_node.o -I. ../extras/module-node/duk_module_node.c
ar cr libduktape.a duktape.o
ar cr libduktape_module.a duk_module_node.o
install -m0644 ./*.a $MINGW_PREFIX/lib
install -m0644 ./duk*.h $MINGW_PREFIX/include
install -m0644 ../extras/module-node/duk_module_node.h $MINGW_PREFIX/include
install -m0644 ./*.a "$MINGW_PREFIX/lib"
install -m0644 ./duk*.h "$MINGW_PREFIX/include"
install -m0644 ../extras/module-node/duk_module_node.h "$MINGW_PREFIX/include"
cd ../../../..
git clone https://github.com/Tencent/rapidjson --depth=1
cd rapidjson
cmake -DRAPIDJSON_BUILD_DOC=OFF -DRAPIDJSON_BUILD_EXAMPLES=OFF -DRAPIDJSON_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX=$MINGW_PREFIX -G "Unix Makefiles" .
cmake -DRAPIDJSON_BUILD_DOC=OFF -DRAPIDJSON_BUILD_EXAMPLES=OFF -DRAPIDJSON_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" -G "Unix Makefiles" .
make install -j4
cd ..

View File

@@ -112,7 +112,7 @@ const std::vector<UAProfile> UAMatchList = {
{"ClashX","\\/([0-9.]+)","0.13","clash",true},
{"Clash","","","clash",true},
{"Kitsunebi","","","v2ray"},
{"LoonWidget","","","loon"},
{"Loon","","","loon"},
{"Pharos","","","mixed"},
{"Potatso","","","mixed"},
{"Quantumult%20X","","","quanx"},
@@ -1339,9 +1339,14 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(argTarget == "auto")
matchUserAgent(request.headers["User-Agent"], argTarget, argClashNewField, intSurgeVer);
/// don't try to load groups or rulesets when generating simple subscriptions
bool lSimpleSubscription = false;
switch(hash_(argTarget))
{
case "clash"_hash: case "clashr"_hash: case "surge"_hash: case "quan"_hash: case "quanx"_hash: case "loon"_hash: case "surfboard"_hash: case "mellow"_hash: case "ss"_hash: case "ssd"_hash: case "ssr"_hash: case "sssub"_hash: case "v2ray"_hash: case "trojan"_hash: case "mixed"_hash:
case "ss"_hash: case "ssd"_hash: case "ssr"_hash: case "sssub"_hash: case "v2ray"_hash: case "trojan"_hash: case "mixed"_hash:
lSimpleSubscription = true;
break;
case "clash"_hash: case "clashr"_hash: case "surge"_hash: case "quan"_hash: case "quanx"_hash: case "loon"_hash: case "surfboard"_hash: case "mellow"_hash:
break;
default:
*status_code = 400;
@@ -1378,11 +1383,11 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(std::find(gRegexBlacklist.cbegin(), gRegexBlacklist.cend(), argIncludeRemark) != gRegexBlacklist.cend() || std::find(gRegexBlacklist.cbegin(), gRegexBlacklist.cend(), argExcludeRemark) != gRegexBlacklist.cend())
return "Invalid request!";
//for external configuration
/// for external configuration
std::string lClashBase = gClashBase, lSurgeBase = gSurgeBase, lMellowBase = gMellowBase, lSurfboardBase = gSurfboardBase;
std::string lQuanBase = gQuanBase, lQuanXBase = gQuanXBase, lLoonBase = gLoonBase, lSSSubBase = gSSSubBase;
//validate urls
/// validate urls
argEnableInsert.define(gEnableInsert);
if(!argUrl.size() && (!gAPIMode || authorized))
argUrl = gDefaultUrls;
@@ -1392,7 +1397,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
return "Invalid request!";
}
//load request arguments as template variables
/// load request arguments as template variables
string_array req_args = split(argument, "&");
string_map req_arg_map;
for(std::string &x : req_args)
@@ -1408,15 +1413,15 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
req_arg_map[x.substr(0, pos)] = x.substr(pos + 1);
}
//save template variables
/// save template variables
template_args tpl_args;
tpl_args.global_vars = gTemplateVars;
tpl_args.request_params = req_arg_map;
//check for proxy settings
/// check for proxy settings
std::string proxy = parseProxy(gProxySubscription);
//check other flags
/// check other flags
ext.append_proxy_type = argAppendType.get(gAppendType);
if((argTarget == "clash" || argTarget == "clashr") && argGenClashScript.is_undef())
argExpandRulesets.define(true);
@@ -1463,20 +1468,24 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
{
if(!ext.nodelist)
{
checkExternalBase(extconf.clash_rule_base, lClashBase);
checkExternalBase(extconf.surge_rule_base, lSurgeBase);
checkExternalBase(extconf.surfboard_rule_base, lSurfboardBase);
checkExternalBase(extconf.mellow_rule_base, lMellowBase);
checkExternalBase(extconf.quan_rule_base, lQuanBase);
checkExternalBase(extconf.quanx_rule_base, lQuanXBase);
checkExternalBase(extconf.loon_rule_base, lLoonBase);
checkExternalBase(extconf.sssub_rule_base, lSSSubBase);
if(extconf.surge_ruleset.size())
lCustomRulesets = extconf.surge_ruleset;
if(extconf.custom_proxy_group.size())
lCustomProxyGroups = extconf.custom_proxy_group;
ext.enable_rule_generator = extconf.enable_rule_generator;
ext.overwrite_original_rules = extconf.overwrite_original_rules;
if(!lSimpleSubscription)
{
checkExternalBase(extconf.clash_rule_base, lClashBase);
checkExternalBase(extconf.surge_rule_base, lSurgeBase);
checkExternalBase(extconf.surfboard_rule_base, lSurfboardBase);
checkExternalBase(extconf.mellow_rule_base, lMellowBase);
checkExternalBase(extconf.quan_rule_base, lQuanBase);
checkExternalBase(extconf.quanx_rule_base, lQuanXBase);
checkExternalBase(extconf.loon_rule_base, lLoonBase);
if(extconf.surge_ruleset.size())
lCustomRulesets = extconf.surge_ruleset;
if(extconf.custom_proxy_group.size())
lCustomProxyGroups = extconf.custom_proxy_group;
ext.enable_rule_generator = extconf.enable_rule_generator;
ext.overwrite_original_rules = extconf.overwrite_original_rules;
}
}
if(extconf.rename.size())
ext.rename_array = extconf.rename;
@@ -1493,15 +1502,18 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
}
else
{
//loading custom groups
if(argCustomGroups.size() && !ext.nodelist)
lCustomProxyGroups = split(argCustomGroups, "@");
if(!lSimpleSubscription)
{
//loading custom groups
if(argCustomGroups.size() && !ext.nodelist)
lCustomProxyGroups = split(argCustomGroups, "@");
//loading custom rulesets
if(argCustomRulesets.size() && !ext.nodelist)
lCustomRulesets = split(argCustomRulesets, "@");
//loading custom rulesets
if(argCustomRulesets.size() && !ext.nodelist)
lCustomRulesets = split(argCustomRulesets, "@");
}
}
if(ext.enable_rule_generator && !ext.nodelist)
if(ext.enable_rule_generator && !ext.nodelist && !lSimpleSubscription)
{
if(lCustomRulesets != gCustomRulesets)
refreshRulesets(lCustomRulesets, lRulesetContent);

View File

@@ -213,6 +213,17 @@ int main(int argc, char *argv[])
return "done\n";
});
append_response("GET", "/flushcache", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
if(getUrlArg(request.argument, "token") != gAccessToken)
{
response.status_code = 403;
return "Forbidden";
}
flushCache();
return "done";
});
append_response("GET", "/sub", "text/plain;charset=utf-8", subconverter);
append_response("GET", "/sub2clashr", "text/plain;charset=utf-8", simpleToClashR);

View File

@@ -5,6 +5,8 @@
#include <vector>
#include <sstream>
#include <algorithm>
#include <sys/types.h>
#include <dirent.h>
#include <yaml-cpp/yaml.h>
@@ -90,6 +92,25 @@ bool fileCopy(const std::string &source, const std::string &dest);
std::string fileToBase64(const std::string &filepath);
std::string fileGetMD5(const std::string &filepath);
template<typename F>
int operateFiles(const std::string &path, F &&op)
{
DIR* dir = opendir(path.data());
if(!dir)
return -1;
struct dirent* dp;
while((dp = readdir(dir)) != NULL)
{
if(strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0)
{
if(op(dp->d_name))
break;
}
}
closedir(dir);
return 0;
}
static inline bool strFind(const std::string &str, const std::string &target)
{
return str.find(target) != str.npos;

View File

@@ -413,13 +413,13 @@ std::string vmessLinkConstruct(const std::string &remarks, const std::string &ad
writer.Key("port");
writer.Int(to_int(port));
writer.Key("type");
writer.String(type.data());
writer.String(type.empty() ? "none" : type.data());
writer.Key("id");
writer.String(id.data());
writer.Key("aid");
writer.Int(to_int(aid));
writer.Key("net");
writer.String(net.data());
writer.String(net.empty() ? "tcp" : net.data());
writer.Key("path");
writer.String(path.data());
writer.Key("host");
@@ -2036,7 +2036,11 @@ void netchToQuan(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<rules
method = "chacha20-ietf-poly1305";
proxyStr = remark + " = vmess, " + hostname + ", " + port + ", " + method + ", \"" + id + "\", group=" + x.group;
if(tlssecure)
{
proxyStr += ", over-tls=true, tls-host=" + host;
if(!scv.is_undef())
proxyStr += ", certificate=" + std::string(scv.get() ? "0" : "1");
}
if(transproto == "ws")
{
proxyStr += ", obfs=ws, obfs-path=\"" + path + "\", obfs-header=\"Host: " + host;
@@ -2044,8 +2048,6 @@ void netchToQuan(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<rules
proxyStr += "[Rr][Nn]Edge: " + edge;
proxyStr += "\"";
}
if(!scv.is_undef())
proxyStr += ", certificate=" + std::string(scv.get() ? "0" : "1");
if(ext.nodelist)
proxyStr = "vmess://" + urlsafe_base64_encode(proxyStr);
@@ -2109,9 +2111,9 @@ void netchToQuan(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<rules
proxyStr += ", over-tls=true";
if(host.size())
proxyStr += ", tls-host=" + host;
if(!scv.is_undef())
proxyStr += ", certificate=" + std::string(scv.get() ? "0" : "1");
}
if(!scv.is_undef())
proxyStr += ", certificate=" + std::string(scv.get() ? "0" : "1");
if(ext.nodelist)
proxyStr = "http://" + urlsafe_base64_encode(proxyStr);
@@ -2132,9 +2134,9 @@ void netchToQuan(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<rules
proxyStr += ", over-tls=true";
if(host.size())
proxyStr += ", tls-host=" + host;
if(!scv.is_undef())
proxyStr += ", certificate=" + std::string(scv.get() ? "0" : "1");
}
if(!scv.is_undef())
proxyStr += ", certificate=" + std::string(scv.get() ? "0" : "1");
if(ext.nodelist)
proxyStr = "socks://" + urlsafe_base64_encode(proxyStr);

View File

@@ -1,6 +1,6 @@
#ifndef VERSION_H_INCLUDED
#define VERSION_H_INCLUDED
#define VERSION "v0.6.3"
#define VERSION "v0.6.4"
#endif // VERSION_H_INCLUDED

View File

@@ -1,7 +1,9 @@
#include <iostream>
#include <unistd.h>
#include <sys/stat.h>
#include <mutex>
//#include <mutex>
#include <thread>
#include <atomic>
#include <curl/curl.h>
@@ -19,8 +21,76 @@
extern bool gPrintDbgInfo, gServeCacheOnFetchFail;
extern int gLogLevel;
/*
typedef std::lock_guard<std::mutex> guarded_mutex;
std::mutex cache_rw_lock;
*/
class RWLock
{
#define WRITE_LOCK_STATUS -1
#define FREE_STATUS 0
private:
const std::thread::id NULL_THREAD;
const bool WRITE_FIRST;
std::thread::id m_write_thread_id;
std::atomic_int m_lockCount;
std::atomic_uint m_writeWaitCount;
public:
RWLock(const RWLock&) = delete;
RWLock& operator=(const RWLock&) = delete;
RWLock(bool writeFirst = true): WRITE_FIRST(writeFirst), m_write_thread_id(), m_lockCount(0), m_writeWaitCount(0) {}
virtual ~RWLock() = default;
int readLock()
{
if (std::this_thread::get_id() != m_write_thread_id)
{
int count;
if (WRITE_FIRST)
do {
while ((count = m_lockCount) == WRITE_LOCK_STATUS || m_writeWaitCount > 0);
} while (!m_lockCount.compare_exchange_weak(count, count + 1));
else
do {
while ((count = m_lockCount) == WRITE_LOCK_STATUS);
} while (!m_lockCount.compare_exchange_weak(count, count + 1));
}
return m_lockCount;
}
int readUnlock()
{
if (std::this_thread::get_id() != m_write_thread_id)
--m_lockCount;
return m_lockCount;
}
int writeLock()
{
if (std::this_thread::get_id() != m_write_thread_id)
{
++m_writeWaitCount;
for (int zero = FREE_STATUS; !m_lockCount.compare_exchange_weak(zero, WRITE_LOCK_STATUS); zero = FREE_STATUS);
--m_writeWaitCount;
m_write_thread_id = std::this_thread::get_id();
}
return m_lockCount;
}
int writeUnlock()
{
if (std::this_thread::get_id() != m_write_thread_id)
{
throw std::runtime_error("writeLock/Unlock mismatch");
}
if (WRITE_LOCK_STATUS != m_lockCount)
{
throw std::runtime_error("RWLock internal error");
}
m_write_thread_id = NULL_THREAD;
m_lockCount.store(FREE_STATUS);
return m_lockCount;
}
};
RWLock cache_rw_lock;
long gMaxAllowedDownloadSize = 1048576L;
@@ -216,7 +286,9 @@ std::string webGet(const std::string &url, const std::string &proxy, unsigned in
if(difftime(now, mtime) <= cache_ttl) // within TTL
{
writeLog(0, "CACHE HIT: '" + url + "', using local cache.");
guarded_mutex guard(cache_rw_lock);
//guarded_mutex guard(cache_rw_lock);
cache_rw_lock.readLock();
defer(cache_rw_lock.readUnlock();)
if(response_headers)
*response_headers = fileGet(path_header, true);
return fileGet(path, true);
@@ -229,7 +301,9 @@ std::string webGet(const std::string &url, const std::string &proxy, unsigned in
curlGet(argument, fetch_res);
if(return_code == CURLE_OK) // success, save new cache
{
guarded_mutex guard(cache_rw_lock);
//guarded_mutex guard(cache_rw_lock);
cache_rw_lock.writeLock();
defer(cache_rw_lock.writeUnlock();)
fileWrite(path, content, true);
if(response_headers)
fileWrite(path_header, *response_headers, true);
@@ -239,7 +313,9 @@ std::string webGet(const std::string &url, const std::string &proxy, unsigned in
if(fileExist(path) && gServeCacheOnFetchFail) // failed, check if cache exist
{
writeLog(0, "Fetch failed. Serving cached content."); // cache exist, serving cache
guarded_mutex guard(cache_rw_lock);
//guarded_mutex guard(cache_rw_lock);
cache_rw_lock.readLock();
defer(cache_rw_lock.readUnlock();)
content = fileGet(path, true);
if(response_headers)
*response_headers = fileGet(path_header, true);
@@ -254,6 +330,14 @@ std::string webGet(const std::string &url, const std::string &proxy, unsigned in
return content;
}
void flushCache()
{
//guarded_mutex guard(cache_rw_lock);
cache_rw_lock.writeLock();
defer(cache_rw_lock.writeUnlock();)
operateFiles("cache", [](const std::string &file){ remove(("cache/" + file).data()); return 0; });
}
int curlPost(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData)
{
CURL *curl_handle;

View File

@@ -22,6 +22,7 @@ struct FetchResult
};
std::string webGet(const std::string &url, const std::string &proxy = "", unsigned int cache_ttl = 0, std::string *response_headers = NULL, string_map *request_headers = NULL);
void flushCache();
int webPost(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData);
int webPatch(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData);
std::string buildSocks5ProxyString(const std::string &addr, int port, const std::string &username, const std::string &password);

View File

@@ -57,7 +57,7 @@ bool matchSpaceSeparatedList(const std::string& source, const std::string &targe
return false;
}
std::string checkMIMEType(const std::string filename)
std::string checkMIMEType(const std::string &filename)
{
string_size name_begin = 0, name_end = 0;
name_begin = filename.rfind('/');
@@ -102,6 +102,7 @@ const char *request_header_blacklist[] = {"host", "accept", "accept-encoding"};
static inline void buffer_cleanup(struct evbuffer *eb)
{
(void)eb;
//evbuffer_free(eb);
#ifdef MALLOC_TRIM
malloc_trim(0);
@@ -121,7 +122,7 @@ static inline int process_request(Request &request, Response &response, std::str
for(responseRoute &x : responses)
{
if(request.method == "OPTIONS" && startsWith(request.postdata, x.method) && x.path == request.url)
if(request.method == "OPTIONS" && matchSpaceSeparatedList(replace_all_distinct(request.postdata, ",", ""), x.method) && x.path == request.url)
{
return 1;
}
@@ -160,6 +161,7 @@ static inline int process_request(Request &request, Response &response, std::str
void OnReq(evhttp_request *req, void *args)
{
(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 *uri = req->uri, *internal_flag = evhttp_find_header(req->input_headers, "SubConverter-Request");
@@ -193,6 +195,9 @@ void OnReq(evhttp_request *req, void *args)
case EVHTTP_REQ_GET: request.method = "GET"; break;
case EVHTTP_REQ_POST: request.method = "POST"; break;
case EVHTTP_REQ_OPTIONS: request.method = "OPTIONS"; break;
case EVHTTP_REQ_PUT: request.method = "PUT"; break;
case EVHTTP_REQ_PATCH: request.method = "PATCH"; break;
case EVHTTP_REQ_DELETE: request.method = "DELETE"; break;
default: break;
}
request.url = uri;
@@ -276,7 +281,7 @@ int start_web_server(void *argv)
return -1;
}
evhttp_set_allowed_methods(Server.get(), EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS);
evhttp_set_allowed_methods(Server.get(), EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS | EVHTTP_REQ_PUT | EVHTTP_REQ_PATCH | EVHTTP_REQ_DELETE);
evhttp_set_gencb(Server.get(), OnReq, nullptr);
evhttp_set_timeout(Server.get(), 30);
if (event_dispatch() == -1)