Big code refactoring #10

Merged
Sirherobrine23 merged 15 commits from code_refactoring into main 2024-03-15 04:08:27 +00:00
9 changed files with 169 additions and 167 deletions
Showing only changes of commit 20a062b6f7 - Show all commits

@ -115,6 +115,7 @@
"xmemory": "cpp", "xmemory": "cpp",
"xtr1common": "cpp", "xtr1common": "cpp",
"xtree": "cpp", "xtree": "cpp",
"xutility": "cpp" "xutility": "cpp",
"fstream": "cpp"
} }
} }

@ -5,18 +5,18 @@ import "C"
import ( import (
"fmt" "fmt"
"os" "os"
"os/signal"
"runtime/debug" "runtime/debug"
"strings" "strings"
"syscall"
"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/ipc"
"golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/tun"
_ "unsafe"
) )
func main() {} const levelLog = device.LogLevelError
// End process function callbacks // End process function callbacks
var TunsEndProcess = make(map[string]func()) var TunsEndProcess = make(map[string]func())
@ -28,6 +28,18 @@ func callEndProcess() {
} }
} }
func main() {}
func init() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
callEndProcess()
}()
}
// Get wireguard-go version
//
//export wgVersion //export wgVersion
func wgVersion() *C.char { func wgVersion() *C.char {
info, ok := debug.ReadBuildInfo() info, ok := debug.ReadBuildInfo()
@ -42,6 +54,8 @@ func wgVersion() *C.char {
return C.CString("unknown") return C.CString("unknown")
} }
// Check if tunnel exist
//
//export existTun //export existTun
func existTun(tunName string) bool { func existTun(tunName string) bool {
Files, err := os.ReadDir(socketDirectory) Files, err := os.ReadDir(socketDirectory)
@ -61,6 +75,8 @@ func existTun(tunName string) bool {
return false return false
} }
// Delete wireguard tunnel if exist
//
//export deleteTun //export deleteTun
func deleteTun(_tunName *C.char) *C.char { func deleteTun(_tunName *C.char) *C.char {
tunName := C.GoString(_tunName) tunName := C.GoString(_tunName)
@ -85,8 +101,8 @@ func deleteTun(_tunName *C.char) *C.char {
return C.CString("Tun does not exist") return C.CString("Tun does not exist")
} }
const levelLog = device.LogLevelVerbose // Create wireguard tunnel
//
//export createTun //export createTun
func createTun(_tunName *C.char) *C.char { func createTun(_tunName *C.char) *C.char {
interfaceName := C.GoString(_tunName) interfaceName := C.GoString(_tunName)
@ -172,10 +188,12 @@ func createTun(_tunName *C.char) *C.char {
break break
} }
} }
return C.CString(strings.Join(([]string{"/", uapiListened}), "")) return C.CString("")
} }
// List wireguard-go UAPI's sockets
// first\0second\0third\0forth\0last\0\0 // first\0second\0third\0forth\0last\0\0
//
//export listUapis //export listUapis
func listUapis() *C.char { func listUapis() *C.char {
Files, err := os.ReadDir(socketDirectory) Files, err := os.ReadDir(socketDirectory)

@ -1,17 +1,11 @@
//go:build linux || darwin || freebsd || openbsd //go:build linux || darwin || freebsd || openbsd
package main package main
import ( import (
"fmt"
_ "unsafe"
_ "golang.zx2c4.com/wireguard/ipc" _ "golang.zx2c4.com/wireguard/ipc"
_ "unsafe"
) )
//go:linkname socketDirectory golang.xz2c4.com/wireguard/ipc.socketDirectory //go:linkname socketDirectory golang.xz2c4.com/wireguard/ipc.socketDirectory
var socketDirectory = "/var/run/wireguard" var socketDirectory = "/var/run/wireguard"
//go:linkname sockPath golang.xz2c4.com/wireguard/ipc.sockPath
func sockPath(iface string) string {
return fmt.Sprintf("%s/%s.sock", socketDirectory, iface)
}

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

@ -8,43 +8,47 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/un.h> #include <sys/un.h>
#include <string>
FILE *interfaceFile(const char *iface) { FILE *interfaceFile(std::string sockPath) {
struct stat sbuf; struct stat sbuf;
struct sockaddr_un addr = { .sun_family = AF_UNIX }; struct sockaddr_un addr = { .sun_family = AF_UNIX };
int fd = -1, ret; int fd = -1, ret;
FILE *f = NULL; FILE *f = NULL;
errno = EINVAL; errno = EINVAL;
if (strchr(iface, '/')) ret = snprintf(addr.sun_path, sizeof(addr.sun_path), sockPath.c_str());
goto out; if (ret < 0)
ret = stat(addr.sun_path, &sbuf); goto out;
if (ret < 0)
goto out;
errno = EBADF;
if (!S_ISSOCK(sbuf.st_mode))
goto out;
ret = fd = socket(AF_UNIX, SOCK_STREAM, 0); ret = stat(addr.sun_path, &sbuf);
if (ret < 0) if (ret < 0)
goto out; goto out;
ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); errno = EBADF;
if (ret < 0) { if (!S_ISSOCK(sbuf.st_mode))
if (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */ goto out;
unlink(addr.sun_path);
goto out; ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
} if (ret < 0)
f = fdopen(fd, "r+"); goto out;
if (f)
errno = 0; 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: out:
ret = -errno; ret = -errno;
if (ret) { if (ret) {
if (fd >= 0) if (fd >= 0)
close(fd); close(fd);
errno = -ret; errno = -ret;
return NULL; return NULL;
} }
return f; return f;
} }

@ -1,47 +0,0 @@
#include "userspace/wg-go.h"
#include "wginterface.hh"
#include <iostream>
#include <filesystem>
#include <string>
#include <vector>
// Get Wireguard-go version
std::string WireguardUserspace::getWireguardVersion() {
return wgVersion();
}
void WireguardUserspace::closeAllWireguardTunnels() {
callEndProcess();
}
// Create tunel and return path to tunel
std::string WireguardUserspace::createWireguardTunnel(std::string wgName) {
std::string PathError = createTun((char*)wgName.c_str());
if (PathError.rfind("/", 0) != 0) throw PathError;
return PathError.substr(1);
}
// Delete tunel by name and return true if success
void WireguardUserspace::deleteWireguardTunnel(std::string wgName) {
std::string deleteStatus = deleteTun((char*)wgName.c_str());
if (!deleteStatus.empty()) throw deleteStatus;
}
std::vector<std::string> WireguardUserspace::listTunnels() {
std::vector<std::string> tunnels;
size_t len; char *device_name, *devicesList = listUapis();
// Set new devices
for (device_name = devicesList, len = 0; (len = strlen(device_name)); device_name += len + 1) tunnels.push_back(std::string(device_name));
return tunnels;
}
bool WireguardUserspace::checkIfExistTunnel(std::string wgName) {
const auto wgTunels = listTunnels();
if (wgTunels.size() == 0) return false;
for (auto tunelName : wgTunels) {
if (tunelName.find_last_of("/") != std::string::npos) tunelName = tunelName.substr(tunelName.find_last_of("/") + 1);
if (tunelName.find_last_of(".sock") != std::string::npos) tunelName = tunelName.substr(0, tunelName.find_last_of(".sock"));
if (tunelName == wgName) return true;
}
return false;
};

@ -37,17 +37,6 @@ 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);
} }
#define NUM(max) ({ \
unsigned long long num; \
char *end; \
if (!char_is_digit(value[0])) \
break; \
num = strtoull(value, &end, 10); \
if (*end || num > max) \
break; \
num; \
})
void WireguardConfig::getWireguardConfig() { void WireguardConfig::getWireguardConfig() {
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))) throw std::string("Wireguard interface not exist"); else if (!(WireguardDevices().exist(this->name))) throw std::string("Wireguard interface not exist");
@ -59,7 +48,7 @@ void WireguardConfig::getWireguardConfig() {
fprintf(f, "get=1\n\n"); fprintf(f, "get=1\n\n");
fflush(f); fflush(f);
std::string peerPubKey; std::string peerPubKey;
bool peer; bool peer = false;
while (getline(&key, &line_buffer_len, f) > 0) { while (getline(&key, &line_buffer_len, f) > 0) {
line_len = strlen(key); line_len = strlen(key);
@ -71,68 +60,51 @@ void WireguardConfig::getWireguardConfig() {
if (this->Peers.size() == 0 && !strcmp(key, "private_key")) { if (this->Peers.size() == 0 && !strcmp(key, "private_key")) {
this->privateKey = wgKeys::HextoBase64(value); this->privateKey = wgKeys::HextoBase64(value);
this->publicKey = wgKeys::generatePublic(this->privateKey); this->publicKey = wgKeys::generatePublic(this->privateKey);
} else if (this->Peers.size() == 0 && !strcmp(key, "listen_port")) { } 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; });
this->portListen = NUM(0xffffU); 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 (this->Peers.size() == 0 && !strcmp(key, "fwmark")) { else if (!strcmp(key, "public_key")) {
this->fwmark = NUM(0xffffffffU);
} else if (!strcmp(key, "public_key")) {
Peer new_peer; Peer new_peer;
peerPubKey = wgKeys::HextoBase64(value); peerPubKey = wgKeys::HextoBase64(value);
this->Peers[peerPubKey] = new_peer; this->Peers[peerPubKey] = new_peer;
peer = true;
} else if (peer && !strcmp(key, "preshared_key")) { } else if (peer && !strcmp(key, "preshared_key")) {
if (strlen(value) == HexWgKeyLength) this->Peers[peerPubKey].presharedKey = wgKeys::HextoBase64(value); if (std::string(value) == "0000000000000000000000000000000000000000000000000000000000000000") continue;
} else if (peer && !strcmp(key, "endpoint")) { 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; char *begin, *end;
struct addrinfo *resolved; struct addrinfo *resolved;
struct addrinfo hints = { struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP };
.ai_family = AF_UNSPEC, if (!strlen(value)) break;
.ai_socktype = SOCK_DGRAM,
.ai_protocol = IPPROTO_UDP
};
if (!strlen(value))
break;
if (value[0] == '[') { if (value[0] == '[') {
begin = &value[1]; begin = &value[1];
end = strchr(value, ']'); end = strchr(value, ']');
if (!end) if (!end) break;
break;
*end++ = '\0'; *end++ = '\0';
if (*end++ != ':' || !*end) if (*end++ != ':' || !*end) break;
break;
} else { } else {
begin = value; begin = value;
end = strrchr(value, ':'); end = strrchr(value, ':');
if (!end || !*(end + 1)) if (!end || !*(end + 1)) break;
break;
*end++ = '\0'; *end++ = '\0';
} }
if (getaddrinfo(begin, end, &hints, &resolved) != 0) { if (getaddrinfo(begin, end, &hints, &resolved) != 0) {
ret = ENETUNREACH; ret = ENETUNREACH;
throw std::string("Failed to resolve endpoint"); throw std::string("Failed to resolve endpoint");
} }
if ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) 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;
|| (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6))) else {
this->Peers[peerPubKey].endpoint = value;
else {
freeaddrinfo(resolved); freeaddrinfo(resolved);
break; break;
} }
freeaddrinfo(resolved); freeaddrinfo(resolved);
} else if (peer && !strcmp(key, "persistent_keepalive_interval")) {
this->Peers[peerPubKey].keepInterval = NUM(0xffffU);
} 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 = NUM(0x7fffffffffffffffULL);
else if (peer && !strcmp(key, "rx_bytes"))
this->Peers[peerPubKey].rxBytes = NUM(0xffffffffffffffffULL);
else if (peer && !strcmp(key, "tx_bytes"))
this->Peers[peerPubKey].txBytes = NUM(0xffffffffffffffffULL);
else if (!strcmp(key, "errno"))
ret = -NUM(0x7fffffffU);
} }
free(key); free(key);
@ -144,11 +116,60 @@ 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))) { else if (!(WireguardDevices().exist(this->name))) {
std::string PathError = createTun((char*)this->name.c_str()); std::string PathError = createTun((char*)this->name.c_str());
if (PathError.rfind("/", 0) != 0) throw PathError; if (PathError.size() > 0) throw PathError;
} }
FILE *f = interfaceFile(WireguardDevices().findSock(this->name).c_str());
if (!f) throw std::string("Failed to open interface file"); FILE* f = interfaceFile(WireguardDevices().findSock(this->name));
fprintf(f, "set=1\n");
if (this->privateKey.length() == Base64WgKeyLength) fprintf(f, "private_key=%s\n", wgKeys::toHex(this->privateKey).c_str());
if (this->portListen >= 0) fprintf(f, "listen_port=%u\n", this->portListen);
if (this->fwmark >= 0) fprintf(f, "fwmark=%u\n", this->fwmark);
if (this->replacePeers) fprintf(f, "replace_peers=true\n");
for (auto peer : this->Peers) {
fprintf(f, "public_key=%s\n", wgKeys::toHex(peer.first).c_str());
if (peer.second.removeMe) {
fprintf(f, "remove=true\n");
continue;
}
if (peer.second.presharedKey.length() == Base64WgKeyLength) fprintf(f, "preshared_key=%s\n", wgKeys::toHex(peer.second.presharedKey).c_str());
if (peer.second.keepInterval) fprintf(f, "persistent_keepalive_interval=%u\n", peer.second.keepInterval);
if (peer.second.endpoint.length() > 2) fprintf(f, "endpoint=%s\n", peer.second.endpoint.c_str());
if (peer.second.allowedIPs.size() > 0) {
fprintf(f, "replace_allowed_ips=true\n");
for (auto s : peer.second.allowedIPs.getIpParsed()) fprintf(f, "allowed_ip=%s/%d\n", s.Address.c_str(), s.Mask);
}
}
fprintf(f, "\n");
fflush(f);
int ret, set_errno = -EPROTO;
size_t line_buffer_len = 0, line_len;
char *key = NULL, *value;
while (getline(&key, &line_buffer_len, f) > 0) {
line_len = strlen(key);
ret = set_errno;
if (line_len == 1 && key[0] == '\n') break;
value = strchr(key, '=');
if (!value || line_len == 0 || key[line_len - 1] != '\n') break;
*value++ = key[--line_len] = '\0';
if (!strcmp(key, "errno")) {
long long num;
char *end;
if (value[0] != '-' && !char_is_digit(value[0])) break;
num = strtoll(value, &end, 10);
if (*end || num > INT_MAX || num < INT_MIN) break;
set_errno = num;
}
}
free(key);
fclose(f); fclose(f);
ret = -(errno ? -errno : -EPROTO);
if (ret < 0) throw std::string("Cannot set configuration, code: ").append(std::to_string(ret));
} }
void IpManeger::SetInInterface(std::string interfaceName) {} void IpManeger::SetInInterface(std::string interfaceName) {}

@ -26,13 +26,10 @@ class WireguardDevices : public std::vector<std::string> {
this->getInterfaces(); this->getInterfaces();
for (auto wgDev = this->begin(); wgDev != this->end(); ++wgDev) { for (auto wgDev = this->begin(); wgDev != this->end(); ++wgDev) {
if (name == *wgDev) return true; if (name == *wgDev) return true;
else if (wgDev->find_last_of(".sock") != std::string::npos) { else {
wgDev->erase(wgDev->find_last_of(".sock")); std::string __nDev = std::string(wgDev->substr(wgDev->find_last_of("/")+1));
if (name == *wgDev) return true; if (__nDev.find(".sock") != std::string::npos) __nDev = __nDev.substr(0, __nDev.find(".sock"));
if (wgDev->find_last_of("/wg") != std::string::npos) { if (__nDev == name) return true;
wgDev->erase(wgDev->find_last_of("/wg"));
if (name == *wgDev) return true;
}
} }
} }
@ -45,12 +42,9 @@ class WireguardDevices : public std::vector<std::string> {
for (auto wgDev = this->begin(); wgDev != this->end(); ++wgDev) { for (auto wgDev = this->begin(); wgDev != this->end(); ++wgDev) {
if (name == *wgDev) return *wgDev; if (name == *wgDev) return *wgDev;
else if (wgDev->find_last_of(".sock") != std::string::npos) { else if (wgDev->find_last_of(".sock") != std::string::npos) {
wgDev->erase(wgDev->find_last_of(".sock")); std::string __nDev = std::string(wgDev->substr(wgDev->find_last_of("/")+1));
if (name == *wgDev) return *wgDev; if (__nDev.find(".sock") != std::string::npos) __nDev = __nDev.substr(0, __nDev.find(".sock"));
if (wgDev->find_last_of("/wg") != std::string::npos) { if (__nDev == name) return *wgDev;
wgDev->erase(wgDev->find_last_of("/wg"));
if (name == *wgDev) return *wgDev;
}
} }
} }

@ -1,6 +1,7 @@
import test from "node:test"; import test from "node:test";
import { Wireguard } from "./wginterface.js"; import { Wireguard, getConfig, setConfig } from "./wginterface.js";
import { presharedKey, privateKey, publicKey } from "./key.js"; import { presharedKey, privateKey, publicKey } from "./key.js";
import assert from "node:assert";
await test("Wireguard interface", async t => { await test("Wireguard interface", async t => {
const config = new Wireguard; const config = new Wireguard;
@ -28,11 +29,22 @@ await test("Wireguard interface", async t => {
] ]
}); });
const jsonConfig = config.toJSON();
let skip: string; let skip: string;
await t.test("Create and Set config in interface", async () => config.deploy().catch(err => { skip = "Cannot set wireguard config"; return Promise.reject(err); })); 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 () => { await t.test("Get config from interface", { skip }, async () => {
await config.getConfig(); const config = await getConfig(jsonConfig.name);
console.dir(config.toJSON(), { depth: null }); // 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()); await t.test("Delete interface if exists", { skip }, async () => config.delete());