WIP: Support for macos #2
4
.vscode/c_cpp_properties.json
vendored
4
.vscode/c_cpp_properties.json
vendored
@ -60,7 +60,9 @@
|
||||
"${workspaceFolder}/node_modules/node-addon-api",
|
||||
"/usr/local/include/node",
|
||||
"${workspaceFolder}/addons/genKey/**",
|
||||
"${workspaceFolder}/addons/tools/**"
|
||||
"${workspaceFolder}/addons/tools/**",
|
||||
"${workspaceFolder}/build/macos/x86_64/**",
|
||||
"${workspaceFolder}/build/macos/aarch64/**"
|
||||
],
|
||||
"defines": [
|
||||
"NAPI_DISABLE_CPP_EXCEPTIONS"
|
||||
|
@ -142,6 +142,19 @@ static void invert(fe o, const fe i) {
|
||||
memzero_explicit(c, sizeof(c));
|
||||
}
|
||||
|
||||
std::string wgKeys::keyToHex(const std::string &keyInput) {
|
||||
static const char hex_digits[] = "0123456789ABCDEF";
|
||||
wg_key key;
|
||||
wgKeys::stringToKey(key, keyInput);
|
||||
std::string output;
|
||||
output.reserve(sizeof(key) * 2);
|
||||
for (unsigned char c : key) {
|
||||
output.push_back(hex_digits[c >> 4]);
|
||||
output.push_back(hex_digits[c & 15]);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void wgKeys::generatePreshared(wg_key preshared_key) {
|
||||
#if _WIN32 || defined(__CYGWIN__)
|
||||
HCRYPTPROV hCryptProv;
|
||||
|
@ -14,6 +14,9 @@ namespace wgKeys {
|
||||
/* base64 to wg_key */
|
||||
void stringToKey(wg_key key, std::string keyBase64);
|
||||
|
||||
/* Convert base64 key to hexadecimal key */
|
||||
std::string keyToHex(const std::string &keyInput);
|
||||
|
||||
// bool key_is_zero(wg_key key);
|
||||
|
||||
/* Generate preshared key */
|
||||
|
77
addons/tools/wginterface-darwin.cpp
Normal file
77
addons/tools/wginterface-darwin.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
#include <napi.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <wginterface.hh>
|
||||
#include <wgkeys.hh>
|
||||
#include "libwg-go.h"
|
||||
|
||||
unsigned long maxName() {
|
||||
return 16;
|
||||
}
|
||||
|
||||
std::string versionDrive() {
|
||||
return wgVersion();
|
||||
}
|
||||
|
||||
void listDevices::Execute() {
|
||||
auto dev = std::string(listWgDevices());
|
||||
while (dev.length() > 0) {
|
||||
if (dev.find(",") == std::string::npos) {
|
||||
deviceNames[dev] = listInfo();
|
||||
deviceNames[dev].tunType = "kernel";
|
||||
break;
|
||||
}
|
||||
auto devName = dev.substr(0, dev.find(","));
|
||||
dev = dev.substr(dev.find(",")+1);
|
||||
deviceNames[devName] = listInfo();
|
||||
deviceNames[devName].tunType = "kernel";
|
||||
}
|
||||
}
|
||||
|
||||
void deleteInterface::Execute() {}
|
||||
|
||||
void setConfig::Execute() {
|
||||
auto tunFd = getTunFile((char*)wgName.c_str());
|
||||
|
||||
if (tunFd == -1) {
|
||||
auto res = std::string(createTun((char*)wgName.c_str()));
|
||||
if (res.length() != 0) {
|
||||
SetError(res);
|
||||
return;
|
||||
}
|
||||
tunFd = getTunFile((char*)wgName.c_str());
|
||||
}
|
||||
|
||||
if (tunFd == -1) {
|
||||
SetError("Cannot get tun fd");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string interfaceConfig = std::string("private_key=").append(wgKeys::keyToHex(this->privateKey));
|
||||
if (this->portListen > 0) interfaceConfig = interfaceConfig.append(std::string("\nlisten_port=").append(std::to_string(this->portListen)));
|
||||
if (this->fwmark > 0) interfaceConfig = interfaceConfig.append(std::string("\nfwmark=").append(std::to_string(this->fwmark)));
|
||||
if (this->replacePeers) interfaceConfig = interfaceConfig.append("\nreplace_peers=true");
|
||||
for (auto it = peersVector.begin(); it != peersVector.end(); ++it) {
|
||||
const std::string peerPubKey = it->first;
|
||||
auto peerConfig = it->second;
|
||||
interfaceConfig = interfaceConfig.append(std::string("\npublic_key=")).append(wgKeys::keyToHex(peerPubKey));
|
||||
|
||||
if (peerConfig.removeMe) interfaceConfig = interfaceConfig.append(std::string("\nremove_me=true"));
|
||||
else {
|
||||
if (peerConfig.presharedKey.length() == B64_WG_KEY_LENGTH) interfaceConfig = interfaceConfig.append("\npreshared_key=").append(wgKeys::keyToHex(peerConfig.presharedKey));
|
||||
if (peerConfig.endpoint.length() > 0) interfaceConfig = interfaceConfig.append("\nendpoint=").append(peerConfig.endpoint);
|
||||
if (peerConfig.keepInterval) interfaceConfig = interfaceConfig.append("\npersistent_keepalive_interval=").append(std::to_string(peerConfig.keepInterval));
|
||||
if (peerConfig.allowedIPs.size() > 0) interfaceConfig = interfaceConfig.append("\nreplace_allowed_ips=true");
|
||||
for (auto ip : peerConfig.allowedIPs) {
|
||||
interfaceConfig = interfaceConfig.append("\nallowed_ip=").append(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << interfaceConfig << std::endl;
|
||||
auto status = std::string(wgTurnOn((char*)interfaceConfig.append("\n\n").c_str(), tunFd));
|
||||
if (status.length() > 0 ) SetError(status);
|
||||
return;
|
||||
}
|
||||
|
||||
void getConfig::Execute() {}
|
2
addons/wg-go/.gitignore
vendored
Normal file
2
addons/wg-go/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.a
|
||||
*.h
|
11
addons/wg-go/go.mod
Normal file
11
addons/wg-go/go.mod
Normal file
@ -0,0 +1,11 @@
|
||||
module sirherobrine23.org/Wireguard/wireguard-tools.js/wg-tools
|
||||
|
||||
go 1.21.6
|
||||
|
||||
require (
|
||||
golang.org/x/crypto v0.13.0 // indirect
|
||||
golang.org/x/net v0.15.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||
)
|
10
addons/wg-go/go.sum
Normal file
10
addons/wg-go/go.sum
Normal file
@ -0,0 +1,10 @@
|
||||
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.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=
|
243
addons/wg-go/main.go
Normal file
243
addons/wg-go/main.go
Normal file
@ -0,0 +1,243 @@
|
||||
/* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Copyright (C) 2018-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
// #include <stdlib.h>
|
||||
// #include <sys/types.h>
|
||||
// static void callLogger(void *func, void *ctx, int level, const char *msg)
|
||||
// {
|
||||
// ((void(*)(void *, int, const char *))func)(ctx, level, msg);
|
||||
// }
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/conn"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
)
|
||||
|
||||
var loggerFunc unsafe.Pointer
|
||||
var loggerCtx unsafe.Pointer
|
||||
|
||||
type CLogger int
|
||||
|
||||
func cstring(s string) *C.char {
|
||||
b, err := unix.BytePtrFromString(s)
|
||||
if err != nil {
|
||||
b := [1]C.char{}
|
||||
return &b[0]
|
||||
}
|
||||
return (*C.char)(unsafe.Pointer(b))
|
||||
}
|
||||
|
||||
func (l CLogger) Printf(format string, args ...interface{}) {
|
||||
if uintptr(loggerFunc) == 0 {
|
||||
return
|
||||
}
|
||||
C.callLogger(loggerFunc, loggerCtx, C.int(l), cstring(fmt.Sprintf(format, args...)))
|
||||
}
|
||||
|
||||
type tunnelHandle struct {
|
||||
*device.Device
|
||||
*device.Logger
|
||||
}
|
||||
|
||||
var tunnelHandles = make(map[int32]tunnelHandle)
|
||||
var Tuns = make(map[string]tun.Device)
|
||||
|
||||
func init() {
|
||||
signals := make(chan os.Signal)
|
||||
signal.Notify(signals, unix.SIGUSR2)
|
||||
go func() {
|
||||
buf := make([]byte, os.Getpagesize())
|
||||
for {
|
||||
select {
|
||||
case <-signals:
|
||||
n := runtime.Stack(buf, true)
|
||||
buf[n] = 0
|
||||
if uintptr(loggerFunc) != 0 {
|
||||
C.callLogger(loggerFunc, loggerCtx, 0, (*C.char)(unsafe.Pointer(&buf[0])))
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
//export wgSetLogger
|
||||
func wgSetLogger(context, loggerFn uintptr) {
|
||||
loggerCtx = unsafe.Pointer(context)
|
||||
loggerFunc = unsafe.Pointer(loggerFn)
|
||||
}
|
||||
|
||||
//export listWgDevices
|
||||
func listWgDevices() *C.char {
|
||||
wgInterfaceList := make([]byte, 0);
|
||||
for tunName := range Tuns {
|
||||
if (len(wgInterfaceList) != 0) { wgInterfaceList = append(wgInterfaceList, []byte(",")...); }
|
||||
wgInterfaceList = append(wgInterfaceList, []byte(tunName)...)
|
||||
}
|
||||
return C.CString(string(wgInterfaceList))
|
||||
}
|
||||
|
||||
//export createTun
|
||||
func createTun(name *C.char) *C.char {
|
||||
goName := C.GoString(name)
|
||||
if Tuns[goName] != nil {
|
||||
return C.CString("")
|
||||
}
|
||||
|
||||
tun, err := tun.CreateTUN(goName, device.DefaultMTU)
|
||||
if err != nil {
|
||||
return C.CString(err.Error())
|
||||
}
|
||||
Tuns[goName] = tun
|
||||
return C.CString("")
|
||||
}
|
||||
|
||||
//export getTunFile
|
||||
func getTunFile(name *C.char) int {
|
||||
goName := C.GoString(name)
|
||||
if Tuns[goName] != nil {
|
||||
return int(Tuns[goName].File().Fd())
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
//export wgTurnOn
|
||||
func wgTurnOn(settings *C.char, tunFd int32) *C.char {
|
||||
logger := &device.Logger{
|
||||
Verbosef: CLogger(0).Printf,
|
||||
Errorf: CLogger(1).Printf,
|
||||
}
|
||||
dupTunFd, err := unix.Dup(int(tunFd))
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("Unable to dup tun fd: %v", err))
|
||||
}
|
||||
|
||||
err = unix.SetNonblock(dupTunFd, true)
|
||||
if err != nil {
|
||||
unix.Close(dupTunFd)
|
||||
return C.CString(fmt.Sprintf("Unable to set tun fd as non blocking: %v", err))
|
||||
}
|
||||
tun, err := tun.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0)
|
||||
if err != nil {
|
||||
unix.Close(dupTunFd)
|
||||
return C.CString(fmt.Sprintf("Unable to create new tun device from fd: %v", err))
|
||||
}
|
||||
logger.Verbosef("Attaching to interface")
|
||||
dev := device.NewDevice(tun, conn.NewStdNetBind(), logger)
|
||||
|
||||
err = dev.IpcSet(C.GoString(settings))
|
||||
if err != nil {
|
||||
unix.Close(dupTunFd)
|
||||
return C.CString(fmt.Sprintf("Unable to set IPC settings: %v", err))
|
||||
}
|
||||
|
||||
dev.Up()
|
||||
logger.Verbosef("Device started")
|
||||
|
||||
tunnelHandles[tunFd] = tunnelHandle{dev, logger}
|
||||
return C.CString("")
|
||||
}
|
||||
|
||||
//export wgTurnOff
|
||||
func wgTurnOff(tunnelHandle int32) {
|
||||
dev, ok := tunnelHandles[tunnelHandle]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
delete(tunnelHandles, tunnelHandle)
|
||||
dev.Close()
|
||||
}
|
||||
|
||||
//export wgSetConfig
|
||||
func wgSetConfig(tunnelHandle int32, settings *C.char) int64 {
|
||||
dev, ok := tunnelHandles[tunnelHandle]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
err := dev.IpcSet(C.GoString(settings))
|
||||
if err != nil {
|
||||
dev.Errorf("Unable to set IPC settings: %v", err)
|
||||
if ipcErr, ok := err.(*device.IPCError); ok {
|
||||
return ipcErr.ErrorCode()
|
||||
}
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
//export wgGetConfig
|
||||
func wgGetConfig(tunnelHandle int32) *C.char {
|
||||
device, ok := tunnelHandles[tunnelHandle]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
settings, err := device.IpcGet()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return C.CString(settings)
|
||||
}
|
||||
|
||||
//export wgBumpSockets
|
||||
func wgBumpSockets(tunnelHandle int32) {
|
||||
dev, ok := tunnelHandles[tunnelHandle]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
for i := 0; i < 10; i++ {
|
||||
err := dev.BindUpdate()
|
||||
if err == nil {
|
||||
dev.SendKeepalivesToPeersWithCurrentKeypair()
|
||||
return
|
||||
}
|
||||
dev.Errorf("Unable to update bind, try %d: %v", i+1, err)
|
||||
time.Sleep(time.Second / 2)
|
||||
}
|
||||
dev.Errorf("Gave up trying to update bind; tunnel is likely dysfunctional")
|
||||
}()
|
||||
}
|
||||
|
||||
//export wgDisableSomeRoamingForBrokenMobileSemantics
|
||||
func wgDisableSomeRoamingForBrokenMobileSemantics(tunnelHandle int32) {
|
||||
dev, ok := tunnelHandles[tunnelHandle]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
dev.DisableSomeRoamingForBrokenMobileSemantics()
|
||||
}
|
||||
|
||||
//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" {
|
||||
parts := strings.Split(dep.Version, "-")
|
||||
if len(parts) == 3 && len(parts[2]) == 12 {
|
||||
return C.CString(parts[2][:7])
|
||||
}
|
||||
return C.CString(dep.Version)
|
||||
}
|
||||
}
|
||||
return C.CString("unknown")
|
||||
}
|
||||
|
||||
func main() {}
|
19
binding.yaml
19
binding.yaml
@ -40,14 +40,31 @@
|
||||
flags_cc:
|
||||
- "-fPIC"
|
||||
macos:
|
||||
defines:
|
||||
- "LISTDEV"
|
||||
- "GETCONFIG"
|
||||
- "SETCONFIG"
|
||||
- "DELIFACE"
|
||||
cflags_cc:
|
||||
- "-fexceptions"
|
||||
cflags:
|
||||
- "-fexceptions"
|
||||
sources:
|
||||
- "!addons/tools/wginterface-dummy.cpp"
|
||||
- "addons/tools/wginterface-darwin.cpp"
|
||||
includes:
|
||||
- ./build/macos/x86_64
|
||||
- ./build/macos/aarch64
|
||||
prebuild:
|
||||
- cwd: ./addons/wg-go
|
||||
shell: bash
|
||||
env:
|
||||
CGO_ENABLED: "1"
|
||||
run: go build -ldflags=-w -trimpath -v -o ${BUILDDIR}/libwg-go.o -buildmode c-archive .
|
||||
windows:
|
||||
sources:
|
||||
- "addons/tools/wginterface-win.cpp"
|
||||
- "!addons/tools/wginterface-dummy.cpp"
|
||||
- "addons/tools/wginterface-win.cpp"
|
||||
includes:
|
||||
- "addons/tools/win"
|
||||
defines:
|
||||
|
@ -45,6 +45,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"node-addon-api": "^7.1.0",
|
||||
"rebory": "^0.1.10"
|
||||
"rebory": "^0.1.11-2"
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,6 @@ const addon = rebory.loadAddon(path.join(__dirname, "../binding.yaml")).wginterf
|
||||
});
|
||||
export const { constants } = addon;
|
||||
|
||||
console.log(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";
|
||||
|
||||
@ -114,13 +112,6 @@ export async function deleteInterface(wgName: string): Promise<void> {
|
||||
* @param config - Interface config
|
||||
*/
|
||||
export async function setConfig(wgName: string, config: WgConfigSet): Promise<void> {
|
||||
if (process.platform === "darwin") {
|
||||
if (!(wgName.match(/^tun([0-9]+)$/))) throw new Error("Invalid name, example to valid: tun0");
|
||||
// Replace to tun name
|
||||
// const devNames = Object.keys(networkInterfaces()).filter(s => s.toLowerCase().startsWith("tun"));
|
||||
// for (let i = 0n; i < BigInt(Number.MAX_SAFE_INTEGER); i++) if (devNames.indexOf((wgName = ("tun").concat(i.toString())))) break;
|
||||
}
|
||||
|
||||
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"));
|
||||
|
33
src/wginterface_test.ts
Normal file
33
src/wginterface_test.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { setConfig, deleteInterface, listDevices } from "./wginterface.js";
|
||||
import { privateKey, publicKey } from "./key.js";
|
||||
|
||||
console.log("Creating interface");
|
||||
|
||||
const peerPub = publicKey(await privateKey());
|
||||
|
||||
await setConfig("utun15", {
|
||||
privateKey: await privateKey(),
|
||||
peers: {
|
||||
[peerPub]: {
|
||||
allowedIPs: [ "10.0.0.1/32" ]
|
||||
}
|
||||
}
|
||||
});
|
||||
await setConfig("utun16", {
|
||||
privateKey: await privateKey(),
|
||||
peers: {
|
||||
[peerPub]: {
|
||||
allowedIPs: [ "10.0.0.1/32" ]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log("List interface")
|
||||
console.log(await listDevices());
|
||||
|
||||
console.log("Deleting interface")
|
||||
await deleteInterface("utun15");
|
||||
await deleteInterface("utun16");
|
||||
|
||||
console.log("List interface 2")
|
||||
console.log(await listDevices());
|
Loading…
x
Reference in New Issue
Block a user