aboutsummaryrefslogtreecommitdiff
path: root/src/wixext/FirewallCompiler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/wixext/FirewallCompiler.cs')
-rw-r--r--src/wixext/FirewallCompiler.cs356
1 files changed, 356 insertions, 0 deletions
diff --git a/src/wixext/FirewallCompiler.cs b/src/wixext/FirewallCompiler.cs
new file mode 100644
index 00000000..0696b4b1
--- /dev/null
+++ b/src/wixext/FirewallCompiler.cs
@@ -0,0 +1,356 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Firewall
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.Xml.Linq;
9 using WixToolset.Data;
10 using WixToolset.Extensibility;
11
12 /// <summary>
13 /// The compiler for the WiX Toolset Firewall Extension.
14 /// </summary>
15 public sealed class FirewallCompiler : BaseCompilerExtension
16 {
17 public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/firewall";
18
19 /// <summary>
20 /// Processes an element for the Compiler.
21 /// </summary>
22 /// <param name="sourceLineNumbers">Source line number for the parent element.</param>
23 /// <param name="parentElement">Parent element of element to process.</param>
24 /// <param name="element">Element to process.</param>
25 /// <param name="contextValues">Extra information about the context in which this element is being parsed.</param>
26 public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context)
27 {
28 switch (parentElement.Name.LocalName)
29 {
30 case "File":
31 string fileId = context["FileId"];
32 string fileComponentId = context["ComponentId"];
33
34 switch (element.Name.LocalName)
35 {
36 case "FirewallException":
37 this.ParseFirewallExceptionElement(intermediate, section, element, fileComponentId, fileId);
38 break;
39 default:
40 this.ParseHelper.UnexpectedElement(parentElement, element);
41 break;
42 }
43 break;
44 case "Component":
45 string componentId = context["ComponentId"];
46
47 switch (element.Name.LocalName)
48 {
49 case "FirewallException":
50 this.ParseFirewallExceptionElement(intermediate, section, element, componentId, null);
51 break;
52 default:
53 this.ParseHelper.UnexpectedElement(parentElement, element);
54 break;
55 }
56 break;
57 default:
58 this.ParseHelper.UnexpectedElement(parentElement, element);
59 break;
60 }
61 }
62
63 /// <summary>
64 /// Parses a FirewallException element.
65 /// </summary>
66 /// <param name="element">The element to parse.</param>
67 /// <param name="componentId">Identifier of the component that owns this firewall exception.</param>
68 /// <param name="fileId">The file identifier of the parent element (null if nested under Component).</param>
69 private void ParseFirewallExceptionElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string fileId)
70 {
71 SourceLineNumber sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
72 Identifier id = null;
73 string name = null;
74 int attributes = 0;
75 string file = null;
76 string program = null;
77 string port = null;
78 string protocolValue = null;
79 int? protocol = null;
80 string profileValue = null;
81 int? profile = null;
82 string scope = null;
83 string remoteAddresses = null;
84 string description = null;
85
86 foreach (XAttribute attrib in element.Attributes())
87 {
88 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
89 {
90 switch (attrib.Name.LocalName)
91 {
92 case "Id":
93 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
94 break;
95 case "Name":
96 name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
97 break;
98 case "File":
99 if (null != fileId)
100 {
101 this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, "File", "File"));
102 }
103 else
104 {
105 file = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
106 }
107 break;
108 case "IgnoreFailure":
109 if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib))
110 {
111 attributes |= 0x1; // feaIgnoreFailures
112 }
113 break;
114 case "Program":
115 if (null != fileId)
116 {
117 this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, "Program", "File"));
118 }
119 else
120 {
121 program = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
122 }
123 break;
124 case "Port":
125 port = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
126 break;
127 case "Protocol":
128 protocolValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
129 switch (protocolValue)
130 {
131 case "tcp":
132 protocol = FirewallConstants.NET_FW_IP_PROTOCOL_TCP;
133 break;
134 case "udp":
135 protocol = FirewallConstants.NET_FW_IP_PROTOCOL_UDP;
136 break;
137 default:
138 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Protocol", protocolValue, "tcp", "udp"));
139 break;
140 }
141 break;
142 case "Scope":
143 scope = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
144 switch (scope)
145 {
146 case "any":
147 remoteAddresses = "*";
148 break;
149 case "localSubnet":
150 remoteAddresses = "LocalSubnet";
151 break;
152 default:
153 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Scope", scope, "any", "localSubnet"));
154 break;
155 }
156 break;
157 case "Profile":
158 profileValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
159 switch (profileValue)
160 {
161 case "domain":
162 profile = FirewallConstants.NET_FW_PROFILE2_DOMAIN;
163 break;
164 case "private":
165 profile = FirewallConstants.NET_FW_PROFILE2_PRIVATE;
166 break;
167 case "public":
168 profile = FirewallConstants.NET_FW_PROFILE2_PUBLIC;
169 break;
170 case "all":
171 profile = FirewallConstants.NET_FW_PROFILE2_ALL;
172 break;
173 default:
174 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Profile", profileValue, "domain", "private", "public", "all"));
175 break;
176 }
177 break;
178 case "Description":
179 description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
180 break;
181 default:
182 this.ParseHelper.UnexpectedAttribute(element, attrib);
183 break;
184 }
185 }
186 else
187 {
188 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
189 }
190 }
191
192 // parse RemoteAddress children
193 foreach (XElement child in element.Elements())
194 {
195 if (this.Namespace == child.Name.Namespace)
196 {
197 SourceLineNumber childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child);
198 switch (child.Name.LocalName)
199 {
200 case "RemoteAddress":
201 if (null != scope)
202 {
203 this.Messaging.Write(FirewallErrors.IllegalRemoteAddressWithScopeAttribute(sourceLineNumbers));
204 }
205 else
206 {
207 this.ParseRemoteAddressElement(intermediate, section, child, ref remoteAddresses);
208 }
209 break;
210 default:
211 this.ParseHelper.UnexpectedElement(element, child);
212 break;
213 }
214 }
215 else
216 {
217 this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child);
218 }
219 }
220
221 // Id and Name are required
222 if (null == id)
223 {
224 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Id"));
225 }
226
227 if (null == name)
228 {
229 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name"));
230 }
231
232 // Scope or child RemoteAddress(es) are required
233 if (null == remoteAddresses)
234 {
235 this.Messaging.Write(ErrorMessages.ExpectedAttributeOrElement(sourceLineNumbers, element.Name.LocalName, "Scope", "RemoteAddress"));
236 }
237
238 // can't have both Program and File
239 if (null != program && null != file)
240 {
241 this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "File", "Program"));
242 }
243
244 // must be nested under File, have File or Program attributes, or have Port attribute
245 if (String.IsNullOrEmpty(fileId) && String.IsNullOrEmpty(file) && String.IsNullOrEmpty(program) && String.IsNullOrEmpty(port))
246 {
247 this.Messaging.Write(FirewallErrors.NoExceptionSpecified(sourceLineNumbers));
248 }
249
250 if (!this.Messaging.EncounteredError)
251 {
252 // at this point, File attribute and File parent element are treated the same
253 if (null != file)
254 {
255 fileId = file;
256 }
257
258 var row = this.ParseHelper.CreateRow(section, sourceLineNumbers, "WixFirewallException", id);
259 row.Set(1, name);
260 row.Set(2, remoteAddresses);
261
262 if (!String.IsNullOrEmpty(port))
263 {
264 row.Set(3, port);
265
266 if (!protocol.HasValue)
267 {
268 // default protocol is "TCP"
269 protocol = FirewallConstants.NET_FW_IP_PROTOCOL_TCP;
270 }
271 }
272
273 if (protocol.HasValue)
274 {
275 row.Set(4, protocol);
276 }
277
278 if (!String.IsNullOrEmpty(fileId))
279 {
280 row.Set(5, $"[#{fileId}]");
281 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "File", fileId);
282 }
283 else if (!String.IsNullOrEmpty(program))
284 {
285 row.Set(5, program);
286 }
287
288 if (CompilerConstants.IntegerNotSet != attributes)
289 {
290 row.Set(6, attributes);
291 }
292
293 // Default is "all"
294 row.Set(7, profile ?? FirewallConstants.NET_FW_PROFILE2_ALL);
295
296 row.Set(8, componentId);
297
298 row.Set(9, description);
299
300 if (this.Context.Platform == Platform.ARM)
301 {
302 // Ensure ARM version of the CA is referenced
303 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "CustomAction", "WixSchedFirewallExceptionsInstall_ARM");
304 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "CustomAction", "WixSchedFirewallExceptionsUninstall_ARM");
305 }
306 else
307 {
308 // All other supported platforms use x86
309 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "CustomAction", "WixSchedFirewallExceptionsInstall");
310 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, "CustomAction", "WixSchedFirewallExceptionsUninstall");
311 }
312 }
313 }
314
315 /// <summary>
316 /// Parses a RemoteAddress element
317 /// </summary>
318 /// <param name="element">The element to parse.</param>
319 private void ParseRemoteAddressElement(Intermediate intermediate, IntermediateSection section, XElement element, ref string remoteAddresses)
320 {
321 SourceLineNumber sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
322
323 // no attributes
324 foreach (XAttribute attrib in element.Attributes())
325 {
326 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
327 {
328 this.ParseHelper.UnexpectedAttribute(element, attrib);
329 }
330 else
331 {
332 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib);
333 }
334 }
335
336 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
337
338 string address = this.ParseHelper.GetTrimmedInnerText(element);
339 if (String.IsNullOrEmpty(address))
340 {
341 this.Messaging.Write(FirewallErrors.IllegalEmptyRemoteAddress(sourceLineNumbers));
342 }
343 else
344 {
345 if (String.IsNullOrEmpty(remoteAddresses))
346 {
347 remoteAddresses = address;
348 }
349 else
350 {
351 remoteAddresses = String.Concat(remoteAddresses, ",", address);
352 }
353 }
354 }
355 }
356}