Droping rebory and migrate to cmake-js #14
13
.github/workflows/test.yaml
vendored
13
.github/workflows/test.yaml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [ 16.x, 18.x, 20.x, latest ]
|
node-version: [ 18.x, 20.x, 21.x, latest ]
|
||||||
steps:
|
steps:
|
||||||
- name: Disable sudo PATH replace
|
- name: Disable sudo PATH replace
|
||||||
run: |
|
run: |
|
||||||
@ -32,18 +32,13 @@ jobs:
|
|||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: addon/userspace/go/go.mod
|
go-version-file: addon/userspace/go/go.mod
|
||||||
go-version: ">=1.22.0"
|
go-version: ">=1.22"
|
||||||
|
|
||||||
- name: "Setup zig"
|
|
||||||
uses: korandoru/setup-zig@v1
|
|
||||||
with:
|
|
||||||
zig-version: "master"
|
|
||||||
|
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: sudo apt update && sudo apt install -y build-essential
|
run: sudo apt update && sudo apt install -y build-essential cmake
|
||||||
|
|
||||||
- name: Install node dependencies
|
- name: Install node dependencies
|
||||||
run: npm install --no-save --no-audit --no-fund --ignore-scripts
|
run: npm install --no-save --no-audit --no-fund --ignore-scripts
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: ./node_modules/.bin/rebory build && sudo node --no-warnings --loader ts-node/esm src/index_test.js
|
run: npm run build && sudo node --no-warnings --loader ts-node/esm src/index_test.js
|
||||||
|
10
.npmignore
10
.npmignore
@ -2,15 +2,19 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
/*.tgz
|
/*.tgz
|
||||||
|
|
||||||
|
# Builder dir
|
||||||
|
build/
|
||||||
|
|
||||||
# Typescript
|
# Typescript
|
||||||
src/**/*_test.*
|
src/**/*_test.*
|
||||||
src/**/*.ts
|
src/**/*.ts
|
||||||
!src/**/*.d.ts
|
!src/**/*.d.ts
|
||||||
|
|
||||||
# vscode
|
# IDEs
|
||||||
.devcontainer/
|
.devcontainer/
|
||||||
.vscode/
|
|
||||||
.vscode-ctags
|
.vscode-ctags
|
||||||
|
.vscode/
|
||||||
|
.zed/
|
||||||
|
|
||||||
# Github and Git
|
# Github and Git
|
||||||
.github/
|
.github/
|
||||||
@ -18,4 +22,4 @@ src/**/*.ts
|
|||||||
|
|
||||||
# Project
|
# Project
|
||||||
.vscode-ctags*
|
.vscode-ctags*
|
||||||
*.addrs.json
|
*.addrs.json
|
||||||
|
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -116,6 +116,8 @@
|
|||||||
"xtr1common": "cpp",
|
"xtr1common": "cpp",
|
||||||
"xtree": "cpp",
|
"xtree": "cpp",
|
||||||
"xutility": "cpp",
|
"xutility": "cpp",
|
||||||
"fstream": "cpp"
|
"fstream": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"stack": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
38
.zed/settings.json
Normal file
38
.zed/settings.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Folder-specific settings
|
||||||
|
//
|
||||||
|
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||||
|
// see the documentation: https://zed.dev/docs/configuring-zed#folder-specific-settings
|
||||||
|
{
|
||||||
|
"remove_trailing_whitespace_on_save": true,
|
||||||
|
"ensure_final_newline_on_save": true,
|
||||||
|
"formatter": "auto",
|
||||||
|
"format_on_save": "off",
|
||||||
|
"tab_size": 2,
|
||||||
|
"languages": {
|
||||||
|
"TypeScript": {
|
||||||
|
"hard_tabs": false
|
||||||
|
"format_on_save": true,
|
||||||
|
"code_actions_on_format": {
|
||||||
|
"source.organizeImports": true,
|
||||||
|
"source.removeUnusedImports": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"JavaScript": {
|
||||||
|
"hard_tabs": false
|
||||||
|
"format_on_save": true,
|
||||||
|
"code_actions_on_format": {
|
||||||
|
"source.organizeImports": true,
|
||||||
|
"source.removeUnusedImports": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Go": {
|
||||||
|
"format_on_save": true
|
||||||
|
},
|
||||||
|
"C++": {
|
||||||
|
"hard_tabs": false
|
||||||
|
},
|
||||||
|
"C": {
|
||||||
|
"hard_tabs": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
CMakeLists.txt
Normal file
98
CMakeLists.txt
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
cmake_policy(SET CMP0091 NEW)
|
||||||
|
cmake_policy(SET CMP0042 NEW)
|
||||||
|
|
||||||
|
project (wg)
|
||||||
|
|
||||||
|
add_compile_definitions(NAPI_VERSION=8 NAPI_CPP_EXCEPTIONS)
|
||||||
|
# set_target_properties(PROPERTIES CXX_STANDARD 17)
|
||||||
|
# set_target_properties(PROPERTIES C_STANDARD 17)
|
||||||
|
set(CMAKE_C_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
include_directories(${CMAKE_JS_INC})
|
||||||
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/addon")
|
||||||
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/addon/genKey")
|
||||||
|
# Include N-API wrappers
|
||||||
|
# $ node -p "require('node-addon-api').include"
|
||||||
|
# "/home/will/projects/financialcpp/financialcpp/node_modules/node-addon-api"
|
||||||
|
execute_process(COMMAND node -p "require('node-addon-api').include"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE NODE_ADDON_API_DIR
|
||||||
|
)
|
||||||
|
|
||||||
|
# strip `"` and `\n` from the output above
|
||||||
|
string(REPLACE "\n" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
|
||||||
|
string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
|
||||||
|
|
||||||
|
include_directories(PRIVATE ${NODE_ADDON_API_DIR})
|
||||||
|
|
||||||
|
if(UNIX)
|
||||||
|
add_definitions(-fpermissive -fexceptions -w -fpermissive -fPIC)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(GLOB GSOURCE_FILES
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/addon/main.cpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/addon/genKey/wgkeys.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
file(GLOB SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/addon/win/wginterface.cpp")
|
||||||
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/addon/win")
|
||||||
|
add_compile_definitions(_HAS_EXCEPTIONS=1 ONSTARTADDON)
|
||||||
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
"wbemuuid.lib"
|
||||||
|
"bcrypt.lib"
|
||||||
|
"crypt32.lib"
|
||||||
|
"iphlpapi.lib"
|
||||||
|
"kernel32.lib"
|
||||||
|
"ntdll.lib"
|
||||||
|
"ws2_32.lib"
|
||||||
|
"setupapi.lib"
|
||||||
|
)
|
||||||
|
elseif(UNIX AND NOT APPLE OR ANDROID)
|
||||||
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/addon/linux")
|
||||||
|
file(GLOB SOURCE_FILES
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/addon/linux/wireguard.c"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/addon/linux/wginterface.cpp"
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "Buiding go Userspace")
|
||||||
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/addon/userspace/wg-go.o)
|
||||||
|
file(REMOVE_RECURSE ${CMAKE_CURRENT_SOURCE_DIR}/addon/userspace/wg-go.o)
|
||||||
|
endif()
|
||||||
|
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/addon/userspace/wg-go.h)
|
||||||
|
file(REMOVE_RECURSE ${CMAKE_CURRENT_SOURCE_DIR}/addon/userspace/wg-go.h)
|
||||||
|
endif()
|
||||||
|
set(ENV{CGO_ENABLED} 1)
|
||||||
|
set(ENV{LDFLAGS} -w)
|
||||||
|
# Remove CXX and CC envs to CGO
|
||||||
|
set(ENV{DCXX} ENV{CXX})
|
||||||
|
set(ENV{DCC} ENV{CC})
|
||||||
|
set(ENV{CXX})
|
||||||
|
set(ENV{CC})
|
||||||
|
execute_process(
|
||||||
|
COMMAND go build -trimpath -v -o ../wg-go.o -buildmode c-archive .
|
||||||
|
# COMMAND env
|
||||||
|
RESULT_VARIABLE GOCODE
|
||||||
|
OUTPUT_VARIABLE GOBUILDLOG
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/addon/userspace/go
|
||||||
|
)
|
||||||
|
set(ENV{CXX} ENV{DCXX})
|
||||||
|
set(ENV{CC} ENV{DCC})
|
||||||
|
if(NOT GOCODE EQUAL "0")
|
||||||
|
message(FATAL_ERROR "cannot build go userspace code exit ${GOCODE}\n${GOBUILDLOG}")
|
||||||
|
endif()
|
||||||
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/addon/userspace")
|
||||||
|
set(USERSPACEOBJ ${CMAKE_CURRENT_SOURCE_DIR}/addon/userspace/wg-go.o)
|
||||||
|
file(GLOB SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/addon/userspace/wginterface.cpp")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} SHARED ${GSOURCE_FILES} ${SOURCE_FILES} ${CMAKE_JS_SRC})
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
|
||||||
|
target_link_libraries(${PROJECT_NAME} ${USERSPACEOBJ} ${CMAKE_JS_LIB})
|
||||||
|
|
||||||
|
if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET)
|
||||||
|
# Generate node.lib
|
||||||
|
execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS})
|
||||||
|
endif()
|
36
README.md
36
README.md
@ -2,8 +2,42 @@
|
|||||||
|
|
||||||
Manage your Wireguard interfaces directly from Node.js without any wrappers over `wg` or `wg-quick`
|
Manage your Wireguard interfaces directly from Node.js without any wrappers over `wg` or `wg-quick`
|
||||||
|
|
||||||
```js
|
> [!WARNING]
|
||||||
|
> Require cmake and tools (GCC/GCC++, clang or Visual Studio) to build this addon
|
||||||
|
>
|
||||||
|
> New versions does't include prebuilt binaries
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { setConfig, getConfig, key, Config } from "../index.js"
|
||||||
|
|
||||||
|
const tunName = process.platform === "darwin" ? "utun10" : "wg3" // Tunnel name, in MacOS/Darwin require start with utun prefix
|
||||||
|
let currentConfig: Config
|
||||||
|
try {
|
||||||
|
currentConfig = await getConfig(tunName) // Check if exists tun
|
||||||
|
} catch {
|
||||||
|
// Create new wireguard tun
|
||||||
|
currentConfig = {
|
||||||
|
name: tunName,
|
||||||
|
privateKey: await key.privateKey(),
|
||||||
|
portListen: 5820,
|
||||||
|
address: [
|
||||||
|
"10.66.66.1/24"
|
||||||
|
],
|
||||||
|
peers: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new Peer
|
||||||
|
const peerPrivate = await key.privateKey()
|
||||||
|
currentConfig.peers[key.publicKey(peerPrivate)] = {
|
||||||
|
presharedKey: await key.presharedKey(),
|
||||||
|
allowedIPs: [
|
||||||
|
"10.66.66.2/24"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deploy new Config
|
||||||
|
await setConfig(currentConfig)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Licences
|
## Licences
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
#include "wgkeys.hh"
|
#include "wgkeys.hh"
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <iostream>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <sstream>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
@ -263,7 +265,7 @@ void wgKeys::generatePublic(wg_key public_key, const wg_key private_key) {
|
|||||||
memzero_explicit(f, sizeof(f));
|
memzero_explicit(f, sizeof(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string wgKeys::generatePublic(const std::string private_key) {
|
std::string wgKeys::generatePublic(const std::string &private_key) {
|
||||||
wg_key public_key;
|
wg_key public_key;
|
||||||
wg_key private_key_;
|
wg_key private_key_;
|
||||||
stringToKey(private_key_, private_key);
|
stringToKey(private_key_, private_key);
|
||||||
@ -282,7 +284,7 @@ bool key_is_zero(const uint8_t key[32]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void wgKeys::stringToKey(wg_key key, std::string keyBase64) {
|
void wgKeys::stringToKey(wg_key key, std::string keyBase64) {
|
||||||
auto base64 = keyBase64.c_str();
|
const char* base64 = keyBase64.c_str();
|
||||||
if (keyBase64.length() != B64_WG_KEY_LENGTH ||
|
if (keyBase64.length() != B64_WG_KEY_LENGTH ||
|
||||||
base64[B64_WG_KEY_LENGTH - 1] != '=')
|
base64[B64_WG_KEY_LENGTH - 1] != '=')
|
||||||
throw std::string("invalid key, length: ")
|
throw std::string("invalid key, length: ")
|
||||||
@ -315,8 +317,7 @@ std::string wgKeys::toString(const wg_key key) {
|
|||||||
wg_key_b64_string base64;
|
wg_key_b64_string base64;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < 32 / 3; ++i)
|
for (i = 0; i < 32 / 3; ++i) encode_base64(&base64[i * 4], &key[i * 3]);
|
||||||
encode_base64(&base64[i * 4], &key[i * 3]);
|
|
||||||
const uint8_t tempKey[3] = {key[i * 3 + 0], key[i * 3 + 1], 0};
|
const uint8_t tempKey[3] = {key[i * 3 + 0], key[i * 3 + 1], 0};
|
||||||
encode_base64(&base64[i * 4], tempKey);
|
encode_base64(&base64[i * 4], tempKey);
|
||||||
base64[sizeof(wg_key_b64_string) - 2] = '=';
|
base64[sizeof(wg_key_b64_string) - 2] = '=';
|
||||||
@ -325,7 +326,7 @@ std::string wgKeys::toString(const wg_key key) {
|
|||||||
return std::string(base64);
|
return std::string(base64);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string wgKeys::toHex(const std::string keyBase64) {
|
std::string wgKeys::toHex(const std::string &keyBase64) {
|
||||||
wg_key key;
|
wg_key key;
|
||||||
wgKeys::stringToKey(key, keyBase64);
|
wgKeys::stringToKey(key, keyBase64);
|
||||||
char hex[65];
|
char hex[65];
|
||||||
@ -334,8 +335,11 @@ std::string wgKeys::toHex(const std::string keyBase64) {
|
|||||||
return std::string(hex);
|
return std::string(hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string wgKeys::HextoBase64(const std::string keyHex) {
|
std::string wgKeys::HextoBase64(const std::string &s_hex) {
|
||||||
wg_key key;
|
wg_key key;
|
||||||
for (int i = 0; i < 32; ++i) sscanf(keyHex.c_str() + i * 2, "%02x", &key[i]);
|
for(unsigned i = 0, uchr ; i < s_hex.length() ; i += 2) {
|
||||||
|
sscanf( s_hex.c_str()+ i, "%2x", &uchr); // conversion
|
||||||
|
key[i/2] = uchr; // save as char
|
||||||
|
}
|
||||||
return wgKeys::toString(key);
|
return wgKeys::toString(key);
|
||||||
}
|
}
|
@ -14,10 +14,10 @@ namespace wgKeys {
|
|||||||
std::string toString(const wg_key key);
|
std::string toString(const wg_key key);
|
||||||
|
|
||||||
// Convert base64 to hex key
|
// Convert base64 to hex key
|
||||||
std::string toHex(const std::string keyBase64);
|
std::string toHex(const std::string &keyBase64);
|
||||||
|
|
||||||
// Convert hex to base64
|
// Convert hex to base64
|
||||||
std::string HextoBase64(const std::string keyHex);
|
std::string HextoBase64(const std::string &keyHex);
|
||||||
|
|
||||||
// Convert base64 to wg_key
|
// Convert base64 to wg_key
|
||||||
void stringToKey(wg_key key, std::string keyBase64);
|
void stringToKey(wg_key key, std::string keyBase64);
|
||||||
@ -31,7 +31,7 @@ namespace wgKeys {
|
|||||||
// Get public key from private key
|
// Get public key from private key
|
||||||
void generatePublic(wg_key public_key, const wg_key private_key);
|
void generatePublic(wg_key public_key, const wg_key private_key);
|
||||||
|
|
||||||
std::string generatePublic(const std::string private_key);
|
std::string generatePublic(const std::string &private_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -30,7 +30,7 @@ std::string getWireguardVersion() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string version = "Unknown";
|
std::string version = "Unknown";
|
||||||
|
|
||||||
// /sys/module/wireguard/version - for kernel module
|
// /sys/module/wireguard/version - for kernel module
|
||||||
if (std::filesystem::exists("/sys/module/wireguard/version")) {
|
if (std::filesystem::exists("/sys/module/wireguard/version")) {
|
||||||
std::ifstream file("/sys/module/wireguard/version");
|
std::ifstream file("/sys/module/wireguard/version");
|
||||||
@ -100,9 +100,9 @@ void WireguardConfig::getWireguardConfig() {
|
|||||||
for ((peer) = (devConfig)->first_peer; (peer); (peer) = (peer)->next_peer) {
|
for ((peer) = (devConfig)->first_peer; (peer); (peer) = (peer)->next_peer) {
|
||||||
auto PeerConfig = Peer();
|
auto PeerConfig = Peer();
|
||||||
if (peer->flags & WGPEER_HAS_PRESHARED_KEY) PeerConfig.presharedKey = wgKeys::toString(peer->preshared_key);
|
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);
|
if (peer->endpoint.addr.sa_family == AF_INET||peer->endpoint.addr.sa_family == AF_INET6) PeerConfig.endpoint = HostAdresses(true, &peer->endpoint.addr);
|
||||||
|
|
||||||
|
PeerConfig.keepInterval = peer->persistent_keepalive_interval;
|
||||||
PeerConfig.lastHandshake = peer->last_handshake_time.tv_sec*1000;
|
PeerConfig.lastHandshake = peer->last_handshake_time.tv_sec*1000;
|
||||||
PeerConfig.rxBytes = peer->rx_bytes;
|
PeerConfig.rxBytes = peer->rx_bytes;
|
||||||
PeerConfig.txBytes = peer->tx_bytes;
|
PeerConfig.txBytes = peer->tx_bytes;
|
||||||
|
@ -36,18 +36,6 @@ Napi::Object StartAddon(const Napi::Env env, const Napi::Object exports) {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
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 {
|
exports.Set("setConfig", Napi::Function::New(env, [](const Napi::CallbackInfo &info) -> Napi::Value {
|
||||||
const Napi::Env env = info.Env();
|
const Napi::Env env = info.Env();
|
||||||
if (!(info[0].IsObject())) Napi::Error::New(env, "Set wireguard config!").ThrowAsJavaScriptException();
|
if (!(info[0].IsObject())) Napi::Error::New(env, "Set wireguard config!").ThrowAsJavaScriptException();
|
||||||
|
@ -1,15 +1,9 @@
|
|||||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
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/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
|
||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||||
|
@ -4,50 +4,24 @@ import "C"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"runtime"
|
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
_ "unsafe"
|
|
||||||
|
|
||||||
"golang.zx2c4.com/wireguard/conn"
|
"golang.zx2c4.com/wireguard/conn"
|
||||||
"golang.zx2c4.com/wireguard/device"
|
"golang.zx2c4.com/wireguard/device"
|
||||||
"golang.zx2c4.com/wireguard/ipc"
|
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const levelLog = device.LogLevelError
|
|
||||||
|
|
||||||
//go:linkname socketDirectory golang.xz2c4.com/wireguard/ipc.socketDirectory
|
|
||||||
var socketDirectory = "/var/run/wireguard"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
socketDirectory = `\\.\pipe\ProtectedPrefix\Administrators\WireGuard`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// End process function callbacks
|
|
||||||
var TunsEndProcess = make(map[string]func())
|
|
||||||
|
|
||||||
//export callEndProcess
|
|
||||||
func callEndProcess() {
|
|
||||||
for _, f := range TunsEndProcess {
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {}
|
func main() {}
|
||||||
func init() {
|
|
||||||
c := make(chan os.Signal, 1)
|
// Default log level to print in terminal
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
const levelLog = device.LogLevelSilent
|
||||||
go func() {
|
|
||||||
<-c
|
func getCharErr(err error) *C.char {
|
||||||
callEndProcess()
|
if err == nil {
|
||||||
}()
|
return C.CString("")
|
||||||
|
}
|
||||||
|
return C.CString(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get wireguard-go version
|
// Get wireguard-go version
|
||||||
@ -66,158 +40,68 @@ func wgVersion() *C.char {
|
|||||||
return C.CString("unknown")
|
return C.CString("unknown")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if tunnel exist
|
// End process function callbacks
|
||||||
//
|
var Tuns = make(map[string]tun.Device)
|
||||||
//export existTun
|
var Devices = make(map[string]*device.Device)
|
||||||
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
|
// List wireguard-go UAPI's sockets
|
||||||
// first\0second\0third\0forth\0last\0\0
|
// first\0second\0third\0forth\0last\0\0
|
||||||
//
|
//
|
||||||
//export listUapis
|
//export listInternalTuns
|
||||||
func listUapis() *C.char {
|
func listInternalTuns() *C.char {
|
||||||
Files, err := os.ReadDir(socketDirectory)
|
|
||||||
if err != nil {
|
|
||||||
return C.CString("")
|
|
||||||
}
|
|
||||||
var uapis []string
|
var uapis []string
|
||||||
for _, file := range Files {
|
for tun := range Tuns {
|
||||||
if file.IsDir() {
|
uapis = append(uapis, tun)
|
||||||
continue
|
|
||||||
}
|
|
||||||
uapis = append(uapis, strings.Join(([]string{socketDirectory, file.Name()}), "/"))
|
|
||||||
}
|
}
|
||||||
return C.CString(strings.Join(uapis, "\x00") + "\x00")
|
return C.CString(strings.Join(uapis, "\x00") + "\x00")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete interface
|
||||||
|
//
|
||||||
|
//export stopWg
|
||||||
|
func stopWg(wgName *C.char) (bool, *C.char) {
|
||||||
|
tunName := C.GoString(wgName)
|
||||||
|
if dev, ok := Devices[tunName]; ok {
|
||||||
|
dev.Close()
|
||||||
|
if tun, ok := Tuns[tunName]; ok {
|
||||||
|
if err := tun.Close(); err != nil {
|
||||||
|
return false, getCharErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set config in tun, if not exist create and add to Tuns
|
||||||
|
//
|
||||||
|
//export setWg
|
||||||
|
func setWg(wgName, wgConfig *C.char) *C.char {
|
||||||
|
tunName, configString := C.GoString(wgName), C.GoString(wgConfig)
|
||||||
|
_, okTuns := Tuns[tunName]
|
||||||
|
_, okDev := Devices[tunName]
|
||||||
|
if !(okTuns || okDev) {
|
||||||
|
logger := device.NewLogger(levelLog, fmt.Sprintf("(%s) ", tunName))
|
||||||
|
// open TUN device (or use supplied fd)
|
||||||
|
tdev, err := tun.CreateTUN(tunName, device.DefaultMTU)
|
||||||
|
Tuns[tunName] = tdev
|
||||||
|
if err != nil {
|
||||||
|
return getCharErr(err)
|
||||||
|
}
|
||||||
|
Devices[tunName] = device.NewDevice(tdev, conn.NewDefaultBind(), logger)
|
||||||
|
}
|
||||||
|
dev := Devices[tunName]
|
||||||
|
return getCharErr(dev.IpcSet(configString))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get config from Tuns device
|
||||||
|
//
|
||||||
|
//export getWg
|
||||||
|
func getWg(wgName *C.char) (*C.char, *C.char) {
|
||||||
|
tunName := C.GoString(wgName)
|
||||||
|
if dev, ok := Devices[tunName]; ok {
|
||||||
|
config, err := dev.IpcGet()
|
||||||
|
return C.CString(config), getCharErr(err)
|
||||||
|
}
|
||||||
|
return nil, getCharErr(fmt.Errorf("device not exists"))
|
||||||
|
}
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
@ -1,8 +1,11 @@
|
|||||||
#include "wginterface.hh"
|
#include "wginterface.hh"
|
||||||
#include "userspace/wg-go.h"
|
#include "userspace/wg-go.h"
|
||||||
#include "userspace/ipc.cpp"
|
|
||||||
#include "genKey/wgkeys.hh"
|
#include "genKey/wgkeys.hh"
|
||||||
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -16,161 +19,134 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
void IpManeger::SetInInterface(std::string interfaceName) {}
|
||||||
|
void IpManeger::GetInInterface(std::string interfaceName) {}
|
||||||
|
|
||||||
// Ignore if required to windows
|
// Ignore if required to windows
|
||||||
std::string driveLoad(std::map<std::string, std::string> load) {}
|
std::string driveLoad(std::map<std::string, std::string> load) { return ""; }
|
||||||
|
|
||||||
std::string getWireguardVersion() {
|
std::string getWireguardVersion() {
|
||||||
return std::string(wgVersion());
|
return std::string(wgVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
void WireguardDevices::getInterfaces() {
|
void WireguardDevices::getInterfaces() {
|
||||||
size_t len; char *device_name, *devicesList = listUapis();
|
size_t len; char *device_name, *devicesList = listInternalTuns();
|
||||||
for (device_name = devicesList, len = 0; (len = strlen(device_name)); device_name += len + 1) this->push_back(std::string(device_name));
|
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) {
|
void WireguardDevices::deleteInterface(std::string wgName) {
|
||||||
std::string deleteStatus = deleteTun((char*)wgName.c_str());
|
auto status = stopWg((char*)wgName.c_str());
|
||||||
if (!deleteStatus.empty()) throw deleteStatus;
|
if (!!status.r1) throw std::string(status.r1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool char_is_digit(int c) {
|
bool char_is_digit(int c) {
|
||||||
return (unsigned int)(('0' - 1 - c) & (c - ('9' + 1))) >> (sizeof(c) * 8 - 1);
|
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() {
|
void WireguardConfig::setWireguardConfig() {
|
||||||
if (this->name.length() == 0) throw std::string("Set wireguard name!");
|
if (this->name.length() == 0) throw std::string("Set wireguard name!");
|
||||||
else if (!(WireguardDevices().exist(this->name))) {
|
std::string userspaceConfig;
|
||||||
std::string PathError = createTun((char*)this->name.c_str());
|
|
||||||
if (PathError.size() > 0) throw PathError;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE* f = interfaceFile(WireguardDevices().findSock(this->name));
|
if (this->privateKey.length() == Base64WgKeyLength) userspaceConfig = userspaceConfig.append("private_key=").append(wgKeys::toHex(this->privateKey)).append("\n");
|
||||||
fprintf(f, "set=1\n");
|
if (this->portListen >= 0) userspaceConfig = userspaceConfig.append("listen_port=").append(std::to_string(this->portListen)).append("\n");
|
||||||
|
if (this->fwmark >= 0) userspaceConfig = userspaceConfig.append("fwmark=").append(std::to_string(this->fwmark)).append("\n");
|
||||||
if (this->privateKey.length() == Base64WgKeyLength) fprintf(f, "private_key=%s\n", wgKeys::toHex(this->privateKey).c_str());
|
if (this->replacePeers) userspaceConfig = userspaceConfig.append("replace_peers=true\n");
|
||||||
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) {
|
for (auto peer : this->Peers) {
|
||||||
fprintf(f, "public_key=%s\n", wgKeys::toHex(peer.first).c_str());
|
userspaceConfig = userspaceConfig.append("public_key=").append(wgKeys::toHex(peer.first)).append("\n");
|
||||||
if (peer.second.removeMe) {
|
auto config = peer.second;
|
||||||
fprintf(f, "remove=true\n");
|
if (config.removeMe) {
|
||||||
continue;
|
userspaceConfig = userspaceConfig.append("remove=true\n");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (peer.second.presharedKey.length() == Base64WgKeyLength) fprintf(f, "preshared_key=%s\n", wgKeys::toHex(peer.second.presharedKey).c_str());
|
if (config.presharedKey.length() == Base64WgKeyLength) userspaceConfig = userspaceConfig.append("preshared_key=").append(wgKeys::toHex(config.presharedKey)).append("\n");
|
||||||
if (peer.second.keepInterval) fprintf(f, "persistent_keepalive_interval=%u\n", peer.second.keepInterval);
|
if (config.keepInterval > 0) userspaceConfig = userspaceConfig.append("persistent_keepalive_interval=").append(std::to_string(config.keepInterval)).append("\n");
|
||||||
if (peer.second.endpoint.length() > 2) fprintf(f, "endpoint=%s\n", peer.second.endpoint.c_str());
|
if (config.endpoint.length() > 0) userspaceConfig = userspaceConfig.append("endpoint=").append(config.endpoint).append("\n");
|
||||||
if (peer.second.allowedIPs.size() > 0) {
|
if (config.allowedIPs.size() > 0) {
|
||||||
fprintf(f, "replace_allowed_ips=true\n");
|
userspaceConfig = userspaceConfig.append("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);
|
for (auto s : config.allowedIPs.getIpParsed()) userspaceConfig = userspaceConfig.append("allowed_ip=").append(s.Address).append("/").append(std::to_string(s.Mask)).append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(f, "\n");
|
std::string err = setWg((char*)this->name.c_str(), (char*)userspaceConfig.append("\n").c_str());
|
||||||
fflush(f);
|
if (!err.empty()) throw err;
|
||||||
|
|
||||||
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) {}
|
std::vector<std::string> splitLines(const std::string& str) {
|
||||||
void IpManeger::GetInInterface(std::string interfaceName) {}
|
std::vector<std::string> result;
|
||||||
|
std::stringstream ss(str);
|
||||||
|
std::string line;
|
||||||
|
// Loop until the end of the string
|
||||||
|
while (std::getline(ss, line)) {
|
||||||
|
if (!line.empty()) {
|
||||||
|
result.push_back(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WireguardConfig::getWireguardConfig() {
|
||||||
|
auto status = getWg((char*)this->name.c_str());
|
||||||
|
if (strlen(status.r1) > 0) throw std::string(status.r1, strlen(status.r1));
|
||||||
|
std::string wgConfig(status.r0, strlen(status.r0));
|
||||||
|
std::string pubKey;
|
||||||
|
int ret = 0;
|
||||||
|
for (auto line : splitLines(wgConfig)) {
|
||||||
|
std::string key(line.substr(0, line.find("="))), value(line.substr(line.find("=")+1));
|
||||||
|
if (key == "private_key") {
|
||||||
|
this->privateKey = wgKeys::HextoBase64(value);
|
||||||
|
} else if (key == "listen_port") {
|
||||||
|
this->portListen = (unsigned short)std::strtoul(value.c_str(), NULL, 0);
|
||||||
|
} else if (key == "public_key") {
|
||||||
|
// Insert to peer key
|
||||||
|
pubKey = wgKeys::HextoBase64(value);
|
||||||
|
this->Peers[pubKey] = Peer{};
|
||||||
|
} else if (key == "preshared_key" && pubKey.length() > 0) {
|
||||||
|
this->Peers[pubKey].presharedKey = wgKeys::HextoBase64(value);
|
||||||
|
} else if (key == "allowed_ip" && pubKey.length() > 0) {
|
||||||
|
this->Peers[pubKey].allowedIPs.push_back(value);
|
||||||
|
} else if (key == "persistent_keepalive_interval" && pubKey.length() > 0) {
|
||||||
|
this->Peers[pubKey].keepInterval = std::stoi(value);
|
||||||
|
} else if (key == "last_handshake_time_sec" && pubKey.length() > 0) {
|
||||||
|
} else if (key == "last_handshake_time_nsec" && pubKey.length() > 0) {
|
||||||
|
this->Peers[pubKey].lastHandshake = ({ unsigned long long num; char *end; if (!char_is_digit(value[0])) break; num = std::strtoull(value.c_str(), &end, 10); if (*end || num > 0x7fffffffffffffffULL) break; num; });
|
||||||
|
} else if (key == "rx_bytes" && pubKey.length() > 0) {
|
||||||
|
this->Peers[pubKey].rxBytes = std::stoull(value);
|
||||||
|
} else if (key == "tx_bytes" && pubKey.length() > 0) {
|
||||||
|
this->Peers[pubKey].txBytes = std::stoull(value);
|
||||||
|
} else if (key == "endpoint" && pubKey.length() > 0) {
|
||||||
|
char *cvalue = strdup(value.c_str());
|
||||||
|
char *begin, *end;
|
||||||
|
struct addrinfo *resolved;
|
||||||
|
struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP };
|
||||||
|
if (!value.length()) break;
|
||||||
|
if (value[0] == '[') {
|
||||||
|
begin = &value[1];
|
||||||
|
end = strchr(cvalue, ']');
|
||||||
|
if (!end) break;
|
||||||
|
*end++ = '\0';
|
||||||
|
if (*end++ != ':' || !*end) break;
|
||||||
|
} else {
|
||||||
|
begin = cvalue;
|
||||||
|
end = strrchr(cvalue, ':');
|
||||||
|
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[pubKey].endpoint = value;
|
||||||
|
else {
|
||||||
|
freeaddrinfo(resolved);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
freeaddrinfo(resolved);
|
||||||
|
} else if (key == "errno") {
|
||||||
|
ret = -({ unsigned long long num; char *end; if (!char_is_digit(value[0])) break; num = std::strtoull(value.c_str(), &end, 10); if (*end || num > 0x7fffffffU) break; num; });
|
||||||
|
break;
|
||||||
|
} else if (key == "protocol_version") {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret < 0) throw std::string("Failed to get wireguard config");
|
||||||
|
}
|
||||||
|
20
addon/wg.hh
20
addon/wg.hh
@ -40,26 +40,6 @@ class DeleteInterface : public Promised {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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 {
|
class SetConfig : public WireguardConfig, public Promised {
|
||||||
public:
|
public:
|
||||||
void Execute() {
|
void Execute() {
|
||||||
|
59
binding.yaml
59
binding.yaml
@ -1,59 +0,0 @@
|
|||||||
name: wginterface
|
|
||||||
defines:
|
|
||||||
- "NODE_VERSION=8"
|
|
||||||
- "NAPI_CPP_EXCEPTIONS"
|
|
||||||
includes:
|
|
||||||
- node_modules/node-addon-api
|
|
||||||
- ./addon
|
|
||||||
sources:
|
|
||||||
- "addon/main.cpp"
|
|
||||||
- "addon/genKey/wgkeys.cpp"
|
|
||||||
- "addon/userspace/wginterface.cpp"
|
|
||||||
prebuild:
|
|
||||||
- shell: bash
|
|
||||||
cwd: ./addon/userspace/go
|
|
||||||
ifOs:
|
|
||||||
- "!win32"
|
|
||||||
- "!linux"
|
|
||||||
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:
|
|
||||||
linux:
|
|
||||||
sources:
|
|
||||||
- "!addon/userspace/wginterface.cpp"
|
|
||||||
- "addon/linux/wginterface.cpp"
|
|
||||||
- "addon/linux/wireguard.c"
|
|
||||||
flags:
|
|
||||||
- "!-fno-exceptions"
|
|
||||||
- "-fpermissive"
|
|
||||||
- "-fexceptions"
|
|
||||||
- "-w"
|
|
||||||
- "-fpermissive"
|
|
||||||
- "-fPIC"
|
|
||||||
win32:
|
|
||||||
sources:
|
|
||||||
- "!addon/userspace/wginterface.cpp"
|
|
||||||
- "addon/win/wginterface.cpp"
|
|
||||||
libraries:
|
|
||||||
- wbemuuid.lib
|
|
||||||
- bcrypt.lib
|
|
||||||
- crypt32.lib
|
|
||||||
- iphlpapi.lib
|
|
||||||
- kernel32.lib
|
|
||||||
- ntdll.lib
|
|
||||||
- ws2_32.lib
|
|
||||||
- setupapi.lib
|
|
||||||
defines:
|
|
||||||
- "_HAS_EXCEPTIONS=1"
|
|
||||||
- "ONSTARTADDON"
|
|
||||||
darwin:
|
|
||||||
flags:
|
|
||||||
- "!-fno-exceptions"
|
|
||||||
- "-fpermissive"
|
|
||||||
- "-fexceptions"
|
|
||||||
- "-w"
|
|
||||||
- "-fpermissive"
|
|
3
compile_flags.txt
Normal file
3
compile_flags.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
-Iaddon/
|
||||||
|
-Iaddon/genKey
|
||||||
|
-std=c++17
|
16
package.json
16
package.json
@ -33,19 +33,19 @@
|
|||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "rebory prebuild",
|
"install": "cmake-js compile",
|
||||||
"dev": "rebory build",
|
"build": "cmake-js rebuild",
|
||||||
"test": "rebory build && node --no-warnings --loader ts-node/esm src/index_test.js",
|
"test": "cmake-js compile && node --no-warnings --loader ts-node/esm src/index_test.js",
|
||||||
"prepack": "tsc --build --clean && tsc --build && rebory build --release",
|
"prepack": "tsc --build --clean && tsc --build",
|
||||||
"postpack": "tsc --build --clean"
|
"postpack": "tsc --build --clean"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.11.26",
|
"@types/node": "^20.14.10",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.5.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-addon-api": "^8.0.0",
|
"cmake-js": "^7.3.0",
|
||||||
"rebory": "^0.2.10"
|
"node-addon-api": "^8.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
55
src/addons.ts
Normal file
55
src/addons.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
import fs from "node:fs/promises";
|
||||||
|
|
||||||
|
const __dirname = import.meta.dirname || path.dirname((await import("node:url")).fileURLToPath(import.meta.url));
|
||||||
|
export const projectRoot: string = !process["resourcesPath"] ? path.resolve(__dirname, "..") : process["resourcesPath"];
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
namespace NodeJS {
|
||||||
|
export interface Moduledlopen {
|
||||||
|
exports: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Process {
|
||||||
|
/**
|
||||||
|
* The `process.dlopen()` method allows dynamically loading shared objects. It is primarily used by `require()` to load C++ Addons, and should not be used directly, except in special cases. In other words, `require()` should be preferred over `process.dlopen()` unless there are specific reasons such as custom dlopen flags or loading from ES modules.
|
||||||
|
*
|
||||||
|
* An important requirement when calling `process.dlopen()` is that the `module` instance must be passed. Functions exported by the C++ Addon are then accessible via `module.exports`.
|
||||||
|
* @param module - module to export
|
||||||
|
* @param filename - Addon path
|
||||||
|
* @param flags - The flags argument is an integer that allows to specify dlopen behavior. See the [os.constants.dlopen](https://nodejs.org/docs/latest/api/os.html#dlopen-constants) documentation for details.
|
||||||
|
* @default flags `os.constants.dlopen.RTLD_LAZY`
|
||||||
|
* @since v9.0.0
|
||||||
|
*/
|
||||||
|
dlopen(module: Moduledlopen, filename: string, flags: number): void;
|
||||||
|
/**
|
||||||
|
* The `process.dlopen()` method allows dynamically loading shared objects. It is primarily used by `require()` to load C++ Addons, and should not be used directly, except in special cases. In other words, `require()` should be preferred over `process.dlopen()` unless there are specific reasons such as custom dlopen flags or loading from ES modules.
|
||||||
|
*
|
||||||
|
* An important requirement when calling `process.dlopen()` is that the `module` instance must be passed. Functions exported by the C++ Addon are then accessible via `module.exports`.
|
||||||
|
* @param module - module to export
|
||||||
|
* @param filename - Addon path
|
||||||
|
* @since v0.1.16
|
||||||
|
*/
|
||||||
|
dlopen(module: Moduledlopen, filename: string): void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function exists(filePath: string) {
|
||||||
|
return fs.access(path.resolve(filePath)).then(() => true, () => false);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function LoadAddon<T = any>(addonFile: string, exports?: Record<string, any>): Promise<T> {
|
||||||
|
let _addonFile: string = null
|
||||||
|
if (await exists(addonFile)) _addonFile = addonFile;
|
||||||
|
else if (await exists(path.resolve(projectRoot, addonFile))) _addonFile = path.resolve(projectRoot, addonFile)
|
||||||
|
else if (await exists(path.resolve(projectRoot, addonFile+".node"))) _addonFile = path.resolve(projectRoot, addonFile+".node")
|
||||||
|
else if (await exists(path.resolve(projectRoot, "build/Release", addonFile))) _addonFile = path.resolve(projectRoot, "build/Release", addonFile)
|
||||||
|
else if (await exists(path.resolve(projectRoot, "build/Release", addonFile+".node"))) _addonFile = path.resolve(projectRoot, "build/Release", addonFile+".node")
|
||||||
|
else if (await exists(path.resolve(projectRoot, "build/Debug", addonFile))) _addonFile = path.resolve(projectRoot, "build/Debug", addonFile)
|
||||||
|
else if (await exists(path.resolve(projectRoot, "build/Debug", addonFile+".node"))) _addonFile = path.resolve(projectRoot, "build/Debug", addonFile+".node")
|
||||||
|
if (!_addonFile) throw new Error("Cannot load required addon")
|
||||||
|
let ext: NodeJS.Moduledlopen = {exports: Object.assign({}, exports)}
|
||||||
|
process.dlopen(ext, _addonFile)
|
||||||
|
return ext.exports
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
export * as key from "./key.js";
|
export * as key from "./key.js";
|
||||||
export * as quickConfig from "./quick.js";
|
export * as quickConfig from "./quick.js";
|
||||||
export * from "./wginterface.js";
|
export * from "./wginterface.js";
|
||||||
export * as wginterface from "./wginterface.js";
|
export * as wginterface from "./wginterface.js";
|
||||||
|
@ -106,10 +106,6 @@ function keyToBase64(key: Uint8Array): string {
|
|||||||
return Buffer.from(key).toString("base64");
|
return Buffer.from(key).toString("base64");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function keyToHex(key: string): string {
|
|
||||||
return Buffer.from(key, "base64").toString("hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate preshared key
|
* Generate preshared key
|
||||||
*/
|
*/
|
||||||
|
79
src/maneger.ts
Normal file
79
src/maneger.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import * as wginterface from "./wginterface.js"
|
||||||
|
import { publicKey, privateKey } from "./key.js"
|
||||||
|
import * as quick from "./quick.js"
|
||||||
|
import { isIP } from "net"
|
||||||
|
|
||||||
|
export class Wireguard extends Map<string, wginterface.Peer> {
|
||||||
|
private _privateKey: string
|
||||||
|
|
||||||
|
/** Current privateKey */
|
||||||
|
get privateKey(): string {
|
||||||
|
return this._privateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get publicKey from privateKey */
|
||||||
|
get publicKey(): string {
|
||||||
|
if (!this._privateKey) throw new Error("Set private key to get publicKey")
|
||||||
|
return publicKey(this._privateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @deprecated set privateKey only */
|
||||||
|
set publicKey(_key: string) {
|
||||||
|
throw new Error("Set privateKey only")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set interface privateKey */
|
||||||
|
set privateKey(key: string) {
|
||||||
|
this._privateKey = key
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generate privateKey to interface */
|
||||||
|
async generatePrivateKey() {
|
||||||
|
this._privateKey = await privateKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
private _portListen: number
|
||||||
|
get portListen() { return this._portListen; }
|
||||||
|
set portListen(port: number) {
|
||||||
|
if (port < 0 || port > 65534) throw new Error("Invalid port to listening");
|
||||||
|
this._portListen = port
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get config */
|
||||||
|
toJSON(wgName: string): wginterface.Config {
|
||||||
|
const config: wginterface.Config = {
|
||||||
|
name: wgName,
|
||||||
|
portListen: this._portListen,
|
||||||
|
privateKey: this.privateKey,
|
||||||
|
peers: {},
|
||||||
|
}
|
||||||
|
for (const [publicKey, peerSettings] of this.entries()) {
|
||||||
|
config.peers[publicKey] = {
|
||||||
|
keepInterval: peerSettings.keepInterval,
|
||||||
|
presharedKey: peerSettings.presharedKey,
|
||||||
|
endpoint: peerSettings.endpoint,
|
||||||
|
allowedIPs: peerSettings.allowedIPs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get quick config from current config */
|
||||||
|
toString(extraConfig?: Omit<quick.QuickConfig, keyof wginterface.SetConfig>) {
|
||||||
|
return quick.stringify(Object.assign({}, extraConfig, this.toJSON("wg0")))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deploy config to wireguard interface */
|
||||||
|
async deployConfig(wgName: string) {
|
||||||
|
return wginterface.setConfig(this.toJSON(wgName))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add peer to Map and check config */
|
||||||
|
async addPeer(publicKey: string, peerConfig: wginterface.Peer) {
|
||||||
|
if (peerConfig.allowedIPs?.length > 0) {
|
||||||
|
for (const ipAddress of peerConfig.allowedIPs) if (isIP(ipAddress.split("/")[0]) === 0) throw new Error("Invalid ip address in allowedIPs")
|
||||||
|
}
|
||||||
|
peerConfig.keepInterval
|
||||||
|
this.set(publicKey, peerConfig)
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ import { isIP } from "net";
|
|||||||
import { format } from "util";
|
import { format } from "util";
|
||||||
import { SetConfig } from "./wginterface.js";
|
import { SetConfig } from "./wginterface.js";
|
||||||
|
|
||||||
export interface QuickConfig extends SetConfig, Partial<Record<`${"Post" | "Pre"}${"Up" | "Down"}`, string[]>> {
|
export interface QuickConfig extends Omit<SetConfig, "name">, Partial<Record<`${"Post" | "Pre"}${"Up" | "Down"}`, string[]>> {
|
||||||
DNS?: string[];
|
DNS?: string[];
|
||||||
Table?: number;
|
Table?: number;
|
||||||
MTU?: number;
|
MTU?: number;
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { loadAddon } from "rebory";
|
import { LoadAddon, projectRoot } from "./addons.js";
|
||||||
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));
|
|
||||||
|
|
||||||
interface Peer {
|
export interface Peer {
|
||||||
/** Preshared key to peer */
|
/** Preshared key to peer */
|
||||||
presharedKey?: string;
|
presharedKey?: string;
|
||||||
|
|
||||||
@ -34,7 +31,7 @@ export interface GetPeer extends Peer {
|
|||||||
lastHandshake: Date;
|
lastHandshake: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Config<T extends Peer> {
|
export interface Config<T extends Peer = Peer> {
|
||||||
/** Wireguard interface name */
|
/** Wireguard interface name */
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@ -63,9 +60,15 @@ export interface SetConfig extends Config<SetPeer> {
|
|||||||
replacePeers?: boolean;
|
replacePeers?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addon = (await loadAddon(path.resolve(__dirname, "../binding.yaml"))).wginterface.load_addon<{
|
/**
|
||||||
/** Current Wireguard drive version */
|
* Exported wireguard-tools.js addon
|
||||||
driveVersion?: string;
|
*/
|
||||||
|
export const addon = await LoadAddon<{
|
||||||
|
/** External functions or drive info */
|
||||||
|
constants: {
|
||||||
|
/** Current Wireguard drive version */
|
||||||
|
driveVersion?: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete interface if exists
|
* Delete interface if exists
|
||||||
@ -73,13 +76,6 @@ export const addon = (await loadAddon(path.resolve(__dirname, "../binding.yaml")
|
|||||||
*/
|
*/
|
||||||
deleteInterface(name: string): Promise<void>;
|
deleteInterface(name: string): Promise<void>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Wireguard interfaces list
|
|
||||||
*
|
|
||||||
* if running in userspace return socket (UAPI) path's
|
|
||||||
*/
|
|
||||||
listDevices(): Promise<string[]>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current config from Wireguard interface
|
* Get current config from Wireguard interface
|
||||||
* @param name - Interface name
|
* @param name - Interface name
|
||||||
@ -91,303 +87,13 @@ export const addon = (await loadAddon(path.resolve(__dirname, "../binding.yaml")
|
|||||||
* @param config - Interface config
|
* @param config - Interface config
|
||||||
*/
|
*/
|
||||||
setConfig(config: SetConfig): Promise<void>;
|
setConfig(config: SetConfig): Promise<void>;
|
||||||
}>({
|
}>("wg", {
|
||||||
WIN32DLLPATH: path.resolve(__dirname, "../addon/win", (process.arch === "x64" && "amd64") || (process.arch === "ia32" && "x86") || process.arch, "wireguard.dll")
|
WIN32DLLPATH: path.resolve(projectRoot, "addon/win", (process.arch === "x64" && "amd64") || (process.arch === "ia32" && "x86") || process.arch, "wireguard.dll")
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
driveVersion,
|
constants: {driveVersion = "Unknown"},
|
||||||
listDevices,
|
|
||||||
getConfig,
|
getConfig,
|
||||||
setConfig,
|
setConfig,
|
||||||
deleteInterface
|
deleteInterface
|
||||||
} = addon;
|
} = 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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maneger Wireguard interface and peers simple and fast
|
|
||||||
*/
|
|
||||||
export class Wireguard {
|
|
||||||
constructor(config?: SetConfig | GetConfig | Config<Peer>) {
|
|
||||||
// super({});
|
|
||||||
if (!config) return;
|
|
||||||
if (typeof config === "object") {
|
|
||||||
if (config instanceof Wireguard) return config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,51 +1,45 @@
|
|||||||
import test from "node:test";
|
|
||||||
import { Wireguard, getConfig, setConfig } from "./wginterface.js";
|
|
||||||
import { presharedKey, privateKey, publicKey } from "./key.js";
|
|
||||||
import assert from "node:assert";
|
import assert from "node:assert";
|
||||||
|
import test from "node:test";
|
||||||
|
import { format } from "node:util";
|
||||||
|
import { presharedKey, privateKey, publicKey } from "./key.js";
|
||||||
|
import { Config, deleteInterface, getConfig, setConfig } from "./wginterface.js";
|
||||||
|
|
||||||
await test("Wireguard interface", async t => {
|
await test("Wireguard interface", async t => {
|
||||||
const config = new Wireguard;
|
const newConfig: Config = {
|
||||||
config.name = "wg23";
|
name: process.platform === "darwin" ? "utun23" : "wg10",
|
||||||
if (process.platform === "darwin") config.name = "utun23";
|
privateKey: await privateKey(),
|
||||||
|
portListen: 8260,
|
||||||
|
address: [
|
||||||
|
"10.66.66.1/24"
|
||||||
|
],
|
||||||
|
peers: {}
|
||||||
|
}
|
||||||
|
for (let i = 0; i != 10; i++) {
|
||||||
|
newConfig.peers[publicKey(await privateKey())] = {
|
||||||
|
presharedKey: await presharedKey(),
|
||||||
|
keepInterval: 25,
|
||||||
|
allowedIPs: [
|
||||||
|
format("10.66.66.%d/32", i+2)
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
config.setPrivateKey(await privateKey());
|
function checkExists<T extends any[]>(arg0: T, arg1: T) {
|
||||||
config.addNewAddress("10.66.66.1/32");
|
for (const a1 of arg0) {
|
||||||
config.addNewAddress("fd42:42:42::1/128");
|
if (!Array.from(arg1).includes(a1)) throw new Error(format("%O not includes in %O", a1, arg1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const peer1 = await privateKey();
|
await t.test("Set config", async () => setConfig(newConfig));
|
||||||
config.addNewPeer(publicKey(peer1), {
|
await t.test("Get config and check", async () => {
|
||||||
keepInterval: 15,
|
const currentConfig = await getConfig(newConfig.name)
|
||||||
presharedKey: await presharedKey(),
|
assert.equal(currentConfig.privateKey, newConfig.privateKey)
|
||||||
allowedIPs: [
|
for (const pubKey in newConfig.peers) {
|
||||||
"10.66.66.2/32"
|
if (!currentConfig.peers[pubKey]) throw new Error("one peer not exists in currentConfig")
|
||||||
]
|
else if (currentConfig.peers[pubKey].presharedKey != newConfig.peers[pubKey].presharedKey) throw new Error("presharedKey is mismatch");
|
||||||
|
else if (currentConfig.peers[pubKey].keepInterval != newConfig.peers[pubKey].keepInterval) throw new Error("keepInterval is mismatch");
|
||||||
|
checkExists(newConfig.peers[pubKey].allowedIPs, currentConfig.peers[pubKey].allowedIPs);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
await t.test("Deleting interface", async () => deleteInterface(newConfig.name));
|
||||||
const peer2 = await privateKey();
|
});
|
||||||
config.addNewPeer(publicKey(peer2), {
|
|
||||||
keepInterval: 0,
|
|
||||||
allowedIPs: [
|
|
||||||
"10.66.66.3/32"
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
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 if exists", { skip }, async () => config.delete());
|
|
||||||
});
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"exclude": [
|
"exclude": [
|
||||||
"**/libs/**",
|
"**/libs/**",
|
||||||
"**/docs/**",
|
"**/docs/**",
|
||||||
|
"build/**",
|
||||||
"node_modules/",
|
"node_modules/",
|
||||||
"index.mjs"
|
"index.mjs"
|
||||||
],
|
],
|
||||||
@ -28,4 +29,4 @@
|
|||||||
"esm": true,
|
"esm": true,
|
||||||
"transpileOnly": true
|
"transpileOnly": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user