RunicVTT Open Source Virtual Tabletop for TTRPG using P2P
Loading...
Searching...
No Matches
FirewallUtils.h
Go to the documentation of this file.
1// FirewallUtils.h
2#pragma once
3#define WIN32_LEAN_AND_MEAN
4#include <Windows.h>
5#include <shellapi.h>
6#include <string>
7#include <sstream>
8
9namespace FirewallUtils
10{
11
12 inline std::string psEscape(const std::string& s)
13 {
14 std::string out;
15 out.reserve(s.size() + 8);
16 for (char c : s)
17 out += (c == '\'') ? "''" : std::string(1, c);
18 return out;
19 }
20 inline bool runPSElevatedWait(const std::string& psCommand)
21 {
22 // Build the PowerShell arguments once, keep the std::string alive
23 std::ostringstream oss;
24 oss << "-NoProfile -ExecutionPolicy Bypass -Command \"" << psCommand << "\"";
25 const std::string argStr = oss.str(); // KEEP this alive for the whole function
26
27 // Convert to UTF-16 safely
28 auto toWide = [](const std::string& s) -> std::wstring
29 {
30 if (s.empty())
31 return std::wstring();
32 int needed = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), nullptr, 0);
33 std::wstring ws(needed, L'\0');
34 MultiByteToWideChar(CP_UTF8, 0, s.c_str(), (int)s.size(), ws.data(), needed);
35 return ws;
36 };
37
38 const std::wstring wExe = L"powershell.exe";
39 const std::wstring wArgs = toWide(argStr);
40
41 SHELLEXECUTEINFOW sei{};
42 sei.cbSize = sizeof(sei);
43 sei.fMask = SEE_MASK_NOCLOSEPROCESS;
44 sei.hwnd = nullptr;
45 sei.lpVerb = L"runas"; // UAC prompt
46 sei.lpFile = wExe.c_str();
47 sei.lpParameters = wArgs.c_str();
48 sei.nShow = SW_HIDE;
49
50 if (!ShellExecuteExW(&sei))
51 {
52 // Optional: GetLastError() logging here
53 return false;
54 }
55 if (sei.hProcess)
56 {
57 WaitForSingleObject(sei.hProcess, INFINITE);
58 CloseHandle(sei.hProcess);
59 }
60 return true;
61 }
62
63 inline void removeRuleElevated(const std::string& displayName)
64 {
65 std::ostringstream ps;
66 ps << "$n='" << psEscape(displayName) << "'; "
67 << "Get-NetFirewallRule -DisplayName $n -ErrorAction SilentlyContinue | "
68 << "Remove-NetFirewallRule -Confirm:$false";
69 runPSElevatedWait(ps.str());
70 }
71
72 // Inbound ANY TCP for a specific program (exe), on Private by default
73 inline void addInboundAnyTcpForExe(const std::string& displayName,
74 const std::string& programPath,
75 bool privateOnly = true)
76 {
77 std::ostringstream ps;
78 ps << "New-NetFirewallRule "
79 << "-DisplayName '" << psEscape(displayName) << "' "
80 << "-Direction Inbound -Action Allow -Protocol TCP -LocalPort Any "
81 << "-Program '" << psEscape(programPath) << "' "
82 << "-Profile " << (privateOnly ? "Private" : "Any");
83 runPSElevatedWait(ps.str());
84 }
85
86 // Inbound ANY UDP for a specific program (optional, for WebRTC/data)
87 inline void addInboundAnyUdpForExe(const std::string& displayName,
88 const std::string& programPath,
89 bool privateOnly = true)
90 {
91 std::ostringstream ps;
92 ps << "New-NetFirewallRule "
93 << "-DisplayName '" << psEscape(displayName) << "' "
94 << "-Direction Inbound -Action Allow -Protocol UDP "
95 << "-Program '" << psEscape(programPath) << "' "
96 << "-Profile " << (privateOnly ? "Private" : "Any");
97 runPSElevatedWait(ps.str());
98 }
99
100} // namespace FirewallUtils
void addInboundAnyTcpForExe(const std::string &displayName, const std::string &programPath, bool privateOnly=true)
bool runPSElevatedWait(const std::string &psCommand)
void addInboundAnyUdpForExe(const std::string &displayName, const std::string &programPath, bool privateOnly=true)
std::string psEscape(const std::string &s)
void removeRuleElevated(const std::string &displayName)