Skip to content

Instantly share code, notes, and snippets.

@cameronsjo
Last active June 9, 2016 14:40
Show Gist options
  • Select an option

  • Save cameronsjo/d3eff20e7abc1cab3fe64e58aaee0563 to your computer and use it in GitHub Desktop.

Select an option

Save cameronsjo/d3eff20e7abc1cab3fe64e58aaee0563 to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace Filters
{
/// <summary>
/// Filter by IP address (ASP.NET Web API version)
/// Originally from http://stackoverflow.com/a/4605086/91551
/// http://www.codeproject.com/Articles/2553/IP-list-Check-an-IP-number-against-a-list-in-C
/// </summary>
public class FilterIpAttribute : AuthorizeAttribute
{
/// <summary>
/// Comma seperated string of allowable IPs. Example "10.2.5.41,192.168.0.22"
/// </summary>
/// <value></value>
public string AllowedSingleIPs { get; set; }
public bool AllowLocal { get; set; }
/// <summary>
/// Comma seperated string of allowable IPs with masks. Example "10.2.0.0;255.255.0.0,10.3.0.0;255.255.0.0"
/// </summary>
/// <value>The masked I ps.</value>
public string AllowedMaskedIPs { get; set; }
/// <summary>
/// List of allowed IPs
/// </summary>
private readonly IpList _allowedIpListToCheck = new IpList();
/// <summary>
/// Comma seperated string of denied IPs. Example "10.2.5.41,192.168.0.22"
/// </summary>
/// <value></value>
public string DeniedSingleIPs { get; set; }
/// <summary>
/// Comma seperated string of denied IPs with masks. Example "10.2.0.0;255.255.0.0,10.3.0.0;255.255.0.0"
/// </summary>
/// <value>The masked I ps.</value>
public string DeniedMaskedIPs { get; set; }
/// <summary>
/// List of denied IPs
/// </summary>
private readonly IpList _deniedIpListToCheck = new IpList();
private bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
if (Environment.CurrentServer != CurrentEnvironment.Development)
{
return true;
}
var userIpAddress = GetRealIp(httpContext.Request);
// Based on my limited knowledge, this is just another check to see if it's running locally.
if (userIpAddress == "::1")
return true;
try
{
// Check that the IP is allowed to access
var ipAllowed = CheckAllowedIPs(userIpAddress);
// Check that the IP is not denied to access
var ipDenied = CheckDeniedIPs(userIpAddress);
// Only allowed if allowed and not denied
var finallyAllowed = ipAllowed && !ipDenied;
return finallyAllowed;
}
catch (Exception e)
{
// Log the exception, probably something wrong with the configuration
}
return true; // if there was an exception, then we return true
}
/// <summary>
/// Checks the allowed IPs.
/// </summary>
/// <param name="userIpAddress">The user ip address.</param>
/// <returns></returns>
private bool CheckAllowedIPs(string userIpAddress)
{
if (AllowLocal)
{
// These addresses were pulled from https://en.wikipedia.org/wiki/Reserved_IP_addresses#IPv4
SplitAndAddMaskedIPs("10.0.0.0/8," +
"100.64.0.0/10," +
"127.0.0.0/8," +
"172.16.0.0/12," +
"192.0.0.0/24," +
"192.168.0.0/16," +
"198.18.0.0/15", _allowedIpListToCheck);
}
// Populate the IPList with the Single IPs
if (!string.IsNullOrEmpty(AllowedSingleIPs))
{
SplitAndAddSingleIPs(AllowedSingleIPs, _allowedIpListToCheck);
}
// Populate the IPList with the Masked IPs
if (!string.IsNullOrEmpty(AllowedMaskedIPs))
{
SplitAndAddMaskedIPs(AllowedMaskedIPs, _allowedIpListToCheck);
}
return _allowedIpListToCheck.CheckNumber(userIpAddress);
}
/// <summary>
/// Checks the denied IPs.
/// </summary>
/// <param name="userIpAddress">The user ip address.</param>
/// <returns></returns>
private bool CheckDeniedIPs(string userIpAddress)
{
// Populate the IPList with the Single IPs
if (!string.IsNullOrEmpty(DeniedSingleIPs))
{
SplitAndAddSingleIPs(DeniedSingleIPs, _deniedIpListToCheck);
}
// Populate the IPList with the Masked IPs
if (!string.IsNullOrEmpty(DeniedMaskedIPs))
{
SplitAndAddMaskedIPs(DeniedMaskedIPs, _deniedIpListToCheck);
}
return _deniedIpListToCheck.CheckNumber(userIpAddress);
}
/// <summary>
/// Splits the incoming ip string of the format "IP,IP" example "10.2.0.0,10.3.0.0" and adds the result to the IPList
/// </summary>
/// <param name="ips">The ips.</param>
/// <param name="list">The list.</param>
private void SplitAndAddSingleIPs(string ips, IpList list)
{
var splitSingleIPs = ips.Split(',');
foreach (var ip in splitSingleIPs)
list.Add(ip);
}
/// <summary>
/// Splits the incoming ip string of the format "IP;MASK,IP;MASK" example "10.2.0.0;255.255.0.0,10.3.0.0;255.255.0.0" and adds the result to the IPList
/// </summary>
/// <param name="ips">The ips.</param>
/// <param name="list">The list.</param>
private void SplitAndAddMaskedIPs(string ips, IpList list)
{
var splitMaskedIPs = ips.Split(',');
foreach (var maskedIp in splitMaskedIPs)
{
if (maskedIp.Contains("/"))
{
var ipAndBlock = maskedIp.Split('/');
list.Add(ipAndBlock[0], int.Parse(ipAndBlock[1]));
}
else
{
var ipAndMask = maskedIp.Split(';');
list.Add(ipAndMask[0], ipAndMask[1]); // IP;MASK
}
}
}
public override void OnAuthorization(HttpActionContext actionContext)
{
if (AuthorizeCore((HttpContextBase)actionContext.Request.Properties["MS_HttpContext"]))
return;
base.HandleUnauthorizedRequest(actionContext);
}
public static string GetRealIp(HttpRequestBase request)
{
// This is used in the case that a load balancer is hit.
var ip = request["HTTP_X_FORWARDED_FOR"];
if (!string.IsNullOrWhiteSpace(ip))
{
ip = ip.Split(',').Last().Trim();
}
if (string.IsNullOrWhiteSpace(ip))
{
ip = request.UserHostAddress;
}
return ip;
}
/// <summary>
/// Internal class for storing a range of IP numbers with the same IP mask
/// </summary>
internal class IpArrayList
{
private bool _isSorted = false;
private readonly ArrayList _ipNumList = new ArrayList();
private readonly uint _ipmask;
/// <summary>
/// Constructor that sets the mask for the list
/// </summary>
public IpArrayList(uint mask)
{
_ipmask = mask;
}
/// <summary>
/// Add a new IP numer (range) to the list
/// </summary>
public void Add(uint ipNum)
{
_isSorted = false;
_ipNumList.Add(ipNum & _ipmask);
}
/// <summary>
/// Checks if an IP number is within the ranges included by the list
/// </summary>
public bool Check(uint ipNum)
{
var found = false;
if (_ipNumList.Count > 0)
{
if (!_isSorted)
{
_ipNumList.Sort();
_isSorted = true;
}
ipNum = ipNum & _ipmask;
if (_ipNumList.BinarySearch(ipNum) >= 0) found = true;
}
return found;
}
/// <summary>
/// Clears the list
/// </summary>
public void Clear()
{
_ipNumList.Clear();
_isSorted = false;
}
/// <summary>
/// The ToString is overriden to generate a list of the IP numbers
/// </summary>
public override string ToString()
{
var buf = new StringBuilder();
foreach (uint ipnum in _ipNumList)
{
if (buf.Length > 0) buf.Append("\r\n");
buf.Append(((int)ipnum & 0xFF000000) >> 24).Append('.');
buf.Append(((int)ipnum & 0x00FF0000) >> 16).Append('.');
buf.Append(((int)ipnum & 0x0000FF00) >> 8).Append('.');
buf.Append(((int)ipnum & 0x000000FF));
}
return buf.ToString();
}
/// <summary>
/// The IP mask for this list of IP numbers
/// </summary>
public uint Mask
{
get
{
return _ipmask;
}
}
}
internal class IpList
{
private readonly ArrayList _ipRangeList = new ArrayList();
private readonly SortedList _maskList = new SortedList();
private readonly ArrayList _usedList = new ArrayList();
public IpList()
{
// Initialize IP mask list and create IPArrayList into the ipRangeList
uint mask = 0x00000000;
for (var level = 1; level < 33; level++)
{
mask = (mask >> 1) | 0x80000000;
_maskList.Add(mask, level);
_ipRangeList.Add(new IpArrayList(mask));
}
}
// Parse a String IP address to a 32 bit unsigned integer
// We can't use System.Net.IPAddress as it will not parse
// our masks correctly eg. 255.255.0.0 is pased as 65535 !
private uint ParseIp(string ipNumber)
{
uint res = 0;
var elements = ipNumber.Split(new Char[] { '.' });
if (elements.Length != 4)
return res;
res = (uint)Convert.ToInt32(elements[0]) << 24;
res += (uint)Convert.ToInt32(elements[1]) << 16;
res += (uint)Convert.ToInt32(elements[2]) << 8;
res += (uint)Convert.ToInt32(elements[3]);
return res;
}
/// <summary>
/// Add a single IP number to the list as a string, ex. 10.1.1.1
/// </summary>
public void Add(string ipNumber)
{
this.Add(ParseIp(ipNumber));
}
/// <summary>
/// Add a single IP number to the list as a unsigned integer, ex. 0x0A010101
/// </summary>
public void Add(uint ip)
{
((IpArrayList)_ipRangeList[31]).Add(ip);
if (!_usedList.Contains((int)31))
{
_usedList.Add((int)31);
_usedList.Sort();
}
}
/// <summary>
/// Adds IP numbers using a mask for range where the mask specifies the number of
/// fixed bits, ex. 172.16.0.0 255.255.0.0 will add 172.16.0.0 - 172.16.255.255
/// </summary>
public void Add(string ipNumber, string mask)
{
this.Add(ParseIp(ipNumber), ParseIp(mask));
}
/// <summary>
/// Adds IP numbers using a mask for range where the mask specifies the number of
/// fixed bits, ex. 0xAC1000 0xFFFF0000 will add 172.16.0.0 - 172.16.255.255
/// </summary>
public void Add(uint ip, uint umask)
{
var level = _maskList[umask];
if (level != null)
{
ip = ip & umask;
((IpArrayList)_ipRangeList[(int)level - 1]).Add(ip);
if (!_usedList.Contains((int)level - 1))
{
_usedList.Add((int)level - 1);
_usedList.Sort();
}
}
}
/// <summary>
/// Adds IP numbers using a mask for range where the mask specifies the number of
/// fixed bits, ex. 192.168.1.0/24 which will add 192.168.1.0 - 192.168.1.255
/// </summary>
public void Add(string ipNumber, int maskLevel)
{
this.Add(ParseIp(ipNumber), (uint)_maskList.GetKey(_maskList.IndexOfValue(maskLevel)));
}
/// <summary>
/// Adds IP numbers using a from and to IP number. The method checks the range and
/// splits it into normal ip/mask blocks.
/// </summary>
public void AddRange(string fromIp, string toIp)
{
this.AddRange(ParseIp(fromIp), ParseIp(toIp));
}
/// <summary>
/// Adds IP numbers using a from and to IP number. The method checks the range and
/// splits it into normal ip/mask blocks.
/// </summary>
public void AddRange(uint fromIp, uint toIp)
{
// If the order is not asending, switch the IP numbers.
if (fromIp > toIp)
{
var tempIp = fromIp;
fromIp = toIp;
toIp = tempIp;
}
if (fromIp == toIp)
{
this.Add(fromIp);
}
else
{
var diff = toIp - fromIp;
var diffLevel = 1;
var range = 0x80000000;
if (diff < 256)
{
diffLevel = 24;
range = 0x00000100;
}
while (range > diff)
{
range = range >> 1;
diffLevel++;
}
var mask = (uint)_maskList.GetKey(_maskList.IndexOfValue(diffLevel));
var minIp = fromIp & mask;
if (minIp < fromIp) minIp += range;
if (minIp > fromIp)
{
this.AddRange(fromIp, minIp - 1);
fromIp = minIp;
}
if (fromIp == toIp)
{
this.Add(fromIp);
}
else
{
if ((minIp + (range - 1)) <= toIp)
{
this.Add(minIp, mask);
fromIp = minIp + range;
}
if (fromIp == toIp)
{
this.Add(toIp);
}
else
{
if (fromIp < toIp) this.AddRange(fromIp, toIp);
}
}
}
}
/// <summary>
/// Checks if an IP number is contained in the lists, ex. 10.0.0.1
/// </summary>
public bool CheckNumber(string ipNumber)
{
return this.CheckNumber(ParseIp(ipNumber)); ;
}
/// <summary>
/// Checks if an IP number is contained in the lists, ex. 0x0A000001
/// </summary>
public bool CheckNumber(uint ip)
{
var found = false;
var i = 0;
while (!found && i < _usedList.Count)
{
found = ((IpArrayList)_ipRangeList[(int)_usedList[i]]).Check(ip);
i++;
}
return found;
}
/// <summary>
/// Clears all lists of IP numbers
/// </summary>
public void Clear()
{
foreach (int i in _usedList)
{
((IpArrayList)_ipRangeList[i]).Clear();
}
_usedList.Clear();
}
/// <summary>
/// Generates a list of all IP ranges in printable format
/// </summary>
public override string ToString()
{
var buffer = new StringBuilder();
foreach (int i in _usedList)
{
buffer.Append("\r\nRange with mask of ").Append(i + 1).Append("\r\n");
buffer.Append(((IpArrayList)_ipRangeList[i]).ToString());
}
return buffer.ToString();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment