The Windows Firewall is probably the most
often used external feature because applications often have to define
new ports to use to communicate with the outside world. In addition,
applications need to know the firewall status so they can interact with
Windows and the outside world correctly. With this in mind, the
following sections demonstrate techniques you can use to access and
interact with the Windows Firewall.
NOTE
The techniques used in the following section rely on the dynamic
keyword that appears as part of the .NET Framework 4. These techniques
won't work with older versions of the .NET Framework. However, you can
still access the Windows Firewall using standard COM techniques when
using older versions of the .NET Framework.
1. Interacting with the Firewall
There are a number of ways to interact with the
Windows Firewall. In the past, developers would often use Component
Object Model (COM)-based code. You can see an example of such code at http://blogs.msdn.com/b/joncole/archive/2005/12/06/managed-classes-to-view-manipulate-the-windows-firewall.aspx.
It doesn't take long to figure out that using COM-based code is
time-consuming and error-prone. You have to write a lot of code to
accomplish most tasks with the Windows Firewall using this approach,
but it does admittedly work with older versions of the .NET Framework.
Fortunately, the .NET Framework 4 provides a better approach through the dynamic
keyword. In this case, you can interact with the Windows Firewall much
as you would using VBScript. The technique is simple and relatively
straightforward, despite not looking very much like the standard C#
code you've used in the past. To test this out for yourself, create a
Windows Forms application and add a Check (btnCheck) button to it. You don't need to add any special references or using statements. Listing 1 shows the code you need to create the Access Firewall example.
Example 1. Creating firewall access using the dynamic keyword
private void btnCheck_Click(object sender, EventArgs e) { // Create the firewall type. Type FWManagerType = Type.GetTypeFromProgID("HNetCfg.FwMgr");
// Use the firewall type to create a firewall manager object. dynamic FWManager = Activator.CreateInstance(FWManagerType);
// Check the status of the firewall. MessageBox.Show("The firewall is turned on: " + Convert.ToString( FWManager.LocalPolicy.CurrentProfile.FirewallEnabled)); }
|
As previously mentioned, this code really does look
like something you'd create using VBScript, rather than C#, but it
works extremely well. The code begins by creating a new Type, FWManagerType, defined using the Type.GetTypeFromProgID() constructor with "HNetCfg.FwMgr" as the object to create.
Now that the code has a Type to use, it can create an instance of the object defined by that Type using the Activator.CreateInstance() constructor. Just in case you've never seen the Activator class before, you can read more about it at http://msdn.microsoft.com/library/system.activator.aspx. At this point, the example has access to the firewall manager using the FWManager object that's described merely as type dynamic.
The code displays a simple on/off indicator in this
case for the Windows Firewall using a message box. Of course, you're
wondering where to obtain the list of objects to access the on/off
state of the Windows Firewall. One such place is at http://technet.microsoft.com/library/cc737845.aspx.
However, you'll find a wealth of VBScript examples on the Internet that
will give you additional information that you can apply directly to
your C# application with a little translation. You can see a few of
these VBScript examples at http://msdn.microsoft.com/library/aa366415.aspx.
2. Verifying the Firewall Status
The Windows Firewall has a lot of settings, many of
which are useful for applications to know. The Firewall Status example
provides a representative example of the available settings, but it
doesn't show all of them. The example begins with a Windows Forms
application. You need to add a Status button (btnStatus) and a list box (lstOutput). The example doesn't require any special references or using statements. However, you do need to add the constants shown in Listing 1.
These constants are used within the application to show the state of
items such as the IP version. The constant names and values come
directly from the Windows SDK.
Example 1. Defining the Firewall Status example constants
// Define Constants from the SDK and their associated string name // Scope const Int32 NET_FW_SCOPE_ALL = 0; const String NET_FW_SCOPE_ALL_NAME = "All subnets"; const Int32 NET_FW_SCOPE_LOCAL_SUBNET = 1; const String NET_FW_SCOPE_LOCAL_SUBNET_NAME = "Local subnet only"; const Int32 NET_FW_SCOPE_CUSTOM = 2; const String NET_FW_SCOPE_CUSTOM_NAME = "Custom Scope (see RemoteAddresses)";
// Profile Type const Int32 NET_FW_PROFILE_DOMAIN = 0; const String NET_FW_PROFILE_DOMAIN_NAME = "Domain"; const Int32 NET_FW_PROFILE_STANDARD = 1; const String NET_FW_PROFILE_STANDARD_NAME = "Standard";
// IP Version const Int32 NET_FW_IP_VERSION_V4 = 0; const String NET_FW_IP_VERSION_V4_NAME = "IPv4"; const Int32 NET_FW_IP_VERSION_V6 = 1; const String NET_FW_IP_VERSION_V6_NAME = "IPv6"; const Int32 NET_FW_IP_VERSION_ANY = 2; const String NET_FW_IP_VERSION_ANY_NAME = "ANY";
// Protocol const Int32 NET_FW_IP_PROTOCOL_TCP = 6; const String NET_FW_IP_PROTOCOL_TCP_NAME = "TCP"; const Int32 NET_FW_IP_PROTOCOL_UDP = 17; const String NET_FW_IP_PROTOCOL_UDP_NAME = "UDP";
|
The use of each of these constants will become
clearer as you work through the example code. The example shows many of
the statistics for the Windows Firewall, but it leaves out a few items
such as globally open ports and a list of services. Listing 2 shows the code used for this example.
Example 2. Obtaining the Windows Firewall status information
private void btnStatus_Click(object sender, EventArgs e) { // Clear the old settings. lstOutput.Items.Clear();
// Create the firewall type. Type FWManagerType = Type.GetTypeFromProgID("HNetCfg.FwMgr");
// Use the firewall type to create a firewall manager object. dynamic FWManager = Activator.CreateInstance(FWManagerType);
// Obtain the firewall profile information.
dynamic FWProfile = FWManager.LocalPolicy.CurrentProfile;
// Output the type of profile on the local machine. switch ((Int32)FWProfile.Type) { case NET_FW_PROFILE_DOMAIN: lstOutput.Items.Add("Profile Type: " + NET_FW_PROFILE_DOMAIN_NAME); break; case NET_FW_PROFILE_STANDARD: lstOutput.Items.Add("Profile Type: " + NET_FW_PROFILE_STANDARD_NAME); break; }
// Check the status of the firewall. lstOutput.Items.Add("Firewall Enabled: " + Convert.ToString(FWProfile.FirewallEnabled));
// Determine whether exceptions are allowed. lstOutput.Items.Add("Exceptions Not Allowed: " + Convert.ToString(FWProfile.ExceptionsNotAllowed));
// Determine whether notifications are disabled. lstOutput.Items.Add("Notifications Disabled: " + Convert.ToString(FWProfile.NotificationsDisabled));
// Verify whether unicast responses to multicast // broadcasts are disabled. lstOutput.Items.Add("Unicast Responses to Multicast " + "Broadcasts Disabled: " + Convert.ToString( FWProfile.UnicastResponsestoMulticastBroadcastDisabled));
// Print the Remote Administration settings. lstOutput.Items.Add(""); lstOutput.Items.Add("Remote Administration:"); dynamic RASettings = FWProfile.RemoteAdminSettings;
// Show whether Remote Administration is enable. lstOutput.Items.Add("\tRemote Administration Enabled: " + Convert.ToString(RASettings.Enabled));
// Get the Remote Adminstration IP version. switch ((Int32)RASettings.IpVersion) { case NET_FW_IP_VERSION_V4: lstOutput.Items.Add("\tIP Version: " + NET_FW_IP_VERSION_V4_NAME); break; case NET_FW_IP_VERSION_V6: lstOutput.Items.Add("\tIP Version: " + NET_FW_IP_VERSION_V6_NAME); break; case NET_FW_IP_VERSION_ANY:
lstOutput.Items.Add("\tIP Version: " + NET_FW_IP_VERSION_ANY_NAME); break; }
// Obtain the Remote Administration scope. switch ((Int32)RASettings.Scope) { case NET_FW_SCOPE_ALL: lstOutput.Items.Add("\tScope: " + NET_FW_SCOPE_ALL_NAME); break; case NET_FW_SCOPE_CUSTOM: lstOutput.Items.Add("\tScope: " + NET_FW_SCOPE_CUSTOM_NAME); break; case NET_FW_SCOPE_LOCAL_SUBNET: lstOutput.Items.Add("\tScope: " + NET_FW_SCOPE_LOCAL_SUBNET_NAME); break; }
// Display the Remote Administration addresses. lstOutput.Items.Add("\tRemote Administration Addresses: " + RASettings.RemoteAddresses);
// Print the ICMP settings. lstOutput.Items.Add(""); lstOutput.Items.Add("ICMP Settings:"); dynamic ICMPSettings = FWProfile.IcmpSettings;
// Obtain the ICMP settings. lstOutput.Items.Add("\tAllowOutboundDestinationUnreachable: " + Convert.ToString(ICMPSettings.AllowOutboundDestinationUnreachable)); lstOutput.Items.Add("\tAllowOutboundSourceQuench: " + Convert.ToString(ICMPSettings.AllowOutboundSourceQuench)); lstOutput.Items.Add("\tAllowRedirect: " + Convert.ToString(ICMPSettings.AllowRedirect)); lstOutput.Items.Add("\tAllowInboundEchoRequest: " + Convert.ToString(ICMPSettings.AllowInboundEchoRequest)); lstOutput.Items.Add("\tAllowInboundRouterRequest: " + Convert.ToString(ICMPSettings.AllowInboundRouterRequest)); lstOutput.Items.Add("\tAllowOutboundTimeExceeded: " + Convert.ToString(ICMPSettings.AllowOutboundTimeExceeded)); lstOutput.Items.Add("\tAllowOutboundParameterProblem: " + Convert.ToString(ICMPSettings.AllowOutboundParameterProblem)); lstOutput.Items.Add("\tAllowInboundTimestampRequest: " + Convert.ToString(ICMPSettings.AllowInboundTimestampRequest)); lstOutput.Items.Add("\tAllowInboundMaskRequest: " + Convert.ToString(ICMPSettings.AllowInboundMaskRequest));
// Display the authorized applications.
lstOutput.Items.Add(""); lstOutput.Items.Add("Authorized Applications:");
// Obtain each port in turn and display its characteristics. foreach (dynamic Application in FWProfile.AuthorizedApplications) { // Display the port name. lstOutput.Items.Add("\tName: " + Application.Name);
// Display the port number. lstOutput.Items.Add("\tImage Filename: " + Application.ProcessImageFileName);
// Display the IP version for the application. switch ((Int32)Application.IpVersion) { case NET_FW_IP_VERSION_V4: lstOutput.Items.Add("\tIP Version: " + NET_FW_IP_VERSION_V4_NAME); break; case NET_FW_IP_VERSION_V6: lstOutput.Items.Add("\tIP Version: " + NET_FW_IP_VERSION_V6_NAME); break; case NET_FW_IP_VERSION_ANY: lstOutput.Items.Add("\tIP Version: " + NET_FW_IP_VERSION_ANY_NAME); break; }
// Display the scope for the application. switch ((Int32)Application.Scope) { case NET_FW_SCOPE_ALL: lstOutput.Items.Add("\tScope: " + NET_FW_SCOPE_ALL_NAME); break; case NET_FW_SCOPE_CUSTOM: lstOutput.Items.Add("\tScope: " + NET_FW_SCOPE_CUSTOM_NAME); break; case NET_FW_SCOPE_LOCAL_SUBNET: lstOutput.Items.Add("\tScope: " + NET_FW_SCOPE_LOCAL_SUBNET_NAME); break; }
// Show the application's enabled state. lstOutput.Items.Add("\tEnabled: " + Convert.ToString(Application.Enabled));
// Get the remote addresses for the application. lstOutput.Items.Add("\tRemote Addresses: " +
Application.RemoteAddresses);
// Add a space. lstOutput.Items.Add(""); } }
|
The example begins by creating a HNetCfg.FwMgr type definition, FWManagerType. It uses this Type to create an instance of the Windows Firewall Manager, FWManager, using Activator.CreateInstance(). At this point, the code creates a second dynamic variable, FWProfile, which contains the FWManager.LocalPolicy.CurrentProfile property values. You want to create this second dynamic
variable to make it easier to interact with the Windows Firewall. In
addition, you can now easily see the property values that the Windows
Firewall supports using the debugger, as shown in Figure 1.
In fact, you can use the debugger to expand the various property values
to see what they contain — making it far easier to write your code.
There are a number of ways to determine the profile type for the machine, but using a switch
is as convenient as any other method. When performing a comparison, you
must coerce the data because the compiler won't know the type of the
data until run time. In this case, FWProfile.Type is an Int32 value.
Once the code lists the common profile settings, it
begins work on the Remote Administration settings. These settings
control whether an administrator can manage the firewall from a remote
system, such as the administrator's machine. The code begins by
obtaining the Remote Administration settings from the FWProfile.RemoteAdminSettings property using another dynamic variable, RASettings. There isn't any limit to the number of levels you can access using this technique. Debugging is easier when you use dynamic
variables for each level so that you can easily see how each level is
constructed. The Remote Administration settings require use of several switch structures that you create using the same approach as the profile type.
The Internet Control Message Protocol (ICMP) settings come next. In this case, all you see is a series of Boolean or String values that you access directly. The FWProfile.IcmpSettings provides full access to all the ICMP settings shown in the example.
The FWProfile object contains a number of
collections that you process just as you would any managed collection.
The code shows how to work with the FWProfile.AuthorizedApplications collection. In this case, each Application object is a dynamic variable that tells you a number of things about each application that's registered with the Windows Firewall, including:
Name
Executable filename and location
IP version of the entry
Scope of the entry
Application's firewall entry enabled state
Remote addresses the application has requested to access
The profile provides access to other kinds of
information. In fact, you can discover anything you need to learn about
the Windows Firewall using the techniques shown in this example. Figure 2 shows typical output from this example.