One of the side-projects I recently was assigned to work on was to switch the Windows Media Connect project from using the home-brewed HTTP server that was originally coded for the product, to using HTTP.SYS, which is included in XP SP2. This was as a part of a company-wide initiative to remove all home-brewed HTTP servers (and there were several) and replace them with a single server. The thinking was that having a half dozen HTTP servers in the system was a bad idea, because each of them was a potential attack vector. Now with a single server, we have the ability to roll out fixes in a single common location.
The HTTP.SYS work was fascinating, and I’ll probably write about it more over time, but I wanted to concentrate on a single aspect of the problem.
I got the server working relatively quickly until we picked up a new version of XP SP2. That one featured additional improvements to the firewall, and all of a sudden, the remote devices couldn’t retrieve content from the web server. The requests weren’t getting to our service at all. What was weird was that they WERE getting the content directory (the names of the files on the machine) but when they tried to retrieve them, they failed.
Well, we had suspected that this was going to happen; the new build of SP2 moved HTTP.SYS behind the firewall (it had been in front of the firewall previously). So now we needed to open a hole in the firewall for our process, the UPnP hosting service had already opened their port, that's why the content directory was available. Over the next several posts, I’ll go through the process that I went through do discover how to do this. Everything I needed was documented, but it wasn’t always obvious.
The first thing we had to deal with was the fact that we only wanted to open the firewall on local subnet addresses. To prevent users’ multimedia content from going outside their home, WMC will only accept connections from IP addresses that are in the private network IP address range (192.168.x.x) and the AutoIP address range (169.254.x.x). We also open up the local address ranges of 10.x.x.x and 172.16.x.x (with a netmask of 0xff, 0xf0, 0, 0) . So we only wanted to open the firewall on private IP addresses, it would be a “bad” thing if we opened the WMC port to public addresses, since that could potentially be used as an attack vector.
The Windows firewall has been documented since Windows XP, the first MSDN hit for “internet connection firewall” returns this page that documents the API. For XP SP2, there’s a new firewall API, if you use an MSDN search for “firewall API” the first hit is this page which describes the XP SP2 firewall API in great detail. For a number of reasons (chief among which was that when I wrote the code the firewall API hadn’t been published), my implementation uses the original firewall API that’s existed since Windows XP. As a result, my code and the techniques I’ve described in the next couple of posts work should work just fine on Windows XP as well as working on XP SP2.
Anyway, on with the story. So, as always, I started with the API documentation. After groveling through the API for a while, I realized I was going to need to use the INetSharingConfiguration interface’s AddPortMapping API to add the port. I’d want to use the INetSharingConfiguration API on each of the IP addresses that WMC was using.
So to add a mapping for my port, I simply called INetSharingConfiguration::AddPortMapping specifying a name (in my case I used the URL for the IP address), internal and external port (the same in my case), and a string with the local IP address. That API returned an INetSharingPortMapping object, which we have to Enable to make it effective.
Edit: Clarified IP addresses used for WMC after further investigation.
Edit: Updated link