Big code refactoring #10
11
.github/dependabot.yml
vendored
11
.github/dependabot.yml
vendored
@ -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
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
|
77
.github/workflows/test.yml
vendored
77
.github/workflows/test.yml
vendored
@ -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
3
.gitignore
vendored
@ -11,6 +11,9 @@ node_modules/
|
||||
.DS_Store
|
||||
*.log
|
||||
|
||||
# Addon
|
||||
addon/userspace/wg-go.*
|
||||
|
||||
# Typescript
|
||||
*.tsbuildinfo
|
||||
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
|
95
.vscode/c_cpp_properties.json
vendored
95
.vscode/c_cpp_properties.json
vendored
@ -1,77 +1,16 @@
|
||||
{
|
||||
"version": 4,
|
||||
"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",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/node_modules/node-addon-api",
|
||||
"/usr/local/include/node",
|
||||
"${workspaceFolder}/addons/genKey/**",
|
||||
"${workspaceFolder}/addons/tools/**"
|
||||
"/usr/local/lib/zig//libc/include/any-linux-any",
|
||||
"${workspaceFolder}/node_modules/node-addon-api",
|
||||
"${workspaceFolder}/**",
|
||||
"${workspaceFolder}/addon"
|
||||
],
|
||||
"defines": [
|
||||
"NAPI_DISABLE_CPP_EXCEPTIONS"
|
||||
],
|
||||
"compilerArgs": [
|
||||
"-fpermissive",
|
||||
"-fexceptions",
|
||||
"-w",
|
||||
"-fpermissive",
|
||||
"-fPIC",
|
||||
"-static"
|
||||
"AF_NETLINK=16"
|
||||
],
|
||||
"macFrameworkPath": [
|
||||
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks"
|
||||
@ -80,6 +19,28 @@
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"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
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
93
.vscode/settings.json
vendored
@ -23,8 +23,99 @@
|
||||
"PATH": "${workspaceFolder}/node_modules/.bin:${env:PATH}"
|
||||
},
|
||||
"files.associations": {
|
||||
"*.dsc": "ini",
|
||||
"*.gyp": "python",
|
||||
"random": "cpp",
|
||||
"limits": "cpp",
|
||||
"xstring": "cpp"
|
||||
"xstring": "cpp",
|
||||
"__bit_reference": "cpp",
|
||||
"__bits": "cpp",
|
||||
"__config": "cpp",
|
||||
"__debug": "cpp",
|
||||
"__errc": "cpp",
|
||||
"__hash_table": "cpp",
|
||||
"__locale": "cpp",
|
||||
"__mutex_base": "cpp",
|
||||
"__node_handle": "cpp",
|
||||
"__nullptr": "cpp",
|
||||
"__split_buffer": "cpp",
|
||||
"__string": "cpp",
|
||||
"__threading_support": "cpp",
|
||||
"__tree": "cpp",
|
||||
"__tuple": "cpp",
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bitset": "cpp",
|
||||
"cctype": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"compare": "cpp",
|
||||
"complex": "cpp",
|
||||
"concepts": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"exception": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"ios": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"locale": "cpp",
|
||||
"map": "cpp",
|
||||
"memory": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"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
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": []
|
||||
}
|
||||
]
|
||||
}
|
29
README.md
29
README.md
@ -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.
|
||||
- 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).
|
||||
### Wireguard
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> we have pre-copiled files for:
|
||||
> - `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)
|
||||
- `Embeddable-wg-library`: LGPL-2.1+.
|
||||
- `Wireguard-nt`: GPL-2.0
|
||||
- `Wireguard-go`: MIT
|
@ -1,8 +1,6 @@
|
||||
#include "wgkeys.hh"
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#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] << 4) | (src[1] >> 4)) & 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;
|
||||
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) {
|
||||
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};
|
||||
encode_base64(&base64[i * 4], tempKey);
|
||||
base64[sizeof(wg_key_b64_string) - 2] = '=';
|
||||
@ -38,15 +39,24 @@ static int decodeBase64(const char src[4]) {
|
||||
int val = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static volatile void * (*memset_func)(void *, int, size_t) = (volatile void * (*)(void *, int, size_t))&memset;
|
||||
void memzero_explicit(void *s, size_t count) {
|
||||
memset_func(s, 0, count);
|
||||
}
|
||||
static volatile void *(*memset_func)(void *, int, size_t) =
|
||||
(volatile void *(*)(void *, int, size_t)) & memset;
|
||||
void memzero_explicit(void *s, size_t count) { memset_func(s, 0, count); }
|
||||
#else
|
||||
static __attribute__((noinline)) void memzero_explicit(void *s, size_t 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) {
|
||||
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) {
|
||||
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) {
|
||||
int i, j;
|
||||
int64_t t[31] = {0};
|
||||
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));
|
||||
carry(o);
|
||||
carry(o);
|
||||
@ -136,7 +150,8 @@ static void invert(fe o, const fe i) {
|
||||
memcpy(c, i, sizeof(c));
|
||||
for (a = 253; a >= 0; --a) {
|
||||
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));
|
||||
memzero_explicit(c, sizeof(c));
|
||||
@ -146,24 +161,35 @@ void wgKeys::generatePreshared(wg_key preshared_key) {
|
||||
#if _WIN32 || defined(__CYGWIN__)
|
||||
HCRYPTPROV hCryptProv;
|
||||
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);
|
||||
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)))
|
||||
if (!getentropy(preshared_key, sizeof(wg_key))) 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)))
|
||||
if (!getentropy(preshared_key, sizeof(wg_key)))
|
||||
return;
|
||||
|
||||
#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__
|
||||
size_t ret, i;
|
||||
int fd;
|
||||
fd = open("/dev/urandom", O_RDONLY);
|
||||
assert(fd >= 0);
|
||||
if (fd <= 0);
|
||||
for (i = 0; i < sizeof(wg_key); i += ret) {
|
||||
ret = read(fd, preshared_key + i, sizeof(wg_key) - i);
|
||||
assert(ret > 0);
|
||||
if (ret < 0);
|
||||
}
|
||||
close(fd);
|
||||
return;
|
||||
@ -237,8 +263,15 @@ void wgKeys::generatePublic(wg_key public_key, const wg_key private_key) {
|
||||
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;
|
||||
|
||||
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) {
|
||||
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;
|
||||
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 + 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);
|
||||
ret |= ((uint32_t)val >> 31) | (val & 0xff);
|
||||
key[i * 3 + 0] = (val >> 16) & 0xff;
|
||||
key[i * 3 + 1] = (val >> 8) & 0xff;
|
||||
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) {
|
||||
wg_key_b64_string base64;
|
||||
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};
|
||||
encode_base64(&base64[i * 4], tempKey);
|
||||
base64[sizeof(wg_key_b64_string) - 2] = '=';
|
||||
@ -284,3 +324,18 @@ std::string wgKeys::toString(const wg_key key) {
|
||||
|
||||
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
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
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
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
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
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
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")
|
||||
}
|
11
addon/userspace/go/main_unix.go
Normal file
11
addon/userspace/go/main_unix.go
Normal file
@ -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"
|
5
addon/userspace/go/main_windows.go
Normal file
5
addon/userspace/go/main_windows.go
Normal file
@ -0,0 +1,5 @@
|
||||
//go:build windows
|
||||
|
||||
package main
|
||||
|
||||
var socketDirectory = `\\.\pipe\ProtectedPrefix\Administrators\WireGuard`
|
54
addon/userspace/ipc.cpp
Normal file
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;
|
||||
}
|
176
addon/userspace/wginterface.cpp
Normal file
176
addon/userspace/wginterface.cpp
Normal file
@ -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
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
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
BIN
addon/win/arm/wireguard.dll
Normal file
Binary file not shown.
530
addon/win/wginterface.cpp
Normal file
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
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;
|
||||
}
|
110
binding.yaml
110
binding.yaml
@ -1,80 +1,56 @@
|
||||
name: wginterface
|
||||
flags:
|
||||
- "!-fno-exceptions"
|
||||
- "-fpermissive"
|
||||
- "-fexceptions"
|
||||
- "-w"
|
||||
- "-fpermissive"
|
||||
flagsCC:
|
||||
- "!-fno-exceptions"
|
||||
- "-fpermissive"
|
||||
- "-fexceptions"
|
||||
- "-w"
|
||||
- "-fpermissive"
|
||||
defines:
|
||||
- "NAPI_DISABLE_CPP_EXCEPTIONS"
|
||||
- "NODE_VERSION=4"
|
||||
- "NODE_VERSION=8"
|
||||
- "NAPI_CPP_EXCEPTIONS"
|
||||
includes:
|
||||
- addons/genKey
|
||||
- addons/tools
|
||||
- node_modules/node-addon-api
|
||||
- ./addon
|
||||
sources:
|
||||
- "addons/genKey/wgkeys.cpp"
|
||||
- "addons/tools/wginterface.cpp"
|
||||
- "addons/tools/wginterface-dummy.cpp"
|
||||
- "addon/main.cpp"
|
||||
- "addon/genKey/wgkeys.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:
|
||||
macos:
|
||||
defines:
|
||||
- USERSPACE_GO
|
||||
linux:
|
||||
sources:
|
||||
- "addons/tools/linux/wireguard.c"
|
||||
- "addons/tools/wginterface-linux.cpp"
|
||||
- "!addons/tools/wginterface-dummy.cpp"
|
||||
defines:
|
||||
- "LISTDEV"
|
||||
- "GETCONFIG"
|
||||
- "SETCONFIG"
|
||||
- "DELIFACE"
|
||||
target:
|
||||
x86_64:
|
||||
release: true
|
||||
- "!addon/userspace/wginterface.cpp"
|
||||
- "addon/linux/wginterface.cpp"
|
||||
- "addon/linux/wireguard.c"
|
||||
flags:
|
||||
- "-fPIC"
|
||||
flagsCC:
|
||||
- "-fPIC"
|
||||
aarch64:
|
||||
release: true
|
||||
flags:
|
||||
- "-fPIC"
|
||||
flagsCC:
|
||||
- "!-fno-exceptions"
|
||||
- "-fpermissive"
|
||||
- "-fexceptions"
|
||||
- "-w"
|
||||
- "-fpermissive"
|
||||
- "-fPIC"
|
||||
windows:
|
||||
flags:
|
||||
- "-undefined"
|
||||
- "dynamic_lookup"
|
||||
target:
|
||||
x86_64:
|
||||
release: true
|
||||
aarch64:
|
||||
release: true
|
||||
sources:
|
||||
- "addons/tools/wginterface-win.cpp"
|
||||
- "!addons/tools/wginterface-dummy.cpp"
|
||||
includes:
|
||||
- "addons/tools/win"
|
||||
defines:
|
||||
- "ONSTARTADDON"
|
||||
- "LISTDEV"
|
||||
- "GETCONFIG"
|
||||
- "SETCONFIG"
|
||||
- "DELIFACE"
|
||||
- "_HAS_EXCEPTIONS=1"
|
||||
- "!addon/userspace/wginterface.cpp"
|
||||
- "addon/win/wginterface.cpp"
|
||||
libraries:
|
||||
- "bcrypt.lib"
|
||||
- "crypt32.lib"
|
||||
- "iphlpapi.lib"
|
||||
- "kernel32.lib"
|
||||
- "ntdll.lib"
|
||||
- "ws2_32.lib"
|
||||
- "setupapi.lib"
|
||||
- wbemuuid.lib
|
||||
- bcrypt.lib
|
||||
- crypt32.lib
|
||||
- iphlpapi.lib
|
||||
- kernel32.lib
|
||||
- ntdll.lib
|
||||
- ws2_32.lib
|
||||
- setupapi.lib
|
||||
defines:
|
||||
- "_HAS_EXCEPTIONS=1"
|
||||
- "ONSTARTADDON"
|
||||
macos:
|
||||
flags:
|
||||
- "!-fno-exceptions"
|
||||
- "-fpermissive"
|
||||
- "-fexceptions"
|
||||
- "-w"
|
||||
- "-fpermissive"
|
||||
|
16
package.json
16
package.json
@ -11,7 +11,7 @@
|
||||
"license": "GPL-3.0-or-later",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://sirherobrine23.org/Wireguard/Wireguard-tools.js.git"
|
||||
"url": "https://sirherobrine23.org/Wireguard/Wireguard-tools.js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"wireguard",
|
||||
@ -34,18 +34,18 @@
|
||||
},
|
||||
"scripts": {
|
||||
"install": "rebory prebuild",
|
||||
"dev": "rebory build -DP",
|
||||
"test": "rebory build -D && node --no-warnings --loader ts-node/esm src/index_test.js",
|
||||
"prepack": "tsc --build --clean && tsc --build",
|
||||
"dev": "rebory build",
|
||||
"test": "rebory build && node --no-warnings --loader ts-node/esm src/index_test.js",
|
||||
"prepack": "tsc --build --clean && tsc --build && rebory build --release",
|
||||
"postpack": "tsc --build --clean"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.19",
|
||||
"@types/node": "^20.11.26",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-addon-api": "^7.1.0",
|
||||
"rebory": "^0.1.12"
|
||||
"node-addon-api": "^8.0.0",
|
||||
"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 wgQuick from "./quick.js";
|
||||
export * as quickConfig from "./quick.js";
|
||||
export * from "./wginterface.js";
|
||||
export * as wginterface from "./wginterface.js";
|
||||
export * as default from "./wginterface.js";
|
||||
|
46
src/key.ts
46
src/key.ts
@ -1,5 +1,7 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
export const KeyLength = 32, Base64Length = 44;
|
||||
|
||||
type BinArray = Float64Array|Uint8Array|number[];
|
||||
|
||||
function gf(init?: number[]) {
|
||||
@ -94,39 +96,31 @@ function clamp(z: BinArray) {
|
||||
z[0] &= 248;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate preshared key blocking loop event
|
||||
*
|
||||
* @deprecated - use presharedKey
|
||||
*/
|
||||
export function presharedKeySync() {
|
||||
var privateKey = new Uint8Array(32);
|
||||
crypto.randomFillSync(privateKey);
|
||||
return keyToBase64(privateKey);
|
||||
function Base64ToKey(keyInput: string): Uint8Array {
|
||||
if (keyInput.length !== Base64Length) throw new Error("Invalid key length", { cause: { Required: Base64Length, Input: keyInput.length } });
|
||||
return new Uint8Array(Buffer.from(keyInput, "base64"));
|
||||
}
|
||||
|
||||
function keyToBase64(key: Uint8Array): string {
|
||||
if (key.length !== KeyLength) throw new Error("Invalid key length", { cause: { Required: KeyLength, Input: key.length } });
|
||||
return Buffer.from(key).toString("base64");
|
||||
}
|
||||
|
||||
export function keyToHex(key: string): string {
|
||||
return Buffer.from(key, "base64").toString("hex");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate preshared key
|
||||
*/
|
||||
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) => {
|
||||
if (err) return reject(err);
|
||||
done(keyToBase64(privateKey));
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create private key
|
||||
*
|
||||
* @deprecated - Use privateKey
|
||||
*/
|
||||
export function privateKeySync() {
|
||||
var privateKey = Base64ToKey(presharedKeySync());
|
||||
clamp(privateKey);
|
||||
return keyToBase64(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create private key
|
||||
*/
|
||||
@ -136,14 +130,6 @@ export async function 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
|
||||
*
|
||||
@ -151,7 +137,7 @@ export function Base64ToKey(keyInput: string): Uint8Array {
|
||||
*/
|
||||
export function publicKey(privKey: string) {
|
||||
var privateKey: Uint8Array = Base64ToKey(privKey);
|
||||
var r: number, z = new Uint8Array(32);
|
||||
var r: number, z = new Uint8Array(KeyLength);
|
||||
var a = gf([1]),
|
||||
b = gf([9]),
|
||||
c = gf(),
|
||||
|
@ -1,46 +1,10 @@
|
||||
import { promises as fs } from "fs";
|
||||
import { isIPv4, createConnection as netConnection } from "net";
|
||||
import path from "path";
|
||||
import readline from "readline";
|
||||
import { finished } from "stream/promises";
|
||||
import rebory from "rebory";
|
||||
import { fileURLToPath } from "url";
|
||||
import path from "node:path";
|
||||
import { loadAddon } from "rebory";
|
||||
import { key } from "./index.js";
|
||||
import { isIP } from "node:net";
|
||||
const __dirname = import.meta.dirname || path.dirname((await import("node:url")).fileURLToPath(import.meta.url));
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const addon = rebory.loadAddon(path.join(__dirname, "../binding.yaml")).wginterface.loadRelease<{
|
||||
listDevices?: () => Promise<{from: "userspace"|"kernel", name: string, path?: string}[]>;
|
||||
deleteInterface?: (wgName: string) => Promise<void>;
|
||||
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 {
|
||||
interface Peer {
|
||||
/** Preshared key to peer */
|
||||
presharedKey?: string;
|
||||
|
||||
@ -54,23 +18,26 @@ export interface Peer {
|
||||
allowedIPs?: string[];
|
||||
};
|
||||
|
||||
export interface PeerSet extends Peer {
|
||||
/** Mark this peer to be removed, any changes remove this option */
|
||||
export interface SetPeer extends Peer {
|
||||
/** Remove this peer */
|
||||
removeMe?: boolean;
|
||||
}
|
||||
|
||||
export interface PeerGet extends Peer {
|
||||
export interface GetPeer extends 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. */
|
||||
txBytes?: number;
|
||||
txBytes: bigint;
|
||||
|
||||
/** 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: string;
|
||||
|
||||
@ -86,164 +53,341 @@ export interface WgConfigBase<T extends Peer> {
|
||||
/** Interface IP address'es */
|
||||
address?: string[];
|
||||
|
||||
/** Interface peers */
|
||||
/** Interface Peers */
|
||||
peers: Record<string, T>;
|
||||
}
|
||||
};
|
||||
|
||||
export interface WgConfigGet extends WgConfigBase<PeerGet> {}
|
||||
export interface WgConfigSet extends WgConfigBase<PeerSet> {
|
||||
export interface GetConfig extends Config<GetPeer> { };
|
||||
export interface SetConfig extends Config<SetPeer> {
|
||||
/** this option will remove all peers if `true` and add new peers */
|
||||
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() {
|
||||
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;
|
||||
}
|
||||
deleteInterface(name: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Delete wireguard interface if present
|
||||
* @param wgName - Interface name
|
||||
* @returns
|
||||
* Get Wireguard interfaces list
|
||||
*
|
||||
* if running in userspace return socket (UAPI) path's
|
||||
*/
|
||||
export async function deleteInterface(wgName: string): Promise<void> {
|
||||
if (typeof addon.deleteInterface === "function") return addon.deleteInterface(wgName);
|
||||
const dev = (await listDevices()).find(s => s.name === wgName);
|
||||
if (dev && dev.path) return fs.rm(dev.path, { force: true });
|
||||
}
|
||||
listDevices(): Promise<string[]>;
|
||||
|
||||
/**
|
||||
* Add the settings to the Wireguard interface, if it does not exist and the interface will be created automatically.
|
||||
*
|
||||
* To update the interface settings, first get the interface settings to update!
|
||||
*
|
||||
* @param wgName - Interface name
|
||||
* Get current config from Wireguard interface
|
||||
* @param name - Interface name
|
||||
*/
|
||||
getConfig(name: string): Promise<GetConfig>;
|
||||
|
||||
/**
|
||||
* Set new config to Wireguard interface or create new interface if not exists
|
||||
* @param config - Interface config
|
||||
*/
|
||||
export async function setConfig(wgName: string, config: WgConfigGet): Promise<void>;
|
||||
/**
|
||||
* Add the settings to the Wireguard interface, if it does not exist and the interface will be created automatically.
|
||||
*
|
||||
* To update the interface settings, first get the interface settings to update!
|
||||
*
|
||||
* @param wgName - Interface name
|
||||
* @param config - Interface config
|
||||
*/
|
||||
export async function setConfig(wgName: string, config: WgConfigSet): Promise<void> {
|
||||
if (wgName.length > constants.nameLength) throw new Error("Interface name more then allowed", { cause: constants.nameLength });
|
||||
if (typeof addon.setConfig === "function") return addon.setConfig(wgName, config);
|
||||
const client = netConnection(path.join(defaultPath, (wgName).concat(".sock")));
|
||||
const writel = (...data: any[]) => client.write(data.map(String).join("").concat("\n"));
|
||||
// Init set config in interface
|
||||
writel("set=1");
|
||||
|
||||
// Port listening
|
||||
if (config.portListen !== undefined && Math.floor(config.portListen) >= 0) writel(("listen_port="), ((Math.floor(config.portListen))));
|
||||
|
||||
// fwmark
|
||||
if (Math.floor(config.fwmark) >= 0) writel(("fwmark="), ((Math.floor(config.fwmark))));
|
||||
|
||||
// Replace peer's
|
||||
if (config.replacePeers) writel("replace_peers=true");
|
||||
|
||||
// Keys
|
||||
if (typeof config.privateKey === "string" && config.privateKey.length > 0) writel(("private_key="), (Buffer.from(config.privateKey, "base64").toString("hex")));
|
||||
|
||||
// Mount peer
|
||||
for (const publicKey of Object.keys(config.peers||{})) {
|
||||
const { presharedKey, endpoint, keepInterval, removeMe, allowedIPs = [] } = config.peers[publicKey];
|
||||
|
||||
// Public key
|
||||
writel(("public_key="), (Buffer.from(publicKey, "base64").toString("hex")));
|
||||
if (removeMe) {
|
||||
writel("remove=true"); // Remove peer
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof endpoint === "string" && endpoint.length > 0) writel(("endpoint="), (endpoint));
|
||||
if (typeof presharedKey === "string" && presharedKey.length > 3) writel(("preshared_key="), (Buffer.from(presharedKey, "base64").toString("hex")));
|
||||
if (typeof keepInterval === "number" && Math.floor(keepInterval) > 0) writel(("persistent_keepalive_interval="), (String(Math.floor(keepInterval))));
|
||||
if (allowedIPs.length > 0) {
|
||||
writel("replace_allowed_ips=true");
|
||||
const fixed = allowedIPs.map(i => i.indexOf("/") === -1 ? i.concat("/", (isIPv4(i) ? "32" : "128")) : i)
|
||||
for (const IIP of fixed) writel(("allowed_ip="), (IIP));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
setConfig(config: SetConfig): Promise<void>;
|
||||
}>({
|
||||
WIN32DLLPATH: path.resolve(__dirname, "../addon/win", (process.arch === "x64" && "amd64") || (process.arch === "ia32" && "x86") || process.arch, "wireguard.dll")
|
||||
});
|
||||
client.write("\n");
|
||||
await finished(client, { error: true });
|
||||
const payloadKeys = payload.split("\n").filter(i => i.length > 3).map(line => { const iit = line.indexOf("="); return [ line.slice(0, iit), line.slice(iit+1) ]; })
|
||||
if (payloadKeys.at(-1)[1] !== "0") {
|
||||
const err = new Error("Invalid send config, check log");
|
||||
throw err;
|
||||
|
||||
export const {
|
||||
driveVersion,
|
||||
listDevices,
|
||||
getConfig,
|
||||
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
|
||||
* @param wgName - Interface name
|
||||
* @returns
|
||||
* Maneger Wireguard interface and peers simple and fast
|
||||
*/
|
||||
export async function getConfig(wgName: string): Promise<WgConfigGet> {
|
||||
if (typeof addon.getConfig === "function") return addon.getConfig(wgName);
|
||||
const info = (await listDevices()).find(int => int.name === wgName);
|
||||
if (!info) throw new Error("Create interface, not exists");
|
||||
const client = netConnection(path.join(defaultPath, wgName.concat(".sock")));
|
||||
const config: WgConfigGet = Object();
|
||||
let latestPeer: string, previewKey: string;
|
||||
|
||||
const tetrisBreak = readline.createInterface(client);
|
||||
tetrisBreak.on("line", function lineProcess(line) {
|
||||
if (line === "") tetrisBreak.removeListener("line", lineProcess).close();
|
||||
const findout = line.indexOf("="), keyName = line.slice(0, findout), value = line.slice(findout+1);
|
||||
if (findout <= 0) return;
|
||||
if (keyName === "errno" && value !== "0") throw new Error(("wireguard-go error, code: ").concat(value));
|
||||
|
||||
// Drop
|
||||
if ((["last_handshake_time_nsec", "protocol_version", "errno"]).includes(keyName)) return;
|
||||
else if (keyName === "private_key") config.privateKey = Buffer.from(value, "hex").toString("base64");
|
||||
else if (keyName === "listen_port") config.portListen = Number(value);
|
||||
else if (keyName === "endpoint") ((config.peers||(config.peers = {}))[latestPeer]).endpoint = value;
|
||||
else if (keyName === "persistent_keepalive_interval") ((config.peers||(config.peers = {}))[latestPeer]).keepInterval = Number(value);
|
||||
else if (keyName === "rx_bytes") ((config.peers||(config.peers = {}))[latestPeer]).rxBytes = Number(value);
|
||||
else if (keyName === "tx_bytes") ((config.peers||(config.peers = {}))[latestPeer]).txBytes = Number(value);
|
||||
else if (keyName === "last_handshake_time_sec") ((config.peers||(config.peers = {}))[latestPeer]).lastHandshake = new Date(Number(value) * 1000);
|
||||
else if (keyName === "allowed_ip") {
|
||||
if (!value) return;
|
||||
((config.peers||(config.peers = {}))[latestPeer]).allowedIPs = (((config.peers||(config.peers = {}))[latestPeer]).allowedIPs||[]).concat(value);
|
||||
} else if (keyName === "preshared_key") {
|
||||
if (value === "0000000000000000000000000000000000000000000000000000000000000000") return;
|
||||
((config.peers||(config.peers = {}))[latestPeer]).presharedKey = Buffer.from(value, "hex").toString("base64");
|
||||
} else if (keyName === "public_key") {
|
||||
const keyDecode = Buffer.from(value, "hex").toString("base64");
|
||||
if (previewKey !== "public_key") (config.peers||(config.peers = {}))[latestPeer] = {};
|
||||
else {
|
||||
config.publicKey = latestPeer;
|
||||
(config.peers||(config.peers = {}))[keyDecode] = (config.peers||(config.peers = {}))[latestPeer];
|
||||
delete (config.peers||(config.peers = {}))[latestPeer];
|
||||
latestPeer = keyDecode;
|
||||
export class Wireguard {
|
||||
constructor(config?: SetConfig | GetConfig | Config<Peer>) {
|
||||
// super({});
|
||||
if (!config) return;
|
||||
if (typeof config === "object") {
|
||||
if (config instanceof Wireguard) return config;
|
||||
}
|
||||
}
|
||||
previewKey = keyName;
|
||||
});
|
||||
client.write("get=1\n\n");
|
||||
await new Promise((done, reject) => tetrisBreak.on("error", reject).once("close", done));
|
||||
await finished(client.end());
|
||||
|
||||
private _name: string;
|
||||
get name() {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 { setConfig, deleteInterface, WgConfigSet, getConfig } from "./wginterface.js";
|
||||
import { publicKey } from "./key.js";
|
||||
import { userInfo } from "os";
|
||||
import { Wireguard, getConfig, setConfig } from "./wginterface.js";
|
||||
import { presharedKey, privateKey, publicKey } from "./key.js";
|
||||
import assert from "node:assert";
|
||||
|
||||
if (process.platform === "win32" || process.platform === "linux" && (userInfo().uid === 0)) {
|
||||
test("Wireguard configuration", async t => {
|
||||
// Config base
|
||||
const peer1Key = 'EKgSatFzZtsv1qFJ6gE8HqfuA+tXzW+7vDeVc7Xaa2E=', peer2Key = '4BSvgiM9j5jjuR0Vg3gbqTFD5+CyuOU2K2kJE5+cakQ=',
|
||||
config: WgConfigSet = {
|
||||
privateKey: "4GTKsUfzodunTXaHtY/u+JhQN1D2CP1Sc+4D1VmpylY=",
|
||||
address: [
|
||||
"10.66.124.1/32"
|
||||
],
|
||||
peers: {}
|
||||
};
|
||||
await test("Wireguard interface", async t => {
|
||||
const config = new Wireguard;
|
||||
config.name = "wg23";
|
||||
if (process.platform === "darwin") config.name = "utun23";
|
||||
|
||||
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: [
|
||||
"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 __config = await getConfig("wg23");
|
||||
if (!__config.peers[publicKey(peer1Key)]) throw new Error("Not exist peer 1!");
|
||||
});
|
||||
|
||||
config.peers[publicKey(peer1Key)].removeMe = true;
|
||||
config.peers[publicKey(peer2Key)] = {
|
||||
const peer2 = await privateKey();
|
||||
config.addNewPeer(publicKey(peer2), {
|
||||
keepInterval: 0,
|
||||
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 __config = await getConfig("wg23");
|
||||
if (__config.peers[publicKey(peer1Key)]) throw new Error("Invalid config get!");
|
||||
if (!__config.peers[publicKey(peer2Key)]) throw new Error("Not exist peer 2!");
|
||||
const jsonConfig = config.toJSON();
|
||||
|
||||
let skip: string;
|
||||
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 deleteInterface("wg23");
|
||||
await t.test("Delete interface if exists", { skip }, async () => config.delete());
|
||||
});
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user