LSTANCZYK
3/21/2017 - 5:02 PM

ASP.NET MVC ActionFilter to allow or deny IPv4 addresses with optional subnet mask. For use with AppHarbor or any other server solution whic

ASP.NET MVC ActionFilter to allow or deny IPv4 addresses with optional subnet mask. For use with AppHarbor or any other server solution which uses the X-Forwarded-For header.

// Copyright by Bo Norgaard, All rights reserved.
// original article: http://www.codeproject.com/Articles/2553/IP-list-Check-an-IP-number-against-a-list-in-C

using System;
using System.Text;
using System.Collections;


namespace AppHarbor.Web {
  /// <summary>
  /// Internal class for storing a range of IP numbers with the same IP mask
  /// </summary>
  internal class IPArrayList {
    private bool isSorted = false;
    private ArrayList ipNumList = new ArrayList();
    private 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) {
      bool 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() {
      StringBuilder 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;
      }
    }
  }

  public class IPList {
    private ArrayList ipRangeList = new ArrayList();
    private SortedList maskList = new SortedList();
    private ArrayList usedList = new ArrayList();

    public IPList() {
      // Initialize IP mask list and create IPArrayList into the ipRangeList
      uint mask = 0x00000000;
      for (int 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;
      string[] elements = IPNumber.Split(new Char[] { '.' });
      if (elements.Length == 4) {
        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) {
      object 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) {
        uint tempIP = fromIP;
        fromIP = toIP;
        toIP = tempIP;
      }
      if (fromIP == toIP) {
        this.Add(fromIP);
      } else {
        uint diff = toIP - fromIP;
        int diffLevel = 1;
        uint range = 0x80000000;
        if (diff < 256) {
          diffLevel = 24;
          range = 0x00000100;
        }
        while (range > diff) {
          range = range >> 1;
          diffLevel++;
        }
        uint mask = (uint)maskList.GetKey(maskList.IndexOfValue(diffLevel));
        uint 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) {
      bool found = false;
      int 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() {
      StringBuilder 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();
    }
  }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;

namespace AppHarbor.Web {
  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true,
    AllowMultiple = false)]
  public class Firewall : ActionFilterAttribute {
    /// <summary>
    /// Comma separated list of allowed IPv4 addresses with optional mask separated with a semicolon.
    /// Example: "127.0.0.1,192.168.0.0;255.255.0.0,10.0.0.1"
    /// </summary>
    public string Allow { get; set; }
    public string Deny { get; set; }
    IPList AllowedIPs = new IPList();
    IPList DeniedIPs = new IPList();

    public Firewall() { }
    public Firewall(string allow, string deny) {
      Allow = allow;
      Deny = deny;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext) {
      if (!IsAllowed(filterContext.HttpContext.Request.Headers["X-Forwarded-For"]) || 
        IsDenied(filterContext.HttpContext.Request.Headers["X-Forwarded-For"]))
        throw new HttpException((int)HttpStatusCode.Forbidden, "Access Denied!");
  
      base.OnActionExecuting(filterContext);
    }

    bool IsAllowed(string IP) {
      if (string.IsNullOrEmpty(Allow))
        return true;

      var ips = Allow.Split(',');
      foreach (var item in ips) {
        if (item.Contains(';')) {
          var parts = item.Split(';');
          AllowedIPs.Add(parts[0], parts[1]);
        } else {
          AllowedIPs.Add(item);
        }
      }

      return AllowedIPs.CheckNumber(IP);
    }

    bool IsDenied(string IP) {
      if (string.IsNullOrEmpty(Deny))
        return false;

      var ips = Deny.Split(',');
      foreach (var item in ips) {
        if (item.Contains(';')) {
          var parts = item.Split(';');
          DeniedIPs.Add(parts[0], parts[1]);
        } else {
          DeniedIPs.Add(item);
        }
      }

      return DeniedIPs.CheckNumber(IP);
    }
  }
}