Big code refactoring #10

Merged
Sirherobrine23 merged 15 commits from code_refactoring into main 2024-03-15 04:08:27 +00:00
49 changed files with 2717 additions and 2596 deletions

@ -1,11 +0,0 @@
version: 2
updates:
- directory: /
package-ecosystem: "github-actions"
schedule:
interval: daily
- directory: /
package-ecosystem: npm
schedule:
interval: monthly

49
.github/workflows/test.yaml vendored Normal file

@ -0,0 +1,49 @@
name: Test
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test_linux:
name: Test Linux
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 16.x, 18.x, 20.x, latest ]
steps:
- name: Disable sudo PATH replace
run: |
sudo cat /etc/sudoers | grep -v "secure_path=" > /tmp/.sudoers.tmp
cat /tmp/.sudoers.tmp | sudo tee /etc/sudoers
rm /tmp/.sudoers.tmp
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- uses: actions/setup-go@v4
with:
go-version-file: addon/userspace/go/go.mod
go-version: ">=1.22.0"
- name: "Setup zig"
uses: korandoru/setup-zig@v1
with:
zig-version: "master"
- name: Install build dependencies
run: sudo apt update && sudo apt install -y build-essential
- name: Install node dependencies
run: npm install --no-save --no-audit --no-fund --ignore-scripts
- name: Run tests
run: ./node_modules/.bin/rebory build && sudo node --no-warnings --loader ts-node/esm src/index_test.js

@ -1,77 +0,0 @@
name: Test
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test_js:
name: Test Javascript code
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
name: Checkout
- uses: actions/setup-node@v4
name: Setup Node.js
with:
node-version: 20.x
- name: Install dependencies
run: |
export DEBIAN_FRONTEND=noninteractive
sudo apt update && sudo apt install -y "build-essential"
npm install --no-save --ignore-scripts
- name: Build addon
run: npm run dev
- name: Run tests
env:
FORCE_COLOR: "true"
run: sudo "$(command -v node)" --no-warnings --loader ts-node/esm src/index_test.ts
build_addon:
runs-on: ubuntu-latest
needs: test_js
strategy:
matrix:
node_version: [ 16.x, 17.x, 18.x, 19.x, 20.x, 21.x ]
steps:
- uses: actions/checkout@v4
name: Checkout
- name: "Setup zig"
uses: korandoru/setup-zig@v1
with:
zig-version: "0.11.0"
- uses: actions/setup-node@v4
name: Setup Node.js
with:
node-version: ${{ matrix.node_version }}
- name: Install dependencies
run: |
export DEBIAN_FRONTEND=noninteractive
sudo apt update && sudo apt install -y "build-essential"
npm install --no-save --ignore-scripts
- name: Build addon linux
run: npm run dev -- --target x86_64-linux --target aarch64-linux
- name: Build addon macos
run: npm run dev -- --target x86_64-macos --target aarch64-macos
- name: Build addon windows
run: npm run dev -- --target x86_64-windows --target aarch64-windows
- name: Upload prebuilds interface
uses: actions/upload-artifact@v3
with:
retention-days: 7
name: build-${{ matrix.node_version }}
path: "build/*"

3
.gitignore vendored

@ -11,6 +11,9 @@ node_modules/
.DS_Store .DS_Store
*.log *.log
# Addon
addon/userspace/wg-go.*
# Typescript # Typescript
*.tsbuildinfo *.tsbuildinfo
src/**/*.d.ts src/**/*.d.ts

@ -1,21 +0,0 @@
image: node:lts
test:
stage: test
image: ghcr.io/catthehacker/ubuntu:act-latest
script:
- |
echo "Host arch: $(uname -m)"
export DEBIAN_FRONTEND=noninteractive
sudo apt update
wget -qO- https://deb.nodesource.com/setup_current.x | sudo bash -
packages=( "binutils-multiarch" "wget" "curl" "nodejs" "build-essential" );
sudo apt install -y ${packages[@]}
ZIGURL="https://ziglang.org/download/0.11.0/zig-linux-x86_64-0.11.0.tar.xz"
wget -qO- "${ZIGURL}" | sudo tar -xvJ -C /usr/local
sudo mv -v /usr/local/zig-* /usr/local/zig
sudo ln -vs /usr/local/zig/bin/zig /usr/bin/zig
- npm install --no-save --ignore-scripts
- npm run dev
- sudo node --no-warnings --loader ts-node/esm ./src/index_test.ts

@ -1,77 +1,16 @@
{ {
"version": 4,
"configurations": [ "configurations": [
{
"name": "Win32",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64",
"compilerArgs": [
"-fpermissive",
"-fexceptions",
"-w",
"-fpermissive",
"-fPIC",
"-static"
],
"defines": [
"NAPI_DISABLE_CPP_EXCEPTIONS",
"ONSTARTADDON",
"LISTDEV",
"GETCONFIG",
"SETCONFIG",
"DELIFACE"
],
"includePath": [
"${env:appdata}/../Local/node-gyp/Cache/18.17.0/include/node",
"${workspaceFolder}/node_modules/node-addon-api",
"${workspaceFolder}/addons/genKey/**",
"${workspaceFolder}/addons/tools/**"
]
},
{
"name": "Linux",
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "linux-clang-x64",
"compilerArgs": [
"-fpermissive",
"-fexceptions",
"-w",
"-fpermissive",
"-fPIC",
"-static"
],
"defines": [
"NAPI_DISABLE_CPP_EXCEPTIONS"
],
"includePath": [
"/usr/include/node",
"${workspaceFolder}/node_modules/node-addon-api/**",
"${workspaceFolder}/node_modules/**",
"${workspaceFolder}/addons/genKey/**",
"${workspaceFolder}/addons/tools/**"
]
},
{ {
"name": "Mac", "name": "Mac",
"includePath": [ "includePath": [
"${workspaceFolder}/node_modules/node-addon-api",
"/usr/local/include/node", "/usr/local/include/node",
"${workspaceFolder}/addons/genKey/**", "/usr/local/lib/zig//libc/include/any-linux-any",
"${workspaceFolder}/addons/tools/**" "${workspaceFolder}/node_modules/node-addon-api",
"${workspaceFolder}/**",
"${workspaceFolder}/addon"
], ],
"defines": [ "defines": [
"NAPI_DISABLE_CPP_EXCEPTIONS" "AF_NETLINK=16"
],
"compilerArgs": [
"-fpermissive",
"-fexceptions",
"-w",
"-fpermissive",
"-fPIC",
"-static"
], ],
"macFrameworkPath": [ "macFrameworkPath": [
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks" "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks"
@ -80,6 +19,28 @@
"cStandard": "c17", "cStandard": "c17",
"cppStandard": "c++17", "cppStandard": "c++17",
"intelliSenseMode": "macos-clang-x64" "intelliSenseMode": "macos-clang-x64"
} },
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/node_modules/node-addon-api",
"/usr/include/node",
"${workspaceFolder}/**",
"${workspaceFolder}/addon"
] ]
},
{
"name": "Win32",
"includePath": [
"${env:appdata}/../.cache/rebory/latest/node/include/node",
"${workspaceFolder}/node_modules/node-addon-api",
"${workspaceFolder}/addon"
],
"defines": [],
"intelliSenseMode": "windows-msvc-x64",
"cStandard": "c17",
"cppStandard": "c++17"
}
],
"version": 4
} }

20
.vscode/launch.json vendored

@ -1,20 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug",
"cwd": "${workspaceFolder}",
"program": "node",
"preLaunchTask": {
"type": "npm",
"script": "dev"
},
"args": [
"node_modules/.bin/mocha",
"./"
]
}
]
}

93
.vscode/settings.json vendored

@ -23,8 +23,99 @@
"PATH": "${workspaceFolder}/node_modules/.bin:${env:PATH}" "PATH": "${workspaceFolder}/node_modules/.bin:${env:PATH}"
}, },
"files.associations": { "files.associations": {
"*.dsc": "ini",
"*.gyp": "python",
"random": "cpp", "random": "cpp",
"limits": "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",
"numeric": "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",
"algorithm": "cpp",
"bit": "cpp",
"charconv": "cpp",
"codecvt": "cpp",
"format": "cpp",
"forward_list": "cpp",
"functional": "cpp",
"iomanip": "cpp",
"iterator": "cpp",
"list": "cpp",
"stop_token": "cpp",
"thread": "cpp",
"utility": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp",
"fstream": "cpp"
} }
} }

12
.vscode/tasks.json vendored

@ -1,12 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "npm: build",
"detail": "npm run dev",
"type": "npm",
"script": "dev",
"problemMatcher": []
}
]
}

@ -1,26 +1,17 @@
# Wireguard-tools.js # Wireguard-tools for Nodejs
Efficiently manage your Wireguard interface right from nodejs, no `wg` required. Manage your Wireguard interfaces directly from Node.js without any wrappers over `wg` or `wg-quick`
other tools are wrappers over `wg`, `wireguard-tools.js` is not like that, it is a `C/C++` addon in which you don't need to have `wg` installed, as this module has full compatibility of its own `wg`. ```js
## CommonJS droping support ```
With a small disappointment I come to inform you that CommonJS will be ignored in the next updates and will be completely an ESM module, if you don't want to migrate to ESM I recommend staying on version `1.8.1` or even `1.8.3`, which will be the last versions but recent in CommonJS. ## Licences
## Support to: - `Wireguard-tools.js`: GPL-3.0
- Userspace [(wireguard-go)](https://git.zx2c4.com/wireguard-go/about/) support. ### Wireguard
- Maneger wireguard interface (linux and windows create if not exist's).
- Generate `preshared`, `private` and `public` keys.
- [wg-quick](https://man7.org/linux/man-pages/man8/wg-quick.8.html) file support.
- More info and example check [`wiki`](https://sirherobrine23.org/Wireguard/Wireguard-tools.js/wiki).
> [!NOTE] - `Embeddable-wg-library`: LGPL-2.1+.
> - `Wireguard-nt`: GPL-2.0
> we have pre-copiled files for: - `Wireguard-go`: MIT
> - `Linux`: x64/amd64, arm64/aarch64
> - `Windows`: x64, arm64
>
> 1. To manage the Wireguard interfaces in linux, root access is required.
> 1. Another system's require `wireguard-go` [(check this page)](https://github.com/WireGuard/wireguard-go)

@ -1,8 +1,6 @@
#include "wgkeys.hh" #include "wgkeys.hh"
#include <errno.h> #include <errno.h>
#include <assert.h>
#include <fcntl.h> #include <fcntl.h>
#include <iostream>
#include <random> #include <random>
#include <string.h> #include <string.h>
@ -18,16 +16,19 @@ static void encode_base64(char dest[4], const uint8_t src[3]) {
static_cast<uint8_t>((src[0] >> 2) & 63), static_cast<uint8_t>((src[0] >> 2) & 63),
static_cast<uint8_t>(((src[0] << 4) | (src[1] >> 4)) & 63), static_cast<uint8_t>(((src[0] << 4) | (src[1] >> 4)) & 63),
static_cast<uint8_t>(((src[1] << 2) | (src[2] >> 6)) & 63), static_cast<uint8_t>(((src[1] << 2) | (src[2] >> 6)) & 63),
static_cast<uint8_t>(src[2] & 63) static_cast<uint8_t>(src[2] & 63)};
};
unsigned int i; unsigned int i;
for (i = 0; i < 4; ++i) dest[i] = input[i] + 'A' + (((25 - input[i]) >> 8) & 6) - (((51 - input[i]) >> 8) & 75) - (((61 - input[i]) >> 8) & 15) + (((62 - input[i]) >> 8) & 3); for (i = 0; i < 4; ++i)
dest[i] = input[i] + 'A' + (((25 - input[i]) >> 8) & 6) -
(((51 - input[i]) >> 8) & 75) - (((61 - input[i]) >> 8) & 15) +
(((62 - input[i]) >> 8) & 3);
} }
void keyToBase64(wg_key_b64_string base64, const wg_key key) { void keyToBase64(wg_key_b64_string base64, const wg_key key) {
unsigned int i; unsigned int i;
for (i = 0; i < 32 / 3; ++i) encode_base64(&base64[i * 4], &key[i * 3]); for (i = 0; i < 32 / 3; ++i)
encode_base64(&base64[i * 4], &key[i * 3]);
const uint8_t tempKey[3] = {key[i * 3 + 0], key[i * 3 + 1], 0}; const uint8_t tempKey[3] = {key[i * 3 + 0], key[i * 3 + 1], 0};
encode_base64(&base64[i * 4], tempKey); encode_base64(&base64[i * 4], tempKey);
base64[sizeof(wg_key_b64_string) - 2] = '='; base64[sizeof(wg_key_b64_string) - 2] = '=';
@ -38,15 +39,24 @@ static int decodeBase64(const char src[4]) {
int val = 0; int val = 0;
unsigned int i; unsigned int i;
for (i = 0; i < 4; ++i) val |= (-1 + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64)) + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70)) + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5)) + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63) + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)) << (18 - 6 * i); for (i = 0; i < 4; ++i)
val |=
(-1 +
((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) &
(src[i] - 64)) +
((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) &
(src[i] - 70)) +
((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5)) +
((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63) +
((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64))
<< (18 - 6 * i);
return val; return val;
} }
#ifdef _WIN32 #ifdef _WIN32
static volatile void * (*memset_func)(void *, int, size_t) = (volatile void * (*)(void *, int, size_t))&memset; static volatile void *(*memset_func)(void *, int, size_t) =
void memzero_explicit(void *s, size_t count) { (volatile void *(*)(void *, int, size_t)) & memset;
memset_func(s, 0, count); void memzero_explicit(void *s, size_t count) { memset_func(s, 0, count); }
}
#else #else
static __attribute__((noinline)) void memzero_explicit(void *s, size_t count) { static __attribute__((noinline)) void memzero_explicit(void *s, size_t count) {
memset(s, 0, count); memset(s, 0, count);
@ -109,21 +119,25 @@ static void pack(uint8_t *o, const fe n) {
static void add(fe o, const fe a, const fe b) { static void add(fe o, const fe a, const fe b) {
int i; int i;
for (i = 0; i < 16; ++i) o[i] = a[i] + b[i]; for (i = 0; i < 16; ++i)
o[i] = a[i] + b[i];
} }
static void subtract(fe o, const fe a, const fe b) { static void subtract(fe o, const fe a, const fe b) {
int i; int i;
for (i = 0; i < 16; ++i) o[i] = a[i] - b[i]; for (i = 0; i < 16; ++i)
o[i] = a[i] - b[i];
} }
static void multmod(fe o, const fe a, const fe b) { static void multmod(fe o, const fe a, const fe b) {
int i, j; int i, j;
int64_t t[31] = {0}; int64_t t[31] = {0};
for (i = 0; i < 16; ++i) { for (i = 0; i < 16; ++i) {
for (j = 0; j < 16; ++j) t[i + j] += a[i] * b[j]; for (j = 0; j < 16; ++j)
t[i + j] += a[i] * b[j];
} }
for (i = 0; i < 15; ++i) t[i] += 38 * t[i + 16]; for (i = 0; i < 15; ++i)
t[i] += 38 * t[i + 16];
memcpy(o, t, sizeof(fe)); memcpy(o, t, sizeof(fe));
carry(o); carry(o);
carry(o); carry(o);
@ -136,7 +150,8 @@ static void invert(fe o, const fe i) {
memcpy(c, i, sizeof(c)); memcpy(c, i, sizeof(c));
for (a = 253; a >= 0; --a) { for (a = 253; a >= 0; --a) {
multmod(c, c, c); multmod(c, c, c);
if (a != 2 && a != 4) multmod(c, c, i); if (a != 2 && a != 4)
multmod(c, c, i);
} }
memcpy(o, c, sizeof(fe)); memcpy(o, c, sizeof(fe));
memzero_explicit(c, sizeof(c)); memzero_explicit(c, sizeof(c));
@ -146,24 +161,35 @@ void wgKeys::generatePreshared(wg_key preshared_key) {
#if _WIN32 || defined(__CYGWIN__) #if _WIN32 || defined(__CYGWIN__)
HCRYPTPROV hCryptProv; HCRYPTPROV hCryptProv;
BOOL winStatus; BOOL winStatus;
if ((winStatus = CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))) winStatus = CryptGenRandom(hCryptProv, sizeof(wg_key), (BYTE*)preshared_key); if ((winStatus = CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)))
winStatus =
CryptGenRandom(hCryptProv, sizeof(wg_key), (BYTE *)preshared_key);
CryptReleaseContext(hCryptProv, 0); CryptReleaseContext(hCryptProv, 0);
if (winStatus) return; if (winStatus)
return;
#elif defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) #elif defined(__OpenBSD__) || \
if (!getentropy(preshared_key, sizeof(wg_key))) return; (defined(__APPLE__) && \
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || \
(defined(__GLIBC__) && \
(__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)))
if (!getentropy(preshared_key, sizeof(wg_key)))
return;
#elif defined(__NR_getrandom) && defined(__linux__) #elif defined(__NR_getrandom) && defined(__linux__)
if (syscall(__NR_getrandom, preshared_key, sizeof(wg_key), 0) == sizeof(wg_key)) return; if (syscall(__NR_getrandom, preshared_key, sizeof(wg_key), 0) ==
sizeof(wg_key))
return;
#elif __linux__ || _ANDROID__ || __termux__ #elif __linux__ || _ANDROID__ || __termux__
size_t ret, i; size_t ret, i;
int fd; int fd;
fd = open("/dev/urandom", O_RDONLY); fd = open("/dev/urandom", O_RDONLY);
assert(fd >= 0); if (fd <= 0);
for (i = 0; i < sizeof(wg_key); i += ret) { for (i = 0; i < sizeof(wg_key); i += ret) {
ret = read(fd, preshared_key + i, sizeof(wg_key) - i); ret = read(fd, preshared_key + i, sizeof(wg_key) - i);
assert(ret > 0); if (ret < 0);
} }
close(fd); close(fd);
return; return;
@ -237,8 +263,15 @@ void wgKeys::generatePublic(wg_key public_key, const wg_key private_key) {
memzero_explicit(f, sizeof(f)); memzero_explicit(f, sizeof(f));
} }
bool key_is_zero(const uint8_t key[32]) std::string wgKeys::generatePublic(const std::string private_key) {
{ wg_key public_key;
wg_key private_key_;
stringToKey(private_key_, private_key);
generatePublic(public_key, private_key_);
return toString(public_key);
}
bool key_is_zero(const uint8_t key[32]) {
volatile uint8_t acc = 0; volatile uint8_t acc = 0;
for (unsigned int i = 0; i < 32; ++i) { for (unsigned int i = 0; i < 32; ++i) {
@ -250,7 +283,10 @@ bool key_is_zero(const uint8_t key[32])
void wgKeys::stringToKey(wg_key key, std::string keyBase64) { void wgKeys::stringToKey(wg_key key, std::string keyBase64) {
auto base64 = keyBase64.c_str(); auto base64 = keyBase64.c_str();
if (keyBase64.length() != B64_WG_KEY_LENGTH || base64[B64_WG_KEY_LENGTH - 1] != '=') throw std::string("invalid key, length: ").append(std::to_string(keyBase64.length())); if (keyBase64.length() != B64_WG_KEY_LENGTH ||
base64[B64_WG_KEY_LENGTH - 1] != '=')
throw std::string("invalid key, length: ")
.append(std::to_string(keyBase64.length()));
unsigned int i; unsigned int i;
int val; int val;
@ -263,20 +299,24 @@ void wgKeys::stringToKey(wg_key key, std::string keyBase64) {
key[i * 3 + 1] = (val >> 8) & 0xff; key[i * 3 + 1] = (val >> 8) & 0xff;
key[i * 3 + 2] = val & 0xff; key[i * 3 + 2] = val & 0xff;
} }
const char tempDecode[4] = {base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A'}; const char tempDecode[4] = {base64[i * 4 + 0], base64[i * 4 + 1],
base64[i * 4 + 2], 'A'};
val = decodeBase64(tempDecode); val = decodeBase64(tempDecode);
ret |= ((uint32_t)val >> 31) | (val & 0xff); ret |= ((uint32_t)val >> 31) | (val & 0xff);
key[i * 3 + 0] = (val >> 16) & 0xff; key[i * 3 + 0] = (val >> 16) & 0xff;
key[i * 3 + 1] = (val >> 8) & 0xff; key[i * 3 + 1] = (val >> 8) & 0xff;
int status = EINVAL & ~((ret - 1) >> 8); int status = EINVAL & ~((ret - 1) >> 8);
if (status != 0) throw std::string("Cannot decode key, ret code: ").append(std::to_string(status)); if (status != 0)
throw std::string("Cannot decode key, ret code: ")
.append(std::to_string(status));
} }
std::string wgKeys::toString(const wg_key key) { std::string wgKeys::toString(const wg_key key) {
wg_key_b64_string base64; wg_key_b64_string base64;
unsigned int i; unsigned int i;
for (i = 0; i < 32 / 3; ++i) encode_base64(&base64[i * 4], &key[i * 3]); for (i = 0; i < 32 / 3; ++i)
encode_base64(&base64[i * 4], &key[i * 3]);
const uint8_t tempKey[3] = {key[i * 3 + 0], key[i * 3 + 1], 0}; const uint8_t tempKey[3] = {key[i * 3 + 0], key[i * 3 + 1], 0};
encode_base64(&base64[i * 4], tempKey); encode_base64(&base64[i * 4], tempKey);
base64[sizeof(wg_key_b64_string) - 2] = '='; base64[sizeof(wg_key_b64_string) - 2] = '=';
@ -284,3 +324,18 @@ std::string wgKeys::toString(const wg_key key) {
return std::string(base64); return std::string(base64);
} }
std::string wgKeys::toHex(const std::string keyBase64) {
wg_key key;
wgKeys::stringToKey(key, keyBase64);
char hex[65];
for (int i = 0; i < 32; ++i) sprintf(hex + i * 2, "%02x", key[i]);
hex[64] = '\0';
return std::string(hex);
}
std::string wgKeys::HextoBase64(const std::string keyHex) {
wg_key key;
for (int i = 0; i < 32; ++i) sscanf(keyHex.c_str() + i * 2, "%02x", &key[i]);
return wgKeys::toString(key);
}

37
addon/genKey/wgkeys.hh Normal file

@ -0,0 +1,37 @@
#ifndef _WGKEY_
#define _WGKEY_
#include <string>
const int WG_KEY_LENGTH = 32, B64_WG_KEY_LENGTH = ((WG_KEY_LENGTH + 2) / 3) * 4;
const int wgKeyLength = 32, Base64WgKeyLength = ((wgKeyLength + 2) / 3) * 4, HexWgKeyLength = wgKeyLength * 2;
typedef unsigned char wg_key[wgKeyLength];
typedef char wg_key_b64_string[Base64WgKeyLength + 1];
typedef long long fe[16];
namespace wgKeys {
// Convert wg_key to std::string in base64
std::string toString(const wg_key key);
// Convert base64 to hex key
std::string toHex(const std::string keyBase64);
// Convert hex to base64
std::string HextoBase64(const std::string keyHex);
// Convert base64 to wg_key
void stringToKey(wg_key key, std::string keyBase64);
// Generate preshared key
void generatePreshared(wg_key key);
// Generate private key
void generatePrivate(wg_key private_key);
// Get public key from private key
void generatePublic(wg_key public_key, const wg_key private_key);
std::string generatePublic(const std::string private_key);
}
#endif

437
addon/linux/wginterface.cpp Normal file

@ -0,0 +1,437 @@
#include "wginterface.hh"
#include "genKey/wgkeys.hh"
extern "C" {
#include "wireguard.h"
}
#include <string>
#include <ifaddrs.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <time.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
#include <stdio.h>
#include <asm/types.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <string.h>
#include <iostream>
#include <sys/ioctl.h>
#include <net/if.h>
#include <filesystem>
#include <fstream>
std::string getWireguardVersion() {
#ifdef WIREGUARD_VERSION
return std::string(WIREGUARD_VERSION);
#endif
std::string version = "Unknown";
// /sys/module/wireguard/version - for kernel module
if (std::filesystem::exists("/sys/module/wireguard/version")) {
std::ifstream file("/sys/module/wireguard/version");
file >> version;
file.close();
}
return version;
}
std::string driveLoad(std::map<std::string, std::string> load) {}
void WireguardDevices::getInterfaces() {
size_t len; char *device_name, *devicesList = wg_list_device_names();
if (!devicesList) throw std::string("Unable to get device names");
// Clear list
this->clear();
// Set new devices
for (device_name = devicesList, len = 0; (len = strlen(device_name)); device_name += len + 1) this->push_back(std::string(device_name));
// Free memory
free(devicesList);
}
void WireguardDevices::deleteInterface(std::string wgName) {
// Check if exist, if not skip
if (this->exist(wgName)) {
int status = wg_del_device(wgName.c_str());
if (status < 0) throw std::string("Cannot delete interface, code: ").append(std::to_string(status));
}
}
std::string HostAdresses(bool addPort, const sockaddr* addr) {
char host[4096 + 1], service[512 + 1];
static char buf[sizeof(host) + sizeof(service) + 4];
memset(buf, 0, sizeof(buf));
int ret;
socklen_t addr_len = 0;
if (addr->sa_family == AF_INET) addr_len = sizeof(struct sockaddr_in);
else if (addr->sa_family == AF_INET6) addr_len = sizeof(struct sockaddr_in6);
ret = getnameinfo(addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST);
if (ret) {
strncpy(buf, gai_strerror(ret), sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
} else {
if (addPort) snprintf(buf, sizeof(buf), (addr->sa_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service);
else snprintf(buf, sizeof(buf), "%s", host);
}
return std::string(buf);
};
void WireguardConfig::getWireguardConfig() {
if (this->name.length() == 0) throw std::string("Set wireguard name!");
else if (this->name.length() > IFNAMSIZ) throw std::string("Wireguard interface name is long, max name length is ").append(std::to_string(IFNAMSIZ));
else if (!(WireguardDevices().exist(this->name))) throw std::string("Wireguard interface not exist");
int status; wg_device *devConfig; wg_peer *peer;
if ((status = wg_get_device(&devConfig, this->name.c_str())) < 0) throw std::string("It was not possible to get the Wireguard interface settings, code: ").append(std::to_string(status));
if (devConfig->flags & WGDEVICE_HAS_PRIVATE_KEY) this->privateKey = wgKeys::toString(devConfig->private_key);
if (devConfig->flags & WGDEVICE_HAS_PUBLIC_KEY) this->publicKey = wgKeys::toString(devConfig->public_key);
if (!!devConfig->listen_port && devConfig->listen_port > 0) this->portListen = devConfig->listen_port;
this->interfaceAddress.GetInInterface(this->name);
for ((peer) = (devConfig)->first_peer; (peer); (peer) = (peer)->next_peer) {
auto PeerConfig = Peer();
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) PeerConfig.presharedKey = wgKeys::toString(peer->preshared_key);
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) PeerConfig.keepInterval = peer->persistent_keepalive_interval;
if (peer->endpoint.addr.sa_family == AF_INET||peer->endpoint.addr.sa_family == AF_INET6) PeerConfig.endpoint = HostAdresses(true, &peer->endpoint.addr);
PeerConfig.lastHandshake = peer->last_handshake_time.tv_sec*1000;
PeerConfig.rxBytes = peer->rx_bytes;
PeerConfig.txBytes = peer->tx_bytes;
wg_allowedip *allowedip;
for ((allowedip) = (peer)->first_allowedip; (allowedip); (allowedip) = (allowedip)->next_allowedip) {
static char buf[INET6_ADDRSTRLEN + 1];
memset(buf, 0, INET6_ADDRSTRLEN + 1);
if (allowedip->family == AF_INET) inet_ntop(AF_INET, &allowedip->ip4, buf, INET6_ADDRSTRLEN);
else if (allowedip->family == AF_INET6) inet_ntop(AF_INET6, &allowedip->ip6, buf, INET6_ADDRSTRLEN);
PeerConfig.allowedIPs.push_back(std::string(buf).append("/").append(std::to_string(allowedip->cidr)));
}
this->Peers[wgKeys::toString(peer->public_key)] = PeerConfig;
}
}
void WireguardConfig::setWireguardConfig() {
int status;
if (this->name.length() > IFNAMSIZ) throw std::string("Wireguard interface name is long, max name length is ").append(std::to_string(IFNAMSIZ));
else if (!(WireguardDevices().exist(this->name)) && (status = wg_add_device(this->name.c_str())) < 0) throw std::string("Unable to create Wireguard interface, code: ").append(std::to_string(status));
if (this->privateKey.length() != Base64WgKeyLength) throw std::string("Set Wireguard interface private key!");
auto wgConfig = new wg_device({});
if (!wgConfig) throw std::string("Cannot alloc memory to set interface configuration!");
strncpy(wgConfig->name, this->name.c_str(), this->name.length());
wgConfig->flags = wg_device_flags::WGDEVICE_HAS_PRIVATE_KEY;
wgKeys::stringToKey(wgConfig->private_key, this->privateKey);
if (this->replacePeers) wgConfig->flags = (wg_device_flags)(wgConfig->flags|wg_device_flags::WGDEVICE_REPLACE_PEERS);
if (this->publicKey.length() == Base64WgKeyLength) {
wgConfig->flags = (wg_device_flags)(wgConfig->flags|wg_device_flags::WGDEVICE_HAS_PUBLIC_KEY);
wgKeys::stringToKey(wgConfig->public_key, this->publicKey);
}
if (this->portListen >= 0) {
wgConfig->flags = (wg_device_flags)(wgConfig->flags|wg_device_flags::WGDEVICE_HAS_LISTEN_PORT);
wgConfig->listen_port = this->portListen;
}
if (this->fwmark >= 0) {
wgConfig->flags = (wg_device_flags)(wgConfig->flags|wg_device_flags::WGDEVICE_HAS_FWMARK);
wgConfig->fwmark = this->fwmark;
}
for (auto &PeerConfig : this->Peers) {
auto peer = new wg_peer({});
peer->flags = wg_peer_flags::WGPEER_HAS_PUBLIC_KEY;
wgKeys::stringToKey(peer->public_key, PeerConfig.first);
if (PeerConfig.second.removeMe) peer->flags = (wg_peer_flags)(peer->flags|wg_peer_flags::WGPEER_REMOVE_ME);
else {
if (PeerConfig.second.presharedKey.length() == Base64WgKeyLength) {
peer->flags = (wg_peer_flags)(peer->flags|wg_peer_flags::WGPEER_HAS_PRESHARED_KEY);
wgKeys::stringToKey(peer->preshared_key, PeerConfig.second.presharedKey);
}
if (PeerConfig.second.keepInterval > 0) {
peer->flags = (wg_peer_flags)(peer->flags|wg_peer_flags::WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL);
peer->persistent_keepalive_interval = PeerConfig.second.keepInterval;
}
if (PeerConfig.second.endpoint.length() > 0) {
sockaddr endpoint; int ret, retries;
char *begin, *end, *Endpoint = strdup(PeerConfig.second.endpoint.c_str());
if (Endpoint[0] == '[') {
begin = &Endpoint[1];
end = strchr(Endpoint, ']');
if (!end) {
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
throw std::string("Unable to find matching brace of endpoint");
return;
}
*end++ = '\0';
if (*end++ != ':' || !*end) {
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
throw std::string("Unable to find port of endpoint");
return;
}
} else {
begin = Endpoint;
end = strrchr(Endpoint, ':');
if (!end || !*(end + 1)) {
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
throw std::string("Unable to find port of endpoint");
}
*end++ = '\0';
}
addrinfo *resolved, hints = { ai_family: AF_UNSPEC, ai_socktype: SOCK_DGRAM, ai_protocol: IPPROTO_UDP };
for (unsigned int timeout = 1000000;; timeout = ((20000000) < (timeout * 6 / 5) ? (20000000) : (timeout * 6 / 5))) {
ret = getaddrinfo(begin, end, &hints, &resolved);
if (!ret) break;
if (ret == EAI_NONAME || ret == EAI_FAIL ||
#ifdef EAI_NODATA
ret == EAI_NODATA ||
#endif
(retries >= 0 && !retries--)) {
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
fprintf(stderr, "%s: `%s'\n", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), PeerConfig.second.endpoint.c_str());
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.second.endpoint.c_str(), timeout / 1000000.0);
usleep(timeout);
}
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(sockaddr_in)) || (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(sockaddr_in6))) {
memcpy(&endpoint, resolved->ai_addr, resolved->ai_addrlen);
memccpy(&peer->endpoint.addr, &endpoint, 0, sizeof(peer->endpoint.addr));
if (resolved->ai_family == AF_INET) {
peer->endpoint.addr4.sin_addr.s_addr = ((sockaddr_in *)&endpoint)->sin_addr.s_addr;
peer->endpoint.addr4.sin_port = ((sockaddr_in *)&endpoint)->sin_port;
peer->endpoint.addr4.sin_family = AF_INET;
} else {
peer->endpoint.addr6.sin6_addr = ((struct sockaddr_in6 *)&endpoint)->sin6_addr;
peer->endpoint.addr6.sin6_port = ((struct sockaddr_in6 *)&endpoint)->sin6_port;
peer->endpoint.addr6.sin6_family = AF_INET6;
}
} else {
freeaddrinfo(resolved);
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
throw std::string("Neither IPv4 nor IPv6 address found");
}
freeaddrinfo(resolved);
// Free memory
for ((peer) = (wgConfig)->first_peer; (peer); (peer) = (peer)->next_peer) { delete peer; }
delete wgConfig;
free(Endpoint);
}
for (const auto Ip : PeerConfig.second.allowedIPs.getIpParsed()) {
if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS) peer->flags = (wg_peer_flags)(peer->flags|WGPEER_REPLACE_ALLOWEDIPS);
auto newAllowedIP = new wg_allowedip({});
newAllowedIP->cidr = Ip.Mask;
if (Ip.Proto == 6 && inet_pton(AF_INET6, Ip.Address.c_str(), &newAllowedIP->ip6) == 1) newAllowedIP->family = AF_INET6;
else if (Ip.Proto == 4 && inet_pton(AF_INET, Ip.Address.c_str(), &newAllowedIP->ip4) == 1) newAllowedIP->family = AF_INET;
else {
delete newAllowedIP;
continue;
}
if (peer->first_allowedip) newAllowedIP->next_allowedip = peer->first_allowedip;
peer->first_allowedip = newAllowedIP;
}
}
if (wgConfig->first_peer) peer->next_peer = wgConfig->first_peer;
wgConfig->first_peer = peer;
}
// Set config
status = wg_set_device(wgConfig);
// Free memory
for (wg_peer* peer = wgConfig->first_peer; peer; peer = peer->next_peer) {
for (wg_allowedip *newAllowedIP = peer->first_allowedip; newAllowedIP; newAllowedIP = newAllowedIP->next_allowedip) delete newAllowedIP;
delete peer;
}
delete wgConfig;
// Return status to tool
if (status < 0) throw std::string("Unable to configure settings, code: ").append(std::to_string(status));
else {
this->interfaceAddress.SetInInterface(this->name);
}
}
void IpManeger::GetInInterface(std::string interfaceName) {
ifaddrs* ptr_ifaddrs = nullptr;
if(getifaddrs(&ptr_ifaddrs) >= 0) {
for (ifaddrs* ptr_entry = ptr_ifaddrs; ptr_entry != nullptr; ptr_entry = ptr_entry->ifa_next) {
if (ptr_entry->ifa_addr == nullptr) continue;
else if (strcmp(ptr_entry->ifa_name, interfaceName.c_str()) != 0) continue;
else if (ptr_entry->ifa_addr->sa_family == AF_INET) this->addIPMask(HostAdresses(false, ptr_entry->ifa_addr));
else if (ptr_entry->ifa_addr->sa_family == AF_INET6) this->addIPMask(HostAdresses(false, ptr_entry->ifa_addr));
}
freeifaddrs(ptr_ifaddrs);
}
}
struct rtnl_handle {
int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;
__u32 seq;
__u32 dump;
};
typedef struct {
__u8 family;
__u8 bytelen;
__s16 bitlen;
__u32 flags;
__u32 data[8];
} inet_prefix;
// This function is to open the netlink socket as the name suggests.
void netlink_open(struct rtnl_handle* rth) {
int addr_len;
memset(rth, 0, sizeof(rth));
// Creating the netlink socket of family NETLINK_ROUTE
rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (rth->fd < 0) throw std::string("cannot open netlink socket");
memset(&rth->local, 0, sizeof(rth->local));
rth->local.nl_family = AF_NETLINK;
rth->local.nl_groups = 0;
// Binding the netlink socket
if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) throw std::string("cannot bind netlink socket");
addr_len = sizeof(rth->local);
if (getsockname(rth->fd, (struct sockaddr*)&rth->local, (socklen_t*) &addr_len) < 0) throw std::string("cannot getsockname");
if (addr_len != sizeof(rth->local)) throw std::string("wrong address lenght").append(std::to_string(addr_len));
if (rth->local.nl_family != AF_NETLINK) throw std::string("wrong address family").append(std::to_string(rth->local.nl_family));
rth->seq = time(NULL);
}
// This function does the actual reading and writing to the netlink socket
int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, unsigned groups, struct nlmsghdr *answer) {
int status;
struct nlmsghdr *h;
struct sockaddr_nl nladdr;
// Forming the iovector with the netlink packet.
struct iovec iov = { (void*)n, n->nlmsg_len };
char buf[8192];
// Forming the message to be sent.
struct msghdr msg = { (void*)&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 };
// Filling up the details of the netlink socket to be contacted in the
// kernel.
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = peer;
nladdr.nl_groups = groups;
n->nlmsg_seq = ++rtnl->seq;
if (answer == NULL) n->nlmsg_flags |= NLM_F_ACK;
// Actual sending of the message, status contains success/failure
return sendmsg(rtnl->fd, &msg, 0);
// if (status < 0) return -1;
}
// This is the utility function for adding the parameters to the packet.
int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) {
int len = RTA_LENGTH(alen);
struct rtattr *rta;
if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
return -1;
rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), data, alen);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
return 0;
}
void IpManeger::SetInInterface(std::string interfaceName) {
if (this->size() == 0) return;
if (!(WireguardDevices().exist(interfaceName))) throw std::string("Wireguard interface not exists!");
int status;
unsigned int ifa_index;
wg_device *devConfig;
if ((status = wg_get_device(&devConfig, interfaceName.c_str())) < 0) throw std::string("It was not possible to get the Wireguard interface settings, code: ").append(std::to_string(status));
ifa_index = devConfig->ifindex;
free(devConfig);
for (const auto ip : this->getIpParsed()) {
struct rtnl_handle * rth;
rth = (rtnl_handle*)malloc(sizeof(rtnl_handle));
netlink_open(rth);
int err;
inet_prefix lcl;
// structure of the netlink packet.
struct {
struct nlmsghdr n;
struct ifaddrmsg ifa;
char buf[1024];
} req;
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_type = RTM_NEWADDR;
req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL | NLM_F_REQUEST;
req.ifa.ifa_family = ip.Proto == 4 ? AF_INET : AF_INET6;
req.ifa.ifa_prefixlen = ip.Mask;
req.ifa.ifa_index = ifa_index ; // get the loopback index
req.ifa.ifa_scope = 0;
memset(&lcl, 0, sizeof(lcl));
lcl.family = req.ifa.ifa_family;
lcl.bytelen = (req.ifa.ifa_family == AF_INET) ? 4 : 16;
lcl.bitlen = (req.ifa.ifa_family == AF_INET) ? 32 : 128;
if (inet_pton(req.ifa.ifa_family, ip.Address.c_str(), &lcl.data) <= 0) throw std::string("Invalid IP address: ").append(ip.Address);
if (req.ifa.ifa_family == AF_UNSPEC) req.ifa.ifa_family = lcl.family;
addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
if ((err = rtnl_talk(rth, &req.n, 0, 0, NULL)) < 0) throw std::string("Cannot set interface IP, code: ").append(std::to_string(err));
}
// Get INET socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) throw std::string("Error creating socket to set interface flags");
struct ifreq ifr;
strcpy(ifr.ifr_name, interfaceName.c_str());
// Set interface flags
ifr.ifr_flags = IFF_POINTOPOINT | IFF_NOARP | IFF_UP | IFF_RUNNING;
if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
close(sockfd);
throw std::string("Error setting interface flags");
}
close(sockfd);
}

83
addon/main.cpp Normal file

@ -0,0 +1,83 @@
#include "wg.hh"
#include <napi.h>
Napi::Object StartAddon(const Napi::Env env, const Napi::Object exports) {
std::map<std::string, std::string> LoadConfig;
const Napi::Array keysOnLoadsNames = exports.GetPropertyNames();
for (unsigned int keyIndex = 0; keyIndex < keysOnLoadsNames.Length(); keyIndex++) {
if (!(exports.Get(keysOnLoadsNames[keyIndex]).IsString())) continue;
LoadConfig[keysOnLoadsNames[keyIndex].ToString().Utf8Value()] = exports.Get(keysOnLoadsNames[keyIndex]).ToString().Utf8Value();
}
#ifdef _WIN32
std::string statusLoading = driveLoad(LoadConfig);
if (!!(statusLoading.length())) {
Napi::Error::New(env, statusLoading).ThrowAsJavaScriptException();
return exports;
}
#endif
const Napi::Object Constants = Napi::Object::New(env);
if (getWireguardVersion().length() > 0) Constants.Set("driveVersion", getWireguardVersion());
exports.Set("deleteInterface", Napi::Function::New(env, [](const Napi::CallbackInfo &info) -> Napi::Value {
const Napi::Env env = info.Env();
if (!(info[0].IsString())) {
Napi::Error::New(env, "Set interface name").ThrowAsJavaScriptException();
return env.Undefined();
}
try {
DeleteInterface *worker = new DeleteInterface(env, info[0].ToString());
worker->Queue();
return worker->NodePromise.Promise();
} catch (std::string &err) {
Napi::Error::New(env, err).ThrowAsJavaScriptException();
return env.Undefined();
}
}));
exports.Set("listDevices", Napi::Function::New(env, [](const Napi::CallbackInfo &info) -> Napi::Value {
const Napi::Env env = info.Env();
try {
ListDevices *worker = new ListDevices(env);
worker->Queue();
return worker->NodePromise.Promise();
} catch (std::string &err) {
Napi::Error::New(env, err).ThrowAsJavaScriptException();
return env.Undefined();
}
}));
exports.Set("setConfig", Napi::Function::New(env, [](const Napi::CallbackInfo &info) -> Napi::Value {
const Napi::Env env = info.Env();
if (!(info[0].IsObject())) Napi::Error::New(env, "Set wireguard config!").ThrowAsJavaScriptException();
try {
SetConfig *worker = new SetConfig(env, info[0].ToObject());
worker->Queue();
return worker->NodePromise.Promise();
} catch (std::string &err) {
Napi::Error::New(env, err).ThrowAsJavaScriptException();
}
return env.Undefined();
}));
exports.Set("getConfig", Napi::Function::New(env, [](const Napi::CallbackInfo &info) -> Napi::Value {
const Napi::Env env = info.Env();
if (!(info[0].IsString())) {
Napi::Error::New(env, "Set interface name in fist argument!").ThrowAsJavaScriptException();
return env.Undefined();
}
try {
GetConfig *worker = new GetConfig(env, info[0].ToString());
worker->Queue();
return worker->NodePromise.Promise();
} catch (std::string &err) {
Napi::Error::New(env, err).ThrowAsJavaScriptException();
return env.Undefined();
}
}));
exports.Set("constants", Constants);
return exports;
}
NODE_API_MODULE(addon, StartAddon);

14
addon/userspace/go/go.mod Normal file

@ -0,0 +1,14 @@
module sirherobrine23.org/Wireguard/wireguard-tools.js/wg-tun
go 1.21.6
require (
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
)
require (
golang.org/x/sys v0.12.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
)

16
addon/userspace/go/go.sum Normal file

@ -0,0 +1,16 @@
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ=
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY=

211
addon/userspace/go/main.go Normal file

@ -0,0 +1,211 @@
package main
import "C"
import (
"fmt"
"os"
"os/signal"
"runtime/debug"
"strings"
"syscall"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/ipc"
"golang.zx2c4.com/wireguard/tun"
)
const levelLog = device.LogLevelError
// End process function callbacks
var TunsEndProcess = make(map[string]func())
//export callEndProcess
func callEndProcess() {
for _, f := range TunsEndProcess {
f()
}
}
func main() {}
func init() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
callEndProcess()
}()
}
// Get wireguard-go version
//
//export wgVersion
func wgVersion() *C.char {
info, ok := debug.ReadBuildInfo()
if !ok {
return C.CString("unknown")
}
for _, dep := range info.Deps {
if dep.Path == "golang.zx2c4.com/wireguard" {
return C.CString(dep.Version)
}
}
return C.CString("unknown")
}
// Check if tunnel exist
//
//export existTun
func existTun(tunName string) bool {
Files, err := os.ReadDir(socketDirectory)
if err != nil {
return false
}
for _, file := range Files {
if file.IsDir() {
continue
}
splits := strings.Split(file.Name(), "/")
splits[len(splits)-1] = strings.TrimSuffix(splits[len(splits)-1], ".sock")
if splits[len(splits)-1] == tunName {
return true
}
}
return false
}
// Delete wireguard tunnel if exist
//
//export deleteTun
func deleteTun(_tunName *C.char) *C.char {
tunName := C.GoString(_tunName)
if !existTun(tunName) {
return C.CString("Tun does not exist")
}
Files, err := os.ReadDir(socketDirectory)
if err != nil {
return C.CString("Tun does not exist")
}
for _, file := range Files {
if file.IsDir() {
continue
}
splits := strings.Split(file.Name(), "/")
splits[len(splits)-1] = strings.TrimSuffix(splits[len(splits)-1], ".sock")
if splits[len(splits)-1] == tunName {
os.Remove(strings.Join(([]string{socketDirectory, file.Name()}), "/"))
return C.CString("")
}
}
return C.CString("Tun does not exist")
}
// Create wireguard tunnel
//
//export createTun
func createTun(_tunName *C.char) *C.char {
interfaceName := C.GoString(_tunName)
if existTun(interfaceName) {
errStr := C.GoString(deleteTun(_tunName))
if errStr != "" {
return C.CString(errStr)
}
}
logger := device.NewLogger(levelLog, fmt.Sprintf("(%s) ", interfaceName))
// open TUN device (or use supplied fd)
tdev, err := tun.CreateTUN(interfaceName, device.DefaultMTU)
if err == nil {
realInterfaceName, err2 := tdev.Name()
if err2 == nil {
interfaceName = realInterfaceName
}
}
if err != nil {
return C.CString(fmt.Sprintf("Failed to create TUN device: %v", err))
}
// open UAPI file (or use supplied fd)
fileUAPI, err := ipc.UAPIOpen(interfaceName)
if err != nil {
tdev.Close()
return C.CString(fmt.Sprintf("Failed to open UAPI file: %v", err))
}
// create device
dev := device.NewDevice(tdev, conn.NewDefaultBind(), logger)
uapi, err := ipc.UAPIListen(interfaceName, fileUAPI)
if err != nil {
dev.Close()
tdev.Close()
return C.CString(fmt.Sprintf("Failed to listen on UAPI file: %v", err))
}
// UAPI Listener
uapiListened := uapi.Addr().String()
clean := func() {
logger.Verbosef("Shutting down")
uapi.Close()
dev.Close()
tdev.Close()
if uapiListened[:1] == "/" {
os.Remove(uapiListened)
}
delete(TunsEndProcess, uapiListened)
}
TunsEndProcess[uapiListened] = clean
go func() {
for {
conn, err := uapi.Accept()
if err != nil {
logger.Verbosef("UAPI listener closed")
clean()
break
}
logger.Verbosef("UAPI recive message")
go dev.IpcHandle(conn)
}
}()
go func() {
logger.Verbosef("Device started")
<-dev.Wait()
logger.Verbosef("Device closing")
clean()
}()
// Wait for device listener socket to be ready
for {
_, err := os.Stat(uapiListened)
if err == nil {
break
}
}
return C.CString("")
}
// List wireguard-go UAPI's sockets
// first\0second\0third\0forth\0last\0\0
//
//export listUapis
func listUapis() *C.char {
Files, err := os.ReadDir(socketDirectory)
if err != nil {
return C.CString("")
}
var uapis []string
for _, file := range Files {
if file.IsDir() {
continue
}
uapis = append(uapis, strings.Join(([]string{socketDirectory, file.Name()}), "/"))
}
return C.CString(strings.Join(uapis, "\x00") + "\x00")
}

@ -0,0 +1,11 @@
//go:build linux || darwin || freebsd || openbsd
package main
import (
_ "golang.zx2c4.com/wireguard/ipc"
_ "unsafe"
)
//go:linkname socketDirectory golang.xz2c4.com/wireguard/ipc.socketDirectory
var socketDirectory = "/var/run/wireguard"

@ -0,0 +1,5 @@
//go:build windows
package main
var socketDirectory = `\\.\pipe\ProtectedPrefix\Administrators\WireGuard`

54
addon/userspace/ipc.cpp Normal file

@ -0,0 +1,54 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <string>
FILE *interfaceFile(std::string sockPath) {
struct stat sbuf;
struct sockaddr_un addr = { .sun_family = AF_UNIX };
int fd = -1, ret;
FILE *f = NULL;
errno = EINVAL;
ret = snprintf(addr.sun_path, sizeof(addr.sun_path), sockPath.c_str());
if (ret < 0)
goto out;
ret = stat(addr.sun_path, &sbuf);
if (ret < 0)
goto out;
errno = EBADF;
if (!S_ISSOCK(sbuf.st_mode))
goto out;
ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (ret < 0)
goto out;
ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
if (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */
unlink(addr.sun_path);
goto out;
}
f = fdopen(fd, "r+");
if (f)
errno = 0;
out:
ret = -errno;
if (ret) {
if (fd >= 0)
close(fd);
errno = -ret;
return NULL;
}
return f;
}

@ -0,0 +1,176 @@
#include "wginterface.hh"
#include "userspace/wg-go.h"
#include "userspace/ipc.cpp"
#include "genKey/wgkeys.hh"
#include <string>
#include <vector>
#include <arpa/inet.h>
#include <errno.h>
#include <limits.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
// Ignore if required to windows
std::string driveLoad(std::map<std::string, std::string> load) {}
std::string getWireguardVersion() {
return std::string(wgVersion());
}
void WireguardDevices::getInterfaces() {
size_t len; char *device_name, *devicesList = listUapis();
for (device_name = devicesList, len = 0; (len = strlen(device_name)); device_name += len + 1) this->push_back(std::string(device_name));
}
void WireguardDevices::deleteInterface(std::string wgName) {
std::string deleteStatus = deleteTun((char*)wgName.c_str());
if (!deleteStatus.empty()) throw deleteStatus;
}
bool char_is_digit(int c) {
return (unsigned int)(('0' - 1 - c) & (c - ('9' + 1))) >> (sizeof(c) * 8 - 1);
}
void WireguardConfig::getWireguardConfig() {
if (this->name.length() == 0) throw std::string("Set wireguard name!");
else if (!(WireguardDevices().exist(this->name))) throw std::string("Wireguard interface not exist");
size_t line_buffer_len = 0, line_len;
char *key = NULL, *value;
int ret = -EPROTO;
FILE *f = interfaceFile(WireguardDevices().findSock(this->name).c_str());
if (!f) throw std::string("Failed to open interface file");
fprintf(f, "get=1\n\n");
fflush(f);
std::string peerPubKey;
bool peer = false;
while (getline(&key, &line_buffer_len, f) > 0) {
line_len = strlen(key);
if (line_len == 1 && key[0] == '\n') return;
value = strchr(key, '=');
if (!value || line_len == 0 || key[line_len - 1] != '\n') break;
*value++ = key[--line_len] = '\0';
if (this->Peers.size() == 0 && !strcmp(key, "private_key")) {
this->privateKey = wgKeys::HextoBase64(value);
this->publicKey = wgKeys::generatePublic(this->privateKey);
} else if (this->Peers.size() == 0 && !strcmp(key, "listen_port")) this->portListen = ({ unsigned long long num; char *end; if (!char_is_digit(value[0])) break; num = strtoull(value, &end, 10); if (*end || num > 0xffffU) break; num; });
else if (this->Peers.size() == 0 && !strcmp(key, "fwmark")) this->fwmark = ({ unsigned long long num; char *end; if (!char_is_digit(value[0])) break; num = strtoull(value, &end, 10); if (*end || num > 0xffffffffU) break; num; });
else if (!strcmp(key, "public_key")) {
Peer new_peer;
peerPubKey = wgKeys::HextoBase64(value);
this->Peers[peerPubKey] = new_peer;
peer = true;
} else if (peer && !strcmp(key, "preshared_key")) {
if (std::string(value) == "0000000000000000000000000000000000000000000000000000000000000000") continue;
if (strlen(value) == HexWgKeyLength) this->Peers[peerPubKey].presharedKey = wgKeys::HextoBase64(value);
} else if (peer && !strcmp(key, "persistent_keepalive_interval")) this->Peers[peerPubKey].keepInterval = ({ unsigned long long num; char *end; if (!char_is_digit(value[0])) break; num = strtoull(value, &end, 10); if (*end || num > 0xffffU) break; num; });
else if (peer && !strcmp(key, "allowed_ip")) this->Peers[peerPubKey].allowedIPs.push_back(value);
else if (peer && !strcmp(key, "last_handshake_time_sec")) {}
else if (peer && !strcmp(key, "last_handshake_time_nsec")) this->Peers[peerPubKey].lastHandshake = ({ unsigned long long num; char *end; if (!char_is_digit(value[0])) break; num = strtoull(value, &end, 10); if (*end || num > 0x7fffffffffffffffULL) break; num; });
else if (peer && !strcmp(key, "rx_bytes")) this->Peers[peerPubKey].rxBytes = ({ unsigned long long num; char *end; if (!char_is_digit(value[0])) break; num = strtoull(value, &end, 10); if (*end || num > 0xffffffffffffffffULL) break; num; });
else if (peer && !strcmp(key, "tx_bytes")) this->Peers[peerPubKey].txBytes = ({ unsigned long long num; char *end; if (!char_is_digit(value[0])) break; num = strtoull(value, &end, 10); if (*end || num > 0xffffffffffffffffULL) break; num; });
else if (!strcmp(key, "errno")) ret = -({ unsigned long long num; char *end; if (!char_is_digit(value[0])) break; num = strtoull(value, &end, 10); if (*end || num > 0x7fffffffU) break; num; });
else if (peer && !strcmp(key, "endpoint")) {
char *begin, *end;
struct addrinfo *resolved;
struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP };
if (!strlen(value)) break;
if (value[0] == '[') {
begin = &value[1];
end = strchr(value, ']');
if (!end) break;
*end++ = '\0';
if (*end++ != ':' || !*end) break;
} else {
begin = value;
end = strrchr(value, ':');
if (!end || !*(end + 1)) break;
*end++ = '\0';
}
if (getaddrinfo(begin, end, &hints, &resolved) != 0) {
ret = ENETUNREACH;
throw std::string("Failed to resolve endpoint");
}
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) || (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6))) this->Peers[peerPubKey].endpoint = value;
else {
freeaddrinfo(resolved);
break;
}
freeaddrinfo(resolved);
}
}
free(key);
fclose(f);
if (ret < 0) throw std::string("Failed to get wireguard config");
}
void WireguardConfig::setWireguardConfig() {
if (this->name.length() == 0) throw std::string("Set wireguard name!");
else if (!(WireguardDevices().exist(this->name))) {
std::string PathError = createTun((char*)this->name.c_str());
if (PathError.size() > 0) throw PathError;
}
FILE* f = interfaceFile(WireguardDevices().findSock(this->name));
fprintf(f, "set=1\n");
if (this->privateKey.length() == Base64WgKeyLength) fprintf(f, "private_key=%s\n", wgKeys::toHex(this->privateKey).c_str());
if (this->portListen >= 0) fprintf(f, "listen_port=%u\n", this->portListen);
if (this->fwmark >= 0) fprintf(f, "fwmark=%u\n", this->fwmark);
if (this->replacePeers) fprintf(f, "replace_peers=true\n");
for (auto peer : this->Peers) {
fprintf(f, "public_key=%s\n", wgKeys::toHex(peer.first).c_str());
if (peer.second.removeMe) {
fprintf(f, "remove=true\n");
continue;
}
if (peer.second.presharedKey.length() == Base64WgKeyLength) fprintf(f, "preshared_key=%s\n", wgKeys::toHex(peer.second.presharedKey).c_str());
if (peer.second.keepInterval) fprintf(f, "persistent_keepalive_interval=%u\n", peer.second.keepInterval);
if (peer.second.endpoint.length() > 2) fprintf(f, "endpoint=%s\n", peer.second.endpoint.c_str());
if (peer.second.allowedIPs.size() > 0) {
fprintf(f, "replace_allowed_ips=true\n");
for (auto s : peer.second.allowedIPs.getIpParsed()) fprintf(f, "allowed_ip=%s/%d\n", s.Address.c_str(), s.Mask);
}
}
fprintf(f, "\n");
fflush(f);
int ret, set_errno = -EPROTO;
size_t line_buffer_len = 0, line_len;
char *key = NULL, *value;
while (getline(&key, &line_buffer_len, f) > 0) {
line_len = strlen(key);
ret = set_errno;
if (line_len == 1 && key[0] == '\n') break;
value = strchr(key, '=');
if (!value || line_len == 0 || key[line_len - 1] != '\n') break;
*value++ = key[--line_len] = '\0';
if (!strcmp(key, "errno")) {
long long num;
char *end;
if (value[0] != '-' && !char_is_digit(value[0])) break;
num = strtoll(value, &end, 10);
if (*end || num > INT_MAX || num < INT_MIN) break;
set_errno = num;
}
}
free(key);
fclose(f);
ret = -(errno ? -errno : -EPROTO);
if (ret < 0) throw std::string("Cannot set configuration, code: ").append(std::to_string(ret));
}
void IpManeger::SetInInterface(std::string interfaceName) {}
void IpManeger::GetInInterface(std::string interfaceName) {}

175
addon/wg.hh Normal file

@ -0,0 +1,175 @@
#ifndef __WG_NODE__
#define __WG_NODE__
#include "wginterface.hh"
#include <napi.h>
#include <functional>
#include <iostream>
class Promised : public Napi::AsyncWorker {
public:
Napi::Promise::Deferred NodePromise;
Promised(const Napi::Env &env): AsyncWorker(env), NodePromise{env} {}
void OnError(const Napi::Error& e) override {
Napi::HandleScope scope(Env());
NodePromise.Reject(e.Value());
}
void OnOK() override {
Napi::HandleScope scope(Env());
auto call = [&](Napi::Value data) -> void { NodePromise.Resolve(data); };
runOk(call);
}
virtual void runOk(std::function<void(Napi::Value)> callback) {
Napi::HandleScope scope(Env());
callback(Env().Undefined());
};
};
class DeleteInterface : public Promised {
std::string wgName;
public:
DeleteInterface(const Napi::Env &env, const Napi::String &name): Promised(env), wgName{name.Utf8Value()} {}
void Execute() override {
WireguardDevices wgDevs = WireguardDevices();
try {
wgDevs.deleteInterface(wgName);
} catch (std::string &err) { SetError(err); }
}
};
class ListDevices : public Promised {
WireguardDevices wgDevs;
public:
ListDevices(const Napi::Env &env): Promised(env), wgDevs{} {}
void Execute() override {
try {
wgDevs.getInterfaces();
} catch (std::string &err) { SetError(err); }
}
void runOk(std::function<void(Napi::Value)> callback) override {
Napi::HandleScope scope(Env());
const Napi::Env env = Env();
const Napi::Array interf = Napi::Array::New(env);
for (auto &ip : wgDevs) interf.Set(interf.Length(), ip);
callback(interf);
}
};
class SetConfig : public WireguardConfig, public Promised {
public:
void Execute() {
try {
this->setWireguardConfig();
} catch (std::string err) {
SetError(err);
}
}
SetConfig(const Napi::Env &env, const Napi::Object config): Promised(env) {
if (!(config.Has("name"))) throw std::string("Set wireguard interface name!");
if (!(config.Has("privateKey") && config.Get("privateKey").IsString())) throw std::string("Set wireguard private key!");
this->name = config.Get("name").ToString().Utf8Value();
this->privateKey = config.Get("privateKey").ToString().Utf8Value();
if (config.Has("publicKey") && config.Get("publicKey").IsString() && config.Get("publicKey").ToString().Utf8Value().length() == Base64WgKeyLength) this->publicKey = config.Get("publicKey").ToString().Utf8Value();
if (config.Has("portListen") && config.Get("portListen").IsNumber() && config.Get("portListen").ToNumber().Int32Value() >= 0) this->portListen = config.Get("portListen").ToNumber().Int32Value();
if (config.Has("fwmark") && config.Get("fwmark").IsNumber() && config.Get("fwmark").ToNumber().Int32Value() >= 0) this->fwmark = config.Get("fwmark").ToNumber().Int32Value();
if (config.Has("replacePeers") && config.Get("replacePeers").IsBoolean()) this->replacePeers = config.Get("replacePeers").ToBoolean().Value();
if (config.Has("address") && config.Get("address").IsArray() && config.Get("address").As<Napi::Array>().Length() > 0) {
const Napi::Array Addrs(config.Get("address").As<Napi::Array>());
for (unsigned int AddrIndex = 0; AddrIndex < Addrs.Length(); AddrIndex++) {
if (!(Addrs[AddrIndex].IsString())) continue;
this->interfaceAddress.addIPMask(Addrs[AddrIndex].ToString().Utf8Value());
}
}
if (config.Has("peers") && config.Get("peers").IsObject()) {
const Napi::Object PeersObject(config.Get("peers").ToObject());
const Napi::Array PeersKeys(PeersObject.GetPropertyNames());
for (unsigned int peerIndex = 0; peerIndex < PeersKeys.Length(); peerIndex++) {
if (!(PeersObject.Get(PeersKeys[peerIndex].ToString()).IsObject())) continue;
const std::string publicKey(PeersKeys[peerIndex].ToString().Utf8Value());
const Napi::Object peerConfig(PeersObject.Get(publicKey).ToObject());
Peer peer;
if (peerConfig.Has("removeMe") && peerConfig.Get("removeMe").IsBoolean() && peerConfig.Get("removeMe").As<Napi::Boolean>().Value()) peer.removeMe = true;
else {
if (peerConfig.Has("presharedKey") && peerConfig.Get("presharedKey").IsString() && peerConfig.Get("presharedKey").ToString().Utf8Value().length() == Base64WgKeyLength) peer.presharedKey = peerConfig.Get("presharedKey").ToString().Utf8Value();
if (peerConfig.Has("keepInterval") && peerConfig.Get("keepInterval").IsNumber() && peerConfig.Get("keepInterval").ToNumber().Int32Value() > 0) peer.keepInterval = peerConfig.Get("keepInterval").ToNumber().Int32Value();
if (peerConfig.Has("endpoint") && peerConfig.Get("endpoint").IsString() && peerConfig.Get("endpoint").ToString().Utf8Value().length() == Base64WgKeyLength) peer.endpoint = peerConfig.Get("endpoint").ToString().Utf8Value();
if (peerConfig.Has("allowedIPs") && peerConfig.Get("allowedIPs").IsArray()) {
const Napi::Array ips = peerConfig.Get("allowedIPs").As<Napi::Array>();
for (unsigned int ipIndex = 0; ipIndex < ips.Length(); ipIndex++) peer.allowedIPs.addIPMask(ips[ipIndex].ToString().Utf8Value());
}
}
// Set peer in map
this->Peers[publicKey] = peer;
}
}
}
};
class GetConfig : public WireguardConfig, public Promised {
public:
GetConfig(const Napi::Env &env, const Napi::String name): Promised(env) {
this->name = name.Utf8Value();
}
void Execute() {
try {
this->getWireguardConfig();
} catch (std::string err) {
SetError(err);
}
}
void runOk(std::function<void(Napi::Value)> callback) {
Napi::HandleScope scope(Env());
const Napi::Env env = Env();
const Napi::Object config = Napi::Object::New(env);
config.Set("name", this->name);
config.Set("privateKey", this->privateKey);
if (this->publicKey.length() == Base64WgKeyLength) config.Set("publicKey", this->publicKey);
if (this->portListen > 0) config.Set("portListen", this->portListen);
if (this->fwmark > 0) config.Set("fwmark", this->fwmark);
// Interface IPs
const Napi::Array ips = Napi::Array::New(env);
for (auto ip : this->interfaceAddress) ips.Set(ips.Length(), ip);
config.Set("address", ips);
// Peers
const Napi::Object peersObj = Napi::Object::New(env);
for (auto &__peer : this->Peers) {
std::string publicKey = __peer.first;
if (publicKey.length() != Base64WgKeyLength) continue;
const Napi::Object peerConfig = Napi::Object::New(env);
const Peer config = __peer.second;
if (config.presharedKey.length() == Base64WgKeyLength) peerConfig.Set("presharedKey", config.presharedKey);
if (config.keepInterval >= 0) peerConfig.Set("keepInterval", config.keepInterval);
if (config.endpoint.length() > 0) peerConfig.Set("endpoint", config.endpoint);
peerConfig.Set("rxBytes", Napi::BigInt::New(env, (uint64_t)config.rxBytes));
peerConfig.Set("txBytes", Napi::BigInt::New(env, (uint64_t)config.txBytes));
peerConfig.Set("lastHandshake", Napi::Date::New(env, config.lastHandshake));
const Napi::Array ips = Napi::Array::New(env);
for (auto &ip : config.allowedIPs) ips.Set(ips.Length(), ip);
peerConfig.Set("allowedIPs", ips);
peersObj.Set(publicKey, peerConfig);
}
config.Set("peers", peersObj);
// Set data
callback(config);
}
};
#endif

237
addon/wginterface.hh Normal file

@ -0,0 +1,237 @@
#ifndef __WIREGUARD_ADDON__
#define __WIREGUARD_ADDON__
#include "genKey/wgkeys.hh"
#include <string>
#include <vector>
#include <map>
extern const int wgKeyLength;
extern const int Base64WgKeyLength;
std::string getWireguardVersion();
std::string driveLoad(std::map<std::string, std::string> load);
class WireguardDevices : public std::vector<std::string> {
public:
~WireguardDevices() { this->clear(); }
/** Get all interfaces from kernel and insert in vector */
void getInterfaces();
/** Delete interface from kernel network */
void deleteInterface(std::string wgName);
/** Check if exists wireguard interface intterface */
bool exist(std::string name) {
this->getInterfaces();
for (auto wgDev = this->begin(); wgDev != this->end(); ++wgDev) {
if (name == *wgDev) return true;
else {
std::string __nDev = std::string(wgDev->substr(wgDev->find_last_of("/")+1));
if (__nDev.find(".sock") != std::string::npos) __nDev = __nDev.substr(0, __nDev.find(".sock"));
if (__nDev == name) return true;
}
}
return false;
}
/** Find sock */
std::string findSock(std::string name) {
this->getInterfaces();
for (auto wgDev = this->begin(); wgDev != this->end(); ++wgDev) {
if (name == *wgDev) return *wgDev;
else if (wgDev->find_last_of(".sock") != std::string::npos) {
std::string __nDev = std::string(wgDev->substr(wgDev->find_last_of("/")+1));
if (__nDev.find(".sock") != std::string::npos) __nDev = __nDev.substr(0, __nDev.find(".sock"));
if (__nDev == name) return *wgDev;
}
}
return std::string("");
}
};
typedef struct {
std::string Address, Subnet;
int Mask, Proto;
} IpReference;
/** Maneger Interface IPs */
class IpManeger : public std::vector<std::string> {
private:
std::string getSubNet4(int Mask) {
switch (Mask) {
case 1: return "128.0.0.0";
case 2: return "192.0.0.0";
case 3: return "224.0.0.0";
case 4: return "240.0.0.0";
case 5: return "248.0.0.0";
case 6: return "252.0.0.0";
case 7: return "254.0.0.0";
case 8: return "255.0.0.0";
case 9: return "255.128.0.0";
case 10: return "255.192.0.0";
case 11: return "255.224.0.0";
case 12: return "255.240.0.0";
case 13: return "255.248.0.0";
case 14: return "255.252.0.0";
case 15: return "255.254.0.0";
case 16: return "255.255.0.0";
case 17: return "255.255.128.0";
case 18: return "255.255.192.0";
case 19: return "255.255.224.0";
case 20: return "255.255.240.0";
case 21: return "255.255.248.0";
case 22: return "255.255.252.0";
case 23: return "255.255.254.0";
case 24: return "255.255.255.0";
case 25: return "255.255.255.128";
case 26: return "255.255.255.192";
case 27: return "255.255.255.224";
case 28: return "255.255.255.240";
case 29: return "255.255.255.248";
case 30: return "255.255.255.252";
case 31: return "255.255.255.254";
default:
case 32: return "255.255.255.255";
}
}
public:
void SetInInterface(std::string interfaceName);
void GetInInterface(std::string interfaceName);
void addIPMask(std::string ip) {
IpReference xTop;
auto maskStart = ip.find("/");
auto isIPv6 = ip.find(":") != std::string::npos;
if (isIPv6) xTop.Mask = 128;
else xTop.Mask = 32;
if (maskStart == std::string::npos) xTop.Address = ip;
else {
xTop.Address = ip.substr(0, maskStart);
xTop.Mask = std::stoi(ip.substr(maskStart+1).c_str());
if (!isIPv6 && xTop.Mask > 32) throw std::string("Set valid mask to ipv4 address!");
}
xTop.Proto = isIPv6 ? 6 : 4;
if (!isIPv6) xTop.Subnet = this->getSubNet4(xTop.Mask);
this->push_back(xTop.Address.append("/").append(std::to_string(xTop.Mask)));
}
std::vector<IpReference> getIpParsed() {
std::vector<IpReference> xTops;
for (auto ipAddrr = this->begin(); ipAddrr != this->end(); ++ipAddrr) {
IpReference nTop;
auto maskStart = ipAddrr->find("/");
nTop.Address = ipAddrr->substr(0, maskStart);
nTop.Mask = std::stoi(ipAddrr->substr(maskStart+1).c_str());
nTop.Proto = (nTop.Address.find(":") != std::string::npos) ? 6 : 4;
if (nTop.Proto == 6) nTop.Subnet = this->getSubNet4(nTop.Mask);
xTops.push_back(nTop);
}
return xTops;
}
};
/**
* peer class
*/
class Peer {
public:
Peer() {
this->removeMe = false;
this->keepInterval = 0;
this->rxBytes = 0;
this->txBytes = 0;
this->lastHandshake = 0;
}
/** Remove specifies if the peer with this public key should be removed from a device's peer list. */
bool removeMe;
/** PresharedKey is an optional preshared key which may be used as an additional layer of security for peer communications. */
std::string presharedKey;
/** Endpoint is the most recent source address used for communication by this Peer. */
std::string endpoint;
/** PersistentKeepaliveInterval specifies how often an "empty" packet is sent to a peer to keep a connection alive.
*
* A value of 0 indicates that persistent keepalives are disabled.
*/
unsigned int keepInterval;
/** Last peer handshake in milisseconds */
unsigned long long lastHandshake;
/** Peer data received (RX) and transferred (TX) */
unsigned long long rxBytes, txBytes;
/** Peer ips allowed to recive data */
IpManeger allowedIPs;
};
/**
* Set and Get Wireguard configs
*/
class WireguardConfig {
public:
WireguardConfig() {
this->portListen = 0;
this->fwmark = 0;
this->replacePeers = false;
}
/** Wireguard interface name */
std::string name;
/** Wireguard interface private key */
std::string privateKey;
/** Wireguard interface public key */
std::string publicKey;
/** Wireguard port listening or listened */
unsigned short portListen;
/** FirewallMark specifies a device's firewall mark else set to 0, the firewall mark will be cleared */
unsigned int fwmark;
/** Replace Wireguard peers if wireguard interface exists */
bool replacePeers;
/** Wireguard interface IPs */
IpManeger interfaceAddress;
/** Wireguard peers */
std::map<std::string, Peer> Peers;
/** Set wireguard config in interface */
void setWireguardConfig();
/** Get configuration from wireguard */
void getWireguardConfig();
};
namespace WireguardUserspace {
// Get Wireguard-go version
std::string getWireguardVersion();
// Close all wireguard tunels
void closeAllWireguardTunnels();
// Create tunel and return path to tunel
std::string createWireguardTunnel(std::string wgName);
// Delete tunel by name and return true if success
void deleteWireguardTunnel(std::string wgName);
// List all tunels
std::vector<std::string> listTunnels();
// Check if tunel exist
bool checkIfExistTunnel(std::string wgName);
};
#endif

BIN
addon/win/arm/wireguard.dll Normal file

Binary file not shown.

530
addon/win/wginterface.cpp Normal file

@ -0,0 +1,530 @@
#include "wginterface.hh"
#include "genKey/wgkeys.hh"
#include <win/wireguard.h>
#include <string>
#include <locale>
#include <codecvt>
#include <vector>
#include <map>
#include <iostream>
#include <windows.h>
#include <tlhelp32.h>
#include <accctrl.h>
#include <aclapi.h>
#include <stdio.h>
#include <stdbool.h>
#include <fcntl.h>
#include <winsock2.h>
#include <ws2ipdef.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <bcrypt.h>
#include <wincrypt.h>
#include <sysinfoapi.h>
#include <winternl.h>
#include <cstdlib>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <devguid.h>
#include <ws2def.h>
#include <netioapi.h>
#include <chrono>
#include <thread>
#include <comdef.h>
#include <Wbemidl.h>
#include <stdexcept>
const DEVPROPKEY devpkey_name = { { 0x65726957, 0x7547, 0x7261, { 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x4b, 0x65, 0x79 } }, DEVPROPID_FIRST_USABLE + 1 };
#define IFNAMSIZ MAX_ADAPTER_NAME - 1
// Create Wireguard adapter
static WIREGUARD_CREATE_ADAPTER_FUNC *WireGuardCreateAdapter;
// Open Wireguard adapter by name if exists
static WIREGUARD_OPEN_ADAPTER_FUNC *WireGuardOpenAdapter;
// Close Wireguard adapter
static WIREGUARD_CLOSE_ADAPTER_FUNC *WireGuardCloseAdapter;
// Get Wireguard adapter LUID
static WIREGUARD_GET_ADAPTER_LUID_FUNC *WireGuardGetAdapterLUID;
// Get running Wireguard driver version
static WIREGUARD_GET_RUNNING_DRIVER_VERSION_FUNC *WireGuardGetRunningDriverVersion;
// Delete Wireguard driver if non exists adapters
static WIREGUARD_DELETE_DRIVER_FUNC *WireGuardDeleteDriver;
// Set logger for Wireguard adapter
static WIREGUARD_SET_LOGGER_FUNC *WireGuardSetLogger;
// Set adapter logging for Wireguard adapter
static WIREGUARD_SET_ADAPTER_LOGGING_FUNC *WireGuardSetAdapterLogging;
// Get adapter state for Wireguard adapter
static WIREGUARD_GET_ADAPTER_STATE_FUNC *WireGuardGetAdapterState;
// Set adapter state for Wireguard adapter
static WIREGUARD_SET_ADAPTER_STATE_FUNC *WireGuardSetAdapterState;
// Get Wireguard adapter configuration
static WIREGUARD_GET_CONFIGURATION_FUNC *WireGuardGetConfiguration;
// Set Wireguard adapter configuration
static WIREGUARD_SET_CONFIGURATION_FUNC *WireGuardSetConfiguration;
LPCWSTR WireguardAddonDescription = WireguardAddonDescription;
// Function to check if the current user has administrator privileges
bool IsRunAsAdmin() {
BOOL fRet = FALSE;
HANDLE hToken = NULL;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
TOKEN_ELEVATION Elevation;
DWORD cbSize = sizeof(TOKEN_ELEVATION);
if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) {
fRet = Elevation.TokenIsElevated;
}
}
if (hToken) CloseHandle(hToken);
return !!fRet;
}
std::string convertWcharString(const wchar_t *wideStr) {
// Create a wstring from the wide character string
std::wstring wideString(wideStr);
// Use std::wstring_convert for conversion
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::string narrowString = converter.to_bytes(wideString);
// Now narrowString is a std::string containing the converted string
return narrowString;
}
LPCWSTR toLpcwstr(std::string s) {
wchar_t* wString = new wchar_t[s.length()+1];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, wString, s.length()+1);
return wString;
}
std::string parseEndpoint(SOCKADDR_INET *input) {
if (!(input->si_family == AF_INET || input->si_family == AF_INET6)) return "";
char saddr[INET6_ADDRSTRLEN];
input->si_family == AF_INET ? inet_ntop(AF_INET, &input->Ipv4.sin_addr, saddr, INET_ADDRSTRLEN) : inet_ntop(AF_INET6, &input->Ipv6.sin6_addr, saddr, INET6_ADDRSTRLEN);
if (input->si_family == AF_INET6) return std::string("[").append(saddr).append("]:").append(std::to_string(htons(input->Ipv6.sin6_port)));
return std::string(saddr).append(":").append(std::to_string(htons(input->Ipv4.sin_port)));
}
std::string getErrorString(DWORD errorMessageID) {
if (errorMessageID == 0 || errorMessageID < 0) std::string("Error code: ").append(std::to_string(errorMessageID));
LPSTR messageBuffer = nullptr;
//Ask Win32 to give us the string version of that message ID.
//The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
//Copy the error message into a std::string.
std::string message(messageBuffer, size);
//Free the Win32's string's buffer.
LocalFree(messageBuffer);
return std::string("Error code: ").append(std::to_string(errorMessageID)).append(", Message: ").append(message);
}
std::string driveLoad(std::map<std::string, std::string> load) {
if (!IsRunAsAdmin()) return "Run nodejs with administrator privilegies";
auto DLLPATH = load["WIN32DLLPATH"];
if (!(DLLPATH.length())) return "Require Wireguard DLL file path!";
LPCWSTR dllPath = toLpcwstr(DLLPATH);
HMODULE WireGuardDll = LoadLibraryExW(dllPath, NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!WireGuardDll) return std::string("Failed to initialize WireGuardNT, ").append(getErrorString(GetLastError()));;
#define X(Name) ((*(FARPROC *)&Name = GetProcAddress(WireGuardDll, #Name)) == NULL)
if (X(WireGuardCreateAdapter) || X(WireGuardOpenAdapter) || X(WireGuardCloseAdapter) || X(WireGuardGetAdapterLUID) || X(WireGuardGetRunningDriverVersion) || X(WireGuardDeleteDriver) || X(WireGuardSetLogger) || X(WireGuardSetAdapterLogging) || X(WireGuardGetAdapterState) || X(WireGuardSetAdapterState) || X(WireGuardGetConfiguration) || X(WireGuardSetConfiguration))
#undef X
{
DWORD LastError = GetLastError();
FreeLibrary(WireGuardDll);
SetLastError(LastError);
return std::string("Failed to set Functions from WireGuardNT DLL, ").append(getErrorString(GetLastError()));;
}
return "";
}
std::string getWireguardVersion() {
// Create interface to get version
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardCreateAdapter(L"getWgVersion", WireguardAddonDescription, NULL);
WireGuardCloseAdapter(Adapter);
DWORD Version = WireGuardGetRunningDriverVersion();
if (Version == 0) {
auto statusErr = GetLastError();
WireGuardCloseAdapter(Adapter);
if (statusErr == ERROR_FILE_NOT_FOUND) return "Driver not loaded";
return std::string("Cannot get version drive, ").append(getErrorString(GetLastError()));
}
return std::string("WireGuardNT v").append(std::to_string((Version >> 16) & 0xff)).append(".").append(std::to_string((Version >> 0) & 0xff)).append(".").append(std::to_string((Version >> 8) & 0xff));
}
void WireguardDevices::getInterfaces() {
HDEVINFO dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, L"SWD\\WireGuard", NULL, DIGCF_PRESENT, NULL, NULL, NULL);
if (dev_info == INVALID_HANDLE_VALUE) throw std::string("Cannot get devices");
for (DWORD devIndex = 0;; ++devIndex) {
SP_DEVINFO_DATA dev_info_data;
dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiEnumDeviceInfo(dev_info, devIndex, &dev_info_data)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) break;
continue;
}
DWORD buf_len; ULONG status, problem_code;
WCHAR adapter_name[MAX_ADAPTER_NAME];
char *interface_name;
DEVPROPTYPE prop_type;
if (!SetupDiGetDevicePropertyW(dev_info, &dev_info_data, &devpkey_name, &prop_type, (PBYTE)adapter_name, sizeof(adapter_name), NULL, 0) || prop_type != DEVPROP_TYPE_STRING) continue;
adapter_name[_countof(adapter_name) - 1] = L'0';
if (!adapter_name[0]) continue;
buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, NULL, 0, NULL, NULL);
if (!buf_len) continue;
interface_name = (char *)malloc(buf_len);
if (!interface_name) continue;
buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, interface_name, buf_len, NULL, NULL);
if (!buf_len) {
free(interface_name);
continue;
}
if (CM_Get_DevNode_Status(&status, &problem_code, dev_info_data.DevInst, 0) == CR_SUCCESS && (status & (DN_DRIVER_LOADED | DN_STARTED)) == (DN_DRIVER_LOADED | DN_STARTED)) this->push_back(std::string(interface_name));
free(interface_name);
}
SetupDiDestroyDeviceInfoList(dev_info);
}
/*
Delete wireguard interface
Current bug from Addon: Set interface down and not delete wireguard interface
*/
void WireguardDevices::deleteInterface(std::string wgName) {
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(wgName));
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);
}
/**
* Change point from calloc or malloc, T: from, C: to
*/
template <typename T, typename C> C* changePoint(T *x) {
return reinterpret_cast<C*>(((char*)x) + sizeof(T));
}
void WireguardConfig::getWireguardConfig() {
if (this->name.length() == 0) throw std::string("Require interface name!");
else if (!(WireguardDevices().exist(this->name))) throw std::string("This interface not exists in Wireguard-Tools.js addon!");
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(this->name));
if (!Adapter) throw std::string("This interface not exists in Wireguard-Tools.js addon!");
NET_LUID InterfaceLuid;
WireGuardGetAdapterLUID(Adapter, &InterfaceLuid);
this->interfaceAddress.GetInInterface(this->name);
DWORD buf_len = 0;
WIREGUARD_INTERFACE *wg_iface = nullptr;
while (!(WireGuardGetConfiguration(Adapter, wg_iface, &buf_len))) {
free(wg_iface);
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) throw std::string("Failed get interface config, ").append(std::to_string(-errno));
}
if (wg_iface->Flags & WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_PRIVATE_KEY) this->privateKey = wgKeys::toString(wg_iface->PrivateKey);
if (wg_iface->Flags & WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_PUBLIC_KEY) this->publicKey = wgKeys::toString(wg_iface->PublicKey);
if (wg_iface->ListenPort > 0) this->portListen = wg_iface->ListenPort;
WIREGUARD_PEER *wg_peer = changePoint<WIREGUARD_INTERFACE, WIREGUARD_PEER>(wg_iface);
for (DWORD i = 0; i < wg_iface->PeersCount; i++) {
auto pubKey = wgKeys::toString(wg_peer->PublicKey);
Peer peerConfig;
peerConfig.txBytes = wg_peer->TxBytes;
peerConfig.rxBytes = wg_peer->RxBytes;
peerConfig.lastHandshake = (wg_peer->LastHandshake - 116444736000000000LL) / (100 / 1000);
if (wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PRESHARED_KEY) peerConfig.presharedKey = wgKeys::toString(wg_peer->PresharedKey);
if (wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_ENDPOINT) peerConfig.endpoint = parseEndpoint(&wg_peer->Endpoint);
if (wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PERSISTENT_KEEPALIVE) peerConfig.keepInterval = wg_peer->PersistentKeepalive;
WIREGUARD_ALLOWED_IP* wg_aip = changePoint<WIREGUARD_PEER, WIREGUARD_ALLOWED_IP>(wg_peer);
for (DWORD __aip = 0; __aip < wg_peer->AllowedIPsCount; __aip++) {
char saddr[INET6_ADDRSTRLEN];
if (wg_aip->AddressFamily == AF_INET) {
inet_ntop(AF_INET, &wg_aip->Address.V6, saddr, INET_ADDRSTRLEN);
peerConfig.allowedIPs.push_back(std::string(saddr).append("/").append(std::to_string(wg_aip->Cidr)));
} else if (wg_aip->AddressFamily == AF_INET6) {
inet_ntop(AF_INET6, &wg_aip->Address.V6, saddr, INET6_ADDRSTRLEN);
peerConfig.allowedIPs.push_back(std::string(saddr).append("/").append(std::to_string(wg_aip->Cidr)));
}
++wg_aip;
}
wg_peer = reinterpret_cast<WIREGUARD_PEER*>(wg_aip);
this->Peers[pubKey] = peerConfig;
}
free(wg_iface);
}
void WireguardConfig::setWireguardConfig() {
if (this->name.length() == 0) throw std::string("Require interface name!");
else if (this->name.length() > IFNAMSIZ) throw std::string("Interface name too long");
DWORD buf_len = sizeof(WIREGUARD_INTERFACE);
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)) 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) throw std::string("Cannot alloc buff");
wg_iface->PeersCount = 0;
wgKeys::stringToKey(wg_iface->PrivateKey, this->privateKey);
wg_iface->Flags = WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_PRIVATE_KEY;
wg_iface->ListenPort = this->portListen;
if (this->portListen >= 0) wg_iface->Flags = (WIREGUARD_INTERFACE_FLAG)(wg_iface->Flags|WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_LISTEN_PORT);
if (this->replacePeers) wg_iface->Flags = (WIREGUARD_INTERFACE_FLAG)(wg_iface->Flags|WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_REPLACE_PEERS);
WIREGUARD_ALLOWED_IP *wg_aip;
WIREGUARD_PEER *wg_peer = changePoint<WIREGUARD_INTERFACE, WIREGUARD_PEER>(wg_iface);
for (auto __peer : this->Peers) {
auto peerPublicKey = __peer.first; auto peerConfig = __peer.second;
wgKeys::stringToKey(wg_peer->PublicKey, peerPublicKey);
wg_peer->Flags = WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PUBLIC_KEY;
wg_peer->AllowedIPsCount = 0;
wg_iface->PeersCount++;
if (peerConfig.removeMe) {
wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_REMOVE);
wg_peer = changePoint<WIREGUARD_PEER, WIREGUARD_PEER>(wg_peer);
} else {
if (peerConfig.presharedKey.size() == B64_WG_KEY_LENGTH) {
wgKeys::stringToKey(wg_peer->PresharedKey, peerConfig.presharedKey);
wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PRESHARED_KEY);
}
wg_peer->PersistentKeepalive = peerConfig.keepInterval;
if (peerConfig.keepInterval >= 0) wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PERSISTENT_KEEPALIVE);
if (peerConfig.endpoint.size() > 0) {
int ret, retries = ([]() -> int {
unsigned long ret;
char *retries = getenv("WG_ENDPOINT_RESOLUTION_RETRIES"), *end;
if (!retries) return 15;
if (!strcmp(retries, "infinity")) return -1;
ret = strtoul(retries, &end, 10);
if (*end || ret > INT_MAX) {
fprintf(stderr, "Unable to parse WG_ENDPOINT_RESOLUTION_RETRIES: `%s'\n", retries);
exit(1);
}
return (int)ret;
})();
char *begin, *end;
auto mmutable = strdup(peerConfig.endpoint.c_str());
if (!mmutable) throw std::string("strdup");
if (!peerConfig.endpoint.size()) {
free(mmutable);
throw std::string("Unable to parse empty endpoint");
}
if (mmutable[0] == '[') {
begin = &mmutable[1];
end = strchr(mmutable, ']');
if (!end) {
free(mmutable);
throw std::string("Unable to find matching brace of endpoint: ").append(peerConfig.endpoint);
}
*end++ = '\0';
if (*end++ != ':' || !*end) {
free(mmutable);
throw std::string("Unable to find port of endpoint: ").append(peerConfig.endpoint);
}
} else {
begin = mmutable;
end = strrchr(mmutable, ':');
if (!end || !*(end + 1)) {
free(mmutable);
throw std::string("Unable to find port of endpoint: ").append(peerConfig.endpoint);
}
*end++ = '\0';
}
ADDRINFOA *resolved;
for (unsigned int timeout = 1000000;; timeout = ((20000000) < (timeout * 6 / 5) ? (20000000) : (timeout * 6 / 5))) {
// ret = getaddrinfo(begin, end, &hints, &resolved);
ret = getaddrinfo(begin, end, NULL, &resolved);
if (!ret) break;
if (ret == EAI_NONAME || ret == EAI_FAIL ||
#ifdef EAI_NODATA
ret == EAI_NODATA ||
#endif
(retries >= 0 && !retries--)) {
free(mmutable);
throw std::string("Error code: ").append(std::to_string(ret));
}
std::this_thread::sleep_for(std::chrono::microseconds(timeout));
}
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(SOCKADDR_IN))) memcpy(&wg_peer->Endpoint.Ipv4, resolved->ai_addr, resolved->ai_addrlen);
else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(SOCKADDR_IN6)) memcpy(&wg_peer->Endpoint.Ipv6, resolved->ai_addr, resolved->ai_addrlen);
else {
freeaddrinfo(resolved);
throw std::string("Neither IPv4 nor IPv6 address found: ").append(peerConfig.endpoint);
}
freeaddrinfo(resolved);
free(mmutable);
wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_ENDPOINT);
}
wg_aip = changePoint<WIREGUARD_PEER, WIREGUARD_ALLOWED_IP>(wg_peer);
for (auto Ip : peerConfig.allowedIPs.getIpParsed()) {
wg_aip->AddressFamily = Ip.Proto == 4 ? AF_INET : AF_INET6;
wg_aip->Cidr = Ip.Mask;
if (Ip.Proto == 6 && inet_pton(AF_INET6, Ip.Address.c_str(), &wg_aip->Address.V6) == 1) wg_aip->AddressFamily = AF_INET6;
else if (Ip.Proto == 4 && inet_pton(AF_INET, Ip.Address.c_str(), &wg_aip->Address.V4) == 1) wg_aip->AddressFamily = AF_INET;
else continue;
wg_peer->AllowedIPsCount++;
wg_aip = changePoint<WIREGUARD_ALLOWED_IP, WIREGUARD_ALLOWED_IP>(wg_aip);
if (!(wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_REPLACE_ALLOWED_IPS)) wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_REPLACE_ALLOWED_IPS);
}
wg_peer = reinterpret_cast<WIREGUARD_PEER*>(((char*)wg_aip));
}
}
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(this->name));
if (!Adapter) Adapter = WireGuardCreateAdapter(toLpcwstr(this->name), WireguardAddonDescription, NULL);
if (!Adapter) throw std::string("Failed to create adapter, ").append(getErrorString(GetLastError()));
auto status = WireGuardSetConfiguration(Adapter, reinterpret_cast<WIREGUARD_INTERFACE*>(wg_iface), buf_len) && WireGuardSetAdapterState(Adapter, WIREGUARD_ADAPTER_STATE::WIREGUARD_ADAPTER_STATE_UP);
free(wg_iface);
if (!status) {
auto status = GetLastError();
WireGuardCloseAdapter(Adapter);
throw std::string("Failed to set interface config, ").append(getErrorString(status));
}
this->interfaceAddress.SetInInterface(this->name);
}
void IpManeger::GetInInterface(std::string interfaceName) {
// Define the InterfaceLuid of the network interface you want to query
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(interfaceName));
if (!Adapter) throw std::string("Cannot open wireguard adapter");
NET_LUID InterfaceLuid;
WireGuardGetAdapterLUID(Adapter, &InterfaceLuid);
ULONG bufferSize = 0; // Initial buffer size
// Call GetAdaptersAddresses to get the required buffer size
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, nullptr, &bufferSize) == ERROR_BUFFER_OVERFLOW) {
// Allocate memory for the buffer
PIP_ADAPTER_ADDRESSES addresses = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(bufferSize));
// Call GetAdaptersAddresses again with the allocated buffer
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, addresses, &bufferSize) == NO_ERROR) {
// Iterate through the list of adapters
for (PIP_ADAPTER_ADDRESSES adapter = addresses; adapter != nullptr; adapter = adapter->Next) {
// Check if the adapter matches the specified InterfaceLuid
if (memcmp(&adapter->Luid, &InterfaceLuid, sizeof(NET_LUID)) == 0) {
// Iterate through the list of IP addresses associated with the adapter
for (PIP_ADAPTER_UNICAST_ADDRESS address = adapter->FirstUnicastAddress; address != nullptr; address = address->Next) {
// Access the IP address in the address structure
sockaddr* sa = address->Address.lpSockaddr;
char ip[INET6_ADDRSTRLEN];
if (sa->sa_family == AF_INET) {
inet_ntop(AF_INET, &reinterpret_cast<sockaddr_in*>(sa)->sin_addr, ip, sizeof(ip));
} else if (sa->sa_family == AF_INET6) {
inet_ntop(AF_INET6, &reinterpret_cast<sockaddr_in6*>(sa)->sin6_addr, ip, sizeof(ip));
} else continue;
// Print or use the IP address as needed
this->addIPMask(std::string(ip).append("/").append(std::to_string(address->Address.iSockaddrLength)));
}
}
}
}
// Free the allocated buffer
free(addresses);
}
}
// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry
void IpManeger::SetInInterface(std::string interfaceName) {
if (this->size() == 0) return;
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(interfaceName));
if (!Adapter) throw std::string("Cannot open wireguard adapter");
NET_LUID InterfaceLuid;
WireGuardGetAdapterLUID(Adapter, &InterfaceLuid);
ULONG bufferSize = 0; // Initial buffer size
// Call GetAdaptersAddresses to get the required buffer size
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, nullptr, &bufferSize) == ERROR_BUFFER_OVERFLOW) {
// Allocate memory for the buffer
PIP_ADAPTER_ADDRESSES addresses = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(bufferSize));
// Call GetAdaptersAddresses again with the allocated buffer
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, addresses, &bufferSize) == NO_ERROR) {
// Iterate through the list of adapters
for (PIP_ADAPTER_ADDRESSES adapter = addresses; adapter != nullptr; adapter = adapter->Next) {
// Check if the adapter matches the specified InterfaceLuid
if (memcmp(&adapter->Luid, &InterfaceLuid, sizeof(NET_LUID)) == 0) {
// Iterate through the list of IP addresses associated with the adapter
for (PIP_ADAPTER_UNICAST_ADDRESS address = adapter->FirstUnicastAddress; address != nullptr; address = address->Next) {
// Access the IP address in the address structure
sockaddr* sa = address->Address.lpSockaddr;
char ip[INET6_ADDRSTRLEN];
if (sa->sa_family == AF_INET) {
inet_ntop(AF_INET, &reinterpret_cast<sockaddr_in*>(sa)->sin_addr, ip, sizeof(ip));
} else if (sa->sa_family == AF_INET6) {
inet_ntop(AF_INET6, &reinterpret_cast<sockaddr_in6*>(sa)->sin6_addr, ip, sizeof(ip));
} else continue;
// Delete the IP address
MIB_UNICASTIPADDRESS_ROW row;
memset(&row, 0, sizeof(row));
WireGuardGetAdapterLUID(Adapter, &row.InterfaceLuid);
if (sa->sa_family == AF_INET) {
row.Address.si_family = AF_INET;
inet_pton(AF_INET, ip, &row.Address.Ipv4.sin_addr);
} else if (sa->sa_family == AF_INET6) {
row.Address.si_family = AF_INET6;
inet_pton(AF_INET6, ip, &row.Address.Ipv6.sin6_addr);
} else continue;
if (DeleteUnicastIpAddressEntry(&row) != NO_ERROR) throw std::string("Cannot delete IP address from interface");
}
}
}
}
// Free the allocated buffer
free(addresses);
}
for (auto ip : this->getIpParsed()) {
MIB_UNICASTIPADDRESS_ROW row;
memset(&row, 0, sizeof(row));
memcpy(&row.InterfaceLuid, &InterfaceLuid, sizeof(NET_LUID));
row.DadState = NldsPreferred;
row.ValidLifetime = 0xffffffff;
row.PreferredLifetime = 0xffffffff;
row.OnLinkPrefixLength = ip.Mask;
if (ip.Proto == 4) {
row.Address.si_family = AF_INET;
inet_pton(AF_INET, ip.Address.c_str(), &row.Address.Ipv4.sin_addr);
} else if (ip.Proto == 6) {
row.Address.si_family = AF_INET6;
inet_pton(AF_INET6, ip.Address.c_str(), &row.Address.Ipv6.sin6_addr);
} else continue;
if (CreateUnicastIpAddressEntry(&row) != NO_ERROR) throw std::string("Cannot add IP address to interface");
}
}

BIN
addon/win/x86/wireguard.dll Normal file

Binary file not shown.

@ -1,163 +0,0 @@
#include <napi.h>
#include "wgkeys.hh"
class privateKeyWorker : public Napi::AsyncWorker {
private:
std::string pskString;
Napi::Promise::Deferred genPromise;
public:
~privateKeyWorker() {}
privateKeyWorker(const Napi::Env env) : AsyncWorker(env), genPromise{env} {}
Napi::Promise getPromise() { return genPromise.Promise(); }
void Execute() override {
wg_key keyg;
wgKeys::generatePrivate(keyg);
pskString = wgKeys::toString(keyg);
}
void OnOK() override {
Napi::HandleScope scope(Env());
genPromise.Resolve(Napi::String::New(Env(), pskString));
}
void OnError(const Napi::Error& e) override {
Napi::HandleScope scope(Env());
genPromise.Reject(e.Value());
}
};
class publicKeyWorker : public Napi::AsyncWorker {
private:
std::string privKey, pubString;
Napi::Promise::Deferred genPromise;
public:
~publicKeyWorker() {}
publicKeyWorker(const Napi::Env env, std::string privateKey) : AsyncWorker(env), privKey(privateKey), genPromise{env} {}
Napi::Promise getPromise() { return genPromise.Promise(); }
void Execute() override {
wg_key interfacePrivateKey, interfacePublicKey;
try {
wgKeys::stringToKey(interfacePrivateKey, privKey);
wgKeys::generatePublic(interfacePublicKey, interfacePrivateKey);
pubString = wgKeys::toString(interfacePublicKey);
} catch (std::string &err) {
SetError(err);
}
}
void OnOK() override {
Napi::HandleScope scope(Env());
genPromise.Resolve(Napi::String::New(Env(), pubString));
}
void OnError(const Napi::Error& e) override {
Napi::HandleScope scope(Env());
genPromise.Reject(e.Value());
}
};
class presharedKeyWorker : public Napi::AsyncWorker {
private:
std::string pskString;
Napi::Promise::Deferred genPromise;
public:
~presharedKeyWorker() {}
presharedKeyWorker(const Napi::Env env) : AsyncWorker(env), genPromise{env} {}
Napi::Promise getPromise() { return genPromise.Promise(); }
void Execute() override {
wg_key keyg;
wgKeys::generatePreshared(keyg);
pskString = wgKeys::toString(keyg);
}
void OnOK() override {
Napi::HandleScope scope(Env());
genPromise.Resolve(Napi::String::New(Env(), pskString));
}
void OnError(const Napi::Error& e) override {
Napi::HandleScope scope(Env());
genPromise.Reject(e.Value());
}
};
class genKeysWorker : public Napi::AsyncWorker {
private:
std::string privateKey, publicKey, presharedKey;
bool withPreshared = false;
Napi::Promise::Deferred genPromise;
public:
~genKeysWorker() {}
genKeysWorker(const Napi::Env env, bool withPresharedKey) : AsyncWorker(env), withPreshared(withPresharedKey), genPromise{env} {}
Napi::Promise getPromise() { return genPromise.Promise(); }
void Execute() override {
wg_key keyPriv, preshe, pub;
wgKeys::generatePrivate(keyPriv);
privateKey = wgKeys::toString(keyPriv);
wgKeys::generatePublic(pub, keyPriv);
publicKey = wgKeys::toString(pub);
if (!withPreshared) return;
wgKeys::generatePreshared(preshe);
presharedKey = wgKeys::toString(preshe);
}
void OnOK() override {
Napi::HandleScope scope(Env());
const Napi::Env env = Env();
auto keys = Napi::Object::New(env);
keys.Set("privateKey", privateKey);
keys.Set("publicKey", publicKey);
if (withPreshared) keys.Set("presharedKey", presharedKey);
genPromise.Resolve(keys);
}
void OnError(const Napi::Error& e) override {
Napi::HandleScope scope(Env());
genPromise.Reject(e.Value());
}
};
Napi::Object Init(Napi::Env exportsEnv, Napi::Object exports) {
auto constants = Napi::Object::New(exportsEnv);
constants.Set("WG_KEY_LENGTH", WG_KEY_LENGTH);
constants.Set("B64_WG_KEY_LENGTH", B64_WG_KEY_LENGTH);
exports.Set("constants", constants);
exports.Set("presharedKey", Napi::Function::New(exportsEnv, [&](const Napi::CallbackInfo& info) {
const Napi::Env env = info.Env();
// Callback function is latest argument
auto *Gen = new presharedKeyWorker(env);
Gen->Queue();
return Gen->getPromise();
}));
exports.Set("privateKey", Napi::Function::New(exportsEnv, [&](const Napi::CallbackInfo& info) {
const Napi::Env env = info.Env();
// Callback function is latest argument
auto *Gen = new privateKeyWorker(env);
Gen->Queue();
return Gen->getPromise();
}));
exports.Set("publicKey", Napi::Function::New(exportsEnv, [&](const Napi::CallbackInfo& info) -> Napi::Value {
const Napi::Env env = info.Env();
if (!(info[0].IsString())) {
Napi::Error::New(env, "Require private key").ThrowAsJavaScriptException();
return env.Undefined();
}
// Callback function is latest argument
auto *Gen = new publicKeyWorker(env, info[0].ToString().Utf8Value().c_str());
Gen->Queue();
return Gen->getPromise();
}));
exports.Set("genKey", Napi::Function::New(exportsEnv, [&](const Napi::CallbackInfo &info) {
const Napi::Env env = info.Env();
bool withPreshared = false;
if (info[0].IsBoolean()) withPreshared = info[0].ToBoolean().Value();
auto Gen = new genKeysWorker(env, withPreshared);
Gen->Queue();
return Gen->getPromise();
}));
return exports;
}
NODE_API_MODULE(addon, Init);

@ -1,29 +0,0 @@
#ifndef _WGKEY_
#define _WGKEY_
#include <string>
const int WG_KEY_LENGTH = 32, B64_WG_KEY_LENGTH = ((WG_KEY_LENGTH + 2) / 3) * 4;
typedef unsigned char wg_key[WG_KEY_LENGTH];
typedef char wg_key_b64_string[B64_WG_KEY_LENGTH + 1];
typedef long long fe[16];
namespace wgKeys {
/* Convert wg_key to std::string */
std::string toString(const wg_key key);
/* base64 to wg_key */
void stringToKey(wg_key key, std::string keyBase64);
// bool key_is_zero(wg_key key);
/* Generate preshared key */
void generatePreshared(wg_key key);
/* Generate private key (based on generatePreshared) */
void generatePrivate(wg_key private_key);
/* Get public key from private key */
void generatePublic(wg_key public_key, const wg_key private_key);
}
#endif

@ -1,6 +0,0 @@
#include <vector>
#include <string>
std::string setIps(std::string name, std::vector<std::string> ips) {
return "";
}

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

@ -1,353 +0,0 @@
#include <napi.h>
#include <iostream>
#include <unistd.h>
#include <time.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <net/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if_link.h>
#include <linux/if_addr.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <sys/ioctl.h>
#include <cerrno>
#include <ifaddrs.h>
#include <string>
#include <sysexits.h>
#include <sys/types.h>
#include "wginterface.hh"
#include "linux/set_ip.cpp"
extern "C" {
#include "linux/wireguard.h"
}
std::string getKernelMesage(int errStatus) {
std::string message = std::string("Error code: ").append(std::to_string(errStatus));
return message;
}
unsigned long maxName() {
return IFNAMSIZ;
}
std::string versionDrive() {
return "Kernel";
}
class List {
public:
std::vector<std::string> devs;
List() {}
~List() {
devs.clear();
}
void getAll() {
char *device_name, *devicesList = wg_list_device_names();
if (!devicesList) throw std::string("Unable to get device names");
size_t len;
for ((device_name) = (devicesList), (len) = 0; ((len) = strlen(device_name)); (device_name) += (len) + 1) devs.push_back(std::string(device_name));
free(devicesList);
}
bool exist(std::string ifname) {
this->getAll();
for (auto wg : devs) if (wg == ifname) return true;
return false;
}
};
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.~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)));
}
int createInterface(std::string &wgName) {
bool createInterface = true;
size_t len = 0;
char *device_name, *devicesList = wg_list_device_names();
if (!!devicesList) {
for ((device_name) = (devicesList), (len) = 0; ((len) = strlen(device_name)); (device_name) += (len) + 1) {
if (std::string(device_name) == wgName) {
createInterface = false;
break;
}
}
free(devicesList);
}
if (createInterface) return wg_add_device(wgName.c_str());
return 0;
}
void setConfig::Execute() {
int res = createInterface(wgName);
if (res < 0) {
SetError(std::string("Cannot create wireguard interface, Code: ").append(std::to_string(res)));
return;
}
// Set device struct
auto deviceStruct = new wg_device({});
strncpy(deviceStruct->name, wgName.c_str(), wgName.length());
// Set private key
wg_key_from_base64(deviceStruct->private_key, privateKey.c_str());
deviceStruct->flags = (wg_device_flags)WGDEVICE_HAS_PRIVATE_KEY;
// Set public key
if (publicKey.length() > 0) {
wg_key_from_base64(deviceStruct->public_key, publicKey.c_str());
deviceStruct->flags = (wg_device_flags)WGDEVICE_HAS_PUBLIC_KEY;
}
// Port listenings
if (portListen > 0 && 25565 < portListen) {
deviceStruct->listen_port = portListen;
deviceStruct->flags = (wg_device_flags)(deviceStruct->flags|WGDEVICE_HAS_LISTEN_PORT);
}
// Linux firewall mark
if (fwmark >= 0) {
deviceStruct->fwmark = fwmark;
deviceStruct->flags = (wg_device_flags)(deviceStruct->flags|WGDEVICE_HAS_FWMARK);
}
// Replace Peers
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) {
const std::string peerPubKey = it->first;
auto peerConfig = it->second;
peerIndex++;
wg_peer *peerStruct = new wg_peer({});
// Set public key
wg_key_from_base64(peerStruct->public_key, peerPubKey.c_str());
peerStruct->flags = (wg_peer_flags)WGPEER_HAS_PUBLIC_KEY;
// Remove Peer
if (peerConfig.removeMe) peerStruct->flags = (wg_peer_flags)(peerStruct->flags|WGPEER_REMOVE_ME);
else {
// Set preshared key if present
if (peerConfig.presharedKey.length() > 0) {
wg_key_from_base64(peerStruct->preshared_key, peerConfig.presharedKey.c_str());
peerStruct->flags = (wg_peer_flags)(peerStruct->flags|WGPEER_HAS_PRESHARED_KEY);
}
// Set Keepalive
if (peerConfig.keepInterval > 0) {
peerStruct->persistent_keepalive_interval = peerConfig.keepInterval;
peerStruct->flags = (wg_peer_flags)(peerStruct->flags|WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL);
}
// Set endpoint
if (peerConfig.endpoint.length() > 0) {
sockaddr endpoint;
int ret, retries;
char *begin, *end;
char *Endpoint = strdup(peerConfig.endpoint.c_str());
if (Endpoint[0] == '[') {
begin = &Endpoint[1];
end = strchr(Endpoint, ']');
if (!end) {
free(Endpoint);
SetError("Unable to find matching brace of endpoint");
return;
}
*end++ = '\0';
if (*end++ != ':' || !*end) {
free(Endpoint);
SetError("Unable to find port of endpoint");
return;
}
} else {
begin = Endpoint;
end = strrchr(Endpoint, ':');
if (!end || !*(end + 1)) {
free(Endpoint);
SetError("Unable to find port of endpoint");
return;
}
*end++ = '\0';
}
addrinfo *resolved;
addrinfo hints = {
ai_family: AF_UNSPEC,
ai_socktype: SOCK_DGRAM,
ai_protocol: IPPROTO_UDP
};
#define min(a, b) ((a) < (b) ? (a) : (b))
for (unsigned int timeout = 1000000;; timeout = min(20000000, timeout * 6 / 5)) {
ret = getaddrinfo(begin, end, &hints, &resolved);
if (!ret) break;
if (ret == EAI_NONAME || ret == EAI_FAIL ||
#ifdef EAI_NODATA
ret == EAI_NODATA ||
#endif
(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");
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);
usleep(timeout);
}
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(sockaddr_in)) || (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(sockaddr_in6))) {
memcpy(&endpoint, resolved->ai_addr, resolved->ai_addrlen);
memccpy(&peerStruct->endpoint.addr, &endpoint, 0, sizeof(peerStruct->endpoint.addr));
if (resolved->ai_family == AF_INET) {
peerStruct->endpoint.addr4.sin_addr.s_addr = ((sockaddr_in *)&endpoint)->sin_addr.s_addr;
peerStruct->endpoint.addr4.sin_port = ((sockaddr_in *)&endpoint)->sin_port;
peerStruct->endpoint.addr4.sin_family = AF_INET;
} else {
peerStruct->endpoint.addr6.sin6_addr = ((struct sockaddr_in6 *)&endpoint)->sin6_addr;
peerStruct->endpoint.addr6.sin6_port = ((struct sockaddr_in6 *)&endpoint)->sin6_port;
peerStruct->endpoint.addr6.sin6_family = AF_INET6;
}
} else {
freeaddrinfo(resolved);
free(Endpoint);
SetError("Neither IPv4 nor IPv6 address found");
return;
}
freeaddrinfo(resolved);
free(Endpoint);
}
// Set allowed IPs
if (peerConfig.allowedIPs.size() > 0) {
peerStruct->flags = (wg_peer_flags)(peerStruct->flags|WGPEER_REPLACE_ALLOWEDIPS);
for (unsigned int allowIndex = 0; allowIndex < peerConfig.allowedIPs.size(); allowIndex++) {
auto ip = peerConfig.allowedIPs[allowIndex];
unsigned long cidr = 0;
if (ip.find("/") != std::string::npos) {
cidr = std::stoi(ip.substr(ip.find("/")+1));
ip = ip.substr(0, ip.find("/"));
}
wg_allowedip *newAllowedIP = new wg_allowedip({family: AF_UNSPEC});
if (strchr(ip.c_str(), ':')) {
if (inet_pton(AF_INET6, ip.c_str(), &newAllowedIP->ip6) == 1) {
newAllowedIP->family = AF_INET6;
if (cidr == 0) cidr = 128;
}
} else {
if (inet_pton(AF_INET, ip.c_str(), &newAllowedIP->ip4) == 1) {
newAllowedIP->family = AF_INET;
if (cidr == 0) cidr = 32;
}
}
if (newAllowedIP->family == AF_UNSPEC || cidr <= 0) continue;
newAllowedIP->cidr = cidr;
if (allowIndex > 0) newAllowedIP->next_allowedip = peerStruct->first_allowedip;
peerStruct->first_allowedip = newAllowedIP;
}
}
}
// Add to Peer struct
if (peerIndex > 0) peerStruct->next_peer = deviceStruct->first_peer;
deviceStruct->first_peer = peerStruct;
}
// Set interface config
if ((res = wg_set_device(deviceStruct)) < 0) SetError(getKernelMesage(res));
}
const char* getHostAddress(bool addPort, const sockaddr* addr) {
char host[4096 + 1], service[512 + 1];
static char buf[sizeof(host) + sizeof(service) + 4];
memset(buf, 0, sizeof(buf));
int ret;
socklen_t addr_len = 0;
if (addr->sa_family == AF_INET) addr_len = sizeof(struct sockaddr_in);
else if (addr->sa_family == AF_INET6) addr_len = sizeof(struct sockaddr_in6);
ret = getnameinfo(addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST);
if (ret) {
strncpy(buf, gai_strerror(ret), sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
} else {
if (addPort) snprintf(buf, sizeof(buf), (addr->sa_family == AF_INET6 && strchr(host, ':')) ? "[%s]:%s" : "%s:%s", host, service);
else snprintf(buf, sizeof(buf), "%s", host);
}
return buf;
}
std::string keyTo64(const uint8_t *key) {
wg_key_b64_string strKey;
wg_key_to_base64(strKey, key);
return strKey;
}
void getConfig::Execute() {
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)));
return;
}
if (device->flags & WGDEVICE_HAS_PRIVATE_KEY) privateKey = keyTo64(device->private_key);
if (device->flags & WGDEVICE_HAS_PUBLIC_KEY) publicKey = keyTo64(device->public_key);
if (device->listen_port > 0) portListen = device->listen_port;
// Set Address array and get interface ip addresses
ifaddrs* ptr_ifaddrs = nullptr;
if(getifaddrs(&ptr_ifaddrs) > 0) {
for (ifaddrs* ptr_entry = ptr_ifaddrs; ptr_entry != nullptr; ptr_entry = ptr_entry->ifa_next) {
if (ptr_entry->ifa_addr == nullptr) continue;
else if (strcmp(ptr_entry->ifa_name, wgName.c_str()) != 0) continue;
else if (ptr_entry->ifa_addr->sa_family == AF_INET) Address.push_back(getHostAddress(false, ptr_entry->ifa_addr));
else if (ptr_entry->ifa_addr->sa_family == AF_INET6) Address.push_back(getHostAddress(false, ptr_entry->ifa_addr));
}
freeifaddrs(ptr_ifaddrs);
}
wg_peer *peer;
for ((peer) = (device)->first_peer; (peer); (peer) = (peer)->next_peer) {
auto PeerConfig = Peer();
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) PeerConfig.presharedKey = keyTo64(peer->preshared_key);
if (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL && peer->persistent_keepalive_interval > 0) PeerConfig.keepInterval = peer->persistent_keepalive_interval;
if (peer->endpoint.addr.sa_family == AF_INET||peer->endpoint.addr.sa_family == AF_INET6) PeerConfig.endpoint = getHostAddress(true, &peer->endpoint.addr);
if (peer->last_handshake_time.tv_sec > 0) PeerConfig.last_handshake = peer->last_handshake_time.tv_sec*1000;
if (peer->rx_bytes > 0) PeerConfig.rxBytes = peer->rx_bytes;
if (peer->tx_bytes > 0) PeerConfig.txBytes = peer->tx_bytes;
if (peer->first_allowedip) {
wg_allowedip *allowedip;
for ((allowedip) = (peer)->first_allowedip; (allowedip); (allowedip) = (allowedip)->next_allowedip) {
static char buf[INET6_ADDRSTRLEN + 1];
memset(buf, 0, INET6_ADDRSTRLEN + 1);
if (allowedip->family == AF_INET) inet_ntop(AF_INET, &allowedip->ip4, buf, INET6_ADDRSTRLEN);
else if (allowedip->family == AF_INET6) inet_ntop(AF_INET6, &allowedip->ip6, buf, INET6_ADDRSTRLEN);
snprintf(buf + strlen(buf), INET6_ADDRSTRLEN - strlen(buf), "/%d", allowedip->cidr);
PeerConfig.allowedIPs.push_back(buf);
}
}
peersVector[keyTo64(peer->public_key)] = PeerConfig;
}
}

@ -1,363 +0,0 @@
#include <napi.h>
#include <string>
#include <vector>
#include <map>
#include <iostream>
#include <wireguard-nt/include/wireguard.h>
#include <windows.h>
#include <tlhelp32.h>
#include <accctrl.h>
#include <aclapi.h>
#include <stdio.h>
#include <stdbool.h>
#include <fcntl.h>
#include <winsock2.h>
#include <ws2ipdef.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <bcrypt.h>
#include <wincrypt.h>
#include <sysinfoapi.h>
#include <winternl.h>
#include <cstdlib>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <devguid.h>
#include "wginterface.hh"
#include "win/shared.cpp"
#include <wgkeys.hh>
const DEVPROPKEY devpkey_name = { { 0x65726957, 0x7547, 0x7261, { 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x4b, 0x65, 0x79 } }, DEVPROPID_FIRST_USABLE + 1 };
#define IFNAMSIZ MAX_ADAPTER_NAME - 1
static WIREGUARD_CREATE_ADAPTER_FUNC *WireGuardCreateAdapter;
static WIREGUARD_OPEN_ADAPTER_FUNC *WireGuardOpenAdapter;
static WIREGUARD_CLOSE_ADAPTER_FUNC *WireGuardCloseAdapter;
static WIREGUARD_GET_ADAPTER_LUID_FUNC *WireGuardGetAdapterLUID;
static WIREGUARD_GET_RUNNING_DRIVER_VERSION_FUNC *WireGuardGetRunningDriverVersion;
static WIREGUARD_DELETE_DRIVER_FUNC *WireGuardDeleteDriver;
static WIREGUARD_SET_LOGGER_FUNC *WireGuardSetLogger;
static WIREGUARD_SET_ADAPTER_LOGGING_FUNC *WireGuardSetAdapterLogging;
static WIREGUARD_GET_ADAPTER_STATE_FUNC *WireGuardGetAdapterState;
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;
//Ask Win32 to give us the string version of that message ID.
//The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
//Copy the error message into a std::string.
std::string message(messageBuffer, size);
//Free the Win32's string's buffer.
LocalFree(messageBuffer);
return std::string("Error code: ").append(std::to_string(errorMessageID)).append(", Message: ").append(message);
}
std::string 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!";
LPCWSTR dllPath = toLpcwstr(DLLPATH.ToString());
HMODULE WireGuardDll = LoadLibraryExW(dllPath, NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!WireGuardDll) return std::string("Failed to initialize WireGuardNT, ").append(getErrorString(GetLastError()));;
#define X(Name) ((*(FARPROC *)&Name = GetProcAddress(WireGuardDll, #Name)) == NULL)
if (X(WireGuardCreateAdapter) || X(WireGuardOpenAdapter) || X(WireGuardCloseAdapter) || X(WireGuardGetAdapterLUID) || X(WireGuardGetRunningDriverVersion) || X(WireGuardDeleteDriver) || X(WireGuardSetLogger) || X(WireGuardSetAdapterLogging) || X(WireGuardGetAdapterState) || X(WireGuardSetAdapterState) || X(WireGuardGetConfiguration) || X(WireGuardSetConfiguration))
#undef X
{
DWORD LastError = GetLastError();
FreeLibrary(WireGuardDll);
SetLastError(LastError);
return std::string("Failed to set Functions from WireGuardNT DLL, ").append(getErrorString(GetLastError()));;
}
return "";
}
std::string versionDrive() {
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardCreateAdapter(L"getWgVersion", L"Wireguard-tools.js", NULL);
DWORD Version = WireGuardGetRunningDriverVersion();
if (Version == 0) {
auto statusErr = GetLastError();
WireGuardCloseAdapter(Adapter);
if (statusErr == ERROR_FILE_NOT_FOUND) return "Driver not loaded";
return std::string("Cannot get version drive, ").append(getErrorString(GetLastError()));
}
WireGuardCloseAdapter(Adapter);
return std::string("WireGuardNT v").append(std::to_string((Version >> 16) & 0xff)).append(".").append(std::to_string((Version >> 0) & 0xff));
}
void listDevices::Execute() {
std::vector<std::string> arrayPrefix;
arrayPrefix.push_back("ProtectedPrefix\\Administrators\\WireGuard\\");
arrayPrefix.push_back("WireGuard\\");
WIN32_FIND_DATA find_data;
HANDLE find_handle;
for (auto &preit : arrayPrefix) {
int ret = 0;
find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
if (find_handle == INVALID_HANDLE_VALUE) continue;
char *iface;
do {
if (strncmp(preit.c_str(), find_data.cFileName, strlen(preit.c_str()))) continue;
iface = find_data.cFileName + strlen(preit.c_str());
listInfo setInfo;
setInfo.tunType = "userspace";
setInfo.pathSock = std::string("\\\\.\\pipe\\").append(preit).append(iface);
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)));
}
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");
for (DWORD i = 0;; ++i) {
DWORD buf_len;
WCHAR adapter_name[MAX_ADAPTER_NAME];
SP_DEVINFO_DATA dev_info_data;
dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
DEVPROPTYPE prop_type;
ULONG status, problem_code;
char *interface_name;
if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) break;
continue;
}
if (!SetupDiGetDevicePropertyW(dev_info, &dev_info_data, &devpkey_name, &prop_type, (PBYTE)adapter_name, sizeof(adapter_name), NULL, 0) || prop_type != DEVPROP_TYPE_STRING) continue;
adapter_name[_countof(adapter_name) - 1] = L'0';
if (!adapter_name[0]) continue;
buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, NULL, 0, NULL, NULL);
if (!buf_len) continue;
interface_name = (char *)malloc(buf_len);
if (!interface_name) continue;
buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, interface_name, buf_len, NULL, NULL);
if (!buf_len) {
free(interface_name);
continue;
}
if (CM_Get_DevNode_Status(&status, &problem_code, dev_info_data.DevInst, 0) == CR_SUCCESS && (status & (DN_DRIVER_LOADED | DN_STARTED)) == (DN_DRIVER_LOADED | DN_STARTED)) {
listInfo setInfo;
setInfo.tunType = "kernel";
deviceNames[std::string(interface_name)] = setInfo;
}
free(interface_name);
}
SetupDiDestroyDeviceInfoList(dev_info);
}
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())));
WireGuardCloseAdapter(Adapter);
}
/**
* Change point from calloc or malloc
*
* T: to
* C: From
*/
template <typename T, typename C> C* changePoint(T *x) {
// reinterpret_cast<WIREGUARD_ALLOWED_IP*>(((char*)x) + sizeof(WIREGUARD_PEER));
// std::cout << "Sizeof: " << sizeof(C) << ", " << typeid(T).name() << " -> " << typeid(C).name() << std::endl;
return reinterpret_cast<C*>(((char*)x) + sizeof(T));
}
void getConfig::Execute() {
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(wgName));
if (!Adapter) return SetError("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);
}
DWORD buf_len = 0;
WIREGUARD_INTERFACE *wg_iface = nullptr;
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())));
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->Flags & WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_PRIVATE_KEY) privateKey = wgKeys::toString(wg_iface->PrivateKey);
if (wg_iface->Flags & WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_PUBLIC_KEY) publicKey = wgKeys::toString(wg_iface->PublicKey);
portListen = 0;
if (wg_iface->Flags & WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_LISTEN_PORT) portListen = wg_iface->ListenPort;
WIREGUARD_PEER *wg_peer = changePoint<WIREGUARD_INTERFACE, WIREGUARD_PEER>(wg_iface);
for (DWORD i = 0; i < wg_iface->PeersCount; i++) {
auto pubKey = wgKeys::toString(wg_peer->PublicKey);
Peer peerConfig;
peerConfig.last_handshake = 0;
peerConfig.txBytes = wg_peer->TxBytes;
peerConfig.rxBytes = wg_peer->RxBytes;
if (wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PRESHARED_KEY) peerConfig.presharedKey = wgKeys::toString(wg_peer->PresharedKey);
if (wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_ENDPOINT) peerConfig.endpoint = parseEndpoint(&wg_peer->Endpoint);
if (wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PERSISTENT_KEEPALIVE) peerConfig.keepInterval = wg_peer->PersistentKeepalive;
if (wg_peer->LastHandshake > 0) peerConfig.last_handshake = (wg_peer->LastHandshake / 10000000 - 11644473600LL) * 1000;
WIREGUARD_ALLOWED_IP* wg_aip = changePoint<WIREGUARD_PEER, WIREGUARD_ALLOWED_IP>(wg_peer);
for (DWORD __aip = 0; __aip < wg_peer->AllowedIPsCount; __aip++) {
char saddr[INET6_ADDRSTRLEN];
if (wg_aip->AddressFamily == AF_INET) {
inet_ntop(AF_INET, &wg_aip->Address.V6, saddr, INET_ADDRSTRLEN);
peerConfig.allowedIPs.push_back(std::string(saddr).append("/").append(std::to_string(wg_aip->Cidr)));
} else if (wg_aip->AddressFamily == AF_INET6) {
inet_ntop(AF_INET6, &wg_aip->Address.V6, saddr, INET6_ADDRSTRLEN);
peerConfig.allowedIPs.push_back(std::string(saddr).append("/").append(std::to_string(wg_aip->Cidr)));
}
++wg_aip;
}
wg_peer = reinterpret_cast<WIREGUARD_PEER*>(wg_aip);
peersVector[pubKey] = peerConfig;
}
free(wg_iface);
}
void setConfig::Execute() {
DWORD buf_len = sizeof(WIREGUARD_INTERFACE);
for (auto peer : peersVector) {
if (DWORD_MAX - buf_len < sizeof(WIREGUARD_PEER)) return SetError("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");
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");
wg_iface->PeersCount = 0;
wgKeys::stringToKey(wg_iface->PrivateKey, privateKey);
wg_iface->Flags = WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_PRIVATE_KEY;
wg_iface->ListenPort = portListen;
if (portListen >= 0 && 65535 <= portListen) wg_iface->Flags = (WIREGUARD_INTERFACE_FLAG)(wg_iface->Flags|WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_LISTEN_PORT);
if (replacePeers) wg_iface->Flags = (WIREGUARD_INTERFACE_FLAG)(wg_iface->Flags|WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_REPLACE_PEERS);
WIREGUARD_ALLOWED_IP *wg_aip;
WIREGUARD_PEER *wg_peer = changePoint<WIREGUARD_INTERFACE, WIREGUARD_PEER>(wg_iface);
for (auto __peer : peersVector) {
auto peerPublicKey = __peer.first; auto peerConfig = __peer.second;
try {
wgKeys::stringToKey(wg_peer->PublicKey, peerPublicKey);
} catch (std::string &err) {
SetError(err);
free(wg_iface);
return;
}
wg_peer->Flags = WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PUBLIC_KEY;
wg_peer->AllowedIPsCount = 0;
wg_iface->PeersCount++;
if (peerConfig.removeMe) {
wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_REMOVE);
wg_peer = changePoint<WIREGUARD_PEER, WIREGUARD_PEER>(wg_peer);
} else {
if (peerConfig.presharedKey.size() == B64_WG_KEY_LENGTH) {
try {
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);
return;
}
}
wg_peer->PersistentKeepalive = peerConfig.keepInterval;
if (peerConfig.keepInterval >= 0) wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PERSISTENT_KEEPALIVE);
if (peerConfig.endpoint.size() > 0) {
try {
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);
return;
}
}
wg_aip = changePoint<WIREGUARD_PEER, WIREGUARD_ALLOWED_IP>(wg_peer);
for (auto aip : peerConfig.allowedIPs) {
unsigned long cidr = 0;
if (aip.find("/") != std::string::npos) {
cidr = std::stoi(aip.substr(aip.find("/")+1));
aip = aip.substr(0, aip.find("/"));
}
aip = aip.substr(0, aip.find("/"));
wg_aip->AddressFamily = strchr(aip.c_str(), ':') ? AF_INET6 : AF_INET;
auto status = wg_aip->AddressFamily == AF_INET6 ? inet_pton(wg_aip->AddressFamily, aip.c_str(), &wg_aip->Address.V6) : inet_pton(wg_aip->AddressFamily, aip.c_str(), &wg_aip->Address.V4);
if (status == 1) {
if (cidr == 0) cidr = wg_aip->AddressFamily == AF_INET6 ? 128 : 32;
} else continue;
wg_aip->Cidr = cidr;
wg_peer->AllowedIPsCount++;
wg_aip = changePoint<WIREGUARD_ALLOWED_IP, WIREGUARD_ALLOWED_IP>(wg_aip);
if (!(wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_REPLACE_ALLOWED_IPS)) wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_REPLACE_ALLOWED_IPS);
}
wg_peer = reinterpret_cast<WIREGUARD_PEER*>(((char*)wg_aip));
}
}
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)) {
auto status = GetLastError();
SetError(std::string("Failed to set interface config, ").append(getErrorString(status)));
WireGuardCloseAdapter(Adapter);
} else if (!WireGuardSetAdapterState(Adapter, WIREGUARD_ADAPTER_STATE::WIREGUARD_ADAPTER_STATE_UP)) {
auto status = GetLastError();
SetError(std::string("Failed to set interface up, ").append(getErrorString(status)));
WireGuardCloseAdapter(Adapter);
} else {
if (Address.size() > 0) {
std::string IPv4, IPv6;
for (auto aip : Address) {
aip = aip.substr(0, aip.find("/"));
auto family = strchr(aip.c_str(), ':') ? AF_INET6 : AF_INET;
SOCKADDR_INET address;
int status = family == AF_INET ? inet_pton(family, aip.c_str(), &address.Ipv4.sin_addr) : inet_pton(family, aip.c_str(), &address.Ipv6.sin6_addr);
if (status != 1) continue;
char saddr[INET6_ADDRSTRLEN];
family == AF_INET ? inet_ntop(AF_INET, &address.Ipv4.sin_addr, saddr, INET_ADDRSTRLEN) : inet_ntop(AF_INET6, &address.Ipv6.sin6_addr, saddr, INET6_ADDRSTRLEN);
if (family == AF_INET) IPv4 = std::string(saddr);
// else IPv6 = std::string(saddr);
}
if (IPv4.size() > 0 || IPv6.size() > 0) {
NET_LUID InterfaceLuid;
WireGuardGetAdapterLUID(Adapter, &InterfaceLuid);
auto setStatus = insertIpAddr(InterfaceLuid, IPv4, IPv6);
if (setStatus.size() > 0) SetError(setStatus);
}
}
}
free(wg_iface);
}

@ -1,116 +0,0 @@
#include <napi.h>
#include <iostream>
#include "wginterface.hh"
Napi::Object Init(Napi::Env initEnv, Napi::Object exports) {
/// Call Addon
#ifdef ONSTARTADDON
auto status = 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);
// Set wireguard version if present
constants.Set("driveVersion", versionDrive());
// Wireguard max name length
constants.Set("nameLength", 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
#ifdef SETCONFIG
exports.Set("setConfig", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value {
const Napi::Env env = info.Env();
const auto wgName = info[0];
const auto wgConfig = info[1];
Napi::Value ret = env.Undefined();
if (!(wgName.IsString())) {
Napi::Error::New(env, "Require wireguard interface name").ThrowAsJavaScriptException();
return env.Undefined();
} else if (wgName.ToString().Utf8Value().length() >= maxName()) {
Napi::Error::New(env, "interface name is so long").ThrowAsJavaScriptException();
return env.Undefined();
} else if (!(wgConfig.IsObject())) {
Napi::Error::New(env, "Require wireguard config object").ThrowAsJavaScriptException();
return env.Undefined();
}
try {
auto worker = new setConfig(env, wgName.ToString().Utf8Value(), wgConfig.ToObject());
worker->Queue();
return worker->setPromise.Promise();
} catch (const Napi::Error &err) {
err.ThrowAsJavaScriptException();
}
return ret;
}));
#endif
#ifdef DELIFACE
exports.Set("deleteInterface", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value {
const Napi::Env env = info.Env();
const auto wgName = info[0];
if (!(wgName.IsString())) {
Napi::Error::New(env, "Require wireguard interface name").ThrowAsJavaScriptException();
return env.Undefined();
} else if (wgName.ToString().Utf8Value().length() >= 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();
}));
#endif
#ifdef GETCONFIG
exports.Set("getConfig", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value {
const Napi::Env env = info.Env();
const auto wgName = info[0];
if (!(wgName.IsString())) {
Napi::Error::New(env, "Require wireguard interface name").ThrowAsJavaScriptException();
return env.Undefined();
} else if (wgName.ToString().Utf8Value().length() >= maxName()) {
Napi::Error::New(env, "interface name is so long").ThrowAsJavaScriptException();
return env.Undefined();
}
try {
auto worker = new getConfig(env, wgName.ToString().Utf8Value());
worker->Queue();
return worker->getPromise.Promise();
} catch (const Napi::Error &err) {
err.ThrowAsJavaScriptException();
}
return env.Undefined();
}));
#endif
#ifdef LISTDEV
exports.Set("listDevices", Napi::Function::New(initEnv, [&](const Napi::CallbackInfo &info) -> Napi::Value {
const Napi::Env env = info.Env();
auto worker = new listDevices(env);
worker->Queue();
return worker->listDevicesPromise.Promise();
}));
#endif
return exports;
}
NODE_API_MODULE(addon, Init);

@ -1,338 +0,0 @@
#pragma once
#include <napi.h>
#include <string>
#include <vector>
#include <map>
#include <wgkeys.hh>
// Get wireguard max name length
unsigned long maxName();
// Get wireguard version
std::string versionDrive();
// On start module call this function
std::string startAddon(const Napi::Env env, Napi::Object exports);
class deleteInterface : public Napi::AsyncWorker {
private:
std::string wgName;
public:
deleteInterface(const Napi::Env env, std::string name): AsyncWorker(env), wgName{name}, deletePromise{env} {}
~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());
};
// Set platform Execute script
void Execute() override;
};
class listInfo {
public:
std::string tunType, pathSock;
};
class listDevices : public Napi::AsyncWorker {
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());
}
void OnOK() override {
Napi::HandleScope scope(Env());
const Napi::Env env = Env();
const auto deviceArray = Napi::Array::New(env);
for (auto it : deviceNames) {
auto name = it.first; auto infoSrc = it.second;
auto info = Napi::Object::New(env);
info.Set("name", name);
info.Set("from", infoSrc.tunType);
if (infoSrc.pathSock.size() > 0) info.Set("path", infoSrc.pathSock);
deviceArray.Set(deviceArray.Length(), info);
}
listDevicesPromise.Resolve(deviceArray);
};
void Execute() override;
};
class Peer {
public:
// Remove specifies if the peer with this public key should be removed
// from a device's peer list.
bool removeMe;
// PresharedKey is an optional preshared key which may be used as an
// additional layer of security for peer communications.
std::string presharedKey;
// Endpoint is the most recent source address used for communication by
// this Peer.
std::string endpoint;
// AllowedIPs specifies which IPv4 and IPv6 addresses this peer is allowed
// to communicate on.
//
// 0.0.0.0/0 indicates that all IPv4 addresses are allowed, and ::/0
// indicates that all IPv6 addresses are allowed.
std::vector<std::string> allowedIPs;
// PersistentKeepaliveInterval specifies how often an "empty" packet is sent
// to a peer to keep a connection alive.
//
// A value of 0 indicates that persistent keepalives are disabled.
unsigned int keepInterval = 0;
// LastHandshakeTime indicates the most recent time a handshake was performed
// with this peer.
//
// A zero-value time.Time indicates that no handshake has taken place with
// this peer.
long long last_handshake = 0;
// rxBytes indicates the number of bytes received from this peer.
unsigned long long rxBytes = 0;
// txBytes indicates the number of bytes transmitted to this peer.
unsigned long long txBytes = 0;
// ProtocolVersion specifies which version of the WireGuard protocol is used
// for this Peer.
//
// A value of 0 indicates that the most recent protocol version will be used.
int ProtocolVersion = 0;
};
/*
Configure uma interface do Wireguard.
*/
class setConfig : 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 short portListen = 0;
// 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;
// Replace peers
bool replacePeers = false;
// Wireguard peers, Map: <publicKey(std::string), Peer>
std::map<std::string, Peer> peersVector;
public:
const Napi::Promise::Deferred setPromise;
void OnOK() override {
Napi::HandleScope scope(Env());
// Callback().Call({ Env().Undefined() });
setPromise.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} {
// Wireguard public key
const auto sppk = config.Get("publicKey");
if (sppk.IsString()) {
publicKey = sppk.ToString().Utf8Value();
if (publicKey.length() != B64_WG_KEY_LENGTH) throw Napi::Error::New(env, "Set valid publicKey");
}
// Private key
const auto sprk = config.Get("privateKey");
if (!(sprk.IsString())) throw Napi::Error::New(env, "privateKey is empty");
privateKey = sprk.ToString().Utf8Value();
if (privateKey.length() != B64_WG_KEY_LENGTH) throw Napi::Error::New(env, (std::string("Set valid privateKey ")).append(std::to_string(privateKey.length())));
// Port to listen Wireguard interface
const auto spor = config.Get("portListen");
if (spor.IsNumber() && (spor.ToNumber().Int32Value() >= 0 && spor.ToNumber().Int32Value() <= 65535)) portListen = spor.ToNumber().Int32Value();
// Firewall mark
const auto sfw = config.Get("fwmark");
if (sfw.IsNumber() && (sfw.ToNumber().Uint32Value() >= 0)) fwmark = sfw.ToNumber().Uint32Value();
else fwmark = -1;
const auto setAddress = config.Get("address");
if (setAddress.IsArray()) {
const Napi::Array addrs = setAddress.As<Napi::Array>();
for (unsigned int i = 0; i < addrs.Length(); i++) {
if (addrs.Get(i).IsString()) Address.push_back(addrs.Get(i).ToString().Utf8Value());
}
}
// Replace peers
const auto setReplace = config.Get("replacePeers");
if (setReplace.IsBoolean()) replacePeers = setReplace.ToBoolean().Value();
// Peers
const auto speers = config.Get("peers");
if (speers.IsObject()) {
const Napi::Object Peers = speers.ToObject();
const Napi::Array Keys = Peers.GetPropertyNames();
for (unsigned int peerIndex = 0; peerIndex < Keys.Length(); peerIndex++) {
const auto peerPubKey = Keys[peerIndex];
if (peerPubKey.IsString() && Peers.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();
Peer peerConfig = Peer();
const auto removeMe = peerConfigObject.Get("removeMe");
if (removeMe.IsBoolean() && removeMe.ToBoolean().Value()) peerConfig.removeMe = true;
else {
// Preshared key
const auto pprekey = peerConfigObject.Get("presharedKey");
if (pprekey.IsString()) {
peerConfig.presharedKey = pprekey.ToString().Utf8Value();
if (peerConfig.presharedKey.length() != B64_WG_KEY_LENGTH) throw Napi::Error::New(env, "Set valid peer presharedKey");
}
// Keep interval
const auto pKeepInterval = peerConfigObject.Get("keepInterval");
if (pKeepInterval.IsNumber() && (pKeepInterval.ToNumber().Int32Value() > 0 && pKeepInterval.ToNumber().Int32Value() <= 65535)) peerConfig.keepInterval = pKeepInterval.ToNumber().Int32Value();
// Peer endpoint
const auto pEndpoint = peerConfigObject.Get("endpoint");
if (pEndpoint.IsString()) peerConfig.endpoint = pEndpoint.ToString().Utf8Value();
// Allowed ip's array
const auto pAllowedIPs = peerConfigObject.Get("allowedIPs");
if (pAllowedIPs.IsArray()) {
const auto AllowedIps = pAllowedIPs.As<Napi::Array>();
for (uint32_t allIndex = 0; allIndex < AllowedIps.Length(); allIndex++) {
if (AllowedIps.Get(allIndex).IsString()) peerConfig.allowedIPs.push_back(AllowedIps.Get(allIndex).ToString().Utf8Value());
}
}
}
// Insert peer
peersVector[ppkey] = peerConfig;
}
}
}
}
// Set platform Execute script
void Execute() override;
};
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;
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());
}
void OnOK() override {
Napi::HandleScope scope(Env());
const Napi::Env env = Env();
const auto config = Napi::Object::New(env);
if (privateKey.length() == B64_WG_KEY_LENGTH) config.Set("privateKey", privateKey);
if (publicKey.length() == B64_WG_KEY_LENGTH) config.Set("publicKey", publicKey);
if (portListen >= 0 && portListen <= 65535) config.Set("portListen", portListen);
if (fwmark >= 0) config.Set("fwmark", fwmark);
if (Address.size() > 0) {
const auto Addrs = Napi::Array::New(env);
for (auto &addr : Address) Addrs.Set(Addrs.Length(), addr);
config.Set("address", Addrs);
}
// Peer object
const auto PeersObject = Napi::Object::New(env);
for (auto &peer : peersVector) {
const auto PeerObject = Napi::Object::New(env);
auto peerConfig = peer.second;
if (peerConfig.presharedKey.length() == B64_WG_KEY_LENGTH) PeerObject.Set("presharedKey", peerConfig.presharedKey);
if (peerConfig.keepInterval > 0 && peerConfig.keepInterval <= 65535) PeerObject.Set("keepInterval", peerConfig.keepInterval);
if (peerConfig.endpoint.length() > 0) PeerObject.Set("endpoint", peerConfig.endpoint);
if (peerConfig.rxBytes >= 0) PeerObject.Set("rxBytes", Napi::BigInt::New(env, (uint64_t)peerConfig.rxBytes));
if (peerConfig.txBytes >= 0) PeerObject.Set("txBytes", Napi::BigInt::New(env, (uint64_t)peerConfig.txBytes));
if (peerConfig.last_handshake >= 0) {
PeerObject.Set("lastHandshake", Napi::Date::New(env, peerConfig.last_handshake));
PeerObject.Set("lastHandshakeBigint", peerConfig.last_handshake); // Debug to windows
}
if (peerConfig.allowedIPs.size() > 0) {
const auto allowedIPs = Napi::Array::New(env);
for (auto &ip : peerConfig.allowedIPs) allowedIPs.Set(allowedIPs.Length(), ip);
PeerObject.Set("allowedIPs", allowedIPs);
}
// const std::string peerPubKey = peer.first;
PeersObject.Set(peer.first, PeerObject);
}
// Set peers to object
config.Set("peers", PeersObject);
// Resolve config json
getPromise.Resolve(config);
};
// Set platform Execute script
void Execute() override;
};

@ -1,179 +0,0 @@
#include <string>
#include <vector>
#include <wireguard-nt/include/wireguard.h>
#include <windows.h>
#include <ws2ipdef.h>
#include <ws2def.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <netioapi.h>
#include <iphlpapi.h>
#include <chrono>
#include <thread>
#include <iostream>
// Function to check if the current user has administrator privileges
bool IsRunAsAdmin()
{
BOOL fRet = FALSE;
HANDLE hToken = NULL;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
TOKEN_ELEVATION Elevation;
DWORD cbSize = sizeof(TOKEN_ELEVATION);
if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) {
fRet = Elevation.TokenIsElevated;
}
}
if (hToken) CloseHandle(hToken);
return !!fRet;
}
LPCWSTR toLpcwstr(std::string s) {
wchar_t* wString = new wchar_t[s.length()+1];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, wString, s.length()+1);
return wString;
}
int parse_dns_retries() {
unsigned long ret;
char *retries = getenv("WG_ENDPOINT_RESOLUTION_RETRIES"), *end;
if (!retries) return 15;
if (!strcmp(retries, "infinity")) return -1;
ret = strtoul(retries, &end, 10);
if (*end || ret > INT_MAX) {
fprintf(stderr, "Unable to parse WG_ENDPOINT_RESOLUTION_RETRIES: `%s'\n", retries);
exit(1);
}
return (int)ret;
}
void insertEndpoint(SOCKADDR_INET *endpoint, std::string value) {
int ret, retries = parse_dns_retries();
char *begin, *end;
auto mmutable = strdup(value.c_str());
if (!mmutable) throw std::string("strdup");
if (!value.size()) {
free(mmutable);
throw std::string("Unable to parse empty endpoint");
}
if (mmutable[0] == '[') {
begin = &mmutable[1];
end = strchr(mmutable, ']');
if (!end) {
free(mmutable);
throw std::string("Unable to find matching brace of endpoint: ").append(value);
}
*end++ = '\0';
if (*end++ != ':' || !*end) {
free(mmutable);
throw std::string("Unable to find port of endpoint: ").append(value);
}
} else {
begin = mmutable;
end = strrchr(mmutable, ':');
if (!end || !*(end + 1)) {
free(mmutable);
throw std::string("Unable to find port of endpoint: ").append(value);
}
*end++ = '\0';
}
ADDRINFOA *resolved;
// #define min(a, b) ((a) < (b) ? (a) : (b))
for (unsigned int timeout = 1000000;; timeout = ((20000000) < (timeout * 6 / 5) ? (20000000) : (timeout * 6 / 5))) {
// ret = getaddrinfo(begin, end, &hints, &resolved);
ret = getaddrinfo(begin, end, NULL, &resolved);
if (!ret) break;
/* The set of return codes that are "permanent failures". All other possibilities are potentially transient.
*
* This is according to https://sourceware.org/glibc/wiki/NameResolver which states:
* "From the perspective of the application that calls getaddrinfo() it perhaps
* doesn't matter that much since EAI_FAIL, EAI_NONAME and EAI_NODATA are all
* permanent failure codes and the causes are all permanent failures in the
* sense that there is no point in retrying later."
*
* So this is what we do, except FreeBSD removed EAI_NODATA some time ago, so that's conditional.
*/
if (ret == EAI_NONAME || ret == EAI_FAIL ||
#ifdef EAI_NODATA
ret == EAI_NODATA ||
#endif
(retries >= 0 && !retries--)) {
free(mmutable);
throw std::string("Error code: ").append(std::to_string(ret));
}
std::this_thread::sleep_for(std::chrono::microseconds(timeout));
}
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(SOCKADDR_IN))) memcpy(&endpoint->Ipv4, resolved->ai_addr, resolved->ai_addrlen);
else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(SOCKADDR_IN6)) memcpy(&endpoint->Ipv6, resolved->ai_addr, resolved->ai_addrlen);
else {
freeaddrinfo(resolved);
throw std::string("Neither IPv4 nor IPv6 address found: ").append(value);
}
freeaddrinfo(resolved);
free(mmutable);
}
std::string parseEndpoint(SOCKADDR_INET *input) {
if (!(input->si_family == AF_INET || input->si_family == AF_INET6)) return "";
char saddr[INET6_ADDRSTRLEN];
input->si_family == AF_INET ? inet_ntop(AF_INET, &input->Ipv4.sin_addr, saddr, INET_ADDRSTRLEN) : inet_ntop(AF_INET6, &input->Ipv6.sin6_addr, saddr, INET6_ADDRSTRLEN);
if (input->si_family == AF_INET6) return std::string("[").append(saddr).append("]:").append(std::to_string(htons(input->Ipv6.sin6_port)));
return std::string(saddr).append(":").append(std::to_string(htons(input->Ipv4.sin_port)));
}
std::string insertIpAddr(NET_LUID InterfaceLuid, std::string IPv4, std::string IPv6) {
NET_IFINDEX ind;
if (ConvertInterfaceLuidToIndex(&InterfaceLuid, &ind) != NO_ERROR) return "Cannot get interface index";
// IPv4
if (IPv4.size() > 0) {
ULONG NTEContext = 0;
ULONG NTEInstance = 0;
UINT iaIPAddress;
inet_pton(AF_INET, IPv4.c_str(), &iaIPAddress);
auto status = AddIPAddress(iaIPAddress, NULL, ind, &NTEContext, &NTEInstance);
if (status != NO_ERROR) {
if (status == 5010) {
} else return std::string("Cannot set IPv4 interface, error code: ").append(std::to_string(status));
}
}
// IPv6
if (IPv6.size() > 0) {
UINT iaIPAddress;
inet_pton(AF_INET6, IPv6.c_str(), &iaIPAddress);
std::cerr << "Current not support IPv6 to set in interface!" << std::endl;
}
return "";
}
std::vector<std::string> getIpAddr(NET_LUID InterfaceLuid) {
NET_IFINDEX ind;
if (ConvertInterfaceLuidToIndex(&InterfaceLuid, &ind) != NO_ERROR) throw std::string("Cannot get interface index");
std::vector<std::string> ips;
IP_ADAPTER_INFO *pAdapterInfo;
ULONG ulOutBufLen;
DWORD dwRetVal;
pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) );
ulOutBufLen = sizeof(IP_ADAPTER_INFO);
if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) != ERROR_SUCCESS) {
free (pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *) malloc ( ulOutBufLen );
}
if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) != ERROR_SUCCESS) throw std::string("GetAdaptersInfo call failed with ").append(std::to_string(dwRetVal));
PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
while (pAdapter) {
if (pAdapter->Index == ind) ips.push_back(std::string(pAdapter->IpAddressList.IpAddress.String).append("/32"));
pAdapter = pAdapter->Next;
}
if (pAdapterInfo) free(pAdapterInfo);
return ips;
}

@ -1,80 +1,56 @@
name: wginterface name: wginterface
flags:
- "!-fno-exceptions"
- "-fpermissive"
- "-fexceptions"
- "-w"
- "-fpermissive"
flagsCC:
- "!-fno-exceptions"
- "-fpermissive"
- "-fexceptions"
- "-w"
- "-fpermissive"
defines: defines:
- "NAPI_DISABLE_CPP_EXCEPTIONS" - "NODE_VERSION=8"
- "NODE_VERSION=4" - "NAPI_CPP_EXCEPTIONS"
includes: includes:
- addons/genKey
- addons/tools
- node_modules/node-addon-api - node_modules/node-addon-api
- ./addon
sources: sources:
- "addons/genKey/wgkeys.cpp" - "addon/main.cpp"
- "addons/tools/wginterface.cpp" - "addon/genKey/wgkeys.cpp"
- "addons/tools/wginterface-dummy.cpp" - "addon/userspace/wginterface.cpp"
prebuild:
- shell: bash
cwd: ./addon/userspace/go
env:
CGO_ENABLED: "1"
LDFLAGS: "-w"
run: |
go build -trimpath -v -o ../wg-go.o -buildmode c-archive .
mv -fv ../wg-go.o "${BUILDDIR}"
target: target:
macos:
defines:
- USERSPACE_GO
linux: linux:
sources: sources:
- "addons/tools/linux/wireguard.c" - "!addon/userspace/wginterface.cpp"
- "addons/tools/wginterface-linux.cpp" - "addon/linux/wginterface.cpp"
- "!addons/tools/wginterface-dummy.cpp" - "addon/linux/wireguard.c"
defines:
- "LISTDEV"
- "GETCONFIG"
- "SETCONFIG"
- "DELIFACE"
target:
x86_64:
release: true
flags: flags:
- "-fPIC" - "!-fno-exceptions"
flagsCC: - "-fpermissive"
- "-fPIC" - "-fexceptions"
aarch64: - "-w"
release: true - "-fpermissive"
flags:
- "-fPIC"
flagsCC:
- "-fPIC" - "-fPIC"
windows: windows:
flags:
- "-undefined"
- "dynamic_lookup"
target:
x86_64:
release: true
aarch64:
release: true
sources: sources:
- "addons/tools/wginterface-win.cpp" - "!addon/userspace/wginterface.cpp"
- "!addons/tools/wginterface-dummy.cpp" - "addon/win/wginterface.cpp"
includes:
- "addons/tools/win"
defines:
- "ONSTARTADDON"
- "LISTDEV"
- "GETCONFIG"
- "SETCONFIG"
- "DELIFACE"
- "_HAS_EXCEPTIONS=1"
libraries: libraries:
- "bcrypt.lib" - wbemuuid.lib
- "crypt32.lib" - bcrypt.lib
- "iphlpapi.lib" - crypt32.lib
- "kernel32.lib" - iphlpapi.lib
- "ntdll.lib" - kernel32.lib
- "ws2_32.lib" - ntdll.lib
- "setupapi.lib" - ws2_32.lib
- setupapi.lib
defines:
- "_HAS_EXCEPTIONS=1"
- "ONSTARTADDON"
macos:
flags:
- "!-fno-exceptions"
- "-fpermissive"
- "-fexceptions"
- "-w"
- "-fpermissive"

@ -11,7 +11,7 @@
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://sirherobrine23.org/Wireguard/Wireguard-tools.js.git" "url": "https://sirherobrine23.org/Wireguard/Wireguard-tools.js.git"
}, },
"keywords": [ "keywords": [
"wireguard", "wireguard",
@ -34,18 +34,18 @@
}, },
"scripts": { "scripts": {
"install": "rebory prebuild", "install": "rebory prebuild",
"dev": "rebory build -DP", "dev": "rebory build",
"test": "rebory build -D && node --no-warnings --loader ts-node/esm src/index_test.js", "test": "rebory build && node --no-warnings --loader ts-node/esm src/index_test.js",
"prepack": "tsc --build --clean && tsc --build", "prepack": "tsc --build --clean && tsc --build && rebory build --release",
"postpack": "tsc --build --clean" "postpack": "tsc --build --clean"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.11.19", "@types/node": "^20.11.26",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.3.3" "typescript": "^5.4.2"
}, },
"dependencies": { "dependencies": {
"node-addon-api": "^7.1.0", "node-addon-api": "^8.0.0",
"rebory": "^0.1.12" "rebory": "^0.2.5"
} }
} }

@ -1,414 +0,0 @@
# Do not edit. File was generated by node-gyp's "configure" step
{
"target_defaults": {
"cflags": [],
"default_configuration": "Release",
"defines": [],
"include_dirs": [],
"libraries": [],
"msbuild_toolset": "v143",
"msvs_windows_target_platform_version": "10.0.22621.0"
},
"variables": {
"asan": 0,
"coverage": "false",
"dcheck_always_on": 0,
"debug_nghttp2": "false",
"debug_node": "false",
"enable_lto": "false",
"enable_pgo_generate": "false",
"enable_pgo_use": "false",
"error_on_warn": "false",
"force_dynamic_crt": 0,
"host_arch": "x64",
"icu_data_in": "..\\..\\deps\\icu-tmp\\icudt73l.dat",
"icu_endianness": "l",
"icu_gyp_path": "tools/icu/icu-generic.gyp",
"icu_path": "deps/icu-small",
"icu_small": "false",
"icu_ver_major": "73",
"is_debug": 0,
"libdir": "lib",
"llvm_version": "0.0",
"napi_build_version": "9",
"nasm_version": "2.16",
"node_builtin_shareable_builtins": [
"deps/cjs-module-lexer/lexer.js",
"deps/cjs-module-lexer/dist/lexer.js",
"deps/undici/undici.js"
],
"node_byteorder": "little",
"node_debug_lib": "false",
"node_enable_d8": "false",
"node_enable_v8_vtunejit": "false",
"node_fipsinstall": "false",
"node_install_corepack": "true",
"node_install_npm": "true",
"node_library_files": [
"lib/_http_agent.js",
"lib/_http_client.js",
"lib/_http_common.js",
"lib/_http_incoming.js",
"lib/_http_outgoing.js",
"lib/_http_server.js",
"lib/_stream_duplex.js",
"lib/_stream_passthrough.js",
"lib/_stream_readable.js",
"lib/_stream_transform.js",
"lib/_stream_wrap.js",
"lib/_stream_writable.js",
"lib/_tls_common.js",
"lib/_tls_wrap.js",
"lib/assert.js",
"lib/assert/strict.js",
"lib/async_hooks.js",
"lib/buffer.js",
"lib/child_process.js",
"lib/cluster.js",
"lib/console.js",
"lib/constants.js",
"lib/crypto.js",
"lib/dgram.js",
"lib/diagnostics_channel.js",
"lib/dns.js",
"lib/dns/promises.js",
"lib/domain.js",
"lib/events.js",
"lib/fs.js",
"lib/fs/promises.js",
"lib/http.js",
"lib/http2.js",
"lib/https.js",
"lib/inspector.js",
"lib/inspector/promises.js",
"lib/internal/abort_controller.js",
"lib/internal/assert.js",
"lib/internal/assert/assertion_error.js",
"lib/internal/assert/calltracker.js",
"lib/internal/async_hooks.js",
"lib/internal/blob.js",
"lib/internal/blocklist.js",
"lib/internal/bootstrap/node.js",
"lib/internal/bootstrap/realm.js",
"lib/internal/bootstrap/switches/does_not_own_process_state.js",
"lib/internal/bootstrap/switches/does_own_process_state.js",
"lib/internal/bootstrap/switches/is_main_thread.js",
"lib/internal/bootstrap/switches/is_not_main_thread.js",
"lib/internal/bootstrap/web/exposed-wildcard.js",
"lib/internal/bootstrap/web/exposed-window-or-worker.js",
"lib/internal/buffer.js",
"lib/internal/child_process.js",
"lib/internal/child_process/serialization.js",
"lib/internal/cli_table.js",
"lib/internal/cluster/child.js",
"lib/internal/cluster/primary.js",
"lib/internal/cluster/round_robin_handle.js",
"lib/internal/cluster/shared_handle.js",
"lib/internal/cluster/utils.js",
"lib/internal/cluster/worker.js",
"lib/internal/console/constructor.js",
"lib/internal/console/global.js",
"lib/internal/constants.js",
"lib/internal/crypto/aes.js",
"lib/internal/crypto/certificate.js",
"lib/internal/crypto/cfrg.js",
"lib/internal/crypto/cipher.js",
"lib/internal/crypto/diffiehellman.js",
"lib/internal/crypto/ec.js",
"lib/internal/crypto/hash.js",
"lib/internal/crypto/hashnames.js",
"lib/internal/crypto/hkdf.js",
"lib/internal/crypto/keygen.js",
"lib/internal/crypto/keys.js",
"lib/internal/crypto/mac.js",
"lib/internal/crypto/pbkdf2.js",
"lib/internal/crypto/random.js",
"lib/internal/crypto/rsa.js",
"lib/internal/crypto/scrypt.js",
"lib/internal/crypto/sig.js",
"lib/internal/crypto/util.js",
"lib/internal/crypto/webcrypto.js",
"lib/internal/crypto/webidl.js",
"lib/internal/crypto/x509.js",
"lib/internal/debugger/inspect.js",
"lib/internal/debugger/inspect_client.js",
"lib/internal/debugger/inspect_repl.js",
"lib/internal/dgram.js",
"lib/internal/dns/callback_resolver.js",
"lib/internal/dns/promises.js",
"lib/internal/dns/utils.js",
"lib/internal/encoding.js",
"lib/internal/error_serdes.js",
"lib/internal/errors.js",
"lib/internal/event_target.js",
"lib/internal/events/symbols.js",
"lib/internal/file.js",
"lib/internal/fixed_queue.js",
"lib/internal/freelist.js",
"lib/internal/freeze_intrinsics.js",
"lib/internal/fs/cp/cp-sync.js",
"lib/internal/fs/cp/cp.js",
"lib/internal/fs/dir.js",
"lib/internal/fs/promises.js",
"lib/internal/fs/read/context.js",
"lib/internal/fs/read/utf8.js",
"lib/internal/fs/recursive_watch.js",
"lib/internal/fs/rimraf.js",
"lib/internal/fs/streams.js",
"lib/internal/fs/sync_write_stream.js",
"lib/internal/fs/utils.js",
"lib/internal/fs/watchers.js",
"lib/internal/heap_utils.js",
"lib/internal/histogram.js",
"lib/internal/http.js",
"lib/internal/http2/compat.js",
"lib/internal/http2/core.js",
"lib/internal/http2/util.js",
"lib/internal/idna.js",
"lib/internal/inspector_async_hook.js",
"lib/internal/js_stream_socket.js",
"lib/internal/legacy/processbinding.js",
"lib/internal/linkedlist.js",
"lib/internal/main/check_syntax.js",
"lib/internal/main/embedding.js",
"lib/internal/main/eval_stdin.js",
"lib/internal/main/eval_string.js",
"lib/internal/main/inspect.js",
"lib/internal/main/mksnapshot.js",
"lib/internal/main/print_help.js",
"lib/internal/main/prof_process.js",
"lib/internal/main/repl.js",
"lib/internal/main/run_main_module.js",
"lib/internal/main/test_runner.js",
"lib/internal/main/watch_mode.js",
"lib/internal/main/worker_thread.js",
"lib/internal/mime.js",
"lib/internal/modules/cjs/loader.js",
"lib/internal/modules/esm/assert.js",
"lib/internal/modules/esm/create_dynamic_module.js",
"lib/internal/modules/esm/fetch_module.js",
"lib/internal/modules/esm/formats.js",
"lib/internal/modules/esm/get_format.js",
"lib/internal/modules/esm/handle_process_exit.js",
"lib/internal/modules/esm/hooks.js",
"lib/internal/modules/esm/initialize_import_meta.js",
"lib/internal/modules/esm/load.js",
"lib/internal/modules/esm/loader.js",
"lib/internal/modules/esm/module_job.js",
"lib/internal/modules/esm/module_map.js",
"lib/internal/modules/esm/package_config.js",
"lib/internal/modules/esm/resolve.js",
"lib/internal/modules/esm/shared_constants.js",
"lib/internal/modules/esm/translators.js",
"lib/internal/modules/esm/utils.js",
"lib/internal/modules/esm/worker.js",
"lib/internal/modules/helpers.js",
"lib/internal/modules/package_json_reader.js",
"lib/internal/modules/run_main.js",
"lib/internal/net.js",
"lib/internal/options.js",
"lib/internal/per_context/domexception.js",
"lib/internal/per_context/messageport.js",
"lib/internal/per_context/primordials.js",
"lib/internal/perf/event_loop_delay.js",
"lib/internal/perf/event_loop_utilization.js",
"lib/internal/perf/nodetiming.js",
"lib/internal/perf/observe.js",
"lib/internal/perf/performance.js",
"lib/internal/perf/performance_entry.js",
"lib/internal/perf/resource_timing.js",
"lib/internal/perf/timerify.js",
"lib/internal/perf/usertiming.js",
"lib/internal/perf/utils.js",
"lib/internal/policy/manifest.js",
"lib/internal/policy/sri.js",
"lib/internal/priority_queue.js",
"lib/internal/process/esm_loader.js",
"lib/internal/process/execution.js",
"lib/internal/process/per_thread.js",
"lib/internal/process/permission.js",
"lib/internal/process/policy.js",
"lib/internal/process/pre_execution.js",
"lib/internal/process/promises.js",
"lib/internal/process/report.js",
"lib/internal/process/signal.js",
"lib/internal/process/task_queues.js",
"lib/internal/process/warning.js",
"lib/internal/process/worker_thread_only.js",
"lib/internal/promise_hooks.js",
"lib/internal/querystring.js",
"lib/internal/readline/callbacks.js",
"lib/internal/readline/emitKeypressEvents.js",
"lib/internal/readline/interface.js",
"lib/internal/readline/promises.js",
"lib/internal/readline/utils.js",
"lib/internal/repl.js",
"lib/internal/repl/await.js",
"lib/internal/repl/history.js",
"lib/internal/repl/utils.js",
"lib/internal/socket_list.js",
"lib/internal/socketaddress.js",
"lib/internal/source_map/prepare_stack_trace.js",
"lib/internal/source_map/source_map.js",
"lib/internal/source_map/source_map_cache.js",
"lib/internal/stream_base_commons.js",
"lib/internal/streams/add-abort-signal.js",
"lib/internal/streams/buffer_list.js",
"lib/internal/streams/compose.js",
"lib/internal/streams/destroy.js",
"lib/internal/streams/duplex.js",
"lib/internal/streams/duplexify.js",
"lib/internal/streams/end-of-stream.js",
"lib/internal/streams/from.js",
"lib/internal/streams/lazy_transform.js",
"lib/internal/streams/legacy.js",
"lib/internal/streams/operators.js",
"lib/internal/streams/passthrough.js",
"lib/internal/streams/pipeline.js",
"lib/internal/streams/readable.js",
"lib/internal/streams/state.js",
"lib/internal/streams/transform.js",
"lib/internal/streams/utils.js",
"lib/internal/streams/writable.js",
"lib/internal/structured_clone.js",
"lib/internal/test/binding.js",
"lib/internal/test/transfer.js",
"lib/internal/test_runner/coverage.js",
"lib/internal/test_runner/harness.js",
"lib/internal/test_runner/mock/mock.js",
"lib/internal/test_runner/mock/mock_timers.js",
"lib/internal/test_runner/reporter/dot.js",
"lib/internal/test_runner/reporter/spec.js",
"lib/internal/test_runner/reporter/tap.js",
"lib/internal/test_runner/reporter/v8-serializer.js",
"lib/internal/test_runner/runner.js",
"lib/internal/test_runner/test.js",
"lib/internal/test_runner/tests_stream.js",
"lib/internal/test_runner/utils.js",
"lib/internal/timers.js",
"lib/internal/tls/secure-context.js",
"lib/internal/tls/secure-pair.js",
"lib/internal/trace_events_async_hooks.js",
"lib/internal/tty.js",
"lib/internal/url.js",
"lib/internal/util.js",
"lib/internal/util/colors.js",
"lib/internal/util/comparisons.js",
"lib/internal/util/debuglog.js",
"lib/internal/util/embedding.js",
"lib/internal/util/inspect.js",
"lib/internal/util/inspector.js",
"lib/internal/util/iterable_weak_map.js",
"lib/internal/util/parse_args/parse_args.js",
"lib/internal/util/parse_args/utils.js",
"lib/internal/util/types.js",
"lib/internal/v8/startup_snapshot.js",
"lib/internal/v8_prof_polyfill.js",
"lib/internal/v8_prof_processor.js",
"lib/internal/validators.js",
"lib/internal/vm.js",
"lib/internal/vm/module.js",
"lib/internal/wasm_web_api.js",
"lib/internal/watch_mode/files_watcher.js",
"lib/internal/watchdog.js",
"lib/internal/webidl.js",
"lib/internal/webstreams/adapters.js",
"lib/internal/webstreams/compression.js",
"lib/internal/webstreams/encoding.js",
"lib/internal/webstreams/queuingstrategies.js",
"lib/internal/webstreams/readablestream.js",
"lib/internal/webstreams/transfer.js",
"lib/internal/webstreams/transformstream.js",
"lib/internal/webstreams/util.js",
"lib/internal/webstreams/writablestream.js",
"lib/internal/worker.js",
"lib/internal/worker/io.js",
"lib/internal/worker/js_transferable.js",
"lib/module.js",
"lib/net.js",
"lib/os.js",
"lib/path.js",
"lib/path/posix.js",
"lib/path/win32.js",
"lib/perf_hooks.js",
"lib/process.js",
"lib/punycode.js",
"lib/querystring.js",
"lib/readline.js",
"lib/readline/promises.js",
"lib/repl.js",
"lib/stream.js",
"lib/stream/consumers.js",
"lib/stream/promises.js",
"lib/stream/web.js",
"lib/string_decoder.js",
"lib/sys.js",
"lib/test.js",
"lib/test/reporters.js",
"lib/timers.js",
"lib/timers/promises.js",
"lib/tls.js",
"lib/trace_events.js",
"lib/tty.js",
"lib/url.js",
"lib/util.js",
"lib/util/types.js",
"lib/v8.js",
"lib/vm.js",
"lib/wasi.js",
"lib/worker_threads.js",
"lib/zlib.js"
],
"node_module_version": 115,
"node_no_browser_globals": "false",
"node_prefix": "\\usr\\local",
"node_release_urlbase": "https://nodejs.org/download/release/",
"node_shared": "false",
"node_shared_brotli": "false",
"node_shared_cares": "false",
"node_shared_http_parser": "false",
"node_shared_libuv": "false",
"node_shared_nghttp2": "false",
"node_shared_nghttp3": "false",
"node_shared_ngtcp2": "false",
"node_shared_openssl": "false",
"node_shared_zlib": "false",
"node_tag": "",
"node_target_type": "executable",
"node_use_bundled_v8": "true",
"node_use_node_code_cache": "true",
"node_use_node_snapshot": "true",
"node_use_openssl": "true",
"node_use_v8_platform": "true",
"node_with_ltcg": "true",
"node_without_node_options": "false",
"openssl_is_fips": "false",
"openssl_quic": "true",
"ossfuzz": "false",
"shlib_suffix": "so.115",
"single_executable_application": "true",
"target_arch": "x64",
"v8_enable_31bit_smis_on_64bit_arch": 0,
"v8_enable_gdbjit": 0,
"v8_enable_hugepage": 0,
"v8_enable_i18n_support": 1,
"v8_enable_inspector": 1,
"v8_enable_javascript_promise_hooks": 1,
"v8_enable_lite_mode": 0,
"v8_enable_object_print": 1,
"v8_enable_pointer_compression": 0,
"v8_enable_shared_ro_heap": 1,
"v8_enable_short_builtin_calls": 1,
"v8_enable_webassembly": 1,
"v8_no_strict_aliasing": 1,
"v8_optimized_debug": 1,
"v8_promise_internal_field_count": 1,
"v8_random_seed": 0,
"v8_trace_maps": 0,
"v8_use_siphash": 1,
"want_separate_host_toolset": 0,
"nodedir": "C:\\Users\\mathe\\AppData\\Local\\node-gyp\\Cache\\20.6.1",
"standalone_static_library": 1,
"msbuild_path": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe"
}
}

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

@ -1,5 +1,7 @@
import crypto from "node:crypto"; import crypto from "node:crypto";
export const KeyLength = 32, Base64Length = 44;
type BinArray = Float64Array|Uint8Array|number[]; type BinArray = Float64Array|Uint8Array|number[];
function gf(init?: number[]) { function gf(init?: number[]) {
@ -94,39 +96,31 @@ function clamp(z: BinArray) {
z[0] &= 248; z[0] &= 248;
} }
/** function Base64ToKey(keyInput: string): Uint8Array {
* Generate preshared key blocking loop event if (keyInput.length !== Base64Length) throw new Error("Invalid key length", { cause: { Required: Base64Length, Input: keyInput.length } });
* return new Uint8Array(Buffer.from(keyInput, "base64"));
* @deprecated - use presharedKey }
*/
export function presharedKeySync() { function keyToBase64(key: Uint8Array): string {
var privateKey = new Uint8Array(32); if (key.length !== KeyLength) throw new Error("Invalid key length", { cause: { Required: KeyLength, Input: key.length } });
crypto.randomFillSync(privateKey); return Buffer.from(key).toString("base64");
return keyToBase64(privateKey); }
export function keyToHex(key: string): string {
return Buffer.from(key, "base64").toString("hex");
} }
/** /**
* Generate preshared key * Generate preshared key
*/ */
export async function presharedKey(): Promise<string> { export async function presharedKey(): Promise<string> {
var privateKey = new Uint8Array(32); var privateKey = new Uint8Array(KeyLength);
return new Promise((done, reject) => crypto.randomFill(privateKey, (err) => { return new Promise((done, reject) => crypto.randomFill(privateKey, (err) => {
if (err) return reject(err); if (err) return reject(err);
done(keyToBase64(privateKey)); done(keyToBase64(privateKey));
})); }));
} }
/**
* Create private key
*
* @deprecated - Use privateKey
*/
export function privateKeySync() {
var privateKey = Base64ToKey(presharedKeySync());
clamp(privateKey);
return keyToBase64(privateKey);
}
/** /**
* Create private key * Create private key
*/ */
@ -136,14 +130,6 @@ export async function privateKey() {
return keyToBase64(privateKey); return keyToBase64(privateKey);
} }
export function keyToBase64(key: Uint8Array): string {
return Buffer.from(key).toString("base64");
}
export function Base64ToKey(keyInput: string): Uint8Array {
return new Uint8Array(Buffer.from(keyInput, "base64"));
}
/** /**
* Get Public key from Private key * Get Public key from Private key
* *
@ -151,7 +137,7 @@ export function Base64ToKey(keyInput: string): Uint8Array {
*/ */
export function publicKey(privKey: string) { export function publicKey(privKey: string) {
var privateKey: Uint8Array = Base64ToKey(privKey); var privateKey: Uint8Array = Base64ToKey(privKey);
var r: number, z = new Uint8Array(32); var r: number, z = new Uint8Array(KeyLength);
var a = gf([1]), var a = gf([1]),
b = gf([9]), b = gf([9]),
c = gf(), c = gf(),

@ -1,46 +1,10 @@
import { promises as fs } from "fs"; import path from "node:path";
import { isIPv4, createConnection as netConnection } from "net"; import { loadAddon } from "rebory";
import path from "path"; import { key } from "./index.js";
import readline from "readline"; import { isIP } from "node:net";
import { finished } from "stream/promises"; const __dirname = import.meta.dirname || path.dirname((await import("node:url")).fileURLToPath(import.meta.url));
import rebory from "rebory";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url)); interface Peer {
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>;
setConfig?: (wgName: string, config: WgConfigSet) => Promise<void>;
getConfig?: (wgName: string) => Promise<WgConfigGet>;
createTun?: () => Promise<number|string>;
deleteTun?: () => void;
checkTun?: () => Promise<boolean>;
getTun?: () => Promise<number|string>;
/** Wireguard addon constants */
constants: {
driveVersion: string;
base64Length: number;
keyLength: number;
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 */ /** Preshared key to peer */
presharedKey?: string; presharedKey?: string;
@ -54,23 +18,26 @@ export interface Peer {
allowedIPs?: string[]; allowedIPs?: string[];
}; };
export interface PeerSet extends Peer { export interface SetPeer extends Peer {
/** Mark this peer to be removed, any changes remove this option */ /** Remove this peer */
removeMe?: boolean; removeMe?: boolean;
} }
export interface PeerGet extends Peer { export interface GetPeer extends Peer {
/** ReceiveBytes indicates the number of bytes received from this peer. */ /** ReceiveBytes indicates the number of bytes received from this peer. */
rxBytes?: number; rxBytes: bigint;
/** TransmitBytes indicates the number of bytes transmitted to this peer. */ /** TransmitBytes indicates the number of bytes transmitted to this peer. */
txBytes?: number; txBytes: bigint;
/** Last peer Handshake */ /** Last peer Handshake */
lastHandshake?: Date; lastHandshake: Date;
} }
export interface WgConfigBase<T extends Peer> { interface Config<T extends Peer> {
/** Wireguard interface name */
name: string;
/** privateKey specifies a private key configuration */ /** privateKey specifies a private key configuration */
privateKey: string; privateKey: string;
@ -86,164 +53,341 @@ export interface WgConfigBase<T extends Peer> {
/** Interface IP address'es */ /** Interface IP address'es */
address?: string[]; address?: string[];
/** Interface peers */ /** Interface Peers */
peers: Record<string, T>; peers: Record<string, T>;
} };
export interface WgConfigGet extends WgConfigBase<PeerGet> {} export interface GetConfig extends Config<GetPeer> { };
export interface WgConfigSet extends WgConfigBase<PeerSet> { export interface SetConfig extends Config<SetPeer> {
/** this option will remove all peers if `true` and add new peers */ /** this option will remove all peers if `true` and add new peers */
replacePeers?: boolean; replacePeers?: boolean;
} };
export type WgGlobalConfig = WgConfigSet & WgConfigGet; export const addon = (await loadAddon(path.resolve(__dirname, "../binding.yaml"))).wginterface.load_addon<{
/** Current Wireguard drive version */
driveVersion?: string;
/** /**
* Get Wireguard devices and locations * Delete interface if exists
* @param name - Interface name
*/ */
export async function listDevices() { deleteInterface(name: string): Promise<void>;
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;
}
/** /**
* Delete wireguard interface if present * Get Wireguard interfaces list
* @param wgName - Interface name *
* @returns * if running in userspace return socket (UAPI) path's
*/ */
export async function deleteInterface(wgName: string): Promise<void> { listDevices(): Promise<string[]>;
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. * Get current config from Wireguard interface
* * @param name - Interface name
* To update the interface settings, first get the interface settings to update! */
* getConfig(name: string): Promise<GetConfig>;
* @param wgName - Interface name
/**
* Set new config to Wireguard interface or create new interface if not exists
* @param config - Interface config * @param config - Interface config
*/ */
export async function setConfig(wgName: string, config: WgConfigGet): Promise<void>; setConfig(config: SetConfig): Promise<void>;
/** }>({
* Add the settings to the Wireguard interface, if it does not exist and the interface will be created automatically. WIN32DLLPATH: path.resolve(__dirname, "../addon/win", (process.arch === "x64" && "amd64") || (process.arch === "ia32" && "x86") || process.arch, "wireguard.dll")
*
* 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));
}
}
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 }); export const {
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) ]; }) driveVersion,
if (payloadKeys.at(-1)[1] !== "0") { listDevices,
const err = new Error("Invalid send config, check log"); getConfig,
throw err; setConfig,
deleteInterface
} = addon;
export class WireGuardPeer {
constructor(public publicKey: string, private __Wg: Wireguard) { }
async getStats() {
const { rxBytes, txBytes, lastHandshake } = await getConfig(this.__Wg.name).then((config) => config.peers[this.publicKey]);
return {
rxBytes,
txBytes,
lastHandshake
};
}
addNewAddress(address: string) {
if (isIP(address.split("/")[0]) === 0) throw new Error("Invalid IP address");
if (!this.__Wg._peers) this.__Wg._peers = new Map();
const _addr = new Set(this.__Wg._peers.get(this.publicKey).allowedIPs);
_addr.add(address.split("/")[0]);
this.__Wg._peers.get(this.publicKey).allowedIPs = Array.from(_addr);
return this;
}
removeAddress(address: string) {
if (isIP(address.split("/")[0]) === 0) throw new Error("Invalid IP address");
if (!this.__Wg._peers) this.__Wg._peers = new Map();
const _addr = new Set(this.__Wg._peers.get(this.publicKey).allowedIPs);
_addr.delete(address.split("/")[0]);
this.__Wg._peers.get(this.publicKey).allowedIPs = Array.from(_addr);
return this;
}
setKeepInterval(keepInterval: number) {
if (typeof keepInterval !== "number" || keepInterval < 0) throw new Error("Invalid keepInterval");
if (!this.__Wg._peers) this.__Wg._peers = new Map();
if (keepInterval > 0) this.__Wg._peers.get(this.publicKey).keepInterval = keepInterval;
else delete this.__Wg._peers.get(this.publicKey).keepInterval;
return this;
}
setEndpoint(endpoint: string) {
if (typeof endpoint !== "string") throw new Error("Invalid endpoint");
if (!this.__Wg._peers) this.__Wg._peers = new Map();
if (endpoint.length > 0) this.__Wg._peers.get(this.publicKey).endpoint = endpoint;
else delete this.__Wg._peers.get(this.publicKey).endpoint;
return this;
}
/**
* Sets the preshared key for the peer.
* @param presharedKey - The preshared key to set. If not provided, a new preshared key will be generated.
* @returns The updated WireGuard interface object.
* @throws {Error} If the provided preshared key is invalid.
*/
setPresharedKey(): Promise<this> & this;
/**
* Sets the preshared key for the peer.
* @param presharedKey - The preshared key to set. If not provided, a new preshared key will be generated.
* @returns The updated WireGuard interface object.
* @throws {Error} If the provided preshared key is invalid.
*/
setPresharedKey(presharedKey: string): this;
/**
* Sets the preshared key for the peer.
* @param presharedKey - The preshared key to set. If not provided, a new preshared key will be generated.
* @returns The updated WireGuard interface object.
* @throws {Error} If the provided preshared key is invalid.
*/
setPresharedKey(presharedKey?: string) {
if (!this.__Wg._peers) this.__Wg._peers = new Map();
if (!presharedKey) return Object.assign(key.presharedKey().then((presharedKey) => this.__Wg._peers.get(this.publicKey).presharedKey = presharedKey), this);
if (typeof presharedKey !== "string" || presharedKey.length !== key.Base64Length) throw new Error("Invalid presharedKey");
if (!this.__Wg._peers) this.__Wg._peers = new Map();
this.__Wg._peers.get(this.publicKey).presharedKey = presharedKey;
return this;
}
/**
* Removes the peer from the WireGuard interface.
* @returns The updated WireGuard interface.
*/
remove() {
if (!this.__Wg._peers) this.__Wg._peers = new Map();
this.__Wg._peers.get(this.publicKey)["removeMe"] = true;
return this;
}
/**
* Converts the `WireGuard Peer` object to a JSON representation.
* @returns The JSON representation of the `WireGuard Peer` object.
*/
toJSON(): [string, SetPeer] {
if (!this.__Wg._peers) this.__Wg._peers = new Map();
const { keepInterval, endpoint, presharedKey, allowedIPs } = this.__Wg._peers.get(this.publicKey);
const peer: SetPeer = Object.create({});
if (presharedKey) peer.presharedKey = presharedKey;
if (keepInterval) peer.keepInterval = keepInterval;
if (endpoint) peer.endpoint = endpoint;
if (allowedIPs) peer.allowedIPs = allowedIPs;
return [this.publicKey, peer];
} }
} }
/** /**
* Get wireguard interface config * Maneger Wireguard interface and peers simple and fast
* @param wgName - Interface name
* @returns
*/ */
export async function getConfig(wgName: string): Promise<WgConfigGet> { export class Wireguard {
if (typeof addon.getConfig === "function") return addon.getConfig(wgName); constructor(config?: SetConfig | GetConfig | Config<Peer>) {
const info = (await listDevices()).find(int => int.name === wgName); // super({});
if (!info) throw new Error("Create interface, not exists"); if (!config) return;
const client = netConnection(path.join(defaultPath, wgName.concat(".sock"))); if (typeof config === "object") {
const config: WgConfigGet = Object(); if (config instanceof Wireguard) return config;
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;
}); private _name: string;
client.write("get=1\n\n"); get name() {
await new Promise((done, reject) => tetrisBreak.on("error", reject).once("close", done)); return this._name;
await finished(client.end()); }
/**
* Set Wireguard interface name
* @param name - Interface name
* @returns Wireguard
*/
set name(name: string) {
if (typeof name !== "string" || name.length === 0) throw new Error("Invalid name");
this._name = name;
}
private _portListen: number;
/**
* Sets the port to listen on.
* @param port - The port number to listen on.
* @returns The current instance of the `Wireguard` class.
* @throws {Error} If the provided port is not a number or is less than 0.
*/
setPortListen(port: number) {
if (typeof port !== "number" || port < 0) throw new Error("Invalid port");
this._portListen = port;
return this;
}
private _fwmark: number;
/**
* Sets the fwmark value for the WireGuard interface.
*
* @param fwmark - The fwmark value to set.
* @returns The current instance of the `Wireguard` class.
* @throws {Error} If the `fwmark` value is not a number or is less than 0.
*/
setFwmark(fwmark: number) {
if (typeof fwmark !== "number" || fwmark < 0) throw new Error("Invalid fwmark");
this._fwmark = fwmark;
return this;
}
private _privateKey: string;
/**
* Get interface public key
*/
public get publicKey() {
return key.publicKey(this._privateKey);
}
/**
* Generate new private key and set to Wireguard interface
*/
setPrivateKey(): Promise<this> & this;
/**
* Set private key to Wireguard interface
* @param privateKey - Private key
* @returns Wireguard
*/
setPrivateKey(privateKey: string): this;
setPrivateKey(privateKey?: string): this {
if (!privateKey) return Object.assign(key.privateKey().then((privateKey) => this._privateKey = privateKey), this);
else this._privateKey = privateKey;
return this;
}
private _address: string[];
addNewAddress(address: string) {
if (isIP(address.split("/")[0]) === 0) throw new Error("Invalid IP address");
const _addr = new Set(this._address);
_addr.add(address.split("/")[0]);
this._address = Array.from(_addr);
return this;
}
removeAddress(address: string) {
if (isIP(address.split("/")[0]) === 0) throw new Error("Invalid IP address");
const _addr = new Set(this._address);
_addr.delete(address.split("/")[0]);
this._address = Array.from(_addr);
return this;
}
_peers: Map<string, Peer>;
/**
* Adds a new peer to the Wireguard interface.
*
* @param publicKey - The public key of the peer.
* @param peer - other configuration options for the peer.
* @throws Error if the peer is invalid.
*/
addNewPeer(publicKey: string, peer: Peer) {
if (!this._peers) this._peers = new Map();
if (!((typeof publicKey === "string" && publicKey.length === key.Base64Length) && typeof peer === "object")) throw new Error("Invalid peer");
let { allowedIPs, endpoint, keepInterval, presharedKey } = peer;
this._peers.set(publicKey, {});
if ((typeof presharedKey === "string" && presharedKey.length === key.Base64Length)) this._peers.get(publicKey).presharedKey = presharedKey;
if (typeof keepInterval === "number") this._peers.get(publicKey).keepInterval = keepInterval;
if (typeof endpoint === "string") this._peers.get(publicKey).endpoint = endpoint;
if (Array.isArray(allowedIPs)) this._peers.get(publicKey).allowedIPs = allowedIPs.filter((ip) => isIP(ip.split("/")[0]) !== 0);
return new WireGuardPeer(publicKey, this);
}
/**
* Removes a peer from the WireGuard interface.
* @param publicKey - The public key of the peer to remove.
* @returns The updated WireGuard interface.
*/
removePeer(publicKey: string) {
if (this._peers) this._peers.delete(publicKey);
return this;
}
/**
* Converts the `Wireguard Interface` object to a JSON representation.
* @returns The JSON representation of the `Wireguard Interface` object.
*/
toJSON(): SetConfig {
const config: SetConfig = Object.create({});
config.name = this._name;
config.privateKey = this._privateKey;
if (this._portListen) config.portListen = this._portListen;
if (this._fwmark) config.fwmark = this._fwmark;
if (this._address) config.address = this._address;
if (this._peers) config.peers = Array.from(this._peers||[]).map(([pubKey]) => new WireGuardPeer(pubKey, this).toJSON()).reduce((obj, [pubKey, peer]) => (obj[pubKey] = peer, obj), {});
return config; return config;
} }
/**
* Set new config to Wireguard interface or create new interface if not exists
* @returns Promise<void>
*/
async deploy() {
return setConfig({
...(this.toJSON()),
replacePeers: true,
});
}
/**
* Deletes the WireGuard interface.
* @returns A promise that resolves when the interface is successfully deleted.
*/
async delete() {
return deleteInterface(this._name);
}
/**
* Retrieves the configuration for the Wireguard interface.
*/
async getConfig() {
const { peers, privateKey, address, fwmark, portListen } = await getConfig(this._name);
this._privateKey = privateKey;
this._portListen = portListen;
this._address = address;
this._fwmark = fwmark;
this._peers = new Map(Object.entries(peers));
for (const [publicKey, { allowedIPs, endpoint, keepInterval, presharedKey }] of this._peers) {
this._peers.set(publicKey, { allowedIPs, endpoint, keepInterval, presharedKey });
if (keepInterval === 0) delete this._peers.get(publicKey).keepInterval;
if (!presharedKey) delete this._peers.get(publicKey).presharedKey;
if (!endpoint) delete this._peers.get(publicKey).endpoint;
if (!allowedIPs) delete this._peers.get(publicKey).allowedIPs;
else if (allowedIPs.length === 0) delete this._peers.get(publicKey).allowedIPs;
}
}
}
export default Wireguard;

@ -1,54 +1,51 @@
import test from "node:test"; import test from "node:test";
import { setConfig, deleteInterface, WgConfigSet, getConfig } from "./wginterface.js"; import { Wireguard, getConfig, setConfig } from "./wginterface.js";
import { publicKey } from "./key.js"; import { presharedKey, privateKey, publicKey } from "./key.js";
import { userInfo } from "os"; import assert from "node:assert";
if (process.platform === "win32" || process.platform === "linux" && (userInfo().uid === 0)) { await test("Wireguard interface", async t => {
test("Wireguard configuration", async t => { const config = new Wireguard;
// Config base config.name = "wg23";
const peer1Key = 'EKgSatFzZtsv1qFJ6gE8HqfuA+tXzW+7vDeVc7Xaa2E=', peer2Key = '4BSvgiM9j5jjuR0Vg3gbqTFD5+CyuOU2K2kJE5+cakQ=', if (process.platform === "darwin") config.name = "utun23";
config: WgConfigSet = {
privateKey: "4GTKsUfzodunTXaHtY/u+JhQN1D2CP1Sc+4D1VmpylY=",
address: [
"10.66.124.1/32"
],
peers: {}
};
config.peers[publicKey(peer1Key)] = { config.setPrivateKey(await privateKey());
config.addNewAddress("10.66.66.1/32");
config.addNewAddress("fd42:42:42::1/128");
const peer1 = await privateKey();
config.addNewPeer(publicKey(peer1), {
keepInterval: 15,
presharedKey: await presharedKey(),
allowedIPs: [ allowedIPs: [
"10.66.124.2" "10.66.66.2/32"
] ]
}
await t.test("Set config in interface", async () => {
await setConfig("wg23", config);
}); });
await t.test("Get config in interface", async () => { const peer2 = await privateKey();
const __config = await getConfig("wg23"); config.addNewPeer(publicKey(peer2), {
if (!__config.peers[publicKey(peer1Key)]) throw new Error("Not exist peer 1!"); keepInterval: 0,
});
config.peers[publicKey(peer1Key)].removeMe = true;
config.peers[publicKey(peer2Key)] = {
allowedIPs: [ allowedIPs: [
"10.66.124.3" "10.66.66.3/32"
] ]
}
await t.test("Set config in interface", async () => {
await setConfig("wg23", config);
}); });
await t.test("Get config in interface", async () => { const jsonConfig = config.toJSON();
const __config = await getConfig("wg23");
if (__config.peers[publicKey(peer1Key)]) throw new Error("Invalid config get!"); let skip: string;
if (!__config.peers[publicKey(peer2Key)]) throw new Error("Not exist peer 2!"); await t.test("Create and Set config in interface", async () => setConfig(jsonConfig).catch(err => { skip = "Cannot set wireguard config"; return Promise.reject(err); }));
await t.test("Get config from interface", { skip }, async () => {
const config = await getConfig(jsonConfig.name);
// console.dir(config, { depth: null });
if (!config.peers[publicKey(peer1)]) throw new Error("Peer not exists in interface");
if (!config.peers[publicKey(peer2)]) throw new Error("Peer not exists in interface");
assert.equal(config.peers[publicKey(peer1)].keepInterval, jsonConfig.peers[publicKey(peer1)].keepInterval);
assert.equal(config.peers[publicKey(peer1)].presharedKey, jsonConfig.peers[publicKey(peer1)].presharedKey);
assert.deepEqual(config.peers[publicKey(peer1)].allowedIPs, jsonConfig.peers[publicKey(peer1)].allowedIPs);
assert.deepEqual(config.peers[publicKey(peer2)].allowedIPs, jsonConfig.peers[publicKey(peer2)].allowedIPs);
}); });
await t.test("Delete interface", async () => { await t.test("Delete interface if exists", { skip }, async () => config.delete());
await deleteInterface("wg23");
}); });
});
}