Add support for WireGuard nodes in Clash, Surge and Loon configs

This commit is contained in:
Tindy X
2023-10-16 04:12:41 +08:00
parent e7380d8a9c
commit 80c2a968d1
7 changed files with 255 additions and 20 deletions

View File

@@ -444,6 +444,20 @@ void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const ProxyGr
if(std::all_of(x.Password.begin(), x.Password.end(), ::isdigit) && !x.Password.empty())
singleproxy["password"].SetTag("str");
break;
case ProxyType::WireGuard:
singleproxy["type"] = "wireguard";
singleproxy["public-key"] = x.PublicKey;
singleproxy["private-key"] = x.PrivateKey;
singleproxy["ip"] = x.SelfIP;
if(!x.SelfIPv6.empty())
singleproxy["ipv6"] = x.SelfIPv6;
if(!x.PreSharedKey.empty())
singleproxy["preshared-key"] = x.PreSharedKey;
if(!x.DnsServers.empty())
singleproxy["dns"] = x.DnsServers;
if(x.Mtu > 0)
singleproxy["mtu"] = x.Mtu;
break;
default:
continue;
}
@@ -595,6 +609,24 @@ std::string proxyToClash(std::vector<Proxy> &nodes, const std::string &base_conf
return output_content;
}
// peer = (public-key = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=, allowed-ips = "0.0.0.0/0, ::/0", endpoint = engage.cloudflareclient.com:2408, client-id = 139/184/125),(public-key = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=, endpoint = engage.cloudflareclient.com:2408)
std::string generatePeer(Proxy &node, bool client_id_as_reserved = false)
{
std::string result;
result += "public-key = " + node.PublicKey;
result += ", endpoint = " + node.Hostname + ":" + std::to_string(node.Port);
if(!node.AllowedIPs.empty())
result += ", allowed-ips = \"" + node.AllowedIPs + "\"";
if(!node.ClientId.empty())
{
if(client_id_as_reserved)
result += ", reserved = [" + node.ClientId + "]";
else
result += ", client-id = " + node.ClientId;
}
return result;
}
std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<RulesetContent> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, int surge_ver, extra_settings &ext)
{
INIReader ini;
@@ -644,7 +676,7 @@ std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf
scv.define(x.AllowInsecure);
tls13.define(x.TLS13);
std::string proxy;
std::string proxy, section, real_section;
string_array args, headers;
switch(x.Type)
@@ -770,6 +802,28 @@ std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf
if(x.SnellVersion != 0)
proxy += ", version=" + std::to_string(x.SnellVersion);
break;
case ProxyType::WireGuard:
if(surge_ver < 4 && surge_ver != -3)
continue;
section = randomStr(5);
real_section = "WireGuard " + section;
proxy = "wireguard, section-name=" + section;
if(!x.TestUrl.empty())
proxy += ", test-url=" + x.TestUrl;
ini.set(real_section, "private-key", x.PrivateKey);
ini.set(real_section, "self-ip", x.SelfIP);
if(!x.SelfIPv6.empty())
ini.set(real_section, "self-ip-v6", x.SelfIPv6);
if(!x.PreSharedKey.empty())
ini.set(real_section, "preshared-key", x.PreSharedKey);
if(!x.DnsServers.empty())
ini.set(real_section, "dns-server", join(x.DnsServers, ","));
if(x.Mtu > 0)
ini.set(real_section, "mtu", std::to_string(x.Mtu));
if(x.KeepAlive > 0)
ini.set(real_section, "keepalive", std::to_string(x.KeepAlive));
ini.set(real_section, "peer", "(" + generatePeer(x) + ")");
break;
default:
continue;
}
@@ -1866,6 +1920,24 @@ std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf,
proxy += ",skip-cert-verify=" + std::string(scv.get() ? "true" : "false");
}
break;
case ProxyType::WireGuard:
proxy = "wireguard, interface-ip=" + x.SelfIP;
if(!x.SelfIPv6.empty())
proxy += ", interface-ipv6=" + x.SelfIPv6;
proxy += ", private-key=" + x.PrivateKey;
for(const auto &y : x.DnsServers)
{
if(isIPv4(y))
proxy += ", dns=" + y;
else if(isIPv6(y))
proxy += ", dnsv6=" + y;
}
if(x.Mtu > 0)
proxy += ", mtu=" + std::to_string(x.Mtu);
if(x.KeepAlive > 0)
proxy += ", keepalive=" + std::to_string(x.KeepAlive);
proxy += ", peers=[{" + generatePeer(x, true) + "}]";
break;
default:
continue;
}

View File

@@ -2,10 +2,12 @@
#define PROXY_H_INCLUDED
#include <string>
#include <vector>
#include "../../utils/tribool.h"
using String = std::string;
using StringArray = std::vector<String>;
enum ProxyType
{
@@ -17,7 +19,8 @@ enum ProxyType
Snell,
HTTP,
HTTPS,
SOCKS5
SOCKS5,
WireGuard
};
inline String getProxyTypeName(int type)
@@ -84,6 +87,18 @@ struct Proxy
uint16_t SnellVersion = 0;
String ServerName;
String SelfIP;
String SelfIPv6;
String PublicKey;
String PrivateKey;
String PreSharedKey;
StringArray DnsServers;
uint16_t Mtu = 0;
String AllowedIPs = "0.0.0.0/0, ::/0";
uint16_t KeepAlive = 0;
String TestUrl;
String ClientId;
};
#define SS_DEFAULT_GROUP "SSProvider"
@@ -93,5 +108,6 @@ struct Proxy
#define HTTP_DEFAULT_GROUP "HTTPProvider"
#define TROJAN_DEFAULT_GROUP "TrojanProvider"
#define SNELL_DEFAULT_GROUP "SnellProvider"
#define WG_DEFAULT_GROUP "WireGuardProvider"
#endif // PROXY_H_INCLUDED

View File

@@ -115,6 +115,20 @@ void snellConstruct(Proxy &node, const std::string &group, const std::string &re
node.SnellVersion = version;
}
void wireguardConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &selfIp, const std::string &selfIpv6, const std::string &privKey, const std::string &pubKey, const std::string &psk, const string_array &dns, const std::string &mtu, const std::string &keepalive, const std::string &testUrl, const std::string &clientId, const tribool &udp) {
commonConstruct(node, ProxyType::WireGuard, group, remarks, server, port, udp, tribool(), tribool(), tribool());
node.SelfIP = selfIp;
node.SelfIPv6 = selfIpv6;
node.PrivateKey = privKey;
node.PublicKey = pubKey;
node.PreSharedKey = psk;
node.DnsServers = dns;
node.Mtu = to_int(mtu);
node.KeepAlive = to_int(keepalive);
node.TestUrl = testUrl;
node.ClientId = clientId;
}
void explodeVmess(std::string vmess, Proxy &node)
{
std::string version, ps, add, port, type, id, aid, net, path, host, tls, sni;
@@ -953,6 +967,8 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes)
std::string plugin, pluginopts, pluginopts_mode, pluginopts_host, pluginopts_mux; //ss
std::string protocol, protoparam, obfs, obfsparam; //ssr
std::string user; //socks
std::string ip, ipv6, private_key, public_key, mtu; //wireguard
string_array dns_server;
tribool udp, tfo, scv;
Node singleproxy;
uint32_t index = nodes.size();
@@ -1151,6 +1167,18 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes)
snellConstruct(node, group, ps, server, port, password, obfs, host, to_int(aid, 0), udp, tfo, scv);
break;
case "wireguard"_hash:
group = WG_DEFAULT_GROUP;
singleproxy["public-key"] >>= public_key;
singleproxy["private-key"] >>= private_key;
singleproxy["dns"] >>= dns_server;
singleproxy["mtu"] >>= mtu;
singleproxy["preshared-key"] >>= password;
singleproxy["ip"] >>= ip;
singleproxy["ipv6"] >>= ipv6;
wireguardConstruct(node, group, ps, server, port, ip, ipv6, private_key, public_key, password, dns_server, mtu, "0", "", "", udp);
break;
default:
continue;
}
@@ -1288,6 +1316,41 @@ void explodeKitsunebi(std::string kit, Proxy &node)
vmessConstruct(node, V2RAY_DEFAULT_GROUP, remarks, add, port, type, id, aid, net, cipher, path, host, "", tls, "");
}
// peer = (public-key = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=, allowed-ips = "0.0.0.0/0, ::/0", endpoint = engage.cloudflareclient.com:2408, client-id = 139/184/125),(public-key = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=, endpoint = engage.cloudflareclient.com:2408)
void parsePeers(Proxy &node, const std::string &data)
{
auto peers = regGetAllMatch(data, R"(\((.*?)\))", true);
if(peers.empty())
return;
auto peer = peers[0];
auto peerdata = regGetAllMatch(peer, R"(([a-z-]+) ?= ?([^" ),]+|".*?"),? ?)", true);
if(peerdata.size() % 2 != 0)
return;
for(size_t i = 0; i < peerdata.size(); i += 2)
{
auto key = peerdata[i];
auto val = peerdata[i + 1];
switch(hash_(key))
{
case "public-key"_hash:
node.PublicKey = val;
break;
case "endpoint"_hash:
node.Hostname = val.substr(0, val.rfind(':'));
node.Port = to_int(val.substr(val.rfind(':') + 1));
break;
case "client-id"_hash:
node.ClientId = val;
break;
case "allowed-ips"_hash:
node.AllowedIPs = trimOf(val, '"');
break;
default:
break;
}
}
}
bool explodeSurge(std::string surge, std::vector<Proxy> &nodes)
{
std::multimap<std::string, std::string> proxies;
@@ -1303,7 +1366,6 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes)
ini.keep_empty_section = false;
ini.allow_dup_section_titles = true;
ini.set_isolated_items_section("Proxy");
ini.include_section("Proxy");
ini.add_direct_save_section("Proxy");
if(surge.find("[Proxy]") != surge.npos)
surge = regReplace(surge, R"(^[\S\s]*?\[)", "[", false);
@@ -1322,6 +1384,9 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes)
std::string plugin, pluginopts, pluginopts_mode, pluginopts_host, mod_url, mod_md5; //ss
std::string id, net, tls, host, edge, path; //v2
std::string protocol, protoparam; //ssr
std::string section, ip, ipv6, private_key, public_key, mtu, test_url, client_id, peer, keepalive; //wireguard
string_array dns_servers;
string_multimap wireguard_config;
std::string version, aead = "1";
std::string itemName, itemVal, config;
std::vector<std::string> configs, vArray, headers, header;
@@ -1660,6 +1725,65 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes)
snellConstruct(node, SNELL_DEFAULT_GROUP, remarks, server, port, password, plugin, host, to_int(version, 0), udp, tfo, scv);
break;
case "wireguard"_hash:
for (i = 1; i < configs.size(); i++)
{
vArray = split(trim(configs[i]), "=");
if(vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
itemVal = trim(vArray[1]);
switch(hash_(itemName))
{
case "section-name"_hash:
section = itemVal;
break;
case "test-url"_hash:
test_url = itemVal;
break;
}
}
if(section.empty())
continue;
ini.get_items("WireGuard " + section, wireguard_config);
if(wireguard_config.empty())
continue;
for (auto &c : wireguard_config)
{
itemName = trim(c.first);
itemVal = trim(c.second);
switch(hash_(itemName))
{
case "self-ip"_hash:
ip = itemVal;
break;
case "self-ip-v6"_hash:
ipv6 = itemVal;
break;
case "private-key"_hash:
private_key = itemVal;
break;
case "dns-server"_hash:
vArray = split(itemVal, ",");
for (auto &y : vArray)
dns_servers.emplace_back(trim(y));
break;
case "mtu"_hash:
mtu = itemVal;
break;
case "peer"_hash:
peer = itemVal;
break;
case "keepalive"_hash:
keepalive = itemVal;
break;
}
}
wireguardConstruct(node, WG_DEFAULT_GROUP, remarks, "", "0", ip, ipv6, private_key, "", "", dns_servers, mtu, keepalive, test_url, "", udp);
parsePeers(node, peer);
break;
default:
switch(hash_(remarks))
{

View File

@@ -10,6 +10,8 @@
using jp = jpcre2::select<char>;
//#endif // USE_STD_REGEX
#include "regexp.h"
/*
#ifdef USE_STD_REGEX
bool regValid(const std::string &reg)
@@ -164,34 +166,54 @@ bool regValid(const std::string &reg)
}
int regGetMatch(const std::string &src, const std::string &match, size_t group_count, ...)
{
auto result = regGetAllMatch(src, match, false);
if(result.empty())
return -1;
va_list vl;
va_start(vl, group_count);
size_t index = 0;
while(group_count)
{
std::string* arg = va_arg(vl, std::string*);
if(arg != nullptr)
*arg = std::move(result[index]);
index++;
group_count--;
if(result.size() <= index)
break;
}
va_end(vl);
return 0;
}
std::vector<std::string> regGetAllMatch(const std::string &src, const std::string &match, bool group_only)
{
jp::Regex reg;
reg.setPattern(match).addModifier("m").addPcre2Option(PCRE2_UTF|PCRE2_ALT_BSUX).compile();
jp::VecNum vec_num;
jp::RegexMatch rm;
size_t count = rm.setRegexObject(&reg).setSubject(src).setNumberedSubstringVector(&vec_num).setModifier("g").match();
std::vector<std::string> result;
if(!count)
return -1;
va_list vl;
va_start(vl, group_count);
size_t index = 0, match_index = 0;
while(group_count)
return result;
size_t begin = 0;
if(group_only)
begin = 1;
size_t index = begin, match_index = 0;
while(true)
{
std::string* arg = va_arg(vl, std::string*);
if(arg != NULL)
*arg = std::move(vec_num[match_index][index]);
index++;
group_count--;
if(vec_num.size() <= match_index)
break;
if(vec_num[match_index].size() <= index)
{
match_index++;
index = 0;
index = begin;
}
if(vec_num.size() <= match_index)
break;
result.push_back(std::move(vec_num[match_index][index]));
index++;
}
va_end(vl);
return 0;
return result;
}
//#endif // USE_STD_REGEX

View File

@@ -8,6 +8,7 @@ bool regFind(const std::string &src, const std::string &match);
std::string regReplace(const std::string &src, const std::string &match, const std::string &rep, bool global = true, bool multiline = true);
bool regMatch(const std::string &src, const std::string &match);
int regGetMatch(const std::string &src, const std::string &match, size_t group_count, ...);
std::vector<std::string> regGetAllMatch(const std::string &src, const std::string &match, bool group_only = false);
std::string regTrim(const std::string &src);
#endif // REGEXP_H_INCLUDED

View File

@@ -354,7 +354,7 @@ bool isStrUTF8(const std::string &data)
return true;
}
std::string randomStr(const int len)
std::string randomStr(int len)
{
std::string retData;
srand(time(NULL));

View File

@@ -33,7 +33,7 @@ std::string trim(const std::string& str, bool before = true, bool after = true);
std::string trimQuote(const std::string &str, bool before = true, bool after = true);
void trimSelfOf(std::string &str, char target, bool before = true, bool after = true);
std::string trimWhitespace(const std::string &str, bool before = false, bool after = true);
std::string randomStr(const int len);
std::string randomStr(int len);
bool isStrUTF8(const std::string &data);
void removeUTF8BOM(std::string &data);