Files
Wireguard-tools.js/addons/tools/wginterface-win.cpp
T
Sirherobrine23andGitHub 1ed4fcad71 Windows Support (#36)
Fist commit to add Windows support to create Wireguard interfaces and Manager configs

Signed-off-by: Matheus Sampaio Queiroga <srherobrine20@gmail.com>
2023-09-11 17:21:07 -03:00

408 lines
18 KiB
C++

#include <napi.h>
#include <string>
#include <vector>
#include <map>
#include <iostream>
#include <wireguard-nt/include/wireguard.h>
#include <win/shared.cpp>
#include <windows.h>
#include <tlhelp32.h>
#include <accctrl.h>
#include <aclapi.h>
#include <stdio.h>
#include <stdbool.h>
#include <fcntl.h>
#include <winsock2.h>
#include <ws2ipdef.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <bcrypt.h>
#include <wincrypt.h>
#include <sysinfoapi.h>
#include <winternl.h>
#include <cstdlib>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <devguid.h>
#include <ndisguid.h>
#include "wginterface.hh"
const DEVPROPKEY devpkey_name = { { 0x65726957, 0x7547, 0x7261, { 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x4b, 0x65, 0x79 } }, DEVPROPID_FIRST_USABLE + 1 };
#define IFNAMSIZ MAX_ADAPTER_NAME - 1
static WIREGUARD_CREATE_ADAPTER_FUNC *WireGuardCreateAdapter;
static WIREGUARD_OPEN_ADAPTER_FUNC *WireGuardOpenAdapter;
static WIREGUARD_CLOSE_ADAPTER_FUNC *WireGuardCloseAdapter;
static WIREGUARD_GET_ADAPTER_LUID_FUNC *WireGuardGetAdapterLUID;
static WIREGUARD_GET_RUNNING_DRIVER_VERSION_FUNC *WireGuardGetRunningDriverVersion;
static WIREGUARD_DELETE_DRIVER_FUNC *WireGuardDeleteDriver;
static WIREGUARD_SET_LOGGER_FUNC *WireGuardSetLogger;
static WIREGUARD_SET_ADAPTER_LOGGING_FUNC *WireGuardSetAdapterLogging;
static WIREGUARD_GET_ADAPTER_STATE_FUNC *WireGuardGetAdapterState;
static WIREGUARD_SET_ADAPTER_STATE_FUNC *WireGuardSetAdapterState;
static WIREGUARD_GET_CONFIGURATION_FUNC *WireGuardGetConfiguration;
static WIREGUARD_SET_CONFIGURATION_FUNC *WireGuardSetConfiguration;
unsigned long maxName() {
return IFNAMSIZ;
}
std::string getErrorString(DWORD errorMessageID) {
if (errorMessageID == 0 || errorMessageID < 0) std::string("Error code: ").append(std::to_string(errorMessageID));
LPSTR messageBuffer = nullptr;
//Ask Win32 to give us the string version of that message ID.
//The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
//Copy the error message into a std::string.
std::string message(messageBuffer, size);
//Free the Win32's string's buffer.
LocalFree(messageBuffer);
return std::string("Error code: ").append(std::to_string(errorMessageID)).append(", Message: ").append(message);
}
std::string startAddon(const Napi::Env env) {
if (!IsRunAsAdmin()) return "Run nodejs with administrator privilegies";
auto DLLPATH = env.Global().ToObject().Get("WIREGUARD_DLL_PATH");
if (!(DLLPATH.IsString())) return "Require WIREGUARD_DLL_PATH in Global process";
LPCWSTR dllPath = toLpcwstr(DLLPATH.ToString());
HMODULE WireGuardDll = LoadLibraryExW(dllPath, NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!WireGuardDll) return ((std::string)"Failed to initialize WireGuardNT, ").append(getErrorString(GetLastError()));;
#define X(Name) ((*(FARPROC *)&Name = GetProcAddress(WireGuardDll, #Name)) == NULL)
if (X(WireGuardCreateAdapter) || X(WireGuardOpenAdapter) || X(WireGuardCloseAdapter) || X(WireGuardGetAdapterLUID) || X(WireGuardGetRunningDriverVersion) || X(WireGuardDeleteDriver) || X(WireGuardSetLogger) || X(WireGuardSetAdapterLogging) || X(WireGuardGetAdapterState) || X(WireGuardSetAdapterState) || X(WireGuardGetConfiguration) || X(WireGuardSetConfiguration))
#undef X
{
DWORD LastError = GetLastError();
FreeLibrary(WireGuardDll);
SetLastError(LastError);
return ((std::string)"Failed to set Functions from WireGuardNT DLL, ").append(getErrorString(GetLastError()));;
}
return "";
}
std::string versionDrive() {
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardCreateAdapter(L"getWgVersion", L"Wireguard-tools.js", NULL);
DWORD Version = WireGuardGetRunningDriverVersion();
if (Version == 0) {
auto statusErr = GetLastError();
WireGuardCloseAdapter(Adapter);
if (statusErr == ERROR_FILE_NOT_FOUND) return "Driver not loaded";
return ((std::string)"Cannot get version drive, ").append(getErrorString(GetLastError()));
}
WireGuardCloseAdapter(Adapter);
return ((std::string)"WireGuardNT v").append(std::to_string((Version >> 16) & 0xff)).append(".").append(std::to_string((Version >> 0) & 0xff));
}
typedef uint8_t wg_key[32];
typedef char wg_key_b64_string[((sizeof(wg_key) + 2) / 3) * 4 + 1];
bool key_is_zero(const uint8_t key[32])
{
volatile uint8_t acc = 0;
for (unsigned int i = 0; i < 32; ++i) {
acc |= key[i];
// asm volatile("" : "=r"(acc) : "0"(acc));
}
return !!(1 & ((acc - 1) >> 8));
}
static int decodeBase64(const char src[4]) {
int val = 0;
unsigned int i;
for (i = 0; i < 4; ++i) val |= (-1 + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64)) + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70)) + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5)) + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63) + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)) << (18 - 6 * i);
return val;
}
static void encode_base64(char dest[4], const uint8_t src[3]) {
const uint8_t input[] = {
static_cast<uint8_t>((src[0] >> 2) & 63),
static_cast<uint8_t>(((src[0] << 4) | (src[1] >> 4)) & 63),
static_cast<uint8_t>(((src[1] << 2) | (src[2] >> 6)) & 63),
static_cast<uint8_t>(src[2] & 63)
};
unsigned int i;
for (i = 0; i < 4; ++i) dest[i] = input[i] + 'A' + (((25 - input[i]) >> 8) & 6) - (((51 - input[i]) >> 8) & 75) - (((61 - input[i]) >> 8) & 15) + (((62 - input[i]) >> 8) & 3);
}
void insertKey(wg_key key, std::string keyBase64) {
auto base64 = keyBase64.c_str();
if (keyBase64.length() != WG_KEY_LENGTH || base64[WG_KEY_LENGTH - 1] != '=') throw std::string("invalid key, length: ").append(std::to_string(keyBase64.length()));
unsigned int i;
int val;
volatile uint8_t ret = 0;
for (i = 0; i < 32 / 3; ++i) {
val = decodeBase64(&base64[i * 4]);
ret |= (uint32_t)val >> 31;
key[i * 3 + 0] = (val >> 16) & 0xff;
key[i * 3 + 1] = (val >> 8) & 0xff;
key[i * 3 + 2] = val & 0xff;
}
const char tempDecode[4] = {base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A'};
val = decodeBase64(tempDecode);
ret |= ((uint32_t)val >> 31) | (val & 0xff);
key[i * 3 + 0] = (val >> 16) & 0xff;
key[i * 3 + 1] = (val >> 8) & 0xff;
int status = EINVAL & ~((ret - 1) >> 8);
if (status != 0) throw std::string("Cannot decode key, ret code: ").append(std::to_string(status));
}
std::string keyToBase64(const wg_key key) {
wg_key_b64_string base64;
unsigned int i;
for (i = 0; i < 32 / 3; ++i) encode_base64(&base64[i * 4], &key[i * 3]);
const uint8_t tempKey[3] = { key[i * 3 + 0], key[i * 3 + 1], 0 };
encode_base64(&base64[i * 4], tempKey);
base64[sizeof(wg_key_b64_string) - 2] = '=';
base64[sizeof(wg_key_b64_string) - 1] = '\0';
return std::string(base64);
}
void listDevices::Execute() {
std::vector<std::string> arrayPrefix;
arrayPrefix.push_back("ProtectedPrefix\\Administrators\\WireGuard\\");
arrayPrefix.push_back("WireGuard\\");
WIN32_FIND_DATA find_data;
HANDLE find_handle;
int ret = 0;
for (auto &preit : arrayPrefix) {
ret = 0;
find_handle = FindFirstFile("\\\\.\\pipe\\*", &find_data);
if (find_handle == INVALID_HANDLE_VALUE) continue;
char *iface;
do {
if (strncmp(preit.c_str(), find_data.cFileName, strlen(preit.c_str()))) continue;
iface = find_data.cFileName + strlen(preit.c_str());
deviceNames[std::string(iface)] = "userspace";
} while (FindNextFile(find_handle, &find_data));
FindClose(find_handle);
if (ret < 0) {
std::string err = "Erro code: ";
err = err.append(std::to_string(ret));
return SetError(err);
}
}
HDEVINFO dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, L"SWD\\WireGuard", NULL, DIGCF_PRESENT, NULL, NULL, NULL);
if (dev_info == INVALID_HANDLE_VALUE) return SetError("Cannot get devices");
for (DWORD i = 0;; ++i) {
DWORD buf_len;
WCHAR adapter_name[MAX_ADAPTER_NAME];
SP_DEVINFO_DATA dev_info_data;
dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
DEVPROPTYPE prop_type;
ULONG status, problem_code;
char *interface_name;
if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) break;
continue;
}
if (!SetupDiGetDevicePropertyW(dev_info, &dev_info_data, &devpkey_name, &prop_type, (PBYTE)adapter_name, sizeof(adapter_name), NULL, 0) || prop_type != DEVPROP_TYPE_STRING) continue;
adapter_name[_countof(adapter_name) - 1] = L'0';
if (!adapter_name[0]) continue;
buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, NULL, 0, NULL, NULL);
if (!buf_len) continue;
interface_name = (char *)malloc(buf_len);
if (!interface_name) continue;
buf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, interface_name, buf_len, NULL, NULL);
if (!buf_len) {
free(interface_name);
continue;
}
if (CM_Get_DevNode_Status(&status, &problem_code, dev_info_data.DevInst, 0) == CR_SUCCESS && (status & (DN_DRIVER_LOADED | DN_STARTED)) == (DN_DRIVER_LOADED | DN_STARTED)) deviceNames[std::string(interface_name)] = "kernel";
}
SetupDiDestroyDeviceInfoList(dev_info);
}
void deleteInterface::Execute() {
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(wgName));
if (!Adapter) return SetError("This interface not exists in Wireguard-Tools.js addon!");
if (!(WireGuardSetAdapterState(Adapter, WIREGUARD_ADAPTER_STATE::WIREGUARD_ADAPTER_STATE_DOWN))) return SetError(((std::string)"Failed to set down interface, ").append(getErrorString(GetLastError())));
WireGuardCloseAdapter(Adapter);
}
void getConfig::Execute() {
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(wgName));
if (!Adapter) return SetError("This interface not exists in Wireguard-Tools.js addon!");
NET_LUID InterfaceLuid;
WireGuardGetAdapterLUID(Adapter, &InterfaceLuid);
try {
for (auto aip : getIpAddr(InterfaceLuid)) Address.push_back(aip);
} catch (void* err) {}
DWORD buf_len = 0;
WIREGUARD_INTERFACE *wg_iface = nullptr;
while (!(WireGuardGetConfiguration(Adapter, wg_iface, &buf_len))) {
free(wg_iface);
if (GetLastError() != ERROR_MORE_DATA) return SetError((std::string("Failed get interface config, code: ")).append(std::to_string(GetLastError())));
wg_iface = (WIREGUARD_INTERFACE *)malloc(buf_len);
if (!wg_iface) return SetError(((std::string)"Failed get interface config, ").append(std::to_string(-errno)));
}
if (wg_iface->Flags & WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_PRIVATE_KEY) privateKey = keyToBase64(wg_iface->PrivateKey);
if (wg_iface->Flags & WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_PUBLIC_KEY) publicKey = keyToBase64(wg_iface->PublicKey);
portListen = 0;
if (wg_iface->Flags & WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_LISTEN_PORT) portListen = wg_iface->ListenPort;
WIREGUARD_PEER *wg_peer = reinterpret_cast<WIREGUARD_PEER*>(((char*)wg_iface) + sizeof(WIREGUARD_INTERFACE));
for (DWORD i = 0; i < wg_iface->PeersCount; i++) {
auto pubKey = keyToBase64(wg_peer->PublicKey);
Peer peerConfig;
if (wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PRESHARED_KEY) peerConfig.presharedKey = keyToBase64(wg_peer->PresharedKey);
if (wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_ENDPOINT) peerConfig.endpoint = parseEndpoint(&wg_peer->Endpoint);
if (wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PERSISTENT_KEEPALIVE) peerConfig.keepInterval = wg_peer->PersistentKeepalive;
peerConfig.rxBytes = wg_peer->RxBytes;
peerConfig.txBytes = wg_peer->TxBytes;
peerConfig.last_handshake = 0;
if (wg_peer->LastHandshake > 0) peerConfig.last_handshake = (wg_peer->LastHandshake / 10000000 - 11644473600LL) * 1000;
WIREGUARD_ALLOWED_IP* wg_aip = reinterpret_cast<WIREGUARD_ALLOWED_IP*>(((char*)wg_peer) + sizeof(WIREGUARD_PEER));
for (DWORD __aip = 0; __aip < wg_peer->AllowedIPsCount; __aip++) {
char saddr[INET6_ADDRSTRLEN];
if (wg_aip->AddressFamily == AF_INET) {
inet_ntop(AF_INET, &wg_aip->Address.V6, saddr, INET_ADDRSTRLEN);
peerConfig.allowedIPs.push_back(std::string(saddr).append("/").append(std::to_string(wg_aip->Cidr)));
} else if (wg_aip->AddressFamily == AF_INET6) {
inet_ntop(AF_INET6, &wg_aip->Address.V6, saddr, INET6_ADDRSTRLEN);
peerConfig.allowedIPs.push_back(std::string(saddr).append("/").append(std::to_string(wg_aip->Cidr)));
}
++wg_aip;
}
wg_peer = reinterpret_cast<WIREGUARD_PEER*>(wg_aip);
peersVector[pubKey] = peerConfig;
}
free(wg_iface);
}
void setConfig::Execute() {
DWORD buf_len = sizeof(WIREGUARD_INTERFACE);
for (auto peer : peersVector) {
if (DWORD_MAX - buf_len < sizeof(WIREGUARD_PEER)) return SetError("Buffer overflow");
buf_len += sizeof(WIREGUARD_PEER);
for (auto aip : peer.second.allowedIPs) {
if (DWORD_MAX - buf_len < sizeof(WIREGUARD_ALLOWED_IP)) return SetError("Buffer overflow");
buf_len += sizeof(WIREGUARD_ALLOWED_IP);
}
}
WIREGUARD_INTERFACE *wg_iface = NULL;
wg_iface = reinterpret_cast<WIREGUARD_INTERFACE*>(calloc(1, buf_len));
if (!wg_iface) return SetError("Cannot alloc buff");
wg_iface->PeersCount = 0;
insertKey(wg_iface->PrivateKey, privateKey);
wg_iface->Flags = WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_PRIVATE_KEY;
wg_iface->ListenPort = portListen;
if (portListen >= 0 && 65535 <= portListen) wg_iface->Flags = (WIREGUARD_INTERFACE_FLAG)(wg_iface->Flags|WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_HAS_LISTEN_PORT);
if (replacePeers) wg_iface->Flags = (WIREGUARD_INTERFACE_FLAG)(wg_iface->Flags|WIREGUARD_INTERFACE_FLAG::WIREGUARD_INTERFACE_REPLACE_PEERS);
WIREGUARD_ALLOWED_IP *wg_aip;
WIREGUARD_PEER *wg_peer = reinterpret_cast<WIREGUARD_PEER*>(reinterpret_cast<char*>(wg_iface) + sizeof(WIREGUARD_INTERFACE));
for (auto __peer : peersVector) {
auto peerPublicKey = __peer.first; auto peerConfig = __peer.second;
insertKey(wg_peer->PublicKey, peerPublicKey);
wg_peer->Flags = WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PUBLIC_KEY;
wg_peer->AllowedIPsCount = 0;
wg_iface->PeersCount++;
if (peerConfig.removeMe) {
wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_REMOVE);
wg_peer = reinterpret_cast<WIREGUARD_PEER*>(((char*)wg_peer) + sizeof(WIREGUARD_PEER));
} else {
if (peerConfig.presharedKey.size() == WG_KEY_LENGTH) {
insertKey(wg_peer->PresharedKey, peerConfig.presharedKey);
wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PRESHARED_KEY);
}
if (peerConfig.keepInterval > 0) {
wg_peer->PersistentKeepalive = peerConfig.keepInterval;
wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_PERSISTENT_KEEPALIVE);
}
if (peerConfig.endpoint.size() > 0) {
try {
insertEndpoint(&wg_peer->Endpoint, peerConfig.endpoint.c_str());
wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_HAS_ENDPOINT);
} catch (std::string &err) {
SetError(std::string("Cannot parse endpoint, ").append(err));
goto outEnd;
}
}
wg_aip = reinterpret_cast<WIREGUARD_ALLOWED_IP*>(((char*)wg_peer) + sizeof(WIREGUARD_PEER));
for (auto aip : peerConfig.allowedIPs) {
unsigned long cidr = 0;
if (aip.find("/") != std::string::npos) {
cidr = std::stoi(aip.substr(aip.find("/")+1));
aip = aip.substr(0, aip.find("/"));
}
aip = aip.substr(0, aip.find("/"));
wg_aip->AddressFamily = strchr(aip.c_str(), ':') ? AF_INET6 : AF_INET;
auto status = wg_aip->AddressFamily == AF_INET6 ? inet_pton(wg_aip->AddressFamily, aip.c_str(), &wg_aip->Address.V6) : inet_pton(wg_aip->AddressFamily, aip.c_str(), &wg_aip->Address.V4);
if (status == 1) {
if (cidr == 0) cidr = wg_aip->AddressFamily == AF_INET6 ? 128 : 32;
} else continue;
wg_aip->Cidr = cidr;
wg_peer->AllowedIPsCount++;
wg_aip = reinterpret_cast<WIREGUARD_ALLOWED_IP*>(((char*)wg_aip) + sizeof(WIREGUARD_ALLOWED_IP));
if (!(wg_peer->Flags & WIREGUARD_PEER_FLAG::WIREGUARD_PEER_REPLACE_ALLOWED_IPS)) wg_peer->Flags = (WIREGUARD_PEER_FLAG)(wg_peer->Flags|WIREGUARD_PEER_FLAG::WIREGUARD_PEER_REPLACE_ALLOWED_IPS);
}
wg_peer = reinterpret_cast<WIREGUARD_PEER*>(((char*)wg_aip));
}
}
WIREGUARD_ADAPTER_HANDLE Adapter = WireGuardOpenAdapter(toLpcwstr(wgName));
if (!Adapter) Adapter = WireGuardCreateAdapter(toLpcwstr(wgName), L"Wireguard-tools.js", NULL);
if (!Adapter) SetError(((std::string)"Failed to create adapter, ").append(getErrorString(GetLastError())));
else if (!WireGuardSetConfiguration(Adapter, reinterpret_cast<WIREGUARD_INTERFACE*>(wg_iface), buf_len)) {
auto status = GetLastError();
SetError(std::string("Failed to set interface config, ").append(getErrorString(status)));
WireGuardCloseAdapter(Adapter);
} else if (!WireGuardSetAdapterState(Adapter, WIREGUARD_ADAPTER_STATE::WIREGUARD_ADAPTER_STATE_UP)) {
auto status = GetLastError();
SetError(std::string("Failed to set interface up, ").append(getErrorString(status)));
WireGuardCloseAdapter(Adapter);
} else {
if (Address.size() > 0) {
std::string IPv4, IPv6;
for (auto aip : Address) {
aip = aip.substr(0, aip.find("/"));
auto family = strchr(aip.c_str(), ':') ? AF_INET6 : AF_INET;
SOCKADDR_INET address;
int status = family == AF_INET ? inet_pton(family, aip.c_str(), &address.Ipv4.sin_addr) : inet_pton(family, aip.c_str(), &address.Ipv6.sin6_addr);
if (status != 1) continue;
char saddr[INET6_ADDRSTRLEN];
family == AF_INET ? inet_ntop(AF_INET, &address.Ipv4.sin_addr, saddr, INET_ADDRSTRLEN) : inet_ntop(AF_INET6, &address.Ipv6.sin6_addr, saddr, INET6_ADDRSTRLEN);
if (family == AF_INET) IPv4 = std::string(saddr);
// else IPv6 = std::string(saddr);
}
if (IPv4.size() > 0 || IPv6.size() > 0) {
NET_LUID InterfaceLuid;
WireGuardGetAdapterLUID(Adapter, &InterfaceLuid);
auto setStatus = insertIpAddr(InterfaceLuid, IPv4, IPv6);
if (setStatus.size() > 0) SetError(setStatus);
}
}
}
outEnd:
free(wg_iface);
}