using System; using System.Linq; using System.Net; using System.Text.RegularExpressions; namespace Znyc.Recruitment.Admin.Commons.Net { /// /// IP v4 and v6 range helper by jsakamoto /// Fork from https://github.com/jsakamoto/ipaddressrange /// /// /// "192.168.0.0/24" /// "fe80::/10" /// "192.168.0.0/255.255.255.0" /// "192.168.0.0-192.168.0.255" /// public class IpAddressRange { public IPAddress Begin { get; set; } public IPAddress End { get; set; } public IpAddressRange() { Begin = new IPAddress(0L); End = new IPAddress(0L); } public IpAddressRange(string ipRangeString) { // remove all spaces. ipRangeString = ipRangeString.Replace(" ", ""); // Pattern 1. CIDR range: "192.168.0.0/24", "fe80::/10" Match m1 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)/(?\d+)$", RegexOptions.IgnoreCase); if (m1.Success) { byte[] baseAdrBytes = IPAddress.Parse(m1.Groups["adr"].Value).GetAddressBytes(); byte[] maskBytes = Bits.GetBitMask(baseAdrBytes.Length, int.Parse(m1.Groups["maskLen"].Value)); baseAdrBytes = Bits.And(baseAdrBytes, maskBytes); Begin = new IPAddress(baseAdrBytes); End = new IPAddress(Bits.Or(baseAdrBytes, Bits.Not(maskBytes))); return; } // Pattern 2. Uni address: "127.0.0.1", ":;1" Match m2 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)$", RegexOptions.IgnoreCase); if (m2.Success) { Begin = End = IPAddress.Parse(ipRangeString); return; } // Pattern 3. Begin end range: "169.258.0.0-169.258.0.255" Match m3 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)-(?[\da-f\.:]+)$", RegexOptions.IgnoreCase); if (m3.Success) { Begin = IPAddress.Parse(m3.Groups["begin"].Value); End = IPAddress.Parse(m3.Groups["end"].Value); return; } // Pattern 4. Bit mask range: "192.168.0.0/255.255.255.0" Match m4 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)/(?[\da-f\.:]+)$", RegexOptions.IgnoreCase); if (m4.Success) { byte[] baseAdrBytes = IPAddress.Parse(m4.Groups["adr"].Value).GetAddressBytes(); byte[] maskBytes = IPAddress.Parse(m4.Groups["bitmask"].Value).GetAddressBytes(); baseAdrBytes = Bits.And(baseAdrBytes, maskBytes); Begin = new IPAddress(baseAdrBytes); End = new IPAddress(Bits.Or(baseAdrBytes, Bits.Not(maskBytes))); return; } throw new FormatException("Unknown IP range string."); } public bool Contains(IPAddress ipaddress) { if (ipaddress.AddressFamily != Begin.AddressFamily) { return false; } byte[] adrBytes = ipaddress.GetAddressBytes(); return Bits.GE(Begin.GetAddressBytes(), adrBytes) && Bits.LE(End.GetAddressBytes(), adrBytes); } } internal static class Bits { internal static byte[] Not(byte[] bytes) { return bytes.Select(b => (byte)~b).ToArray(); } internal static byte[] And(byte[] A, byte[] B) { return A.Zip(B, (a, b) => (byte)(a & b)).ToArray(); } internal static byte[] Or(byte[] A, byte[] B) { return A.Zip(B, (a, b) => (byte)(a | b)).ToArray(); } internal static bool GE(byte[] A, byte[] B) { return A.Zip(B, (a, b) => a == b ? 0 : a < b ? 1 : -1) .SkipWhile(c => c == 0) .FirstOrDefault() >= 0; } internal static bool LE(byte[] A, byte[] B) { return A.Zip(B, (a, b) => a == b ? 0 : a < b ? 1 : -1) .SkipWhile(c => c == 0) .FirstOrDefault() <= 0; } internal static byte[] GetBitMask(int sizeOfBuff, int bitLen) { byte[] maskBytes = new byte[sizeOfBuff]; int bytesLen = bitLen / 8; int bitsLen = bitLen % 8; for (int i = 0; i < bytesLen; i++) { maskBytes[i] = 0xff; } if (bitsLen > 0) { maskBytes[bytesLen] = (byte)~Enumerable.Range(1, 8 - bitsLen).Select(n => 1 << (n - 1)) .Aggregate((a, b) => a | b); } return maskBytes; } } }