This happened to us recently and we decided to start out by blocking all traffic
from China and Korea. Nigeria is another candidate but lately it seems their
activity has subsided. I'm sorry, I know there are good people in those countries,
but that's the way it has to be.
There are some very good IP block lists available from reliable sources, but they
are often in what is called CIDR format. For example, the IPv4 block 192.168.0.0/22
represents the 1024 IPv4 addresses from 192.168.0.0 to 192.168.3.255.
You will see a lot of attempts by ASP.NET developers to deny IP addresses in their
web.config or with HttpModules. You don't want to do this. You want to block
at the IIS level, before the request even gets to see the ASP.NET runtime.
As .NET Developers, we have the new System.Web.Administration namespace that allows
us to work with the applicationHost.config file, which is the de facto main IIS "metabase" for IIS7 and above. If you aren't familiar with
this XML configuration file, it is normally found in C:\windows\System32\Inetsrv\config . Whenever IIS starts, it always reads this file.
However, I discovered that with some of these larger block lists such as Korea or
China, this API is incredibly slow. I'm talking literally hours of waiting.
Being an efficiency - minded sort of guy, I thought about how this situation
could be rectified. The answer was pretty simple: write a utility that would
convert the block lists to IIS format "deny" elements, and simply
paste the long list of elements directly into the applicationHost.config file
at the appropriate location. End of problem.
Here is what an IIS deny Element looks like:
<system.webServer>
<security>
<ipSecurity>
<add ipAddress="41.58.0.0" subnetMask="255.255.0.0" allowed="false" />
</ipSecurity>
</security>
</system.webServer>
You would have an <ipSecurity> node like the above for each web site that is
running under your IIS. If you cannot find it, just add an IP address in the
"IP Address and Domain Restrictions" applet in IIS Manager, and you'll
have no problem finding it when you edit applicationHost.config in your favorite
text editor.
So what we need to do, in exact order, is:
1. Load the CIDR format IP block list.
2. Parse each line to get the IP Address and the netmask abbreviation (e.g., "/24"
)
3. Look up the proper netmask in an appropriate lookup table
4. Add the entry to a StringBuilder that holds the created IIS applicationHost.config
elements.
5. Save your "fixed" file for pasting into the config file.
Before I go any farther, I want to make a quick note that represents simple common
sense:
Before you do ANYTHING to your applicationHost.config file, MAKE A BACKUP FIRST!
Some of these IP block lists have comment lines at the beginning and also some have
comments at the end of each line, so you either need to clean up the file before
processing, or write additional parsing code. Since this is something you don't
do very often, I chose to simply clean up the files.
Here is the simple code that I wrote in a Windows Form project to do all this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace NetmaskFixer
{
public partial class Form1 : Form
{
// declare a Dictionary (for the lookup table), a string for the results to write
to file, and the filename
private Dictionary<string, string> dict = new Dictionary<string, string>();
private String results;
private string fileName;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// add the netmask equivalents to our dictionary
dict.Add("32", "255.255.255.255");
dict.Add("31", "255.255.255.254");
dict.Add("30", "255.255.255.252");
dict.Add("29", "255.255.255.248");
dict.Add("28", "255.255.255.240");
dict.Add("27", "255.255.255.224");
dict.Add("26", "255.255.255.192");
dict.Add("25", "255.255.255.128");
dict.Add("24", "255.255.255.0");
dict.Add("23", "255.255.254.0");
dict.Add("22", "255.255.252.0");
dict.Add("21", "255.255.248.0");
dict.Add("20", "255.255.240.0");
dict.Add("19", "255.255.224.0");
dict.Add("18", "255.255.192.0");
dict.Add("17", "255.255.128.0");
dict.Add("16", "255.255.0.0");
dict.Add("15", "255.254.0.0");
dict.Add("14", "255.252.0.0");
dict.Add("13", "255.248.0.0");
dict.Add("12", "255.240.0.0");
dict.Add("11", "255.224.0.0");
dict.Add("10", "255.192.0.0");
dict.Add("9", "255.128.0.0");
dict.Add("8", "255.0.0.0");
dict.Add("7", "254.0.0.0");
dict.Add("6", "252.0.0.0");
dict.Add("5", "248.0.0.0");
dict.Add("4", "240.0.0.0");
dict.Add("3", "224.0.0.0");
dict.Add("2", "192.0.0.0");
dict.Add("1", "128.0.0.0");
}
private void button1_Click(object sender, EventArgs e)
{
DialogResult res = this.openFileDialog1.ShowDialog();
// read in the input file
if (res == DialogResult.OK)
{
string[] lines= System.IO.File.ReadAllLines(openFileDialog1.FileName);
fileName = openFileDialog1.FileName;
foreach (string line in lines)
{
string temp = line.Trim();
// split the line into it's two parts
string[] items = temp.Split("/".ToCharArray());
string ip = items[0];
// lookup the correct netmask
string netmask = dict[items[1]];
//add the item to our string of config elements in the format of the line below
// <add ipAddress="60.191.73.186" subnetMask="255.255.255.255"
allowed="false" />
results += "<add ipAddress=\"" + ip + "\" subnetMask=\"" + netmask + "\"" +
" allowed=\"false\" />\r\n";
}
}
// Let the user know they can save the file.
label1.Text = "Ready to Save.";
}
private void button2_Click(object sender, EventArgs e)
{
// save the file in the same folder as the original with "fixed" at the
end of the filename
fileName = fileName.Substring(0, fileName.IndexOf('.')) + "Fixed.txt";
System.IO.File.WriteAllText(fileName,results);
}
}
}
The above code, which is well commented, should serve to be self-explanatory. And
here's a pic of the cute little UI I put together:

You can download the Visual Studio 2010 solution here.