First public version

This commit is contained in:
Tindy X committed 2019-10-31 13:18:40 +08:00
1 parent ec0b5dd789
commit e8a822d010
26 files changed
+7941

No files matched your search

+2876
View File
File diff suppressed because it is too large. Load diff
+711
View File
@@ -0,0 +1,711 @@
#ifndef INI_READER_H_INCLUDED
#define INI_READER_H_INCLUDED
#include <map>
#include <vector>
#include <algorithm>
#define MAX_LINE_LENGTH 4096
typedef std::map<std::string, std::multimap<std::string, std::string>> ini_data_struct;
typedef std::multimap<std::string, std::string> string_multimap;
class INIReader
{
/**
* @brief A simple INI reader which utilize map and vector
* to store sections and items, allowing access in logarithmic time.
*/
private:
bool parsed = false;
std::string current_section;
ini_data_struct ini_content;
string_array exclude_sections, include_sections, read_sections;
std::string cached_section;
string_multimap cached_section_content;
std::string isolated_items_section;
bool chkIgnore(std::string section)
{
bool excluded = false, included = false;
if(count(exclude_sections.begin(), exclude_sections.end(), section) > 0)
excluded = true;
if(include_sections.size() != 0)
{
if(count(include_sections.begin(), include_sections.end(), section) > 0)
included = true;
}
else
included = true;
return excluded || !included;
}
public:
/**
* @brief Set this flag to true to do a UTF8-To-GBK conversion before parsing data. Only useful in Windows.
*/
bool do_utf8_to_gbk = false;
/**
* @brief Set this flag to true so any line within the section will be stored even it doesn't follow the "name=value" format.
* These lines will store as the name "{NONAME}".
*/
bool store_any_line = false;
/**
* @brief Save isolated items before any section definitions.
*/
bool store_isolated_line = false;
/**
* @brief Allow a section title to appear multiple times.
*/
bool allow_dup_section_titles = false;
/**
* @brief Initialize the reader.
*/
INIReader()
{
parsed = false;
}
/**
* @brief Parse a file during initialization.
*/
INIReader(std::string filePath)
{
parsed = false;
ParseFile(filePath);
}
~INIReader()
{
//nothing to do
}
/**
* @brief Exclude a section with the given name.
*/
void ExcludeSection(std::string section)
{
exclude_sections.emplace_back(section);
}
/**
* @brief Include a section with the given name.
*/
void IncludeSection(std::string section)
{
include_sections.emplace_back(section);
}
/**
* @brief Set isolated items to given section.
*/
void SetIsolatedItemsSection(std::string section)
{
isolated_items_section = section;
}
/**
* @brief Parse INI content into mapped data structure.
* If exclude sections are set, these sections will not be stored.
* If include sections are set, only these sections will be stored.
*/
int Parse(std::string content) //parse content into mapped data
{
if(!content.size()) //empty content
return -1;
//remove UTF-8 BOM
removeUTF8BOM(content);
bool inExcludedSection = false;
std::string strLine, thisSection, curSection, itemName, itemVal;
string_multimap itemGroup, existItemGroup;
std::stringstream strStrm;
unsigned int lineSize = 0, epos = 0;
char delimiter = count(content.begin(), content.end(), '\n') <= 1 ? '\r' : '\n';
EraseAll(); //first erase all data
if(do_utf8_to_gbk && is_str_utf8(content))
content = UTF8ToGBK(content); //do conversion if flag is set
if(store_isolated_line)
curSection = isolated_items_section; //items before any section define will be store in this section
strStrm<<content;
while(getline(strStrm, strLine, delimiter)) //get one line of content
{
lineSize = strLine.size();
if(!lineSize || lineSize > MAX_LINE_LENGTH || strLine[0] == ';' || strLine[0] == '#') //empty lines, lines longer than MAX_LINE_LENGTH and comments are ignored
continue;
if(strLine[lineSize - 1] == '\r') //remove line break
{
strLine.replace(lineSize - 1, 0, "");
lineSize--;
}
epos = strLine.find("=");
if(epos != strLine.npos) //is an item
{
if(inExcludedSection) //this section is excluded
continue;
if(!curSection.size()) //not in any section
return -1;
itemName = trim(strLine.substr(0, epos));
itemVal = trim(strLine.substr(epos + 1));
itemGroup.emplace(itemName, itemVal); //insert to current section
}
else if(strLine[0] == '[' && strLine[lineSize - 1] == ']') //is a section title
{
thisSection = strLine.substr(1, lineSize - 2); //save section title
inExcludedSection = chkIgnore(thisSection); //check if this section is excluded
if(curSection.size() && itemGroup.size()) //just finished reading a section
{
if(ini_content.count(curSection)) //a section with the same name has been inserted
{
if(allow_dup_section_titles)
{
eraseElements(existItemGroup);
existItemGroup = ini_content.at(curSection); //get the old items
for(auto &x : existItemGroup)
itemGroup.emplace(x); //insert them all into new section
ini_content.erase(curSection); //remove the old section
}
else
return -1; //not allowed, stop
}
ini_content.emplace(curSection, itemGroup); //insert previous section to content map
read_sections.emplace_back(curSection); //add to read sections list
}
eraseElements(itemGroup); //reset section storage
curSection = thisSection; //start a new section
}
else if(store_any_line && !inExcludedSection && curSection.size()) //store a line without name
{
itemGroup.emplace("{NONAME}", strLine);
}
if(include_sections.size() && include_sections == read_sections) //all included sections has been read
break; //exit now
}
if(curSection.size() && itemGroup.size()) //final section
{
if(ini_content.count(curSection)) //a section with the same name has been inserted
{
if(allow_dup_section_titles)
{
eraseElements(existItemGroup);
existItemGroup = ini_content.at(curSection); //get the old items
for(auto &x : existItemGroup)
itemGroup.emplace(x); //insert them all into new section
ini_content.erase(curSection); //remove the old section
}
else
return -1; //not allowed, stop
}
ini_content.emplace(curSection, itemGroup); //insert this section to content map
read_sections.emplace_back(curSection); //add to read sections list
}
parsed = true;
return 0; //all done
}
/**
* @brief Parse an INI file into mapped data structure.
*/
int ParseFile(std::string filePath)
{
if(!fileExist(filePath))
return -1;
return Parse(fileGet(filePath, false));
}
/**
* @brief Check whether a section exist.
*/
bool SectionExist(std::string section)
{
return ini_content.find(section) != ini_content.end();
}
/**
* @brief Count of sections in the whole INI.
*/
unsigned int SectionCount()
{
return ini_content.size();
}
/**
* @brief Return all section names inside INI.
*/
string_array GetSections()
{
string_array retData;
for(auto &x : ini_content)
{
retData.emplace_back(x.first);
}
return retData;
}
/**
* @brief Enter a section with the given name. Section name and data will be cached to speed up the following reading process.
*/
int EnterSection(std::string section)
{
if(!SectionExist(section))
return -1;
current_section = cached_section = section;
cached_section_content = ini_content.at(section);
return 0;
}
/**
* @brief Set current section.
*/
void SetCurrentSection(std::string section)
{
current_section = section;
}
/**
* @brief Check whether an item exist in the given section. Return false if the section does not exist.
*/
bool ItemExist(std::string section, std::string itemName)
{
if(!SectionExist(section))
return false;
if(section != cached_section)
{
cached_section = section;
cached_section_content= ini_content.at(section);
}
return cached_section_content.find(itemName) != cached_section_content.end();
}
/**
* @brief Check whether an item exist in current section. Return false if the section does not exist.
*/
bool ItemExist(std::string itemName)
{
return current_section.size() ? ItemExist(current_section, itemName) : false;
}
/**
* @brief Check whether an item with the given name prefix exist in the given section. Return false if the section does not exist.
*/
bool ItemPrefixExist(std::string section, std::string itemName)
{
if(!SectionExist(section))
return false;
if(section != cached_section)
{
cached_section = section;
cached_section_content= ini_content.at(section);
}
for(auto &x : cached_section_content)
{
if(x.first.find(itemName) == 0)
return true;
}
return false;
}
/**
* @brief Check whether an item with the given name prefix exist in current section. Return false if the section does not exist.
*/
bool ItemPrefixExist(std::string itemName)
{
return current_section.size() ? ItemPrefixExist(current_section, itemName) : false;
}
/**
* @brief Count of items in the given section. Return 0 if the section does not exist.
*/
unsigned int ItemCount(std::string section)
{
if(!parsed || !SectionExist(section))
return 0;
return ini_content.at(section).size();
}
/**
* @brief Erase all data from the data structure and reset parser status.
*/
void EraseAll()
{
eraseElements(ini_content);
parsed = false;
}
/**
* @brief Retrieve all items in the given section.
*/
int GetItems(std::string section, std::multimap<std::string, std::string> &data)
{
if(!parsed || !SectionExist(section))
return -1;
if(cached_section != section)
{
cached_section = section;
cached_section_content = ini_content.at(section);
}
data = cached_section_content;
return 0;
}
/**
* @brief Retrieve all items in current section.
*/
int GetItems(string_multimap &data)
{
return current_section.size() ? GetItems(current_section, data) : -1;
}
/**
* @brief Retrieve item(s) with the same name prefix in the given section.
*/
int GetAll(std::string section, std::string itemName, string_array &results) //retrieve item(s) with the same itemName prefix
{
if(!parsed)
return -1;
string_multimap mapTemp;
if(GetItems(section, mapTemp) != 0)
return -1;
for(auto &x : mapTemp)
{
if(x.first.find(itemName) == 0)
results.emplace_back(x.second);
}
return 0;
}
/**
* @brief Retrieve item(s) with the same name prefix in current section.
*/
int GetAll(std::string itemName, string_array &results)
{
return current_section.size() ? GetAll(current_section, itemName, results) : -1;
}
/**
* @brief Retrieve one item with the exact same name in the given section.
*/
std::string Get(std::string section, std::string itemName) //retrieve one item with the exact same itemName
{
if(!parsed)
return std::string();
string_multimap mapTemp;
if(GetItems(section, mapTemp) != 0)
return std::string();
for(auto &x : mapTemp)
{
if(x.first == itemName)
return x.second;
}
return std::string();
}
/**
* @brief Retrieve one item with the exact same name in current section.
*/
std::string Get(std::string itemName)
{
return current_section.size() ? Get(current_section, itemName) : std::string();
}
/**
* @brief Retrieve one boolean item value with the exact same name in the given section.
*/
bool GetBool(std::string section, std::string itemName)
{
return Get(section, itemName) == "true";
}
/**
* @brief Retrieve one boolean item value with the exact same name in current section.
*/
bool GetBool(std::string itemName)
{
return current_section.size() ? Get(current_section, itemName) == "true" : false;
}
/**
* @brief Retrieve one integer item value with the exact same name in the given section.
*/
int GetInt(std::string section, std::string itemName)
{
return stoi(Get(section, itemName));
}
/**
* @brief Retrieve one integer item value with the exact same name in current section.
*/
int GetInt(std::string itemName)
{
return GetInt(current_section, itemName);
}
/**
* @brief Retrieve the first item found in the given section.
*/
std::string GetFirst(std::string section, std::string itemName) //return the first item value found in section
{
if(!parsed)
return std::string();
string_array result;
if(GetAll(section, itemName, result) != -1)
return result[0];
else
return std::string();
}
/**
* @brief Retrieve the first item found in current section.
*/
std::string GetFirst(std::string itemName)
{
return current_section.size() ? GetFirst(current_section, itemName) : std::string();
}
/**
* @brief Retrieve a string style array with specific separator and write into integer array.
*/
template <typename T> void GetIntArray(std::string section, std::string itemName, std::string separator, T &Array)
{
string_array vArray;
unsigned int index, UBound = sizeof(Array) / sizeof(Array[0]);
vArray = split(Get(section, itemName), separator);
for(index = 0; index < vArray.size() && index < UBound; index++)
Array[index] = stoi(vArray[index]);
for(; index < UBound; index++)
Array[index] = 0;
}
/**
* @brief Retrieve a string style array with specific separator and write into integer array.
*/
template <typename T> void GetIntArray(std::string itemName, std::string separator, T &Array)
{
if(current_section.size())
GetIntArray(current_section, itemName, separator, Array);
}
/**
* @brief Add a std::string value with given values.
*/
int Set(std::string section, std::string itemName, std::string itemVal)
{
std::string value;
if(!section.size())
return -1;
if(!parsed)
parsed = true;
if(SectionExist(section))
{
string_multimap &mapTemp = ini_content.at(section);
mapTemp.insert(std::pair<std::string, std::string>(itemName, itemVal));
}
else
{
string_multimap mapTemp;
mapTemp.insert(std::pair<std::string, std::string>(itemName, itemVal));
ini_content.insert(std::pair<std::string, std::multimap<std::string, std::string>>(section, mapTemp));
}
return 0;
}
/**
* @brief Add a string value with given values.
*/
int Set(std::string itemName, std::string itemVal)
{
if(!current_section.size())
return -1;
return Set(current_section, itemName, itemVal);
}
/**
* @brief Add a boolean value with given values.
*/
int SetBool(std::string section, std::string itemName, bool itemVal)
{
return Set(section, itemName, itemVal ? "true" : "false");
}
/**
* @brief Add a boolean value with given values.
*/
int SetBool(std::string itemName, bool itemVal)
{
return SetBool(current_section, itemName, itemVal);
}
/**
* @brief Add a double value with given values.
*/
int SetDouble(std::string section, std::string itemName, double itemVal)
{
return Set(section, itemName, std::__cxx11::to_string(itemVal));
}
/**
* @brief Add a double value with given values.
*/
int SetDouble(std::string itemName, double itemVal)
{
return SetDouble(current_section, itemName, itemVal);
}
/**
* @brief Add a long value with given values.
*/
int SetLong(std::string section, std::string itemName, long itemVal)
{
return Set(section, itemName, std::__cxx11::to_string(itemVal));
}
/**
* @brief Add a long value with given values.
*/
int SetLong(std::string itemName, long itemVal)
{
return SetLong(current_section, itemName, itemVal);
}
/**
* @brief Add an array with the given separator.
*/
template <typename T> int SetArray(std::string section, std::string itemName, std::string separator, T &Array)
{
std::string data;
for(auto &x : Array)
data += std::__cxx11::to_string(x) + separator;
data = data.substr(0, data.size() - separator.size());
return Set(section, itemName, data);
}
/**
* @brief Add an array with the given separator.
*/
template <typename T> int SetArray(std::string itemName, std::string separator, T &Array)
{
return current_section.size() ? SetArray(current_section, itemName, separator, Array) : -1;
}
/**
* @brief Erase all items with the given name.
*/
int Erase(std::string section, std::string itemName)
{
int retVal;
if(!SectionExist(section))
return -1;
retVal = ini_content.at(section).erase(itemName);
if(retVal && cached_section == section)
{
cached_section_content = ini_content.at(section);
}
return retVal;
}
/**
* @brief Erase all items with the given name.
*/
int Erase(std::string itemName)
{
return current_section.size() ? Erase(current_section, itemName) : -1;
}
/**
* @brief Erase the first item with the given name.
*/
int EraseFirst(std::string section, std::string itemName)
{
string_multimap &mapTemp = ini_content.at(section);
string_multimap::iterator iter = mapTemp.find(itemName);
if(iter != mapTemp.end())
{
mapTemp.erase(iter);
if(cached_section == section)
{
cached_section_content = mapTemp;
}
return 0;
}
else
{
return -1;
}
}
/**
* @brief Erase the first item with the given name.
*/
int EraseFirst(std::string itemName)
{
return current_section.size() ? EraseFirst(current_section, itemName) : -1;
}
/**
* @brief Export the whole INI data structure into a string.
*/
std::string ToString()
{
std::string content;
if(!parsed)
return std::string();
for(auto &x : ini_content)
{
content += "[" + x.first + "]\n";
for(auto &y : x.second)
{
if(y.first != "{NONAME}")
content += y.first + " = ";
content += y.second + "\n";
}
content += "\n";
}
return content;
}
/**
* @brief Export the whole INI data structure into a file.
*/
int ToFile(std::string filePath)
{
return fileWrite(filePath, ToString(), true);
}
};
#endif // INI_READER_H_INCLUDED
+8
View File
@@ -0,0 +1,8 @@
#include <string>
#include "logger.h"
void writeLog(int type, std::string content)
{
//placeholder
}
+35
View File
@@ -0,0 +1,35 @@
#ifndef LOGGER_H_INCLUDED
#define LOGGER_H_INCLUDED
#include <string>
#include "misc.h"
#define LOG_TYPE_INFO 1
#define LOG_TYPE_ERROR 2
#define LOG_TYPE_RAW 3
#define LOG_TYPE_WARN 4
#define LOG_TYPE_TCPING 5
#define LOG_TYPE_FILEDL 6
#define LOG_TYPE_GEOIP 7
#define LOG_TYPE_RULES 8
#define LOG_TYPE_GPING 9
#define LOG_TYPE_RENDER 10
#define LOG_TYPE_FILEUL 11
extern std::string resultPath, logPath;
int makeDir(const char *path);
std::string getTime(int type);
void logInit(bool rpcmode);
void resultInit();
void writeLog(int type, std::string content);
void logEOF();
/*
void resultInit(bool export_with_maxspeed);
void writeResult(nodeInfo *node, bool export_with_maxspeed);
void resultEOF(std::string traffic, int worknodes, int totnodes);
void exportResult(std::string outpath, std::string utiljspath, std::string stylepath, bool export_with_maxspeed);
*/
#endif // LOGGER_H_INCLUDED
+241
View File
@@ -0,0 +1,241 @@
#include <iostream>
#include <string>
#include <yaml-cpp/yaml.h>
#include "socket.h"
#include "misc.h"
#include "nodeinfo.h"
#include "speedtestutil.h"
#include "nodemanip.h"
#include "ini_reader.h"
#include "webget.h"
#include "webserver.h"
#include "subexport.h"
//common settings
string_array def_exclude_remarks, def_include_remarks, rulesets;
std::vector<ruleset_content> ruleset_content_array;
std::string listen_address = "127.0.0.1", default_url;
int listen_port = 25500, max_pending_connections = 10, max_concurrent_threads = 4;
bool api_mode = true, update_ruleset_on_request = false, overwrite_original_rules = true;
extern std::string custom_group;
//preferences
string_array renames, emojis;
bool add_emoji = false, remove_old_emoji = false;
//clash custom
std::string clash_rule_base, clash_base_content;
string_array clash_extra_group;
std::string refreshRulesets()
{
eraseElements(ruleset_content_array);
string_array vArray;
std::string rule_group, rule_url, proxy = getSystemProxy();
ruleset_content rc;
for(std::string &x : rulesets)
{
vArray = split(x, ",");
if(vArray.size() != 2)
continue;
rule_group = trim(vArray[0]);
rule_url = trim(vArray[1]);
if(rule_url.find("[]") == 0)
{
std::cerr<<"Adding rule '"<<rule_url.substr(2)<<","<<rule_group<<"'."<<std::endl;
rc = {rule_group, rule_url};
ruleset_content_array.emplace_back(rc);
continue;
}
else
{
std::cerr<<"Updating ruleset url '"<<rule_url<<"' with group '"<<rule_group<<"'."<<std::endl;
if(fileExist(rule_url))
{
rc = {rule_group, fileGet(rule_url, false)};
}
else
{
rc = {rule_group, webGet(rule_url, proxy)};
}
}
if(rc.rule_content.size())
ruleset_content_array.emplace_back(rc);
}
return "done";
}
void readConf()
{
eraseElements(def_exclude_remarks);
eraseElements(def_include_remarks);
eraseElements(clash_extra_group);
eraseElements(rulesets);
eraseElements(renames);
eraseElements(emojis);
INIReader ini;
//ini.do_utf8_to_gbk = true;
ini.ParseFile("pref.ini");
ini.EnterSection("common");
if(ini.ItemExist("api_mode"))
api_mode = ini.GetBool("api_mode");
if(ini.ItemExist("default_url"))
default_url = ini.Get("default_url");
if(ini.ItemPrefixExist("exclude_remarks"))
ini.GetAll("exclude_remarks", def_exclude_remarks);
if(ini.ItemPrefixExist("include_remarks"))
ini.GetAll("include_remarks", def_exclude_remarks);
if(ini.ItemExist("clash_rule_base"))
clash_rule_base = ini.Get("clash_rule_base");
if(ini.ItemPrefixExist("rename_node"))
ini.GetAll("rename_node", renames);
ini.EnterSection("emojis");
if(ini.ItemExist("add_emoji"))
add_emoji = ini.GetBool("add_emoji");
if(ini.ItemExist("remove_old_emoji"))
remove_old_emoji = ini.GetBool("remove_old_emoji");
if(ini.ItemPrefixExist("rule"))
ini.GetAll("rule", emojis);
ini.EnterSection("ruleset");
if(ini.GetBool("enabled"))
{
if(ini.ItemExist("overwrite_original_rules"))
overwrite_original_rules = ini.GetBool("overwrite_original_rules");
if(ini.ItemExist("update_ruleset_on_request"))
update_ruleset_on_request = ini.GetBool("update_ruleset_on_request");
if(ini.ItemPrefixExist("surge_ruleset"))
ini.GetAll("surge_ruleset", rulesets);
}
else
{
overwrite_original_rules = false;
update_ruleset_on_request = false;
}
ini.EnterSection("clash_proxy_group");
if(ini.ItemPrefixExist("custom_proxy_group"))
ini.GetAll("custom_proxy_group", clash_extra_group);
ini.EnterSection("server");
if(ini.ItemExist("listen"))
listen_address = ini.Get("listen");
if(ini.ItemExist("port"))
listen_port = ini.GetInt("port");
ini.EnterSection("advanced");
if(ini.ItemExist("max_pending_connections"))
max_pending_connections = ini.GetInt("max_pending_connections");
if(ini.ItemExist("max_concurrent_threads"))
max_concurrent_threads = ini.GetInt("max_concurrent_threads");
remarksInit(def_exclude_remarks, def_include_remarks);
}
std::string netchToClash(std::vector<nodeInfo> &nodes, std::string &baseConf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, bool clashR);
int main()
{
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
{
fprintf(stderr, "WSAStartup failed.\n");
return 1;
}
SetConsoleOutputCP(65001);
#endif // _WIN32
readConf();
if(!update_ruleset_on_request)
refreshRulesets();
append_response("GET", "/refreshrules", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
return refreshRulesets();
});
append_response("GET", "/clash", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
readConf();
std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")), group = UrlDecode(getUrlArg(argument, "group"));
if(!url.size()) url = default_url;
string_array urls = split(url, "|");
std::vector<nodeInfo> 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 '"<<x<<"'. Generate target: Clash"<<std::endl;
addNodes(x, nodes);
}
if(fileExist(clash_rule_base))
clash_base_content = fileGet(clash_rule_base, false);
else
clash_base_content = webGet(clash_rule_base, getSystemProxy());
if(update_ruleset_on_request)
refreshRulesets();
return netchToClash(nodes, clash_base_content, ruleset_content_array, clash_extra_group, false);
});
append_response("GET", "/clashr", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
readConf();
std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")), group = UrlDecode(getUrlArg(argument, "group"));
if(!url.size()) url = default_url;
string_array urls = split(url, "|");
std::vector<nodeInfo> 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 '"<<x<<"'. Generate target: ClashR"<<std::endl;
addNodes(x, nodes);
}
if(fileExist(clash_rule_base))
clash_base_content = fileGet(clash_rule_base, false);
else
clash_base_content = webGet(clash_rule_base, getSystemProxy());
if(update_ruleset_on_request)
refreshRulesets();
return netchToClash(nodes, clash_base_content, ruleset_content_array, clash_extra_group, true);
});
if(!api_mode)
{
append_response("GET", "/get", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
std::string url = UrlDecode(getUrlArg(argument, "url"));
return webGet(url, "");
});
append_response("GET", "/getlocal", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
return fileGet(UrlDecode(getUrlArg(argument, "path")));
});
}
listener_args args = {listen_address, listen_port, 10240, 4};
std::cout<<"Serving HTTP @ http://"<<listen_address<<":"<<listen_port<<std::endl;
start_web_server(&args);
#ifdef _WIN32
WSACleanup();
#endif // _WIN32
return 0;
}
+776
View File
@@ -0,0 +1,776 @@
#include <chrono>
#include <regex>
#include <fstream>
#include <thread>
#include <sstream>
#include <iosfwd>
#include <filesystem>
#include <unistd.h>
#include <rapidjson/document.h>
#include <openssl/md5.h>
#include "misc.h"
#ifdef _WIN32
//#include <io.h>
#include <windows.h>
#include <winreg.h>
#else
#ifndef __hpux
#include <sys/select.h>
#endif /* __hpux */
#ifndef _access
#define _access access
#endif // _access
#include <sys/socket.h>
#endif // _WIN32
using namespace std::__cxx11;
void sleep(int interval)
{
/*
#ifdef _WIN32
Sleep(interval);
#else
// Portable sleep for platforms other than Windows.
struct timeval wait = { 0, interval * 1000 };
select(0, NULL, NULL, NULL, &wait);
#endif
*/
//upgrade to c++11 standard
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
}
std::string GBKToUTF8(std::string str_src)
{
#ifdef _WIN32
const char* strGBK = str_src.c_str();
int len = MultiByteToWideChar(CP_ACP, 0, strGBK, -1, NULL, 0);
wchar_t* wstr = new wchar_t[len + 1];
memset(wstr, 0, len + 1);
MultiByteToWideChar(CP_ACP, 0, strGBK, -1, wstr, len);
len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
char* str = new char[len + 1];
memset(str, 0, len + 1);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
std::string strTemp = str;
if(wstr)
delete[] wstr;
if(str)
delete[] str;
return strTemp;
#else
return str_src;
#endif // _WIN32
}
std::string UTF8ToGBK(std::string str_src)
{
#ifdef _WIN32
const char* strUTF8 = str_src.data();
int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);
wchar_t* wszGBK = new wchar_t[len + 1];
memset(wszGBK, 0, len * 2 + 2);
MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len);
len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
char* szGBK = new char[len + 1];
memset(szGBK, 0, len + 1);
WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
std::string strTemp(szGBK);
if (wszGBK)
delete[] wszGBK;
if (szGBK)
delete[] szGBK;
return strTemp;
#else
return str_src;
#endif
}
#ifdef _WIN32
// std::string to wstring
void StringToWstring(std::wstring& szDst, std::string str)
{
std::string temp = str;
int len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)temp.c_str(), -1, NULL,0);
wchar_t* wszUtf8 = new wchar_t[len + 1];
memset(wszUtf8, 0, len * 2 + 2);
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)temp.c_str(), -1, (LPWSTR)wszUtf8, len);
szDst = wszUtf8;
std::wstring r = wszUtf8;
delete[] wszUtf8;
}
#endif // _WIN32
unsigned char FromHex(unsigned char x)
{
unsigned char y = '\0';
if (x >= 'A' && x <= 'Z')
y = x - 'A' + 10;
else if (x >= 'a' && x <= 'z')
y = x - 'a' + 10;
else if (x >= '0' && x <= '9')
y = x - '0';
else
assert(0);
return y;
}
std::string UrlDecode(const std::string& str)
{
std::string strTemp = "";
size_t length = str.length();
for (size_t i = 0; i < length; i++)
{
if (str[i] == '+')
strTemp += ' ';
else if (str[i] == '%')
{
assert(i + 2 < length);
unsigned char high = FromHex((unsigned char)str[++i]);
unsigned char low = FromHex((unsigned char)str[++i]);
strTemp += high * 16 + low;
}
else
strTemp += str[i];
}
return strTemp;
}
static inline bool is_base64(unsigned char c)
{
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(std::string string_to_encode)
{
char const* bytes_to_encode = string_to_encode.data();
unsigned int in_len = string_to_encode.size();
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--)
{
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3)
{
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
std::string base64_decode(std::string encoded_string)
{
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_]))
{
char_array_4[i++] = encoded_string[in_];
in_++;
if (i ==4)
{
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i)
{
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++)
ret += char_array_3[j];
}
return ret;
}
std::vector<std::string> split(const std::string &s, const std::string &seperator)
{
std::vector<std::string> result;
typedef std::string::size_type string_size;
string_size i = 0;
while(i != s.size())
{
int flag = 0;
while(i != s.size() && flag == 0)
{
flag = 1;
for(string_size x = 0; x < seperator.size(); ++x)
if(s[i] == seperator[x])
{
++i;
flag = 0;
break;
}
}
flag = 0;
string_size j = i;
while(j != s.size() && flag == 0)
{
for(string_size x = 0; x < seperator.size(); ++x)
if(s[j] == seperator[x])
{
flag = 1;
break;
}
if(flag == 0)
++j;
}
if(i != j)
{
result.push_back(s.substr(i, j-i));
i = j;
}
}
return result;
}
std::string getSystemProxy()
{
#ifdef _WIN32
HKEY key;
auto ret = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings)", 0, KEY_ALL_ACCESS, &key);
if(ret != ERROR_SUCCESS)
{
//std::cout << "open failed: " << ret << std::endl;
return std::string();
}
DWORD values_count, max_value_name_len, max_value_len;
ret = RegQueryInfoKey(key, NULL, NULL, NULL, NULL, NULL, NULL,
&values_count, &max_value_name_len, &max_value_len, NULL, NULL);
if(ret != ERROR_SUCCESS)
{
//std::cout << "query failed" << std::endl;
return std::string();
}
std::vector<std::tuple<std::shared_ptr<char>, DWORD, std::shared_ptr<BYTE>>> values;
for(DWORD i = 0; i < values_count; i++)
{
std::shared_ptr<char> value_name(new char[max_value_name_len + 1],
std::default_delete<char[]>());
DWORD value_name_len = max_value_name_len + 1;
DWORD value_type, value_len;
RegEnumValue(key, i, value_name.get(), &value_name_len, NULL, &value_type, NULL, &value_len);
std::shared_ptr<BYTE> value(new BYTE[value_len],
std::default_delete<BYTE[]>());
value_name_len = max_value_name_len + 1;
RegEnumValue(key, i, value_name.get(), &value_name_len, NULL, &value_type, value.get(), &value_len);
values.push_back(std::make_tuple(value_name, value_type, value));
}
DWORD ProxyEnable = 0;
for (auto x : values)
{
if (strcmp(std::get<0>(x).get(), "ProxyEnable") == 0)
{
ProxyEnable = *(DWORD*)(std::get<2>(x).get());
}
}
if (ProxyEnable)
{
for (auto x : values)
{
if (strcmp(std::get<0>(x).get(), "ProxyServer") == 0)
{
//std::cout << "ProxyServer: " << (char*)(std::get<2>(x).get()) << std::endl;
return std::string((char*)(std::get<2>(x).get()));
}
}
}
/*
else {
//std::cout << "Proxy not Enabled" << std::endl;
}
*/
//return 0;
return std::string();
#else
char* proxy = getenv("ALL_PROXY");
if(proxy != NULL)
return std::string(proxy);
else
return std::string();
#endif // _WIN32
}
std::string trim(const std::string& str)
{
std::string::size_type pos = str.find_first_not_of(' ');
if (pos == std::string::npos)
{
return str;
}
std::string::size_type pos2 = str.find_last_not_of(' ');
if (pos2 != std::string::npos)
{
return str.substr(pos, pos2 - pos + 1);
}
return str.substr(pos);
}
std::string getUrlArg(std::string url, std::string request)
{
std::smatch result;
if (regex_search(url.cbegin(), url.cend(), result, std::regex(request + "=(.*?)&")))
{
return result[1];
}
else if (regex_search(url.cbegin(), url.cend(), result, std::regex(request + "=(.*)")))
{
return result[1];
}
else
{
return std::string();
}
}
std::string replace_all_distinct(std::string str, std::string old_value, std::string new_value)
{
for(std::string::size_type pos(0); pos != std::string::npos; pos += new_value.length())
{
if((pos = str.find(old_value, pos)) != std::string::npos)
str.replace(pos, old_value.length(), new_value);
else
break;
}
return str;
}
bool regFind(std::string src, std::string target)
{
std::regex reg(target);
return regex_search(src, reg);
}
std::string regReplace(std::string src, std::string match, std::string rep)
{
std::string result = "";
std::regex reg(match);
regex_replace(back_inserter(result), src.begin(), src.end(), reg, rep);
return result;
}
bool regMatch(std::string src, std::string match)
{
std::regex reg(match);
return regex_match(src, reg);
}
std::string speedCalc(double speed)
{
if(speed == 0.0)
return std::string("0.00B");
char str[10];
std::string retstr;
if(speed >= 1073741824.0)
sprintf(str, "%.2fGB", speed / 1073741824.0);
else if(speed >= 1048576.0)
sprintf(str, "%.2fMB", speed / 1048576.0);
else if(speed >= 1024.0)
sprintf(str, "%.2fKB", speed / 1024.0);
else
sprintf(str, "%.2fB", speed);
retstr = str;
return retstr;
}
std::string urlsafe_base64_reverse(std::string encoded_string)
{
return replace_all_distinct(replace_all_distinct(encoded_string, "-", "+"), "_", "/");
}
std::string urlsafe_base64_decode(std::string encoded_string)
{
return base64_decode(urlsafe_base64_reverse(encoded_string));
}
std::string getMD5(std::string data)
{
MD5_CTX ctx;
std::string result;
unsigned int i = 0;
unsigned char digest[16] = {};
MD5_Init(&ctx);
MD5_Update(&ctx, data.data(), data.size());
MD5_Final((unsigned char *)&digest, &ctx);
char tmp[3] = {};
for(i = 0; i < 16; i++)
{
snprintf(tmp, 3, "%02x", digest[i]);
result += tmp;
}
return result;
}
std::string fileGet(std::string path, bool binary)
{
std::ifstream infile;
std::stringstream strstrm;
std::ios::openmode mode = binary ? std::ios::binary : std::ios::in;
infile.open(path, mode);
if(infile)
{
strstrm<<infile.rdbuf();
infile.close();
return strstrm.str();
}
return std::string();
}
bool fileExist(std::string path)
{
//using c++17 standard
return std::filesystem::exists(path);
//return _access(path.data(), 4) != -1;
}
bool fileCopy(std::string source, std::string dest)
{
std::ifstream infile;
std::ofstream outfile;
infile.open(source, std::ios::binary);
if(!infile)
return false;
outfile.open(dest, std::ios::binary);
if(!outfile)
return false;
try
{
outfile<<infile.rdbuf();
}
catch (std::exception &e)
{
return false;
}
infile.close();
outfile.close();
return true;
}
std::string fileToBase64(std::string filepath)
{
return base64_encode(fileGet(filepath));
}
std::string fileGetMD5(std::string filepath)
{
return getMD5(fileGet(filepath));
}
int fileWrite(std::string path, std::string content, bool overwrite)
{
std::fstream outfile;
std::ios::openmode mode = overwrite ? std::ios::out : std::ios::app;
outfile.open(path, mode);
outfile << content << std::endl;
outfile.close();
return 0;
}
bool isIPv4(std::string address)
{
return regMatch(address, "^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
}
bool isIPv6(std::string address)
{
std::vector<std::string> regLists = {"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$", "^((?:[0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::((?:([0-9A-Fa-f]{1,4}:)*[0-9A-Fa-f]{1,4})?)$", "^(::(?:[0-9A-Fa-f]{1,4})(?::[0-9A-Fa-f]{1,4}){5})|((?:[0-9A-Fa-f]{1,4})(?::[0-9A-Fa-f]{1,4}){5}::)$"};
for(unsigned int i = 0; i < regLists.size(); i++)
{
if(regMatch(address, regLists[i]))
return true;
}
return false;
}
std::string rand_str(const int len)
{
std::string retData;
srand(time(NULL));
int cnt = 0;
while(cnt < len)
{
switch((rand() % 3))
{
case 1:
retData += ('A' + rand() % 26);
break;
case 2:
retData += ('a' + rand() % 26);
break;
default:
retData += ('0' + rand() % 10);
break;
}
cnt++;
}
return retData;
}
void urlParse(std::string url, std::string &host, std::string &path, int &port, bool &isTLS)
{
std::vector<std::string> args;
if(regMatch(url, "^https://(.*)"))
isTLS = true;
url = regReplace(url, "^(http|https)://", "");
if(url.find("/") == url.npos)
{
host = url;
path = "/";
}
else
{
host = url.substr(0, url.find("/"));
path = url.substr(url.find("/"));
}
if(regFind(host, "\\[(.*)\\]")) //IPv6
{
args = split(regReplace(host, "\\[(.*)\\](.*)", "$1,$2"), ",");
if(args.size() == 2) //with port
port = stoi(args[1].substr(1));
host = args[0];
}
else if(strFind(host, ":"))
{
port = stoi(host.substr(host.rfind(":") + 1));
host = host.substr(0, host.rfind(":"));
}
if(port == 0)
{
if(isTLS)
port = 443;
else
port = 80;
}
}
bool is_str_utf8(std::string data)
{
const char *str = data.c_str();
unsigned int nBytes = 0;
unsigned char chr = *str;
bool bAllAscii = true;
for (unsigned int i = 0; str[i] != '\0'; ++i)
{
chr = *(str + i);
if (nBytes == 0 && (chr & 0x80) != 0)
{
bAllAscii = false;
}
if (nBytes == 0)
{
if (chr >= 0x80)
{
if (chr >= 0xFC && chr <= 0xFD)
{
nBytes = 6;
}
else if (chr >= 0xF8)
{
nBytes = 5;
}
else if (chr >= 0xF0)
{
nBytes = 4;
}
else if (chr >= 0xE0)
{
nBytes = 3;
}
else if (chr >= 0xC0)
{
nBytes = 2;
}
else
{
return false;
}
nBytes--;
}
}
else
{
if ((chr & 0xC0) != 0x80)
{
return false;
}
nBytes--;
}
}
if (nBytes != 0)
{
return false;
}
if (bAllAscii)
{
return true;
}
return true;
}
void removeUTF8BOM(std::string &data)
{
int BOM[3] = {0xef, 0xbb, 0xbf};
if(data.compare(0, 3, (char*)BOM) == 0)
data = data.substr(3);
}
int shortAssemble(unsigned short num_a, unsigned short num_b)
{
return (int)num_b << 16 | num_a;
}
void shortDisassemble(int source, unsigned short &num_a, unsigned short &num_b)
{
num_a = (unsigned short)source;
num_b = (unsigned short)(source >> 16);
}
std::string to_string(YAML::Node &node)
{
std::stringstream ss;
ss << node;
return ss.str();
}
std::string getFormData(std::string &raw_data)
{
std::stringstream strstrm;
std::string line;
std::string boundary;
std::string disp; /* content disposition string */
std::string type; /* content type string */
std::string file; /* actual file content */
int i = 0;
strstrm<<raw_data;
while (std::getline(strstrm, line))
{
if(i == 0)
{
// Get boundary
boundary = line.substr(0, line.length() - 1);
}
else if(line.find("Content-Disposition") == 0)
{
disp = line;
}
else if(line.find("Content-Type") == 0)
{
type = line;
}
else if(line.find(boundary) == 0)
{
// The end
type = line;
break;
}
else if(line.length() == 1)
{
// Time to get raw data
char c;
int bl = boundary.length();
bool endfile = false;
char buffer[256];
while(!endfile)
{
int j = 0;
int k;
while(j < 256 && strstrm.get(c) && !endfile)
{
buffer[j] = c;
k = 0;
// Verify if we are at the end
while(boundary[bl - 1 - k] == buffer[j - k])
{
if(k >= bl - 1)
{
// We are at the end of the file
endfile = true;
break;
}
k++;
}
j++;
}
file.append(buffer, j);
j = 0;
};
break;
}
i++;
}
return file;
}
+90
View File
@@ -0,0 +1,90 @@
#ifndef MISC_H_INCLUDED
#define MISC_H_INCLUDED
#include <string>
#include <vector>
#include <sstream>
#include <yaml-cpp/yaml.h>
#ifdef _WIN32
#define PATH_SLASH "\\"
#else
#define PATH_SLASH "//"
#endif // _WIN32
typedef std::vector<std::string> string_array;
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string UrlDecode(const std::string& str);
std::string base64_decode(std::string encoded_string);
std::string base64_encode(std::string string_to_encode);
std::vector<std::string> split(const std::string &s, const std::string &seperator);
std::string getUrlArg(std::string url, std::string request);
std::string replace_all_distinct(std::string str, std::string old_value, std::string new_value);
std::string urlsafe_base64_reverse(std::string encoded_string);
std::string urlsafe_base64_decode(std::string encoded_string);
std::string UTF8ToGBK(std::string str_src);
std::string GBKToUTF8(std::string str_src);
std::string trim(const std::string& str);
std::string getSystemProxy();
std::string rand_str(const int len);
bool is_str_utf8(std::string data);
std::string getFormData(std::string &raw_data);
void sleep(int interval);
bool regFind(std::string src, std::string target);
std::string regReplace(std::string src, std::string match, std::string rep);
bool regMatch(std::string src, std::string match);
std::string speedCalc(double speed);
std::string getMD5(std::string data);
bool isIPv4(std::string address);
bool isIPv6(std::string address);
void urlParse(std::string url, std::string &host, std::string &path, int &port, bool &isTLS);
void removeUTF8BOM(std::string &data);
int shortAssemble(unsigned short num_a, unsigned short num_b);
void shortDisassemble(int source, unsigned short &num_a, unsigned short &num_b);
std::string to_string(YAML::Node &node);
std::string fileGet(std::string path, bool binary = true);
int fileWrite(std::string path, std::string content, bool overwrite);
bool fileExist(std::string path);
bool fileCopy(std::string source,std::string dest);
std::string fileToBase64(std::string filepath);
std::string fileGetMD5(std::string filepath);
static inline bool strFind(std::string str, std::string target)
{
return str.find(target) != str.npos;
}
template <typename T> static inline void eraseElements(T &target)
{
T().swap(target);
}
#ifdef _MACOS
namespace std
{
namespace __cxx11
{
template <typename T> std::string to_string(const T& n)
{
std::ostringstream ss;
ss << n ;
return ss.str();
}
}
}
#endif // _MACOS
#ifdef _WIN32
void StringToWstring(std::wstring& szDst, std::string str);
#endif // _WIN32
#endif // MISC_H_INCLUDED
+17
View File
@@ -0,0 +1,17 @@
#ifndef NODEINFO_H_INCLUDED
#define NODEINFO_H_INCLUDED
#include <string>
struct nodeInfo
{
int linkType = -1;
int id = 0;
std::string group;
std::string remarks;
std::string server;
int port = 0;
std::string proxyStr;
};
#endif // NODEINFO_H_INCLUDED
+108
View File
@@ -0,0 +1,108 @@
#include <string>
#include <vector>
#include <iostream>
#include "nodeinfo.h"
#include "printout.h"
#include "logger.h"
#include "webget.h"
#include "speedtestutil.h"
std::string override_conf_port, custom_group;
int socksport;
bool ss_libev, ssr_libev;
void copyNodes(std::vector<nodeInfo> *source, std::vector<nodeInfo> *dest)
{
for(auto &x : *source)
{
dest->push_back(x);
}
}
void addNodes(std::string link, std::vector<nodeInfo> &allNodes)
{
int linkType = -1;
std::vector<nodeInfo> nodes;
nodeInfo node;
std::string strSub, strInput, fileContent, strProxy;
link = replace_all_distinct(link, "\"", "");
writeLog(LOG_TYPE_INFO, "Received Link.");
if(strFind(link, "vmess://") || strFind(link, "vmess1://"))
linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
else if(strFind(link, "ss://"))
linkType = SPEEDTEST_MESSAGE_FOUNDSS;
else if(strFind(link, "ssr://"))
linkType = SPEEDTEST_MESSAGE_FOUNDSSR;
else if(strFind(link, "socks://") || strFind(link, "https://t.me/socks") || strFind(link, "tg://socks"))
linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
else if(strFind(link, "http://") || strFind(link, "https://") || strFind(link, "surge:///install-config"))
linkType = SPEEDTEST_MESSAGE_FOUNDSUB;
else if(strFind(link, "Netch://"))
linkType = SPEEDTEST_MESSAGE_FOUNDNETCH;
else if(fileExist(link))
linkType = SPEEDTEST_MESSAGE_FOUNDLOCAL;
switch(linkType)
{
case SPEEDTEST_MESSAGE_FOUNDSUB:
writeLog(LOG_TYPE_INFO, "Downloading subscription data...");
if(strFind(link, "surge:///install-config")) //surge config link
link = UrlDecode(getUrlArg(link, "url"));
strSub = webGet(link);
if(strSub.size() == 0)
{
//try to get it again with system proxy
writeLog(LOG_TYPE_WARN, "Cannot download subscription directly. Using system proxy.");
strProxy = getSystemProxy();
if(strProxy != "")
{
strSub = webGet(link, strProxy);
}
else
writeLog(LOG_TYPE_WARN, "No system proxy is set. Skipping.");
}
if(strSub.size())
{
writeLog(LOG_TYPE_INFO, "Parsing subscription data...");
explodeConfContent(strSub, override_conf_port, socksport, ss_libev, ssr_libev, nodes);
copyNodes(&nodes, &allNodes);
}
else
{
writeLog(LOG_TYPE_ERROR, "Cannot download subscription data.");
}
break;
case SPEEDTEST_MESSAGE_FOUNDLOCAL:
writeLog(LOG_TYPE_INFO, "Parsing configuration file data...");
if(explodeConf(link, override_conf_port, socksport, ss_libev, ssr_libev, nodes) == SPEEDTEST_ERROR_UNRECOGFILE)
{
writeLog(LOG_TYPE_ERROR, "Invalid configuration file!");
}
else
{
copyNodes(&nodes, &allNodes);
}
break;
default:
if(linkType > 0)
{
explode(link, ss_libev, ssr_libev, override_conf_port, socksport, node);
if(custom_group.size() != 0)
node.group = custom_group;
if(node.server == "")
{
writeLog(LOG_TYPE_ERROR, "No valid link found.");
}
else
{
allNodes.push_back(node);
}
}
else
{
writeLog(LOG_TYPE_ERROR, "No valid link found.");
}
}
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef NODEMANIP_H_INCLUDED
#define NODEMANIP_H_INCLUDED
#include <string>
#include <vector>
#include "nodeinfo.h"
void addNodes(std::string link, std::vector<nodeInfo> &allNodes);
#endif // NODEMANIP_H_INCLUDED
+139
View File
@@ -0,0 +1,139 @@
[common]
;Default URLs, used when no URL is provided in request, use "|" to separate multiple subscription links, supports local files/URL
default_url=
;Exclude nodes which remarks match the following patterns. Supports regular expression.
exclude_remarks=(剩余流量|到期时间|过期时间|官网地址|产品名称)
;exclude_remarks=(other rule)
;Only include nodes which remarks match the following patterns. Supports regular expression.
;include_remarks=V3.*港
;Clash config base used by the generator, supports local files/URL
clash_rule_base=simple_base.yml
;Rename remarks with the following patterns. Supports regular expression.
;Format: Search_Pattern@Replace_Pattern
;rename_node=IPLC@专线
;rename_node=RELAY@中转
;rename_node=BGP-@
;rename_node=\((.*?) 倍率\)@x$1
;rename_node=\((.*?)\)$@ x$1
[emojis]
add_emoji=true
remove_old_emoji=true
;Rule to add emojis. Supports regular expression.
;Format: Remark_Search_Pattern,emoji
rule=(流量|时间|应急),🏳️‍🌈
rule=阿根廷,🇦🇷
rule=(奥地利|维也纳),🇦🇹
rule=(澳大利亚|悉尼),🇦🇺
rule=(巴西|圣保罗),🇧🇷
rule=(加拿大|蒙特利尔|温哥华),🇨🇦
rule=(瑞士|苏黎世),🇨🇭
rule=(中国|江苏|北京|上海|广州|深圳|杭州|徐州|青岛|宁波|镇江),🇨🇳
rule=(德|德国|法兰克福),🇩🇪
rule=(芬兰|赫尔辛基),🇫🇮
rule=(法国|巴黎),🇫🇷
rule=(英国|伦敦),🇬🇧
rule=(港|香港|深港|沪港),🇭🇰
rule=(印尼|印度尼西亚|雅加达),🇮🇩
rule=(爱尔兰|都柏林),🇮🇪
rule=(印度|孟买),🇮🇳
rule=(意大利|米兰),🇮🇹
rule=(日|日本|东京|大阪|埼玉|沪日),🇯🇵
rule=朝鲜,🇰🇵
rule=(韩国|首尔),🇰🇷
rule=澳门,🇲🇴
rule=马来西亚,🇲🇾
rule=(荷兰|阿姆斯特丹),🇳🇱
rule=菲律宾,🇵🇭
rule=罗马尼亚,🇷🇴
rule=(俄罗斯|伯力|莫斯科|圣彼得堡|西伯利亚|新西伯利亚),🇷🇺
rule=(新|新加坡|狮城),🇸🇬
rule=(泰国|曼谷),🇹🇭
rule=(土耳其|伊斯坦布尔),🇹🇷
rule=(台|台湾|台北|台中|新北|彰化),🇹🇼
rule=(美|美国|波特兰|达拉斯|俄勒冈|凤凰城|费利蒙|硅谷|拉斯维加斯|洛杉矶|圣何塞|圣克拉拉|西雅图|芝加哥|沪美),🇺🇲
rule=越南,🇻🇳
rule=南非,🇿🇦
[ruleset]
;Enable generating rules with rulesets
enabled=true
;Overwrite the existing rules in rule_base
overwrite_original_rules=false
;Perform a ruleset update on request
update_ruleset_on_request=false
;Ruleset addresses, supports local files/URL
;Format: Group name,URL
; 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=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=DIRECT,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/China.list
surge_ruleset=Final,[]MATCH
[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
;Rule with "[]" prefix will be added directly.
;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
;for forcerule.yml
;custom_proxy_group=Proxy`select`.*
;custom_proxy_group=google`select`.*
;custom_proxy_group=netflix`select`.*
;custom_proxy_group=动画疯`select`(深台|彰化|新北|台)
;custom_proxy_group=fox+`select`(HGC|HKBN|PCCW|HKT|深台|彰化|新北|台|新加坡|sg|hk|tw)
;custom_proxy_group=美区影视`select`(美|美国)
;custom_proxy_group=Global_media`select`.*
;custom_proxy_group=Domestic`select`[]DIRECT`[]Proxy
;custom_proxy_group=Apple`select`[]DIRECT`[]Proxy
;custom_proxy_group=Final`select`[]Proxy`[]DIRECT
;custom_proxy_group=屏蔽广告`select`[]REJECT`[]DIRECT
;custom_proxy_group=UnblockNeteaseMusic`select`云音乐解锁`[]DIRECT
;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
[server]
;Address to bind on for Web Server
listen=0.0.0.0
;Port to bind on for Web Server
port=25500
[advanced]
max_pending_connections=10
max_concurrent_threads=4
+71
View File
@@ -0,0 +1,71 @@
#ifndef PRINTOUT_H_INCLUDED
#define PRINTOUT_H_INCLUDED
#include <string>
#include <vector>
#include <unistd.h>
#include "misc.h"
#include "nodeinfo.h"
#define SPEEDTEST_ERROR_NONE 0
#define SPEEDTEST_ERROR_UNDEFINED 100
#define SPEEDTEST_ERROR_WSAERR 99
#define SPEEDTEST_ERROR_SOCKETERR 98
#define SPEEDTEST_ERROR_NORECOGLINK 97
#define SPEEDTEST_ERROR_NOCONNECTION 96
#define SPEEDTEST_ERROR_INVALIDSUB 95
#define SPEEDTEST_ERROR_NONODES 94
#define SPEEDTEST_ERROR_NORESOLVE 93
#define SPEEDTEST_ERROR_RETEST 92
#define SPEEDTEST_ERROR_NOSPEED 91
#define SPEEDTEST_ERROR_UNRECOGFILE 90
#define SPEEDTEST_ERROR_SUBFETCHERR 89
#define SPEEDTEST_ERROR_GEOIPERR 88
#define SPEEDTEST_MESSAGE_WELCOME 0
#define SPEEDTEST_MESSAGE_FOUNDVMESS 1
#define SPEEDTEST_MESSAGE_FOUNDSS 2
#define SPEEDTEST_MESSAGE_FOUNDSSR 3
#define SPEEDTEST_MESSAGE_FOUNDSUB 4
#define SPEEDTEST_MESSAGE_GOTSERVER 5
#define SPEEDTEST_MESSAGE_STARTPING 6
#define SPEEDTEST_MESSAGE_GOTPING 7
#define SPEEDTEST_MESSAGE_STARTSPEED 8
#define SPEEDTEST_MESSAGE_GOTSPEED 9
#define SPEEDTEST_MESSAGE_GOTRESULT 10
#define SPEEDTEST_MESSAGE_TRAFFIC 11
#define SPEEDTEST_MESSAGE_PICSAVING 12
#define SPEEDTEST_MESSAGE_PICSAVED 13
#define SPEEDTEST_MESSAGE_GROUP 14
#define SPEEDTEST_MESSAGE_FETCHSUB 15
#define SPEEDTEST_MESSAGE_BEGIN 16
#define SPEEDTEST_MESSAGE_FOUNDLOCAL 17
#define SPEEDTEST_MESSAGE_PARSING 18
#define SPEEDTEST_MESSAGE_FOUNDUPD 19
#define SPEEDTEST_MESSAGE_PICDATA 20
#define SPEEDTEST_MESSAGE_STARTGPING 21
#define SPEEDTEST_MESSAGE_GOTGPING 22
#define SPEEDTEST_MESSAGE_FOUNDSOCKS 23
#define SPEEDTEST_MESSAGE_STARTGEOIP 24
#define SPEEDTEST_MESSAGE_GOTGEOIP 25
#define SPEEDTEST_MESSAGE_FOUNDSSCONF 26
#define SPEEDTEST_MESSAGE_MULTILINK 27
#define SPEEDTEST_MESSAGE_PICSAVINGMULTI 28
#define SPEEDTEST_MESSAGE_PICSAVEDMULTI 29
#define SPEEDTEST_MESSAGE_STARTUPD 30
#define SPEEDTEST_MESSAGE_GOTUPD 31
#define SPEEDTEST_MESSAGE_FOUNDSSTAP 32
#define SPEEDTEST_MESSAGE_FOUNDNETCH 33
#define SPEEDTEST_MESSAGE_EOF 49
#define SS_DEFAULT_GROUP "SSProvider"
#define SSR_DEFAULT_GROUP "SSRProvider"
#define V2RAY_DEFAULT_GROUP "V2RayProvider"
#define SOCKS_DEFAULT_GROUP "SocksProvider"
void printMsg(int index, nodeInfo *node, bool rpcmode);
void printMsgWithDict(int index, bool rpcmode, std::vector<std::string> dict, std::vector<std::string> trans);
void printMsgDirect(int index, bool rpcmode);
#endif // PRINTOUT_H_INCLUDED
+56
View File
@@ -0,0 +1,56 @@
#include <rapidjson/writer.h>
#include "rapidjson_extra.h"
void operator >> (const rapidjson::Value& value, std::string& i)
{
if(value.IsNull())
i = std::string();
else if(value.IsInt64())
i = std::to_string(value.GetInt64());
else if(value.IsDouble())
i = std::to_string(value.GetDouble());
else if(value.IsString())
i = std::string(value.GetString());
else if(value.IsBool())
i = value.GetBool() ? "true" : "false";
else
i = std::string();
}
void operator >> (const rapidjson::Value& value, int& i)
{
if(value.IsNull())
i = 0;
else if(value.IsInt())
i = value.GetInt();
else if(value.IsString())
i = std::stoi(value.GetString());
else if(value.IsBool())
i = value.GetBool() ? 1 : 0;
else
i = 0;
}
std::string GetMember(const rapidjson::Value& value, std::string member)
{
std::string retStr;
if(value.HasMember(member.data()))
value[member.data()] >> retStr;
return retStr;
}
void GetMember(const rapidjson::Value& value, std::string member, std::string* target)
{
std::string retStr = GetMember(value, member);
if(retStr.size())
target->assign(retStr);
}
std::string SerializeObject(const rapidjson::Value& value)
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer_json(sb);
value.Accept(writer_json);
return sb.GetString();
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef RAPIDJSON_EXTRA_H_INCLUDED
#define RAPIDJSON_EXTRA_H_INCLUDED
#include <rapidjson/document.h>
#include <string>
void operator >> (const rapidjson::Value& value, std::string& i);
void operator >> (const rapidjson::Value& value, int& i);
std::string GetMember(const rapidjson::Value& value, std::string member);
void GetMember(const rapidjson::Value& value, std::string member, std::string* target);
std::string SerializeObject(const rapidjson::Value& value);
#endif // RAPIDJSON_EXTRA_H_INCLUDED
+15
View File
@@ -0,0 +1,15 @@
port: 7890
socks-port: 7891
allow-lan: true
mode: Rule
log-level: info
external-controller: 127.0.0.1:9090
dns:
enable: false
listen: 0.0.0.0:53
ipv6: false
enhanced-mode: fake-ip
nameserver:
- 119.29.29.29
- 114.114.114.114
- 223.5.5.5
+56
View File
@@ -0,0 +1,56 @@
#ifndef SOCKET_H_INCLUDED
#define SOCKET_H_INCLUDED
#include <string>
#ifdef _WIN32
#ifndef WINVER
#define WINVER 0x0501
#endif // WINVER
#include <ws2tcpip.h>
#include <winsock2.h>
#else
//translate windows functions to linux functions
#include <string.h>
#define SOCKET int
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR (-1)
#define closesocket close
#define SOCKADDR_IN sockaddr_in
#define ZeroMemory(d,l) memset((d), 0, (l))
#define ioctlsocket ioctl
#ifndef SA_INTERRUPT
#define SA_INTERRUPT 0 //ignore this setting
#endif
#ifndef __hpux
#include <sys/select.h>
#endif /* __hpux */
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netdb.h>
#include <signal.h>
#include <unistd.h>
typedef sockaddr *LPSOCKADDR;
#endif // _WIN32
#define BUF_SIZE 8192
int getNetworkType(std::string addr);
int Send(SOCKET sHost, const char* data, int len, int flags);
int Recv(SOCKET sHost, char* data, int len, int flags);
int socks5_do_auth_userpass(SOCKET sHost, std::string user, std::string pass);
int setTimeout(SOCKET s, int timeout);
int startConnect(SOCKET sHost, std::string addr, int port);
int simpleSend(std::string addr, int port, std::string data);
int send_simple(SOCKET sHost, std::string data);
std::string hostnameToIPAddr(std::string host);
int connectSocks5(SOCKET sHost, std::string username, std::string password);
int connectThruSocks(SOCKET sHost, std::string host, int port);
int connectThruHTTP(SOCKET sHost, std::string username, std::string password, std::string dsthost, int dstport);
int checkPort(int startport);
#endif // SOCKET_H_INCLUDED
+1558
View File
File diff suppressed because it is too large. Load diff
+27
View File
@@ -0,0 +1,27 @@
#ifndef SPEEDTESTUTIL_H_INCLUDED
#define SPEEDTESTUTIL_H_INCLUDED
#include <string>
#include <yaml-cpp/yaml.h>
#include "misc.h"
#include "nodeinfo.h"
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);
std::string ssrConstruct(std::string group, std::string remarks, std::string remarks_base64, std::string server, std::string port, std::string protocol, std::string method, std::string obfs, std::string password, std::string obfsparam, std::string protoparam, int local_port, bool libev);
std::string ssConstruct(std::string server, std::string port, std::string password, std::string method, std::string plugin, std::string pluginopts, std::string remarks, int local_port, bool libev);
void explodeVmess(std::string vmess, std::string custom_port, int local_port, nodeInfo &node);
void explodeSSR(std::string ssr, bool ss_libev, bool libev, std::string custom_port, int local_port, nodeInfo &node);
void explodeSS(std::string ss, bool libev, std::string custom_port, int local_port, nodeInfo &node);
void explodeQuan(std::string quan, std::string custom_port, int local_port, nodeInfo &node);
void explodeKitsunebi(std::string kit, std::string custom_port, int local_port, nodeInfo &node);
void explodeKitsunebiNew(std::string kit, std::string custom_port, int local_port, nodeInfo &node);
void explode(std::string link, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, nodeInfo &node);
void explodeSSD(std::string link, bool libev, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes);
void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes);
int explodeConf(std::string filepath, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector<nodeInfo> &nodes);
int explodeConfContent(std::string content, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector<nodeInfo> &nodes);
void remarksInit(string_array &exclude, string_array &include);
bool chkIgnore(nodeInfo &node);
#endif // SPEEDTESTUTIL_H_INCLUDED
+415
View File
@@ -0,0 +1,415 @@
#include "misc.h"
#include "speedtestutil.h"
#include "ini_reader.h"
#include "rapidjson_extra.h"
#include "yamlcpp_extra.h"
#include "webget.h"
#include "subexport.h"
#include <iostream>
#include <rapidjson/writer.h>
#include <rapidjson/document.h>
#include <yaml-cpp/yaml.h>
extern bool overwrite_original_rules;
extern string_array renames, emojis;
extern bool add_emoji, remove_old_emoji;
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)
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
writer.StartObject();
writer.Key("Type");
writer.String("VMess");
writer.Key("Remark");
writer.String(std::string(add + ":" + port).data());
writer.Key("Hostname");
writer.String(add.data());
writer.Key("Port");
writer.Int(shortAssemble((unsigned short)stoi(port), (unsigned short)local_port));
writer.Key("UserID");
writer.String(id.data());
writer.Key("AlterID");
writer.Int(stoi(aid));
writer.Key("EncryptMethod");
writer.String(cipher.data());
writer.Key("TransferProtocol");
writer.String(net.data());
if(net == "ws")
{
writer.Key("Host");
writer.String(host.data());
writer.Key("Path");
writer.String(path.data());
}
else
{
if(net == "quic")
{
writer.Key("QUICSecure");
writer.String(host.data());
writer.Key("QUICSecret");
writer.String(path.data());
}
writer.Key("FakeType");
writer.String(type.data());
}
writer.Key("TLSSecure");
writer.Bool(tls == "tls");
writer.EndObject();
return sb.GetString();
}
std::string ssrConstruct(std::string group, std::string remarks, std::string remarks_base64, std::string server, std::string port, std::string protocol, std::string method, std::string obfs, std::string password, std::string obfsparam, std::string protoparam, int local_port, bool libev)
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
writer.StartObject();
writer.Key("Type");
writer.String("SSR");
writer.Key("Remark");
writer.String(remarks.data());
writer.Key("Hostname");
writer.String(server.data());
writer.Key("Port");
writer.Int(shortAssemble((unsigned short)stoi(port), (unsigned short)local_port));
writer.Key("Password");
writer.String(password.data());
writer.Key("EncryptMethod");
writer.String(method.data());
writer.Key("Protocol");
writer.String(protocol.data());
writer.Key("ProtocolParam");
writer.String(protoparam.data());
writer.Key("OBFS");
writer.String(obfs.data());
writer.Key("OBFSParam");
writer.String(obfsparam.data());
writer.EndObject();
return sb.GetString();
}
std::string ssConstruct(std::string server, std::string port, std::string password, std::string method, std::string plugin, std::string pluginopts, std::string remarks, int local_port, bool libev)
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
writer.StartObject();
writer.Key("Type");
writer.String("SS");
writer.Key("Remark");
writer.String(remarks.data());
writer.Key("Hostname");
writer.String(server.data());
writer.Key("Port");
writer.Int(shortAssemble((unsigned short)stoi(port), (unsigned short)local_port));
writer.Key("Password");
writer.String(password.data());
writer.Key("EncryptMethod");
writer.String(method.data());
writer.Key("Plugin");
writer.String(plugin.data());
writer.Key("PluginOption");
writer.String(pluginopts.data());
writer.EndObject();
return sb.GetString();
}
std::string nodeRename(std::string remark)
{
string_array vArray;
for(std::string &x : renames)
{
vArray = split(x, "@");
if(vArray.size() == 1)
{
remark = regReplace(remark, vArray[0], "");
}
else if(vArray.size() == 2)
{
remark = regReplace(remark, vArray[0], vArray[1]);
}
else
continue;
}
return remark;
}
std::string removeEmoji(std::string remark)
{
if(!remove_old_emoji)
return remark;
char emoji_id[2] = {(char)-16, (char)-97};
while(true)
{
if(remark[0] == emoji_id[0] && remark[1] == emoji_id[1])
remark = remark.substr(4);
else
break;
}
return remark;
}
std::string addEmoji(std::string remark)
{
if(!add_emoji)
return remark;
string_array vArray;
for(std::string &x : emojis)
{
vArray = split(x, ",");
if(vArray.size() != 2)
continue;
if(regFind(remark, vArray[0]))
{
remark = vArray[1] + " " + remark;
break;
}
}
return remark;
}
void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset_content_array)
{
string_array allRules, vArray;
std::string rule_group, rule_path, retrived_rules, strLine;
std::stringstream strStrm;
YAML::Node Rules;
if(!overwrite_original_rules)
Rules = base_rule["Rule"];
for(ruleset_content &x : ruleset_content_array)
{
rule_group = x.rule_group;
retrived_rules = x.rule_content;
if(retrived_rules.find("[]") == 0)
{
allRules.emplace_back(retrived_rules.substr(2) + "," + rule_group);
continue;
}
char delimiter = count(retrived_rules.begin(), retrived_rules.end(), '\n') <= 1 ? '\r' : '\n';
strStrm.clear();
strStrm<<retrived_rules;
while(getline(strStrm, strLine, delimiter))
{
strLine = replace_all_distinct(strLine, "\r", ""); //remove line break
if(!strLine.size() || strLine.find("#") == 0 || strLine.find(";") == 0) //remove comments
continue;
if(strLine.find("USER-AGENT") == 0 || strLine.find("URL-REGEX") == 0 || strLine.find("PROCESS-NAME") == 0) //remove unsupported types
continue;
if(strLine.find("IP-CIDR") == 0)
strLine = replace_all_distinct(strLine, ",no-resolve", "");
else if(strLine.find("DOMAIN-SUFFIX") == 0)
strLine = replace_all_distinct(strLine, ",force-remote-dns", "");
strLine += "," + rule_group;
allRules.emplace_back(strLine);
}
}
for(std::string &x : allRules)
{
Rules.push_back(x);
}
base_rule["Rule"] = Rules;
}
std::string netchToClash(std::vector<nodeInfo> &nodes, std::string &baseConf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, bool clashR)
{
YAML::Node yamlnode, proxies, singleproxy, singlegroup, original_groups;
rapidjson::Document json;
std::string type, remark, hostname, port, username, password, method;
std::string plugin, pluginopts;
std::string protocol, protoparam, obfs, obfsparam;
std::string id, aid, transproto, faketype, host, path, quicsecure, quicsecret;
std::vector<std::string> nodelist;
bool tlssecure, replace_flag;
string_array vArray, filtered_nodelist;
try
{
yamlnode = YAML::Load(baseConf);
}
catch (std::exception &e)
{
return std::string();
}
for(nodeInfo &x : nodes)
{
singleproxy.reset();
json.Parse(x.proxyStr.data());
type = GetMember(json, "Type");
remark = addEmoji(trim(removeEmoji(nodeRename(x.remarks))));
hostname = GetMember(json, "Hostname");
port = GetMember(json, "Port");
username = GetMember(json, "Username");
password = GetMember(json, "Password");
method = GetMember(json, "EncryptMethod");
if(type == "SS")
{
plugin = GetMember(json, "Plugin");
pluginopts = replace_all_distinct(GetMember(json, "PluginOption"), ";", "&");
singleproxy["type"] = "ss";
singleproxy["cipher"] = method;
if(plugin == "simple-obfs" || plugin == "obfs-local")
{
singleproxy["plugin"] = "obfs";
singleproxy["plugin-opts"]["mode"] = UrlDecode(getUrlArg(pluginopts, "obfs"));
singleproxy["plugin-opts"]["host"] = UrlDecode(getUrlArg(pluginopts, "obfs-host"));
}
}
else if(type == "VMess")
{
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";
singleproxy["type"] = "vmess";
singleproxy["uuid"] = id;
singleproxy["alterId"] = stoi(aid);
singleproxy["cipher"] = method;
singleproxy["tls"] = tlssecure;
if(transproto == "ws")
{
singleproxy["network"] = transproto;
singleproxy["ws-path"] = path;
singleproxy["ws-headers"]["Host"] = host;
}
else if(transproto == "kcp" || transproto == "h2" || transproto == "quic")
continue;
}
else if(type == "SSR" && clashR)
{
protocol = GetMember(json, "Protocol");
protoparam = GetMember(json, "ProtocolParam");
obfs = GetMember(json, "OBFS");
obfsparam = GetMember(json, "OBFSParam");
singleproxy["type"] = "ssr";
singleproxy["cipher"] = method;
singleproxy["protocol"] = protocol;
singleproxy["protocolparam"] = protoparam;
singleproxy["obfs"] = obfs;
singleproxy["obfsparam"] = obfsparam;
}
else if(type == "Socks5")
{
singleproxy["type"] = "socks";
singleproxy["username"] = username;
}
else if(type == "HTTP" || type == "HTTPS")
{
singleproxy["type"] = "http";
singleproxy["username"] = username;
singleproxy["tls"] = type == "HTTPS";
}
else
continue;
singleproxy["password"] = password;
singleproxy["name"] = remark;
singleproxy["server"] = hostname;
singleproxy["port"] = (unsigned short)stoi(port);
proxies.push_back(singleproxy);
nodelist.emplace_back(remark);
}
yamlnode["Proxy"] = proxies;
std::string groupname;
/*
if(!overwrite_original_groups)
{
original_groups = yamlnode["Proxy Group"];
for(unsigned int i = 0; i < original_groups.size(); i++)
{
groupname = original_groups[i]["name"].as<std::string>();
if(groupname == "Proxy" || groupname == "PROXY")
{
original_groups[i]["name"] = "Proxy";
original_groups[i]["proxies"] = vectorToNode(nodelist);
break;
}
}
}
else
{
singlegroup["name"] = "Proxy";
singlegroup["type"] = "select";
singlegroup["proxies"] = vectorToNode(nodelist);
original_groups.push_back(singlegroup);
}
*/
for(std::string &x : extra_proxy_group)
{
singlegroup.reset();
eraseElements(filtered_nodelist);
replace_flag = false;
unsigned int rules_upper_bound = 0;
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;
singlegroup["url"] = vArray[vArray.size() - 2];
singlegroup["interval"] = stoi(vArray[vArray.size() - 1]);
}
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");
singlegroup["name"] = vArray[0];
singlegroup["type"] = vArray[1];
singlegroup["proxies"] = vectorToNode(filtered_nodelist);
for(unsigned int i = 0; i < original_groups.size(); i++)
{
if(original_groups[i]["name"].as<std::string>() == vArray[0])
{
original_groups[i] = singlegroup;
replace_flag = true;
break;
}
}
if(!replace_flag)
original_groups.push_back(singlegroup);
}
yamlnode["Proxy Group"] = original_groups;
rulesetToClash(yamlnode, ruleset_content_array);
return to_string(yamlnode);
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef SUBEXPORT_H_INCLUDED
#define SUBEXPORT_H_INCLUDED
#include <string>
struct ruleset_content
{
std::string rule_group;
std::string rule_content;
};
#endif // SUBEXPORT_H_INCLUDED
+57
View File
@@ -0,0 +1,57 @@
#include <iostream>
#include <unistd.h>
#include <curl/curl.h>
#include "webget.h"
std::string user_agent_str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36";
static int writer(char *data, size_t size, size_t nmemb, std::string *writerData)
{
if(writerData == NULL)
return 0;
writerData->append(data, size*nmemb);
return size * nmemb;
}
std::string curlGet(std::string url, std::string proxy)
{
CURL *curl_handle;
std::string data;
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_VERBOSE, 0L);
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 10L);
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, user_agent_str.data());
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writer);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &data);
if(proxy != "")
curl_easy_setopt(curl_handle, CURLOPT_PROXY, proxy.data());
curl_easy_perform(curl_handle);
curl_easy_cleanup(curl_handle);
return data;
}
std::string buildSocks5ProxyString(std::string addr, int port, std::string username, std::string password)
{
std::string authstr = username != "" && password != "" ? username + ":" + password + "@" : "";
std::string proxystr = "socks5://" + authstr + addr + ":" + std::__cxx11::to_string(port);
return proxystr;
}
std::string webGet(std::string url, std::string proxy)
{
return curlGet(url, proxy);
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef WEBGET_H_INCLUDED
#define WEBGET_H_INCLUDED
#include <string>
#ifdef _WIN32
#include <ws2tcpip.h>
#endif // _WIN32
#include "misc.h"
std::string webGet(std::string url, std::string proxy = "");
std::string httpGet(std::string host, std::string addr, std::string uri);
std::string httpsGet(std::string host, std::string addr, std::string uri);
long curlPost(std::string url, std::string data, std::string proxy);
std::string buildSocks5ProxyString(std::string addr, int port, std::string username, std::string password);
#endif // WEBGET_H_INCLUDED
+376
View File
@@ -0,0 +1,376 @@
#include <iostream>
#include <fstream>
#include <map>
#include <mutex>
#include <thread>
#include <pthread.h>
#include "misc.h"
#include "socket.h"
#include "webserver.h"
typedef std::vector<std::string> string_array;
struct responseRoute
{
std::string method;
std::string path;
std::string content_type;
response_callback rc;
};
std::vector<responseRoute> responses;
//for use of multi-thread socket test
typedef std::lock_guard<std::mutex> guarded_mutex;
int working_thread = 0, max_send_failure = 10;
bool SERVER_EXIT_FLAG = false;
std::mutex working_mutex, server_exit_flag_mutex;
static inline void working_acc()
{
guarded_mutex guard(working_mutex);
working_thread++;
}
static inline void working_dec()
{
guarded_mutex guard(working_mutex);
working_thread--;
}
static inline int safe_read_working()
{
int retVal;
guarded_mutex guard(working_mutex);
retVal = working_thread;
return retVal;
}
static inline bool safe_read_server_exit_flag()
{
bool retVal;
guarded_mutex guard(server_exit_flag_mutex);
retVal = SERVER_EXIT_FLAG;
return retVal;
}
static inline void safe_set_server_exit_flag()
{
guarded_mutex guard(server_exit_flag_mutex);
SERVER_EXIT_FLAG = true;
}
int sendall(SOCKET sock, std::string data)
{
unsigned int total = 0, bytesleft = data.size();
int sent = 0;
const char* datastr = data.data();
while(total < bytesleft)
{
sent = send(sock, datastr + total, bytesleft, 0);
if(sent < 0)
{
std::cerr<<strerror(errno)<<std::endl;
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
continue;
}
else
{
break;
}
}
total += sent;
bytesleft -= sent;
}
return sent == -1 ? -1 : 0;
}
void wrong_req(SOCKET sock)
{
std::string response = "HTTP/1.1 501 Not Implemented\r\n"
"Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\n"
"Content-Type: text/plain\r\n\r\n"
"The command is not yet completed\r\n";
if (sendall(sock, response) == -1)
{
std::cerr << "Sending failed!" << std::endl;
}
}
void file_not_found(std::string arguments, int sock)
{
std::string prompt_info = "Not found: " + arguments;
std::string response = "HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/plain\r\nConnection: close\r\n"
"Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\n"
"Content-Length: " + std::__cxx11::to_string(prompt_info.size()) + "\r\n\r\n" + prompt_info + "\r\n";
if (sendall(sock, response) == -1)
{
printf("Sending error!");
return;
}
}
void send_header(SOCKET send_to, std::string content_type)
{
std::string header = "HTTP/1.1 200 OK\r\nConnection: close\r\nCache-Control: no-cache, no-store, must-revalidate\r\nAccess-Control-Allow-Origin: *\r\n";
if(content_type.size())
header += "Content-Type: " + content_type + "\r\n";
if(sendall(send_to, header) == -1)
{
printf("Sending error!");
}
}
void send_options_header(SOCKET send_to)
{
std::string header = "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\n";
if(sendall(send_to, header) == -1)
{
printf("Sending error!");
}
}
char* file_type(const char* arg)
{
char * temp;
if ((temp=strrchr(arg,'.')) != NULL)
{
return temp+1;
}
return nullptr;
}
void serve_options(SOCKET sock)
{
send_options_header(sock);
std::string extra_header = "Content-Length: 0\r\n\r\n";
sendall(sock, extra_header);
sendall(sock, "\r\n\r\n");
}
void serve_content(SOCKET sock, std::string type, std::string content)
{
send_header(sock, type.data());
std::string extra_header = "Content-Length: " + std::__cxx11::to_string(content.size()) + "\r\n";
sendall(sock, extra_header);
send(sock, "\r\n", 2, 0);
if (sendall(sock, content) == -1)
{
printf("Sending error!");
}
sendall(sock, "\r\n\r\n");
}
void send_file(std::string arguments, int sock)
{
char* extension = file_type(arguments.data());
std::string content_type = "text/plain", data;
char sizestr[16] = {};
int len;
if (strcmp(extension, "html") == 0)
{
content_type = "text/html";
}
if (strcmp(extension, "gif") == 0)
{
content_type = "image/gif";
}
if (strcmp(extension, "jpg") == 0)
{
content_type = "image/jpg";
}
send_header(sock, content_type);
sendall(sock, "Transfer-Encoding: chunked\r\n\r\n");
data = fileGet(arguments);
len = data.size();
sprintf(sizestr, "%x\r\n", len);
if (sendall(sock, sizestr) == -1)
{
printf("Sending error!");
}
if (sendall(sock, data) == -1)
{
printf("Sending error!");
}
len = 2;
sendall(sock, "\r\n");
len = 7;
sendall(sock, "0\r\n\r\n");
}
void handle_req(std::string request, int client_sock)
{
working_acc();
std::cerr<<"worker startup"<<std::endl;
string_array vArray;
char command[16] = {};
char arguments[BUFSIZ] = {};
std::string uri, args, target, postdata;
if (sscanf(request.data(), "%s%s", command, arguments) != 2)
{
goto end;
}
printf("handle_cmd: %s\n", command);
printf("handle_path: %s\n", arguments);
vArray = split(arguments, "?");
if(vArray.size() > 2)
{
wrong_req(client_sock);
goto end;
}
else if(vArray.size() > 1)
{
uri = vArray[0];
args = vArray[1];
}
else
uri = arguments;
if(strcmp(command, "POST") == 0)
{
if(request.find("\r\n\r\n") != request.npos)
postdata = request.substr(request.find("\r\n\r\n") + 4);
}
else if(strcmp(command, "OPTIONS") == 0)
{
serve_options(client_sock);
goto end;
}
for(std::vector<responseRoute>::iterator iter = responses.begin(); iter != responses.end(); iter++)
{
if(iter->method.compare(command) == 0 && iter->path == uri)
{
response_callback &rc = iter->rc;
serve_content(client_sock, iter->content_type, rc(args, postdata));
goto end;
}
}
file_not_found(uri, client_sock);
end:
std::cerr<<"worker stop"<<std::endl;
sleep(1);
closesocket(client_sock);
working_dec();
}
void append_response(std::string type, std::string request, std::string content_type, response_callback response)
{
responseRoute rr;
rr.method = type;
rr.path = request;
rr.content_type = content_type;
rr.rc = response;
responses.push_back(rr);
}
void* start_web_server(void *argv)
{
//log startup
struct listener_args *args = (listener_args*)argv;
std::string listen_address = args->listen_address, request;
int port = args->port, max_conn = args->max_conn, max_workers = args->max_workers, numbytes, worker_index = 0;
socklen_t sock_size = sizeof(struct sockaddr_in);
char buf[BUFSIZ];
struct sockaddr_in user_socket, server_addr;
SOCKET acc_socket;
int server_socket, fail_counter = 0;
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
std::thread workers[max_workers];
if (server_socket == -1)
{
//log socket error
std::cerr<<"socket build error!"<<std::endl;
return NULL;
}
ZeroMemory(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons((short)port);
server_addr.sin_addr.s_addr = inet_addr(listen_address.data());
if (::bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
{
//log bind error
std::cerr<<"socket bind error!"<<std::endl;
return NULL;
}
if (listen(server_socket, max_conn) == -1 )
{
//log listen error
std::cerr<<"socket listen error!"<<std::endl;
return NULL;
}
setTimeout(server_socket, 500);
while(true)
{
acc_socket = accept(server_socket, (struct sockaddr *)&user_socket, &sock_size); //wait for connection
if(acc_socket < 0)
{
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
fail_counter++;
if(fail_counter > max_send_failure)
break;
continue;
}
else
{
break;
}
}
request = "";
while(true) //receive the complete request
{
numbytes = recv(acc_socket, buf, BUFSIZ - 1, 0);
if(numbytes > 0) //received
request.append(buf);
if(numbytes < 0)
{
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
continue;
}
else
{
break;
}
}
if(numbytes == 0)
break;
}
//handle_req(buf, acc_socket);
while(safe_read_working() >= max_workers)
{
sleep(10);
if(safe_read_server_exit_flag())
break;
}
while(workers[worker_index].get_id() != std::thread::id())
{
worker_index++;
}
workers[worker_index] = std::thread(handle_req, request, acc_socket);
workers[worker_index].detach();
worker_index++;
if(worker_index > max_workers)
worker_index = 0;
}
return 0;
}
+20
View File
@@ -0,0 +1,20 @@
#ifndef WEBSERVER_H_INCLUDED
#define WEBSERVER_H_INCLUDED
typedef std::string (*response_callback)(std::string, std::string); //process arguments and POST data and return served-content
#define RESPONSE_CALLBACK_ARGS std::string argument, std::string postdata
struct listener_args
{
std::string listen_address;
int port;
int max_conn;
int max_workers;
};
void append_response(std::string type, std::string request, std::string content_type, response_callback response);
int start_web_server(void *argv);
int start_web_server_multi(void *argv);
#endif // WEBSERVER_H_INCLUDED
+221
View File
@@ -0,0 +1,221 @@
#include <memory>
#include <cstdint>
#include <iostream>
#include <evhttp.h>
#include <string>
#include <vector>
#include <string.h>
#include <pthread.h>
#include "misc.h"
#include "webserver.h"
#include "socket.h"
struct responseRoute
{
std::string method;
std::string path;
std::string content_type;
response_callback rc;
};
std::vector<responseRoute> responses;
static inline int process_request(const char *method_str, std::string uri, std::string &postdata, std::string &content_type, std::string &return_data)
{
std::string path, arguments;
//std::cerr << "handle_cmd: " << method_str << std::endl << "handle_uri: " << uri << std::endl;
if(strFind(uri, "?"))
{
path = uri.substr(0, uri.find("?"));
arguments = uri.substr(uri.find("?") + 1);
}
else
path = uri;
for(responseRoute &x : responses)
{
if(strcmp(method_str, "OPTIONS") == 0 && x.method == postdata && x.path == path)
{
return 1;
}
else if(x.method.compare(method_str) == 0 && x.path == path)
{
response_callback &rc = x.rc;
return_data = rc(arguments, postdata);
content_type = x.content_type;
return 0;
}
}
return -1;
}
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 = evhttp_request_get_uri(req);
int retVal;
std::string postdata, content_type, return_data;
if(EVBUFFER_LENGTH(req->input_buffer) != 0)
{
postdata.assign((char *)EVBUFFER_DATA(req->input_buffer), EVBUFFER_LENGTH(req->input_buffer));
if(req_content_type != NULL && strcmp(req_content_type, "application/x-www-form-urlencoded") == 0)
postdata = UrlDecode(postdata);
}
else if(req_ac_method != NULL)
{
postdata.assign(req_ac_method);
}
retVal = process_request(req_method, uri, postdata, content_type, return_data);
auto *OutBuf = evhttp_request_get_output_buffer(req);
if (!OutBuf)
return;
switch(retVal)
{
case 1: //found OPTIONS
evhttp_add_header(req->output_headers, "Access-Control-Allow-Origin", "*");
evhttp_add_header(req->output_headers, "Access-Control-Allow-Headers", "*");
evhttp_send_reply(req, HTTP_OK, "", NULL);
return;
break;
case 0: //found normal
if(content_type.size())
{
if(content_type == "REDIRECT")
{
evhttp_add_header(req->output_headers, "Location", return_data.c_str());
evhttp_send_reply(req, HTTP_MOVETEMP, "", NULL);
return;
}
else
evhttp_add_header(req->output_headers, "Content-Type", content_type.c_str());
}
evhttp_add_header(req->output_headers, "Access-Control-Allow-Origin", "*");
evhttp_add_header(req->output_headers, "Connection", "close");
evbuffer_add(OutBuf, return_data.data(), return_data.size());
evhttp_send_reply(req, HTTP_OK, "", OutBuf);
break;
case -1: //not found
evhttp_send_error(req, HTTP_NOTFOUND, "Resource not found");
break;
default: //undefined behavior
evhttp_send_error(req, HTTP_INTERNAL, "");
}
}
int start_web_server(void *argv)
{
struct listener_args *args = (listener_args*)argv;
std::string listen_address = args->listen_address;
int port = args->port;
if (!event_init())
{
std::cerr << "Failed to init libevent." << std::endl;
return -1;
}
const char *SrvAddress = listen_address.c_str();
std::uint16_t SrvPort = port;
std::unique_ptr<evhttp, decltype(&evhttp_free)> Server(evhttp_start(SrvAddress, SrvPort), &evhttp_free);
if (!Server)
{
std::cerr << "Failed to init http server." << std::endl;
return -1;
}
evhttp_set_allowed_methods(Server.get(), EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS);
evhttp_set_gencb(Server.get(), OnReq, nullptr);
if (event_dispatch() == -1)
{
std::cerr << "Failed to run message loop." << std::endl;
return -1;
}
return 0;
}
void* httpserver_dispatch(void *arg)
{
event_base_dispatch((struct event_base*)arg);
return NULL;
}
int httpserver_bindsocket(std::string listen_address, int listen_port, int backlog)
{
int ret;
SOCKET nfd;
nfd = socket(AF_INET, SOCK_STREAM, 0);
if (nfd < 0)
return -1;
int one = 1;
ret = setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(int));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(listen_address.data());
addr.sin_port = htons(listen_port);
ret = bind(nfd, (struct sockaddr*)&addr, sizeof(addr));
if (ret < 0)
return -1;
ret = listen(nfd, backlog);
if (ret < 0)
return -1;
unsigned long ul = 1;
ioctlsocket(nfd, FIONBIO, &ul); //set to non-blocking mode
return nfd;
}
int start_web_server_multi(void *argv)
{
struct listener_args *args = (listener_args*)argv;
std::string listen_address = args->listen_address;
int port = args->port, nthreads = args->max_workers;
int i, ret;
int nfd = httpserver_bindsocket(listen_address, port, args->max_conn);
if (nfd < 0)
return -1;
pthread_t ths[nthreads];
for (i = 0; i < nthreads; i++)
{
struct event_base *base = event_init();
if (base == NULL)
return -1;
struct evhttp *httpd = evhttp_new(base);
if (httpd == NULL)
return -1;
ret = evhttp_accept_socket(httpd, nfd);
if (ret != 0)
return -1;
evhttp_set_allowed_methods(httpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS);
evhttp_set_gencb(httpd, OnReq, nullptr);
ret = pthread_create(&ths[i], NULL, httpserver_dispatch, base);
if (ret != 0)
return -1;
}
return 0;
}
void append_response(std::string type, std::string request, std::string content_type, response_callback response)
{
responseRoute rr;
rr.method = type;
rr.path = request;
rr.content_type = content_type;
rr.rc = response;
responses.emplace_back(rr);
}
+14
View File
@@ -0,0 +1,14 @@
#include <string>
#include <vector>
#include <yaml-cpp/yaml.h>
#include "misc.h"
YAML::Node vectorToNode(string_array data)
{
YAML::Node node;
for(std::string &x : data)
node.push_back(x);
return node;
}