add firewall extension decompiler
chris_bednarski committed
Aug 26, 2023 at 08:31 UTC
b3deb46def7d303a79f4a5eba6a921e45d5f16ef
5 files changed
+164
-81
src/ext/Firewall/test/WixToolsetTest.Firewall/FirewallExtensionFixture.cs
+58
-1
@@ -2,9 +2,11 @@
2
3
namespace WixToolsetTest.Firewall
4
{
5
+ using System.IO;
6
using System.Linq;
6
- using WixInternal.TestSupport;
7
+ using System.Xml.Linq;
8
using WixInternal.Core.TestPackage;
9
+ using WixInternal.TestSupport;
10
using WixToolset.Firewall;
11
using Xunit;
12
@@ -50,6 +52,55 @@ namespace WixToolsetTest.Firewall
52
}, results);
53
}
54
55
+ [Fact]
56
+ public void CanRoundtripFirewallExceptions()
57
+ {
58
+ var folder = TestData.Get(@"TestData", "UsingFirewall");
59
+ var build = new Builder(folder, typeof(FirewallExtensionFactory), new[] { folder });
60
+ var output = Path.Combine(folder, "FirewallExceptionDecompile.xml");
61
+
62
+ build.BuildAndDecompileAndBuild(Build, Decompile, output);
63
+
64
+ var doc = XDocument.Load(output);
65
+ var actual = doc.Descendants()
66
+ .Where(e => e.Name.Namespace == "http://wixtoolset.org/schemas/v4/wxs/firewall")
67
+ .Select(fe => new { Name = fe.Name.LocalName, Attributes = fe.Attributes().Select(a => $"{a.Name.LocalName}={a.Value}").ToArray() })
68
+ .ToArray();
69
+
70
+ WixAssert.CompareLineByLine(new[]
71
+ {
72
+ "FirewallException",
73
+ "FirewallException",
74
+ }, actual.Select(a => a.Name).ToArray());
75
+
76
+ WixAssert.CompareLineByLine(new[]
77
+ {
78
+ "Id=ExampleFirewall",
79
+ "Name=ExampleApp",
80
+ "Scope=any",
81
+ "Port=42",
82
+ "Protocol=tcp",
83
+ "Program=[#filNdJBJmq3UCUIwmXS8x21aAsvqzk]",
84
+ "Profile=all",
85
+ "Description=An app-based firewall exception",
86
+ "Outbound=no",
87
+ "xmlns=http://wixtoolset.org/schemas/v4/wxs/firewall",
88
+ }, actual[0].Attributes);
89
+
90
+ WixAssert.CompareLineByLine(new[]
91
+ {
92
+ "Id=fex70IVsYNnbwiHQrEepmdTPKH8XYs",
93
+ "Name=ExamplePort",
94
+ "Scope=localSubnet",
95
+ "Port=42",
96
+ "Protocol=tcp",
97
+ "Profile=all",
98
+ "Description=A port-based firewall exception",
99
+ "Outbound=yes",
100
+ "xmlns=http://wixtoolset.org/schemas/v4/wxs/firewall",
101
+ }, actual[1].Attributes);
102
+ }
103
+
104
private static void Build(string[] args)
105
{
106
var result = WixRunner.Execute(args);
@@ -65,5 +116,11 @@ namespace WixToolsetTest.Firewall
116
var result = WixRunner.Execute(newArgs.ToArray());
117
result.AssertSuccess();
118
}
119
+
120
+ private static void Decompile(string[] args)
121
+ {
122
+ var result = WixRunner.Execute(args);
123
+ result.AssertSuccess();
124
+ }
125
}
126
}
src/ext/Firewall/wixext/FirewallCompiler.cs
+1
-1
@@ -15,7 +15,7 @@ namespace WixToolset.Firewall
15
/// </summary>
16
public sealed class FirewallCompiler : BaseCompilerExtension
17
{
18
- public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/firewall";
18
+ public override XNamespace Namespace => FirewallConstants.Namespace;
19
20
/// <summary>
21
/// Processes an element for the Compiler.
src/ext/Firewall/wixext/FirewallConstants.cs
+5
-3
@@ -2,12 +2,14 @@
2
3
namespace WixToolset.Firewall
4
{
5
- using System;
6
- using System.Collections.Generic;
7
- using System.Text;
5
+ using System.Xml.Linq;
6
7
static class FirewallConstants
8
{
9
+ internal static readonly XNamespace Namespace = "http://wixtoolset.org/schemas/v4/wxs/firewall";
10
+ internal static readonly XName FirewallExceptionName = Namespace + "FirewallException";
11
+ internal static readonly XName RemoteAddressName = Namespace + "RemoteAddress";
12
+
13
// from icftypes.h
14
public const int NET_FW_RULE_DIR_IN = 1;
15
public const int NET_FW_RULE_DIR_OUT = 2;
src/ext/Firewall/wixext/FirewallDecompiler.cs
+98
-75
@@ -2,54 +2,53 @@
2
3
namespace WixToolset.Firewall
4
{
5
-#if TODO_CONSIDER_DECOMPILER
5
using System;
7
- using System.Collections;
8
- using System.Diagnostics;
9
- using System.Globalization;
6
+ using System.Collections.Generic;
7
+ using System.Xml.Linq;
8
using WixToolset.Data;
9
+ using WixToolset.Data.WindowsInstaller;
10
using WixToolset.Extensibility;
12
- using Firewall = WixToolset.Extensions.Serialize.Firewall;
13
- using Wix = WixToolset.Data.Serialize;
11
12
/// <summary>
13
/// The decompiler for the WiX Toolset Firewall Extension.
14
/// </summary>
18
- public sealed class FirewallDecompiler : DecompilerExtension
15
+ public sealed class FirewallDecompiler : BaseWindowsInstallerDecompilerExtension
16
{
20
- /// <summary>
21
- /// Creates a decompiler for Firewall Extension.
22
- /// </summary>
23
- public FirewallDecompiler()
24
- {
25
- this.TableDefinitions = FirewallExtensionData.GetExtensionTableDefinitions();
26
- }
17
+ public override IReadOnlyCollection<TableDefinition> TableDefinitions => FirewallTableDefinitions.All;
18
19
/// <summary>
29
- /// Get the extensions library to be removed.
20
+ /// Called at the beginning of the decompilation of a database.
21
/// </summary>
31
- /// <param name="tableDefinitions">Table definitions for library.</param>
32
- /// <returns>Library to remove from decompiled output.</returns>
33
- public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions)
22
+ /// <param name="tables">The collection of all tables.</param>
23
+ public override void PreDecompileTables(TableIndexedCollection tables)
24
{
35
- return FirewallExtensionData.GetExtensionLibrary(tableDefinitions);
25
}
26
27
/// <summary>
28
/// Decompiles an extension table.
29
/// </summary>
30
/// <param name="table">The table to decompile.</param>
42
- public override void DecompileTable(Table table)
31
+ public override bool TryDecompileTable(Table table)
32
{
33
switch (table.Name)
34
{
46
- case "WixFirewallException":
35
+ case "Wix4FirewallException":
36
this.DecompileWixFirewallExceptionTable(table);
37
break;
38
default:
50
- base.DecompileTable(table);
51
- break;
39
+ return false;
40
}
41
+
42
+ return true;
43
+ }
44
+
45
+ /// <summary>
46
+ /// Finalize decompilation.
47
+ /// </summary>
48
+ /// <param name="tables">The collection of all tables.</param>
49
+ public override void PostDecompileTables(TableIndexedCollection tables)
50
+ {
51
+ this.FinalizeFirewallExceptionTable(tables);
52
}
53
54
/// <summary>
@@ -60,38 +59,42 @@ namespace WixToolset.Firewall
59
{
60
foreach (Row row in table.Rows)
61
{
63
- Firewall.FirewallException fire = new Firewall.FirewallException();
64
- fire.Id = (string)row[0];
65
- fire.Name = (string)row[1];
62
+ var firewallException = new XElement(FirewallConstants.FirewallExceptionName,
63
+ new XAttribute("Id", row.FieldAsString(0)),
64
+ new XAttribute("Name", row.FieldAsString(1))
65
+ );
66
67
- string[] addresses = ((string)row[2]).Split(',');
68
- if (1 == addresses.Length)
67
+ if (!row.IsColumnEmpty(2))
68
{
70
- // special-case the Scope attribute values
71
- if ("*" == addresses[0])
69
+ string[] addresses = ((string)row[2]).Split(',');
70
+ if (addresses.Length == 1)
71
{
73
- fire.Scope = Firewall.FirewallException.ScopeType.any;
74
- }
75
- else if ("LocalSubnet" == addresses[0])
76
- {
77
- fire.Scope = Firewall.FirewallException.ScopeType.localSubnet;
72
+ // special-case the Scope attribute values
73
+ if (addresses[0] == "*")
74
+ {
75
+ firewallException.Add(new XAttribute("Scope", "any"));
76
+ }
77
+ else if (addresses[0] == "LocalSubnet")
78
+ {
79
+ firewallException.Add(new XAttribute("Scope", "localSubnet"));
80
+ }
81
+ else
82
+ {
83
+ FirewallDecompiler.AddRemoteAddress(firewallException, addresses[0]);
84
+ }
85
}
86
else
87
{
81
- FirewallDecompiler.AddRemoteAddress(fire, addresses[0]);
82
- }
83
- }
84
- else
85
- {
86
- foreach (string address in addresses)
87
- {
88
- FirewallDecompiler.AddRemoteAddress(fire, address);
88
+ foreach (string address in addresses)
89
+ {
90
+ FirewallDecompiler.AddRemoteAddress(firewallException, address);
91
+ }
92
}
93
}
94
95
if (!row.IsColumnEmpty(3))
96
{
94
- fire.Port = (string)row[3];
97
+ firewallException.Add(new XAttribute("Port", row.FieldAsString(3)));
98
}
99
100
if (!row.IsColumnEmpty(4))
@@ -99,26 +102,23 @@ namespace WixToolset.Firewall
102
switch (Convert.ToInt32(row[4]))
103
{
104
case FirewallConstants.NET_FW_IP_PROTOCOL_TCP:
102
- fire.Protocol = Firewall.FirewallException.ProtocolType.tcp;
105
+ firewallException.Add(new XAttribute("Protocol", "tcp"));
106
break;
107
case FirewallConstants.NET_FW_IP_PROTOCOL_UDP:
105
- fire.Protocol = Firewall.FirewallException.ProtocolType.udp;
108
+ firewallException.Add(new XAttribute("Protocol", "udp"));
109
break;
110
}
111
}
112
113
if (!row.IsColumnEmpty(5))
114
{
112
- fire.Program = (string)row[5];
115
+ firewallException.Add(new XAttribute("Program", row.FieldAsString(5)));
116
}
117
118
if (!row.IsColumnEmpty(6))
119
{
117
- int attr = Convert.ToInt32(row[6]);
118
- if (0x1 == (attr & 0x1)) // feaIgnoreFailures
119
- {
120
- fire.IgnoreFailure = Firewall.YesNoType.yes;
121
- }
120
+ var attr = Convert.ToInt32(row[6]);
121
+ AttributeIfNotNull("IgnoreFailure", (attr & 0x1) == 0x1);
122
}
123
124
if (!row.IsColumnEmpty(7))
@@ -126,24 +126,23 @@ namespace WixToolset.Firewall
126
switch (Convert.ToInt32(row[7]))
127
{
128
case FirewallConstants.NET_FW_PROFILE2_DOMAIN:
129
- fire.Profile = Firewall.FirewallException.ProfileType.domain;
129
+ firewallException.Add(new XAttribute("Profile", "domain"));
130
break;
131
case FirewallConstants.NET_FW_PROFILE2_PRIVATE:
132
- fire.Profile = Firewall.FirewallException.ProfileType.@private;
132
+ firewallException.Add(new XAttribute("Profile", "private"));
133
break;
134
case FirewallConstants.NET_FW_PROFILE2_PUBLIC:
135
- fire.Profile = Firewall.FirewallException.ProfileType.@public;
135
+ firewallException.Add(new XAttribute("Profile", "public"));
136
break;
137
case FirewallConstants.NET_FW_PROFILE2_ALL:
138
- fire.Profile = Firewall.FirewallException.ProfileType.all;
138
+ firewallException.Add(new XAttribute("Profile", "all"));
139
break;
140
}
141
}
142
143
- // Description column is new in v3.6
144
- if (9 < row.Fields.Length && !row.IsColumnEmpty(9))
143
+ if (!row.IsColumnEmpty(9))
144
{
146
- fire.Description = (string)row[9];
145
+ firewallException.Add(new XAttribute("Description", row.FieldAsString(9)));
146
}
147
148
if (!row.IsColumnEmpty(10))
@@ -151,32 +150,56 @@ namespace WixToolset.Firewall
150
switch (Convert.ToInt32(row[10]))
151
{
152
case FirewallConstants.NET_FW_RULE_DIR_IN:
154
- fire.Direction = Firewall.FirewallException.DirectionType.@in;
153
+
154
+ firewallException.Add(AttributeIfNotNull("Outbound", false));
155
break;
156
case FirewallConstants.NET_FW_RULE_DIR_OUT:
157
- fire.Direction = Firewall.FirewallException.DirectionType.@out;
157
+ firewallException.Add(AttributeIfNotNull("Outbound", true));
158
break;
159
}
160
}
161
162
- Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[8]);
163
- if (null != component)
164
- {
165
- component.AddChild(fire);
166
- }
167
- else
168
- {
169
- this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[6], "Component"));
170
- }
162
+ this.DecompilerHelper.IndexElement(row, firewallException);
163
}
164
}
165
174
- private static void AddRemoteAddress(Firewall.FirewallException fire, string address)
166
+ private static void AddRemoteAddress(XElement firewallException, string address)
167
+ {
168
+ var remoteAddress = new XElement(FirewallConstants.RemoteAddressName,
169
+ new XAttribute("Value", address)
170
+ );
171
+
172
+ firewallException.AddAfterSelf(remoteAddress);
173
+ }
174
+
175
+ private static XAttribute AttributeIfNotNull(string name, bool value)
176
+ {
177
+ return new XAttribute(name, value ? "yes" : "no");
178
+ }
179
+
180
+ /// <summary>
181
+ /// Finalize the FirewallException table.
182
+ /// </summary>
183
+ /// <param name="tables">Collection of all tables.</param>
184
+ private void FinalizeFirewallExceptionTable(TableIndexedCollection tables)
185
{
176
- Firewall.RemoteAddress remote = new Firewall.RemoteAddress();
177
- remote.Content = address;
178
- fire.AddChild(remote);
186
+ if (tables.TryGetTable("Wix4FirewallException", out var firewallExceptionTable))
187
+ {
188
+ foreach (var row in firewallExceptionTable.Rows)
189
+ {
190
+ var xmlConfig = this.DecompilerHelper.GetIndexedElement(row);
191
+
192
+ var componentId = row.FieldAsString(8);
193
+ if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
194
+ {
195
+ component.Add(xmlConfig);
196
+ }
197
+ else
198
+ {
199
+ this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, firewallExceptionTable.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
200
+ }
201
+ }
202
+ }
203
}
204
}
181
-#endif
205
}
src/ext/Firewall/wixext/FirewallExtensionFactory.cs
+2
-1
@@ -1,4 +1,4 @@
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.
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
3
namespace WixToolset.Firewall
4
{
@@ -13,6 +13,7 @@ namespace WixToolset.Firewall
13
typeof(FirewallCompiler),
14
typeof(FirewallExtensionData),
15
typeof(FirewallWindowsInstallerBackendBinderExtension),
16
+ typeof(FirewallDecompiler),
17
};
18
}
19
}