Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
SystemFirewall.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <cstdlib>
7#if qStroika_Foundation_Common_Platform_Windows
8#include <windows.h>
9#endif
10
11#if qStroika_Foundation_Common_Platform_Windows
12#include "Stroika/Foundation/Characters/Platform/Windows/SmartBSTR.h"
13#endif
15#include "Stroika/Foundation/Containers/Collection.h"
18#if qStroika_Foundation_Common_Platform_Windows
19#include "Stroika/Foundation/Execution/Platform/Windows/COM.h"
20#include "Stroika/Foundation/Execution/Platform/Windows/HRESULTErrorException.h"
21#endif
22
23#include "SystemFirewall.h"
24
25using namespace std;
26
27using namespace Stroika::Foundation;
29#if qStroika_Foundation_Common_Platform_Windows
31#endif
33#if qStroika_Foundation_Common_Platform_Windows
35#endif
37using namespace Stroika::Foundation::IO::Network::SystemFirewall;
38
39// Comment this in to turn on aggressive noisy DbgTrace in this module
40//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
41
42#if qStroika_Foundation_Common_Platform_Windows
43#pragma comment(lib, "ole32.lib")
44#pragma comment(lib, "oleaut32.lib")
45#endif
46
47/*
48 ********************************************************************************
49 ******************** IO::Network::SystemFirewall::Rule *************************
50 ********************************************************************************
51 */
53{
55 sb << "{"sv;
56 sb << "Name: "sv << fName << ","sv;
57 sb << "Description: "sv << fDescription << ","sv;
58 sb << "Application: "sv << fApplication << ","sv;
59#if qStroika_Foundation_Common_Platform_Windows
60 sb << "ProfileMask: "sv << (int)fProfileMask << ","sv;
61 sb << "Direction: "sv << (int)fDirection << ","sv;
62 sb << "Protocol: "sv << (int)fProtocol << ","sv;
63#endif
64 sb << "LocalPorts: "sv << fLocalPorts << ","sv;
65 sb << "RemotePorts: "sv << fRemotePorts << ","sv;
66#if qStroika_Foundation_Common_Platform_Windows
67 sb << "Action: "sv << (int)fAction << ","sv;
68#endif
69 sb << "Enabled: "sv << fEnabled << ","sv;
70 sb << "}"sv;
71 return sb;
72}
73
74/*
75 ********************************************************************************
76 ***************** IO::Network::SystemFirewall::Manager *************************
77 ********************************************************************************
78 */
79#if qStroika_Foundation_Common_Platform_Windows
80namespace {
81 Rule ReadRule_ (INetFwRule* pFwRule)
82 {
83 AssertNotNull (pFwRule);
84#if USE_NOISY_TRACE_IN_THIS_MODULE_
85 Debug::TraceContextBumper ctx{"{}ReadRule_"};
86#endif
87 BSTR name = nullptr;
88 BSTR desc = nullptr;
89 BSTR group = nullptr;
90 BSTR application = nullptr;
91 BSTR localPorts = nullptr;
92 BSTR remotePorts = nullptr;
93 [[maybe_unused]] auto&& cleanup = Execution::Finally ([=] () noexcept {
94 if (name != nullptr) {
95 ::SysFreeString (name);
96 }
97 if (desc != nullptr) {
98 ::SysFreeString (desc);
99 }
100 if (group != nullptr) {
101 ::SysFreeString (group);
102 }
103 if (application != nullptr) {
104 ::SysFreeString (application);
105 }
106 if (localPorts != nullptr) {
107 ::SysFreeString (localPorts);
108 }
109 if (remotePorts != nullptr) {
110 ::SysFreeString (remotePorts);
111 }
112 });
113 ThrowIfErrorHRESULT (pFwRule->get_Name (&name));
114 ThrowIfErrorHRESULT (pFwRule->get_Description (&desc));
115 ThrowIfErrorHRESULT (pFwRule->get_Grouping (&group));
116 ThrowIfErrorHRESULT (pFwRule->get_ApplicationName (&application));
117 long profileMask = 0;
118 ThrowIfErrorHRESULT (pFwRule->get_Profiles (&profileMask));
119 NET_FW_RULE_DIRECTION direction = NET_FW_RULE_DIR_MAX;
120 ThrowIfErrorHRESULT (pFwRule->get_Direction (&direction));
121 LONG protocol = 0;
122 ThrowIfErrorHRESULT (pFwRule->get_Protocol (&protocol));
123 ThrowIfErrorHRESULT (pFwRule->get_LocalPorts (&localPorts));
124 ThrowIfErrorHRESULT (pFwRule->get_RemotePorts (&remotePorts));
125 NET_FW_ACTION action = NET_FW_ACTION_MAX;
126 ThrowIfErrorHRESULT (pFwRule->get_Action (&action));
127 VARIANT_BOOL enabled = VARIANT_FALSE;
128 ThrowIfErrorHRESULT (pFwRule->get_Enabled (&enabled));
129 return Rule{name == nullptr ? wstring{} : wstring{name},
130 desc == nullptr ? wstring{} : wstring{desc},
131 group == nullptr ? wstring{} : wstring{group},
132 application == nullptr ? wstring{} : wstring{application},
133 (NET_FW_PROFILE_TYPE2)(profileMask),
134 direction,
135 (NET_FW_IP_PROTOCOL_)(protocol),
136 localPorts == nullptr ? wstring{} : wstring{localPorts},
137 remotePorts == nullptr ? wstring{} : wstring{remotePorts},
138 action,
139 enabled != VARIANT_FALSE};
140 }
141 optional<Rule> ReadRule_ (INetFwRules* pFwRules, const String& ruleName)
142 {
143 AssertNotNull (pFwRules);
144#if USE_NOISY_TRACE_IN_THIS_MODULE_
145 Debug::TraceContextBumper ctx{"{}ReadRule_"};
146#endif
147 INetFwRule* pFwRule = nullptr;
148 [[maybe_unused]] auto&& cleanupCOMObjects = Execution::Finally ([=] () noexcept {
149 if (pFwRule != nullptr) {
150 pFwRule->Release ();
151 }
152 });
153 HRESULT hr = pFwRules->Item (SmartBSTR{ruleName.As<wstring> ().c_str ()}, &pFwRule);
154 if (hr == S_OK and pFwRule != nullptr) {
155 return ReadRule_ (pFwRule);
156 }
157 return nullopt;
158 }
159}
160#endif
161
163{
164 Debug::TraceContextBumper ctx{"SystemFirewall::Manager::Register", "rule={}"_f, rule};
165
166 for (const auto& r : LookupByGroup (rule.fGroup)) {
167 if (r == rule) {
168 DbgTrace ("run unchanged, so returning false"_f);
169 return false;
170 }
171 }
172
173#if qStroika_Foundation_Common_Platform_Windows
174 COMInitializer comInitializeContext{COINIT_APARTMENTTHREADED};
175 INetFwPolicy2* pNetFwPolicy2 = nullptr;
176 INetFwRules* pFwRules = nullptr;
177 INetFwRule* pFwRule = nullptr;
178 [[maybe_unused]] auto&& cleanupCOMObjects = Execution::Finally ([=] () noexcept {
179 if (pFwRule != nullptr) {
180 pFwRule->Release ();
181 }
182 if (pFwRules != nullptr) {
183 pFwRules->Release ();
184 }
185 if (pNetFwPolicy2 != nullptr) {
186 pNetFwPolicy2->Release ();
187 }
188 });
189
190 // Retrieve INetFwPolicy2
191 ThrowIfErrorHRESULT (::CoCreateInstance (__uuidof (NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER, __uuidof (INetFwPolicy2), (void**)&pNetFwPolicy2));
192
193 // Retrieve INetFwRules
194 ThrowIfErrorHRESULT (pNetFwPolicy2->get_Rules (&pFwRules));
195
196 // Create a new Firewall Rule object.
197 ThrowIfErrorHRESULT (::CoCreateInstance (__uuidof (NetFwRule), NULL, CLSCTX_INPROC_SERVER, __uuidof (INetFwRule), (void**)&pFwRule));
198
199 // Populate the Firewall Rule object
200 ThrowIfErrorHRESULT (pFwRule->put_Name (SmartBSTR{rule.fName.As<wstring> ().c_str ()}));
201 ThrowIfErrorHRESULT (pFwRule->put_Description (SmartBSTR{rule.fDescription.As<wstring> ().c_str ()}));
202 ThrowIfErrorHRESULT (pFwRule->put_ApplicationName (SmartBSTR{rule.fApplication.c_str ()}));
203 ThrowIfErrorHRESULT (pFwRule->put_Protocol (rule.fProtocol));
204 ThrowIfErrorHRESULT (pFwRule->put_LocalPorts (SmartBSTR{rule.fLocalPorts.As<wstring> ().c_str ()}));
205 ThrowIfErrorHRESULT (pFwRule->put_RemotePorts (SmartBSTR{rule.fRemotePorts.As<wstring> ().c_str ()}));
206 ThrowIfErrorHRESULT (pFwRule->put_Direction (rule.fDirection));
207 ThrowIfErrorHRESULT (pFwRule->put_Grouping (SmartBSTR{rule.fGroup.As<wstring> ().c_str ()}));
208 ThrowIfErrorHRESULT (pFwRule->put_Profiles (rule.fProfileMask));
209 ThrowIfErrorHRESULT (pFwRule->put_Action (rule.fAction));
210 ThrowIfErrorHRESULT (pFwRule->put_Enabled (rule.fEnabled ? VARIANT_TRUE : VARIANT_FALSE));
211
212 // Add the Firewall Rule
213 DbgTrace ("Updating firewall rule"_f);
214 ThrowIfErrorHRESULT (pFwRules->Add (pFwRule));
215#endif
216 return true;
217}
218
219optional<Rule> SystemFirewall::Manager::Lookup (const String& ruleName) const
220{
221 Debug::TraceContextBumper ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs ("SystemFirewall::Manager::Lookup", "ruleName={}"_f, ruleName)};
222#if qStroika_Foundation_Common_Platform_Windows
223 COMInitializer comInitializeContext{COINIT_APARTMENTTHREADED};
224 INetFwPolicy2* pNetFwPolicy2 = nullptr;
225 INetFwRules* pFwRules = nullptr;
226 [[maybe_unused]] auto&& cleanupCOMObjects = Execution::Finally ([=] () noexcept {
227 if (pFwRules != nullptr) {
228 pFwRules->Release ();
229 }
230 if (pNetFwPolicy2 != nullptr) {
231 pNetFwPolicy2->Release ();
232 }
233 });
234
235 // Retrieve INetFwPolicy2
236 ThrowIfErrorHRESULT (::CoCreateInstance (__uuidof (NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER, __uuidof (INetFwPolicy2), (void**)&pNetFwPolicy2));
237
238 // Retrieve INetFwRules
239 ThrowIfErrorHRESULT (pNetFwPolicy2->get_Rules (&pFwRules));
240 return ReadRule_ (pFwRules, ruleName);
241#endif
242 return nullopt;
243}
244
245Traversal::Iterable<Rule> SystemFirewall::Manager::LookupByGroup (const String& groupName) const
246{
247 Debug::TraceContextBumper ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs ("SystemFirewall::Manager::LookupByGroup", "groupName={}"_f, groupName)};
248 Collection<Rule> rules;
249 for (const Rule& r : LookupAll ()) {
250 if (r.fGroup == groupName) {
251 rules += r;
252 }
253 }
254 return move (rules);
255}
256
257Traversal::Iterable<Rule> SystemFirewall::Manager::LookupAll () const
258{
259 Debug::TraceContextBumper ctx{"SystemFirewall::Manager::LookupAll"};
260 Collection<Rule> rules;
261#if qStroika_Foundation_Common_Platform_Windows
262 COMInitializer comInitializeContext{COINIT_APARTMENTTHREADED};
263 INetFwPolicy2* pNetFwPolicy2 = nullptr;
264 INetFwRules* pFwRules = nullptr;
265 IEnumVARIANT* pEnum = nullptr;
266 [[maybe_unused]] auto&& cleanupCOMObjects = Execution::Finally ([=] () noexcept {
267 if (pEnum != nullptr) {
268 pEnum->Release ();
269 }
270 if (pFwRules != nullptr) {
271 pFwRules->Release ();
272 }
273 if (pNetFwPolicy2 != nullptr) {
274 pNetFwPolicy2->Release ();
275 }
276 });
277
278 // Retrieve INetFwPolicy2
279 ThrowIfErrorHRESULT (::CoCreateInstance (__uuidof (NetFwPolicy2), nullptr, CLSCTX_INPROC_SERVER, __uuidof (INetFwPolicy2), (void**)&pNetFwPolicy2));
280
281 // Retrieve INetFwRules
282 ThrowIfErrorHRESULT (pNetFwPolicy2->get_Rules (&pFwRules));
283
284 ThrowIfErrorHRESULT (pFwRules->get__NewEnum ((IUnknown**)&pEnum));
285
286 VARIANT nextElt;
287 ULONG nRead = 0;
288 ::VariantInit (&nextElt);
289 for (; SUCCEEDED (pEnum->Next (1, &nextElt, &nRead)) and nRead == 1;) {
290 INetFwRule* r = nullptr;
291 ThrowIfErrorHRESULT (nextElt.punkVal->QueryInterface (&r));
292 rules += ReadRule_ (r);
293 r->Release ();
294 }
295#endif
296 return move (rules);
297}
#define AssertNotNull(p)
Definition Assertions.h:333
#define DbgTrace
Definition Trace.h:309
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Definition Trace.h:270
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
Definition Collection.h:102
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31
STL namespace.