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",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp"
"xutility": "cpp",
"fstream": "cpp"
}
}

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

@ -1,17 +1,11 @@
//go:build linux || darwin || freebsd || openbsd
package main
import (
"fmt"
_ "unsafe"
_ "golang.zx2c4.com/wireguard/ipc"
_ "unsafe"
)
//go:linkname socketDirectory golang.xz2c4.com/wireguard/ipc.socketDirectory
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/stat.h>
#include <sys/un.h>
#include <string>
FILE *interfaceFile(const char *iface) {
struct stat sbuf;
struct sockaddr_un addr = { .sun_family = AF_UNIX };
int fd = -1, ret;
FILE *f = NULL;
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;
if (strchr(iface, '/'))
goto out;
ret = stat(addr.sun_path, &sbuf);
if (ret < 0)
goto out;
errno = EBADF;
if (!S_ISSOCK(sbuf.st_mode))
goto out;
errno = EINVAL;
ret = snprintf(addr.sun_path, sizeof(addr.sun_path), sockPath.c_str());
if (ret < 0)
goto out;
ret = fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (ret < 0)
goto out;
ret = stat(addr.sun_path, &sbuf);
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;
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;
ret = -errno;
if (ret) {
if (fd >= 0)
close(fd);
errno = -ret;
return NULL;
}
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);
}
#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() {
if (this->name.length() == 0) throw std::string("Set wireguard name!");
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");
fflush(f);
std::string peerPubKey;
bool peer;
bool peer = false;
while (getline(&key, &line_buffer_len, f) > 0) {
line_len = strlen(key);
@ -71,68 +60,51 @@ void WireguardConfig::getWireguardConfig() {
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 = NUM(0xffffU);
} else if (this->Peers.size() == 0 && !strcmp(key, "fwmark")) {
this->fwmark = NUM(0xffffffffU);
} else if (!strcmp(key, "public_key")) {
} 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 (strlen(value) == HexWgKeyLength) this->Peers[peerPubKey].presharedKey = wgKeys::HextoBase64(value);
} else if (peer && !strcmp(key, "endpoint")) {
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;
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;
if (!end) break;
*end++ = '\0';
if (*end++ != ':' || !*end)
break;
if (*end++ != ':' || !*end) break;
} else {
begin = value;
end = strrchr(value, ':');
if (!end || !*(end + 1))
break;
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 {
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);
} 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);
@ -144,11 +116,60 @@ void WireguardConfig::setWireguardConfig() {
if (this->name.length() == 0) throw std::string("Set wireguard name!");
else if (!(WireguardDevices().exist(this->name))) {
std::string PathError = createTun((char*)this->name.c_str());
if (PathError.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);
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) {}

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

@ -1,6 +1,7 @@
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 assert from "node:assert";
await test("Wireguard interface", async t => {
const config = new Wireguard;
@ -28,11 +29,22 @@ await test("Wireguard interface", async t => {
]
});
const jsonConfig = config.toJSON();
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 config.getConfig();
console.dir(config.toJSON(), { depth: null });
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());