WIP: Test - Update addon #9

Closed
Sirherobrine23 wants to merge 1 commits from new_set_config into main
13 changed files with 325 additions and 360 deletions

@ -2,20 +2,19 @@
node_modules/
/*.tgz
# Addon
build/**/*
!build/**/*.node
# Typescript
src/**/*_test.*
src/**/*test*
src/**/*.ts
!src/**/*.d.ts
# vscode
.devcontainer/
.vscode/
.vscode-ctags
# Github and Git
# Github, Gitlab, Git
.github/
.git*
# Project
.vscode-ctags*
*.addrs.json

64
.vscode/settings.json vendored

@ -23,8 +23,70 @@
"PATH": "${workspaceFolder}/node_modules/.bin:${env:PATH}"
},
"files.associations": {
"*.dsc": "ini",
"*.gyp": "python",
"random": "cpp",
"limits": "cpp",
"xstring": "cpp"
"xstring": "cpp",
"__bit_reference": "cpp",
"__bits": "cpp",
"__config": "cpp",
"__debug": "cpp",
"__errc": "cpp",
"__hash_table": "cpp",
"__locale": "cpp",
"__mutex_base": "cpp",
"__node_handle": "cpp",
"__nullptr": "cpp",
"__split_buffer": "cpp",
"__string": "cpp",
"__threading_support": "cpp",
"__tree": "cpp",
"__tuple": "cpp",
"array": "cpp",
"atomic": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"exception": "cpp",
"initializer_list": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"locale": "cpp",
"map": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"optional": "cpp",
"ostream": "cpp",
"ratio": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"string": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"variant": "cpp",
"vector": "cpp"
}
}

@ -1,15 +1,16 @@
#include <napi.h>
#include <wginterface.hh>
unsigned long maxName() {
unsigned long SetupAddon::maxName() {
return 16;
}
std::string versionDrive() {
std::string SetupAddon::versionDrive() {
return "Userspace";
}
void listDevices::Execute() {}
void deleteInterface::Execute() {}
void setConfig::Execute() {}
void getConfig::Execute() {}
void ConfigBase::getWireguardConfig() {}
void ConfigBase::setWireguardConfig() {}

@ -33,11 +33,11 @@ std::string getKernelMesage(int errStatus) {
return message;
}
unsigned long maxName() {
unsigned long SetupAddon::maxName() {
return IFNAMSIZ;
}
std::string versionDrive() {
std::string SetupAddon::versionDrive() {
return "Kernel";
}
@ -67,22 +67,17 @@ class List {
void listDevices::Execute() {
List l;
try {
l.getAll();
for (auto ifname : l.devs) {
listInfo setInfo;
setInfo.tunType = "kernel";
deviceNames[ifname] = setInfo;
}
} catch (std::string err) {
SetError(err);
l.getAll();
for (auto ifname : l.devs) {
listInfo setInfo;
setInfo.tunType = "kernel";
deviceNames[ifname] = setInfo;
}
l.~List();
}
void deleteInterface::Execute() {
int status = wg_del_device(wgName.c_str());
if (status < 0) SetError(std::string("Cannot delete interface, code status: ").append(std::to_string(status)));
if (status < 0) throw std::string("Cannot delete interface, code status: ").append(std::to_string(status));
}
int createInterface(std::string &wgName) {
@ -102,11 +97,10 @@ int createInterface(std::string &wgName) {
return 0;
}
void setConfig::Execute() {
void ConfigBase::setWireguardConfig() {
int res = createInterface(wgName);
if (res < 0) {
SetError(std::string("Cannot create wireguard interface, Code: ").append(std::to_string(res)));
return;
throw std::string("Cannot create wireguard interface, Code: ").append(std::to_string(res));
}
// Set device struct
@ -139,7 +133,7 @@ void setConfig::Execute() {
if (replacePeers) deviceStruct->flags = (wg_device_flags)(deviceStruct->flags|WGDEVICE_REPLACE_PEERS);
unsigned int peerIndex = 0;
for (auto it = peersVector.begin(); it != peersVector.end(); ++it) {
for (auto it = this->Peers.begin(); it != this->Peers.end(); ++it) {
const std::string peerPubKey = it->first;
auto peerConfig = it->second;
peerIndex++;
@ -175,13 +169,13 @@ void setConfig::Execute() {
end = strchr(Endpoint, ']');
if (!end) {
free(Endpoint);
SetError("Unable to find matching brace of endpoint");
throw std::string("Unable to find matching brace of endpoint");
return;
}
*end++ = '\0';
if (*end++ != ':' || !*end) {
free(Endpoint);
SetError("Unable to find port of endpoint");
throw std::string("Unable to find port of endpoint");
return;
}
} else {
@ -189,7 +183,7 @@ void setConfig::Execute() {
end = strrchr(Endpoint, ':');
if (!end || !*(end + 1)) {
free(Endpoint);
SetError("Unable to find port of endpoint");
throw std::string("Unable to find port of endpoint");
return;
}
*end++ = '\0';
@ -211,7 +205,7 @@ void setConfig::Execute() {
(retries >= 0 && !retries--)) {
free(Endpoint);
fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), peerConfig.endpoint.c_str());
SetError("Unable to resolve endpoint");
throw std::string("Unable to resolve endpoint");
return;
}
fprintf(stderr, "%s: `%s'. Trying again in %.2f seconds...\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), peerConfig.endpoint.c_str(), timeout / 1000000.0);
@ -232,7 +226,7 @@ void setConfig::Execute() {
} else {
freeaddrinfo(resolved);
free(Endpoint);
SetError("Neither IPv4 nor IPv6 address found");
throw std::string("Neither IPv4 nor IPv6 address found");
return;
}
freeaddrinfo(resolved);
@ -275,7 +269,7 @@ void setConfig::Execute() {
}
// Set interface config
if ((res = wg_set_device(deviceStruct)) < 0) SetError(getKernelMesage(res));
if ((res = wg_set_device(deviceStruct)) < 0) throw std::string(getKernelMesage(res));
}
const char* getHostAddress(bool addPort, const sockaddr* addr) {
@ -304,10 +298,10 @@ std::string keyTo64(const uint8_t *key) {
return strKey;
}
void getConfig::Execute() {
void ConfigBase::getWireguardConfig() {
int res; wg_device *device;
if ((res = wg_get_device(&device, strdup(wgName.c_str()))) < 0) {
SetError(std::string("Device not exists or cannot get config from this interface!, code error: ").append(std::to_string(res)));
throw std::string("Device not exists or cannot get config from this interface!, code error: ").append(std::to_string(res));
return;
}
@ -348,6 +342,6 @@ void getConfig::Execute() {
}
}
peersVector[keyTo64(peer->public_key)] = PeerConfig;
this->Peers[keyTo64(peer->public_key)] = PeerConfig;
}
}

@ -43,10 +43,6 @@ static WIREGUARD_SET_ADAPTER_STATE_FUNC *WireGuardSetAdapterState;
static WIREGUARD_GET_CONFIGURATION_FUNC *WireGuardGetConfiguration;
static WIREGUARD_SET_CONFIGURATION_FUNC *WireGuardSetConfiguration;
unsigned long maxName() {
return IFNAMSIZ;
}
std::string getErrorString(DWORD errorMessageID) {
if (errorMessageID == 0 || errorMessageID < 0) std::string("Error code: ").append(std::to_string(errorMessageID));
LPSTR messageBuffer = nullptr;
@ -60,7 +56,7 @@ std::string getErrorString(DWORD errorMessageID) {
return std::string("Error code: ").append(std::to_string(errorMessageID)).append(", Message: ").append(message);
}
std::string startAddon(const Napi::Env env, Napi::Object exports) {
std::string SetupAddon::startAddon(const Napi::Env env, Napi::Object exports) {
if (!IsRunAsAdmin()) return "Run nodejs with administrator privilegies";
auto DLLPATH = exports.Get("WIN32DLLPATH");
if (!(DLLPATH.IsString())) return "Require WIREGUARD_DLL_PATH in addon load!";
@ -81,7 +77,11 @@ std::string startAddon(const Napi::Env env, Napi::Object exports) {
return "";
}
std::string versionDrive() {
unsigned long SetupAddon::maxName() {
return IFNAMSIZ;
}
std::string SetupAddon::versionDrive() {
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardCreateAdapter(L"getWgVersion", L"Wireguard-tools.js", NULL);
DWORD Version = WireGuardGetRunningDriverVersion();
if (Version == 0) {
@ -116,11 +116,13 @@ void listDevices::Execute() {
deviceNames[std::string(iface)] = setInfo;
} while (FindNextFile(find_handle, &find_data));
FindClose(find_handle);
if (ret < 0) return SetError(std::string("Erro code: ").append(std::to_string(ret)));
if (ret < 0) {
throw std::string(std::string("Erro code: ").append(std::to_string(ret)));
}
}
HDEVINFO dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, L"SWD\\WireGuard", NULL, DIGCF_PRESENT, NULL, NULL, NULL);
if (dev_info == INVALID_HANDLE_VALUE) return SetError("Cannot get devices");
if (dev_info == INVALID_HANDLE_VALUE) throw std::string("Cannot get devices");
for (DWORD i = 0;; ++i) {
DWORD buf_len;
@ -161,8 +163,8 @@ void listDevices::Execute() {
void deleteInterface::Execute() {
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(wgName));
if (!Adapter) return SetError("This interface not exists in Wireguard-Tools.js addon!");
if (!(WireGuardSetAdapterState(Adapter, WIREGUARD_ADAPTER_STATE::WIREGUARD_ADAPTER_STATE_DOWN))) return SetError(std::string("Failed to set down interface, ").append(getErrorString(GetLastError())));
if (!Adapter) throw std::string("This interface not exists in Wireguard-Tools.js addon!");
if (!(WireGuardSetAdapterState(Adapter, WIREGUARD_ADAPTER_STATE::WIREGUARD_ADAPTER_STATE_DOWN))) throw std::string("Failed to set down interface, ").append(getErrorString(GetLastError()));
WireGuardCloseAdapter(Adapter);
}
@ -178,15 +180,16 @@ template <typename T, typename C> C* changePoint(T *x) {
return reinterpret_cast<C*>(((char*)x) + sizeof(T));
}
void getConfig::Execute() {
void ConfigBase::getWireguardConfig() {
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(wgName));
if (!Adapter) return SetError("This interface not exists in Wireguard-Tools.js addon!");
if (!Adapter) throw std::string("This interface not exists in Wireguard-Tools.js addon!");
NET_LUID InterfaceLuid;
WireGuardGetAdapterLUID(Adapter, &InterfaceLuid);
try {
for (auto aip : getIpAddr(InterfaceLuid)) Address.push_back(aip);
} catch (std::string err) {
return SetError(err);
throw std::string(err);
}
DWORD buf_len = 0;
@ -194,9 +197,9 @@ void getConfig::Execute() {
while (!(WireGuardGetConfiguration(Adapter, wg_iface, &buf_len))) {
free(wg_iface);
if (GetLastError() != ERROR_MORE_DATA) return SetError((std::string("Failed get interface config, code: ")).append(std::to_string(GetLastError())));
if (GetLastError() != ERROR_MORE_DATA) throw std::string("Failed get interface config, code: ").append(std::to_string(GetLastError()));
wg_iface = (WIREGUARD_INTERFACE *)malloc(buf_len);
if (!wg_iface) return SetError(std::string("Failed get interface config, ").append(std::to_string(-errno)));
if (!wg_iface) throw std::string("Failed get interface config, ").append(std::to_string(-errno));
}
if (wg_iface->Flags & WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_PRIVATE_KEY) privateKey = wgKeys::toString(wg_iface->PrivateKey);
@ -231,24 +234,24 @@ void getConfig::Execute() {
++wg_aip;
}
wg_peer = reinterpret_cast<WIREGUARD_PEER*>(wg_aip);
peersVector[pubKey] = peerConfig;
this->Peers[pubKey] = peerConfig;
}
free(wg_iface);
}
void setConfig::Execute() {
void ConfigBase::setWireguardConfig() {
DWORD buf_len = sizeof(WIREGUARD_INTERFACE);
for (auto peer : peersVector) {
if (DWORD_MAX - buf_len < sizeof(WIREGUARD_PEER)) return SetError("Buffer overflow");
for (auto peer : this->Peers) {
if (DWORD_MAX - buf_len < sizeof(WIREGUARD_PEER)) throw std::string("Buffer overflow");
buf_len += sizeof(WIREGUARD_PEER);
for (auto aip : peer.second.allowedIPs) {
if (DWORD_MAX - buf_len < sizeof(WIREGUARD_ALLOWED_IP)) return SetError("Buffer overflow");
if (DWORD_MAX - buf_len < sizeof(WIREGUARD_ALLOWED_IP)) throw std::string("Buffer overflow");
buf_len += sizeof(WIREGUARD_ALLOWED_IP);
}
}
WIREGUARD_INTERFACE *wg_iface = reinterpret_cast<WIREGUARD_INTERFACE*>(calloc(1, buf_len));
if (!wg_iface) return SetError("Cannot alloc buff");
if (!wg_iface) throw std::string("Cannot alloc buff");
wg_iface->PeersCount = 0;
wgKeys::stringToKey(wg_iface->PrivateKey, privateKey);
@ -261,13 +264,13 @@ void setConfig::Execute() {
WIREGUARD_ALLOWED_IP *wg_aip;
WIREGUARD_PEER *wg_peer = changePoint<WIREGUARD_INTERFACE, WIREGUARD_PEER>(wg_iface);
for (auto __peer : peersVector) {
for (auto __peer : this->Peers) {
auto peerPublicKey = __peer.first; auto peerConfig = __peer.second;
try {
wgKeys::stringToKey(wg_peer->PublicKey, peerPublicKey);
} catch (std::string &err) {
SetError(err);
free(wg_iface);
throw std::string(err);
return;
}
wg_peer->Flags = WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PUBLIC_KEY;
@ -283,8 +286,8 @@ void setConfig::Execute() {
wgKeys::stringToKey(wg_peer->PresharedKey, peerConfig.presharedKey);
wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PRESHARED_KEY);
} catch (std::string &err) {
SetError(err);
free(wg_iface);
throw std::string(err);
return;
}
}
@ -297,8 +300,8 @@ void setConfig::Execute() {
insertEndpoint(&wg_peer->Endpoint, peerConfig.endpoint.c_str());
wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_ENDPOINT);
} catch (std::string &err) {
SetError(std::string("Cannot parse endpoint, ").append(err));
free(wg_iface);
throw std::string(std::string("Cannot parse endpoint, ").append(err));
return;
}
}
@ -327,15 +330,21 @@ void setConfig::Execute() {
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(wgName));
if (!Adapter) Adapter = WireGuardCreateAdapter(toLpcwstr(wgName), L"Wireguard-tools.js", NULL);
if (!Adapter) SetError(std::string("Failed to create adapter, ").append(getErrorString(GetLastError())));
else if (!WireGuardSetConfiguration(Adapter, reinterpret_cast<WIREGUARD_INTERFACE*>(wg_iface), buf_len)) {
if (!Adapter) {
free(wg_iface);
throw std::string(std::string("Failed to create adapter, ").append(getErrorString(GetLastError())));
} else if (!WireGuardSetConfiguration(Adapter, reinterpret_cast<WIREGUARD_INTERFACE*>(wg_iface), buf_len)) {
free(wg_iface);
auto status = GetLastError();
SetError(std::string("Failed to set interface config, ").append(getErrorString(status)));
auto err = std::string(std::string("Failed to set interface config, ").append(getErrorString(status)));
WireGuardCloseAdapter(Adapter);
throw err;
} else if (!WireGuardSetAdapterState(Adapter, WIREGUARD_ADAPTER_STATE::WIREGUARD_ADAPTER_STATE_UP)) {
free(wg_iface);
auto status = GetLastError();
SetError(std::string("Failed to set interface up, ").append(getErrorString(status)));
auto err = std::string(std::string("Failed to set interface up, ").append(getErrorString(status)));
WireGuardCloseAdapter(Adapter);
throw err;
} else {
if (Address.size() > 0) {
std::string IPv4, IPv6;
@ -355,7 +364,10 @@ void setConfig::Execute() {
NET_LUID InterfaceLuid;
WireGuardGetAdapterLUID(Adapter, &InterfaceLuid);
auto setStatus = insertIpAddr(InterfaceLuid, IPv4, IPv6);
if (setStatus.size() > 0) SetError(setStatus);
if (setStatus.size() > 0) {
free(wg_iface);
throw std::string(setStatus);
}
}
}
}

@ -5,34 +5,28 @@
Napi::Object Init(Napi::Env initEnv, Napi::Object exports) {
/// Call Addon
#ifdef ONSTARTADDON
auto status = startAddon(initEnv, exports);
auto status = SetupAddon::startAddon(initEnv, exports);
if (status.length() >= 1) {
Napi::Error::New(initEnv, status).ThrowAsJavaScriptException();
return exports;
}
#endif
// Wireguard constants set
const Napi::Object constants = Napi::Object::New(initEnv);
// Init Objects
const Napi::Object Constant = Napi::Object::New(initEnv), Userspace = Napi::Object::New(initEnv);
// Set wireguard version if present
constants.Set("driveVersion", versionDrive());
const auto wgVersion = SetupAddon::versionDrive();
if (wgVersion.length() > 0) Constant.Set("driveVersion", wgVersion);
else Constant.Set("driveVersion", initEnv.Null());
// Wireguard max name length
constants.Set("nameLength", maxName());
Constant.Set("nameLength", SetupAddon::maxName());
constants.Set("base64Length", B64_WG_KEY_LENGTH);
constants.Set("keyLength", WG_KEY_LENGTH);
// Set addon constants
exports.Set("constants", constants);
// Function's
#ifdef USERSPACE_GO
exports.Set("createTun", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value { return info.Env().Undefined(); }));
exports.Set("deleteTun", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value { return info.Env().Undefined(); }));
exports.Set("checkTun", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value { return info.Env().Undefined(); }));
exports.Set("getTun", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value { return info.Env().Undefined(); }));
#endif
Userspace.Set("createTun", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value { return info.Env().Undefined(); }));
Userspace.Set("deleteTun", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value { return info.Env().Undefined(); }));
Userspace.Set("checkTun", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value { return info.Env().Undefined(); }));
Userspace.Set("getTunPath", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value { return info.Env().Undefined(); }));
#ifdef SETCONFIG
exports.Set("setConfig", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value {
@ -43,7 +37,7 @@ Napi::Object Init(Napi::Env initEnv, Napi::Object exports) {
if (!(wgName.IsString())) {
Napi::Error::New(env, "Require wireguard interface name").ThrowAsJavaScriptException();
return env.Undefined();
} else if (wgName.ToString().Utf8Value().length() >= maxName()) {
} else if (wgName.ToString().Utf8Value().length() >= SetupAddon::maxName()) {
Napi::Error::New(env, "interface name is so long").ThrowAsJavaScriptException();
return env.Undefined();
} else if (!(wgConfig.IsObject())) {
@ -54,7 +48,7 @@ Napi::Object Init(Napi::Env initEnv, Napi::Object exports) {
try {
auto worker = new setConfig(env, wgName.ToString().Utf8Value(), wgConfig.ToObject());
worker->Queue();
return worker->setPromise.Promise();
return worker->Promised.Promise();
} catch (const Napi::Error &err) {
err.ThrowAsJavaScriptException();
}
@ -69,14 +63,14 @@ Napi::Object Init(Napi::Env initEnv, Napi::Object exports) {
if (!(wgName.IsString())) {
Napi::Error::New(env, "Require wireguard interface name").ThrowAsJavaScriptException();
return env.Undefined();
} else if (wgName.ToString().Utf8Value().length() >= maxName()) {
} else if (wgName.ToString().Utf8Value().length() >= SetupAddon::maxName()) {
Napi::Error::New(env, "interface name is so long").ThrowAsJavaScriptException();
return env.Undefined();
}
auto worker = new deleteInterface(env, wgName.ToString().Utf8Value());
worker->Queue();
return worker->deletePromise.Promise();
return worker->Promised.Promise();
}));
#endif
@ -87,7 +81,7 @@ Napi::Object Init(Napi::Env initEnv, Napi::Object exports) {
if (!(wgName.IsString())) {
Napi::Error::New(env, "Require wireguard interface name").ThrowAsJavaScriptException();
return env.Undefined();
} else if (wgName.ToString().Utf8Value().length() >= maxName()) {
} else if (wgName.ToString().Utf8Value().length() >= SetupAddon::maxName()) {
Napi::Error::New(env, "interface name is so long").ThrowAsJavaScriptException();
return env.Undefined();
}
@ -95,7 +89,7 @@ Napi::Object Init(Napi::Env initEnv, Napi::Object exports) {
try {
auto worker = new getConfig(env, wgName.ToString().Utf8Value());
worker->Queue();
return worker->getPromise.Promise();
return worker->Promised.Promise();
} catch (const Napi::Error &err) {
err.ThrowAsJavaScriptException();
}
@ -108,9 +102,12 @@ Napi::Object Init(Napi::Env initEnv, Napi::Object exports) {
const Napi::Env env = info.Env();
auto worker = new listDevices(env);
worker->Queue();
return worker->listDevicesPromise.Promise();
return worker->Promised.Promise();
}));
#endif
exports.Set("constant", Constant);
exports.Set("userspace", Userspace);
return exports;
}
NODE_API_MODULE(addon, Init);

@ -5,31 +5,38 @@
#include <map>
#include <wgkeys.hh>
// Get wireguard max name length
unsigned long maxName();
// namespace SetupAddon
namespace SetupAddon {
// Get wireguard max name length
unsigned long maxName();
// Get wireguard version
std::string versionDrive();
// Get wireguard version
std::string versionDrive();
// On start module call this function
std::string startAddon(const Napi::Env env, Napi::Object exports);
// On start module call this function
std::string startAddon(const Napi::Env env, Napi::Object exports);
};
class deleteInterface : public Napi::AsyncWorker {
class GlobalPromise : public Napi::AsyncWorker {
public:
GlobalPromise(const Napi::Env env): AsyncWorker(env), Promised{env} {}
~GlobalPromise() {}
const Napi::Promise::Deferred Promised;
void OnError(const Napi::Error &e) override {
Napi::HandleScope scope(Env());
Promised.Reject(e.Value());
}
};
class deleteInterface : public GlobalPromise {
private:
std::string wgName;
public:
deleteInterface(const Napi::Env env, std::string name): AsyncWorker(env), wgName{name}, deletePromise{env} {}
deleteInterface(const Napi::Env env, std::string name): GlobalPromise(env), wgName{name} {}
~deleteInterface() {}
const Napi::Promise::Deferred deletePromise;
void OnError(const Napi::Error &e) override {
Napi::HandleScope scope(Env());
deletePromise.Reject(e.Value());
}
void OnOK() override {
Napi::HandleScope scope(Env());
deletePromise.Resolve(Env().Undefined());
Promised.Resolve(Env().Undefined());
};
// Set platform Execute script
@ -41,19 +48,12 @@ class listInfo {
std::string tunType, pathSock;
};
class listDevices : public Napi::AsyncWorker {
class listDevices : public GlobalPromise {
private:
std::map<std::string, listInfo> deviceNames;
public:
~listDevices() {}
listDevices(const Napi::Env env) : AsyncWorker(env), listDevicesPromise{env} {}
const Napi::Promise::Deferred listDevicesPromise;
void OnError(const Napi::Error& e) override {
Napi::HandleScope scope(Env());
listDevicesPromise.Reject(e.Value());
}
listDevices(const Napi::Env env) : GlobalPromise(env) {}
void OnOK() override {
Napi::HandleScope scope(Env());
const Napi::Env env = Env();
@ -66,7 +66,7 @@ class listDevices : public Napi::AsyncWorker {
if (infoSrc.pathSock.size() > 0) info.Set("path", infoSrc.pathSock);
deviceArray.Set(deviceArray.Length(), info);
}
listDevicesPromise.Resolve(deviceArray);
Promised.Resolve(deviceArray);
};
void Execute() override;
};
@ -118,11 +118,8 @@ class Peer {
int ProtocolVersion = 0;
};
/*
Configure uma interface do Wireguard.
*/
class setConfig : public Napi::AsyncWorker {
private:
class ConfigBase {
public:
// Wireguard interface name (required)
std::string wgName;
@ -133,11 +130,11 @@ class setConfig : public Napi::AsyncWorker {
std::string publicKey;
// Wireguard port listen
unsigned short portListen = 0;
unsigned short portListen;
// FirewallMark specifies a device's firewall mark
// else set to 0, the firewall mark will be cleared.
int fwmark = -1;
int fwmark;
// Interface address'es
std::vector<std::string> Address;
@ -146,24 +143,39 @@ class setConfig : public Napi::AsyncWorker {
bool replacePeers = false;
// Wireguard peers, Map: <publicKey(std::string), Peer>
std::map<std::string, Peer> peersVector;
public:
const Napi::Promise::Deferred setPromise;
std::map<std::string, Peer> Peers;
ConfigBase() {
this->fwmark = 0;
this->portListen = 0;
}
~ConfigBase() {
this->Address.clear();
this->Peers.clear();
}
/**
* Set configuration in interface
*/
void setWireguardConfig();
/**
* Get configuration from interface and insert in class
*/
void getWireguardConfig();
};
/*
Configure uma interface do Wireguard.
*/
class setConfig : public GlobalPromise, ConfigBase {
public:
void OnOK() override {
Napi::HandleScope scope(Env());
// Callback().Call({ Env().Undefined() });
setPromise.Resolve(Env().Undefined());
Promised.Resolve(Env().Undefined());
};
void OnError(const Napi::Error& e) override {
Napi::HandleScope scope(Env());
// Callback().Call({ e.Value() });
setPromise.Reject(e.Value());
}
~setConfig() {}
setConfig(const Napi::Env env, std::string name, const Napi::Object &config) : AsyncWorker(env), wgName{name}, setPromise{env} {
setConfig(const Napi::Env env, std::string name, const Napi::Object &config) : GlobalPromise(env) {
// Wireguard public key
const auto sppk = config.Get("publicKey");
if (sppk.IsString()) {
@ -201,14 +213,14 @@ class setConfig : public Napi::AsyncWorker {
// Peers
const auto speers = config.Get("peers");
if (speers.IsObject()) {
const Napi::Object Peers = speers.ToObject();
const Napi::Array Keys = Peers.GetPropertyNames();
const Napi::Object PeersObject = speers.ToObject();
const Napi::Array Keys = PeersObject.GetPropertyNames();
for (unsigned int peerIndex = 0; peerIndex < Keys.Length(); peerIndex++) {
const auto peerPubKey = Keys[peerIndex];
if (peerPubKey.IsString() && Peers.Get(Keys[peerIndex]).IsObject()) {
if (peerPubKey.IsString() && PeersObject.Get(Keys[peerIndex]).IsObject()) {
std::string ppkey = peerPubKey.ToString().Utf8Value();
if (ppkey.length() != B64_WG_KEY_LENGTH) throw Napi::Error::New(env, std::string("Set valid peer publicKey, value: ").append(ppkey));
const Napi::Object peerConfigObject = Peers.Get(Keys[peerIndex]).ToObject();
const Napi::Object peerConfigObject = PeersObject.Get(Keys[peerIndex]).ToObject();
Peer peerConfig = Peer();
const auto removeMe = peerConfigObject.Get("removeMe");
@ -240,52 +252,33 @@ class setConfig : public Napi::AsyncWorker {
}
// Insert peer
peersVector[ppkey] = peerConfig;
this->Peers[ppkey] = peerConfig;
}
}
}
}
// Set platform Execute script
void Execute() override;
void Execute() override {
try {
this->setWireguardConfig();
} catch (std::string &err) {
SetError(err);
}
}
};
class getConfig : public Napi::AsyncWorker {
private:
// Wireguard interface name (required)
std::string wgName;
// Wireguard private key (required)
std::string privateKey;
// Wireguard interface publicKey <optional>
std::string publicKey;
// Wireguard port listen
unsigned int portListen;
// FirewallMark specifies a device's firewall mark
// else set to 0, the firewall mark will be cleared.
int fwmark = -1;
// Interface address'es
std::vector<std::string> Address;
/*
Wireguard peers
Map: <publicKey, Peer>
*/
std::map<std::string, Peer> peersVector;
class getConfig : public GlobalPromise, ConfigBase {
public:
~getConfig() {}
getConfig(const Napi::Env env, std::string name): AsyncWorker(env), wgName{name}, getPromise{env} {}
const Napi::Promise::Deferred getPromise;
void OnError(const Napi::Error& e) override {
Napi::HandleScope scope(Env());
getPromise.Reject(e.Value());
getConfig(const Napi::Env env, std::string name): GlobalPromise(env) {}
void Execute() override {
try {
this->getWireguardConfig();
} catch (std::string &err) {
SetError(err);
}
}
void OnOK() override {
Napi::HandleScope scope(Env());
const Napi::Env env = Env();
@ -303,7 +296,7 @@ class getConfig : public Napi::AsyncWorker {
// Peer object
const auto PeersObject = Napi::Object::New(env);
for (auto &peer : peersVector) {
for (auto &peer : this->Peers) {
const auto PeerObject = Napi::Object::New(env);
auto peerConfig = peer.second;
@ -330,9 +323,6 @@ class getConfig : public Napi::AsyncWorker {
config.Set("peers", PeersObject);
// Resolve config json
getPromise.Resolve(config);
Promised.Resolve(config);
};
// Set platform Execute script
void Execute() override;
};

@ -46,6 +46,6 @@
},
"dependencies": {
"node-addon-api": "^7.1.0",
"rebory": "^0.1.12"
"rebory": "^0.2.4"
}
}

@ -1,5 +1,6 @@
export * as key from "./key.js";
export * as wgQuick from "./quick.js";
export * as key from "./key.js";
export * from "./wginterface.js";
export * as wginterface from "./wginterface.js";
export * as default from "./wginterface.js";
export * as wginterface from "./wginterface.js";

@ -1,45 +1,33 @@
import { promises as fs } from "fs";
import { isIPv4, createConnection as netConnection } from "net";
import path from "path";
import readline from "readline";
import { finished } from "stream/promises";
import rebory from "rebory";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const addon = rebory.loadAddon(path.join(__dirname, "../binding.yaml")).wginterface.loadRelease<{
listDevices?: () => Promise<{from: "userspace"|"kernel", name: string, path?: string}[]>;
deleteInterface?: (wgName: string) => Promise<void>;
const addon = (await rebory.loadAddon(path.join(__dirname, "../binding.yaml"))).wginterface.load_addon<{
setConfig?: (wgName: string, config: WgConfigSet) => Promise<void>;
getConfig?: (wgName: string) => Promise<WgConfigGet>;
listDevices?: () => Promise<string[]>;
deleteInterface?: (wgName: string) => Promise<void>;
createTun?: () => Promise<number|string>;
deleteTun?: () => void;
checkTun?: () => Promise<boolean>;
getTun?: () => Promise<number|string>;
/**
* Wireguard addon userspace
*/
userspace: {
createTun?: () => Promise<number|string>;
deleteTun?: () => void;
checkTun?: () => Promise<boolean>;
getTunPath?: () => Promise<number|string>;
};
/** Wireguard addon constants */
constants: {
driveVersion: string;
base64Length: number;
keyLength: number;
driveVersion: string|null;
nameLength: number;
};
}>({
WIN32DLLPATH: path.resolve(__dirname, "../addons/tools/win/wireguard-nt/bin", ((process.arch === "x64" && "amd64") || (process.arch === "ia32" && "i386"))||process.arch, "wireguard.dll")
});
export const {
constants
} = addon;
/** default location to run socket's */
const defaultPath = (process.env.WIRWGUARD_GO_RUN||"").length > 0 ? path.resolve(process.cwd(), process.env.WIRWGUARD_GO_RUN) : process.platform === "win32" ? "\\\\.\\pipe\\WireGuard" : "/var/run/wireguard";
async function exists(path: string) {
return fs.open(path).then(o => o && (o.close().then(() => true, () => true))||true, () => false);
}
export interface Peer {
/** Preshared key to peer */
presharedKey?: string;
@ -99,151 +87,71 @@ export interface WgConfigSet extends WgConfigBase<PeerSet> {
export type WgGlobalConfig = WgConfigSet & WgConfigGet;
/**
* Get Wireguard devices and locations
* Userspace wireguard
*/
export async function listDevices() {
let devices: {from: "userspace"|"kernel", name: string, path?: string}[] = [];
if (typeof addon.listDevices === "function") devices = devices.concat(await addon.listDevices());
if (await exists(defaultPath)) (await fs.readdir(defaultPath)).forEach(file => devices.push({ from: "userspace", name: file.endsWith(".sock") ? file.slice(0, -5) : file, path: path.join("/var/run/wireguard", file) }));
return devices;
export namespace Userspace {
/**
* Set Wireguard interface configuration
* @param name - Interface name
* @param config - Interface config
*/
export async function SetConfig(name: string, config: WgConfigSet) {}
/**
* Get Wireguard interface config if exist
* @param name - Interface name
*/
export async function GetConfig(name: string): Promise<WgConfigGet> { return null; }
/**
* List Wireguard userspace devices
* @returns Wireguard userspace devices
*/
export async function ListDevices(): Promise<{name: string, path?: string}[]> { return []; }
/**
* Delete Wireguard Userspace interface
* @param name - Interface name
*/
export async function DeleteDevice(name: string) {}
}
/**
* Delete wireguard interface if present
* @param wgName - Interface name
* @returns
*/
export async function deleteInterface(wgName: string): Promise<void> {
if (typeof addon.deleteInterface === "function") return addon.deleteInterface(wgName);
const dev = (await listDevices()).find(s => s.name === wgName);
if (dev && dev.path) return fs.rm(dev.path, { force: true });
}
/**
* Add the settings to the Wireguard interface, if it does not exist and the interface will be created automatically.
*
* To update the interface settings, first get the interface settings to update!
*
* @param wgName - Interface name
* @param config - Interface config
*/
export async function setConfig(wgName: string, config: WgConfigGet): Promise<void>;
/**
* Add the settings to the Wireguard interface, if it does not exist and the interface will be created automatically.
*
* To update the interface settings, first get the interface settings to update!
*
* @param wgName - Interface name
* @param config - Interface config
*/
export async function setConfig(wgName: string, config: WgConfigSet): Promise<void> {
if (wgName.length > constants.nameLength) throw new Error("Interface name more then allowed", { cause: constants.nameLength });
if (typeof addon.setConfig === "function") return addon.setConfig(wgName, config);
const client = netConnection(path.join(defaultPath, (wgName).concat(".sock")));
const writel = (...data: any[]) => client.write(data.map(String).join("").concat("\n"));
// Init set config in interface
writel("set=1");
// Port listening
if (config.portListen !== undefined && Math.floor(config.portListen) >= 0) writel(("listen_port="), ((Math.floor(config.portListen))));
// fwmark
if (Math.floor(config.fwmark) >= 0) writel(("fwmark="), ((Math.floor(config.fwmark))));
// Replace peer's
if (config.replacePeers) writel("replace_peers=true");
// Keys
if (typeof config.privateKey === "string" && config.privateKey.length > 0) writel(("private_key="), (Buffer.from(config.privateKey, "base64").toString("hex")));
// Mount peer
for (const publicKey of Object.keys(config.peers||{})) {
const { presharedKey, endpoint, keepInterval, removeMe, allowedIPs = [] } = config.peers[publicKey];
// Public key
writel(("public_key="), (Buffer.from(publicKey, "base64").toString("hex")));
if (removeMe) {
writel("remove=true"); // Remove peer
continue;
}
if (typeof endpoint === "string" && endpoint.length > 0) writel(("endpoint="), (endpoint));
if (typeof presharedKey === "string" && presharedKey.length > 3) writel(("preshared_key="), (Buffer.from(presharedKey, "base64").toString("hex")));
if (typeof keepInterval === "number" && Math.floor(keepInterval) > 0) writel(("persistent_keepalive_interval="), (String(Math.floor(keepInterval))));
if (allowedIPs.length > 0) {
writel("replace_allowed_ips=true");
const fixed = allowedIPs.map(i => i.indexOf("/") === -1 ? i.concat("/", (isIPv4(i) ? "32" : "128")) : i)
for (const IIP of fixed) writel(("allowed_ip="), (IIP));
}
* Kernel Wireguard
*/
export namespace Kernel {
export const { constants: { driveVersion, nameLength } } = addon;
/**
* Set Wireguard interface configuration and if not exists create
*
* @param name - Wireguard interface name
* @param config - Interface configuration
*/
export async function SetConfig(name: string, config: WgConfigSet) {
await addon.setConfig(name, config);
}
let payload = "";
client.once("data", function processBuff(buff) {
payload = payload.concat(buff.toString("utf8"));
if (payload[payload.length - 1] === "\n" && payload[payload.length - 2] === "\n") {
client.end(); // Close conenction
return;
}
client.once("data", processBuff);
});
client.write("\n");
await finished(client, { error: true });
const payloadKeys = payload.split("\n").filter(i => i.length > 3).map(line => { const iit = line.indexOf("="); return [ line.slice(0, iit), line.slice(iit+1) ]; })
if (payloadKeys.at(-1)[1] !== "0") {
const err = new Error("Invalid send config, check log");
throw err;
/**
* Get Wireguard interface configuration
* @param name - Wireguard interface name
*/
export async function GetConfig(name: string): Promise<WgConfigGet> {
return addon.getConfig(name);
}
/**
* Lista Wireguard interfaces
* @returns Devices
*/
export async function ListDevices(): Promise<string[]> {
return addon.listDevices();
}
/**
* Delete Wireguard interface
* @param name - Wireguard interface name
*/
export async function DeleteDevice(name: string) {
return addon.deleteInterface(name);
}
}
/**
* Get wireguard interface config
* @param wgName - Interface name
* @returns
*/
export async function getConfig(wgName: string): Promise<WgConfigGet> {
if (typeof addon.getConfig === "function") return addon.getConfig(wgName);
const info = (await listDevices()).find(int => int.name === wgName);
if (!info) throw new Error("Create interface, not exists");
const client = netConnection(path.join(defaultPath, wgName.concat(".sock")));
const config: WgConfigGet = Object();
let latestPeer: string, previewKey: string;
const tetrisBreak = readline.createInterface(client);
tetrisBreak.on("line", function lineProcess(line) {
if (line === "") tetrisBreak.removeListener("line", lineProcess).close();
const findout = line.indexOf("="), keyName = line.slice(0, findout), value = line.slice(findout+1);
if (findout <= 0) return;
if (keyName === "errno" && value !== "0") throw new Error(("wireguard-go error, code: ").concat(value));
// Drop
if ((["last_handshake_time_nsec", "protocol_version", "errno"]).includes(keyName)) return;
else if (keyName === "private_key") config.privateKey = Buffer.from(value, "hex").toString("base64");
else if (keyName === "listen_port") config.portListen = Number(value);
else if (keyName === "endpoint") ((config.peers||(config.peers = {}))[latestPeer]).endpoint = value;
else if (keyName === "persistent_keepalive_interval") ((config.peers||(config.peers = {}))[latestPeer]).keepInterval = Number(value);
else if (keyName === "rx_bytes") ((config.peers||(config.peers = {}))[latestPeer]).rxBytes = Number(value);
else if (keyName === "tx_bytes") ((config.peers||(config.peers = {}))[latestPeer]).txBytes = Number(value);
else if (keyName === "last_handshake_time_sec") ((config.peers||(config.peers = {}))[latestPeer]).lastHandshake = new Date(Number(value) * 1000);
else if (keyName === "allowed_ip") {
if (!value) return;
((config.peers||(config.peers = {}))[latestPeer]).allowedIPs = (((config.peers||(config.peers = {}))[latestPeer]).allowedIPs||[]).concat(value);
} else if (keyName === "preshared_key") {
if (value === "0000000000000000000000000000000000000000000000000000000000000000") return;
((config.peers||(config.peers = {}))[latestPeer]).presharedKey = Buffer.from(value, "hex").toString("base64");
} else if (keyName === "public_key") {
const keyDecode = Buffer.from(value, "hex").toString("base64");
if (previewKey !== "public_key") (config.peers||(config.peers = {}))[latestPeer] = {};
else {
config.publicKey = latestPeer;
(config.peers||(config.peers = {}))[keyDecode] = (config.peers||(config.peers = {}))[latestPeer];
delete (config.peers||(config.peers = {}))[latestPeer];
latestPeer = keyDecode;
}
}
previewKey = keyName;
});
client.write("get=1\n\n");
await new Promise((done, reject) => tetrisBreak.on("error", reject).once("close", done));
await finished(client.end());
return config;
}

@ -1,10 +1,10 @@
import test from "node:test";
import { setConfig, deleteInterface, WgConfigSet, getConfig } from "./wginterface.js";
import { Kernel, Userspace, WgConfigSet } from "./wginterface.js";
import { publicKey } from "./key.js";
import { userInfo } from "os";
if (process.platform === "win32" || process.platform === "linux" && (userInfo().uid === 0)) {
test("Wireguard configuration", async t => {
await test(String.prototype.concat("Wireguard kernel (", Kernel.driveVersion, ")"), async t => {
// Config base
const peer1Key = 'EKgSatFzZtsv1qFJ6gE8HqfuA+tXzW+7vDeVc7Xaa2E=', peer2Key = '4BSvgiM9j5jjuR0Vg3gbqTFD5+CyuOU2K2kJE5+cakQ=',
config: WgConfigSet = {
@ -22,11 +22,11 @@ if (process.platform === "win32" || process.platform === "linux" && (userInfo().
}
await t.test("Set config in interface", async () => {
await setConfig("wg23", config);
await Kernel.SetConfig("wg23", config);
});
await t.test("Get config in interface", async () => {
const __config = await getConfig("wg23");
const __config = await Kernel.GetConfig("wg23");
if (!__config.peers[publicKey(peer1Key)]) throw new Error("Not exist peer 1!");
});
@ -38,17 +38,21 @@ if (process.platform === "win32" || process.platform === "linux" && (userInfo().
}
await t.test("Set config in interface", async () => {
await setConfig("wg23", config);
await Kernel.SetConfig("wg23", config);
});
await t.test("Get config in interface", async () => {
const __config = await getConfig("wg23");
const __config = await Kernel.GetConfig("wg23");
if (__config.peers[publicKey(peer1Key)]) throw new Error("Invalid config get!");
if (!__config.peers[publicKey(peer2Key)]) throw new Error("Not exist peer 2!");
});
await t.test("Delete interface", async () => {
await deleteInterface("wg23");
await Kernel.DeleteDevice("wg23");
});
});
await test("Wireguard Userspace", async t => {
Userspace;
});
}

@ -18,10 +18,7 @@
]
},
"exclude": [
"**/libs/**",
"**/docs/**",
"node_modules/",
"index.mjs"
"node_modules/"
],
"ts-node": {
"files": true,