Attempting to retrieve a user's real IP address

Lately, I was in need of retrieving user's IP address. The first place I went looking was REMOTE_ADDR server variable. Unfortunately, due to all proxy servers out there, this variable can be pretty far from user's real IP address. After some googling I have put together a list of HTTP headers that might contain the real IP address:
  • CLIENT-IP
  • X-FORWARDED-FOR
  • X-FORWARDED
  • X-CLUSTER-CLIENT-IP
  • FORWARDED-FOR
  • FORWARDED
It's important to know, that X-FORWARDED-FOR may contain a comma+space separated list of IP addresses.
Having that list in mind, we can write a simple method which will attempt to retrieve user's real IP address:
public string DetermineIP(HttpContext context)
{
if (context.Request.ServerVariables.AllKeys.Contains("HTTP_CLIENT_IP") && CheckIP(context.Request.ServerVariables["HTTP_CLIENT_IP"]))
return context.Request.ServerVariables["HTTP_CLIENT_IP"];

if (context.Request.ServerVariables.AllKeys.Contains("HTTP_X_FORWARDED_FOR"))
foreach (string ip in context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].Split(','))
if (CheckIP(ip.Trim()))
return ip.Trim();

if (context.Request.ServerVariables.AllKeys.Contains("HTTP_X_FORWARDED") && CheckIP(context.Request.ServerVariables["HTTP_X_FORWARDED"]))
return context.Request.ServerVariables["HTTP_X_FORWARDED"];

if (context.Request.ServerVariables.AllKeys.Contains("HTTP_X_CLUSTER_CLIENT_IP") && CheckIP(context.Request.ServerVariables["HTTP_X_CLUSTER_CLIENT_IP"]))
return context.Request.ServerVariables["HTTP_X_CLUSTER_CLIENT_IP"];

if (context.Request.ServerVariables.AllKeys.Contains("HTTP_FORWARDED_FOR") && CheckIP(context.Request.ServerVariables["HTTP_FORWARDED_FOR"]))
return context.Request.ServerVariables["HTTP_FORWARDED_FOR"];

if (context.Request.ServerVariables.AllKeys.Contains("HTTP_FORWARDED") && CheckIP(context.Request.ServerVariables["HTTP_FORWARDED"]))
return context.Request.ServerVariables["HTTP_FORWARDED"];

return context.Request.ServerVariables["REMOTE_ADDR"];
}

So how about this CheckIP method? The purpose of this method is to ensure that IP address is both a valid IP and does not fall within a private network range:
private bool CheckIP(string ip)
{
if (!String.IsNullOrEmpty(ip))
{
long ipToLong = -1;
//Is it valid IP address
if (TryConvertIPToLong(ip, out ipToLong))
{
//Does it fall within a private network range
foreach (long[] privateIp in _privateIps)
if ((ipToLong >= privateIp[0]) && (ipToLong <= privateIp[1]))
return false;
return true;
}
else
return false;
}
else
return false;
}

Now we are missing only two things. The first one is a method which converts IP from string to long:
private long ConvertIPToLong(string ip)
{
string[] ipSplit = ip.Split('.');
return (16777216 * Convert.ToInt32(ipSplit[0]) + 65536 * Convert.ToInt32(ipSplit[1]) + 256 * Convert.ToInt32(ipSplit[2]) + Convert.ToInt32(ipSplit[3]));
}

private bool TryConvertIPToLong(string ip, out long ipToLong)
{
try
{
ipToLong = ConvertIPToLong(ip);
return true;
}
catch
{
ipToLong = -1;
return false;
}
}

The second thing is an array of private network IP ranges:
private long[][] _privateIps = new long[][] {
new long[] {ConvertIPToLong("0.0.0.0"), ConvertIPToLong("2.255.255.255")},
new long[] {ConvertIPToLong("10.0.0.0"), ConvertIPToLong("10.255.255.255")},
new long[] {ConvertIPToLong("127.0.0.0"), ConvertIPToLong("127.255.255.255")},
new long[] {ConvertIPToLong("169.254.0.0"), ConvertIPToLong("169.254.255.255")},
new long[] {ConvertIPToLong("172.16.0.0"), ConvertIPToLong("172.31.255.255")},
new long[] {ConvertIPToLong("192.0.2.0"), ConvertIPToLong("192.0.2.255")},
new long[] {ConvertIPToLong("192.168.0.0"), ConvertIPToLong("192.168.255.255")},
new long[] {ConvertIPToLong("255.255.255.0"), ConvertIPToLong("255.255.255.255")}
};

Now we are good to go. Please keep in mind, that this approach doesn't guarantee that you will retrieve real IP address, as all of those headers are optional.