From 384edaf3a30c5c5f55e03976220724e5c83ca5cd Mon Sep 17 00:00:00 2001 From: Greg MacLellan Date: Wed, 17 Jun 2015 19:02:30 -0400 Subject: [PATCH 01/10] Add constructors that take IPAddress objects, and modify Parse method to invoke proper constructor. Includes tests. --- .../IPAddressRange.Test/IPAddressRangeTest.cs | 84 +++++++++- IPAddressRange/IPAddressRange.cs | 146 ++++++++++++------ 2 files changed, 178 insertions(+), 52 deletions(-) diff --git a/IPAddressRange/IPAddressRange.Test/IPAddressRangeTest.cs b/IPAddressRange/IPAddressRange.Test/IPAddressRangeTest.cs index 6fe0a09..c35bc33 100644 --- a/IPAddressRange/IPAddressRange.Test/IPAddressRangeTest.cs +++ b/IPAddressRange/IPAddressRange.Test/IPAddressRangeTest.cs @@ -14,7 +14,7 @@ public class IPAddressRangeTest { [TestMethod] - public void CtorTest() + public void CtorTest_Empty() { var range = new IPAddressRange(); range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); @@ -23,6 +23,88 @@ public void CtorTest() range.End.ToString().Is("0.0.0.0"); } + [TestMethod] + public void CtorTest_Single() + { + var range = new IPAddressRange(IPAddress.Parse("192.168.0.88")); + range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); + range.Begin.ToString().Is("192.168.0.88"); + range.End.AddressFamily.Is(AddressFamily.InterNetwork); + range.End.ToString().Is("192.168.0.88"); + } + + + + [TestMethod] + public void CtorTest_Array_Throws() + { + AssertEx.Throws(() => new IPAddressRange((IPAddress[])null)); + AssertEx.Throws(() => new IPAddressRange(new IPAddress[] {})); + AssertEx.Throws(() => new IPAddressRange(new[] {IPAddress.Loopback, IPAddress.IPv6Loopback}), "Should not allow mixing of ipv4 and v6"); + } + + [TestMethod] + public void CtorTest_Array() + { + var range = new IPAddressRange(new[] { IPAddress.Parse("192.168.0.80"), IPAddress.Parse("192.168.0.88") }); + range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); + range.Begin.ToString().Is("192.168.0.80"); + range.End.AddressFamily.Is(AddressFamily.InterNetwork); + range.End.ToString().Is("192.168.0.88"); + } + + [TestMethod] + public void CtorTest_Array_Single() + { + var range = new IPAddressRange(new[] { IPAddress.Parse("192.168.0.80") }); + range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); + range.Begin.ToString().Is("192.168.0.80"); + range.End.AddressFamily.Is(AddressFamily.InterNetwork); + range.End.ToString().Is("192.168.0.80"); + } + + [TestMethod] + public void CtorTest_Array_Two_Equal() + { + var range = new IPAddressRange(new[] { IPAddress.Parse("192.168.0.80"), IPAddress.Parse("192.168.0.80") }); + range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); + range.Begin.ToString().Is("192.168.0.80"); + range.End.AddressFamily.Is(AddressFamily.InterNetwork); + range.End.ToString().Is("192.168.0.80"); + } + + [TestMethod] + public void CtorTest_Array_Middle_Ignored() + { + var range = new IPAddressRange(new[] { IPAddress.Parse("192.168.0.80"), IPAddress.Parse("192.168.0.254"), IPAddress.Parse("192.168.0.88") }); + range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); + range.Begin.ToString().Is("192.168.0.80"); + range.End.AddressFamily.Is(AddressFamily.InterNetwork); + range.End.ToString().Is("192.168.0.88"); + } + + + [TestMethod] + public void CtorTest_MaskLength() + { + var range = new IPAddressRange(IPAddress.Parse("192.168.0.80"), 24); + range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); + range.Begin.ToString().Is("192.168.0.0"); + range.End.AddressFamily.Is(AddressFamily.InterNetwork); + range.End.ToString().Is("192.168.0.255"); + } + + + [TestMethod] + public void CtorTest_SubnetMask() + { + var range = new IPAddressRange(IPAddress.Parse("192.168.0.80"), IPAddress.Parse("255.255.255.0")); + range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); + range.Begin.ToString().Is("192.168.0.0"); + range.End.AddressFamily.Is(AddressFamily.InterNetwork); + range.End.ToString().Is("192.168.0.255"); + } + [TestMethod] public void ParseTest_IPv4_Uniaddress() { diff --git a/IPAddressRange/IPAddressRange.cs b/IPAddressRange/IPAddressRange.cs index 518b9f1..4a94215 100644 --- a/IPAddressRange/IPAddressRange.cs +++ b/IPAddressRange/IPAddressRange.cs @@ -16,62 +16,75 @@ public class IPAddressRange : ISerializable, IEnumerable public IPAddress End { get; set; } - public IPAddressRange() + public IPAddressRange() : this(new IPAddress(0L)) { } + + /// + /// Creates an empty range object, equivalent to "0.0.0.0/0". + /// + /// + public IPAddressRange(IPAddress singleAddress) { - this.Begin = new IPAddress(0L); - this.End = new IPAddress(0L); + Begin = End = singleAddress; } - [Obsolete("Use IPAddressRange.Parse static method instead.")] - public IPAddressRange(string ipRangeString) + /// + /// Create a new range based on the first and last IPs in an array. + /// Throws an exception if the array is empty or null. All but the first and last values + /// are ignored. Passing a single value is equivalent to a range of one address. + /// + /// + public IPAddressRange(ICollection beginEnd) { - // remove all spaces. - ipRangeString = ipRangeString.Replace(" ", ""); - - // Pattern 1. CIDR range: "192.168.0.0/24", "fe80::/10" - var m1 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)/(?\d+)$", RegexOptions.IgnoreCase); - if (m1.Success) - { - var baseAdrBytes = IPAddress.Parse(m1.Groups["adr"].Value).GetAddressBytes(); - var maskLen = int.Parse(m1.Groups["maskLen"].Value); - if (baseAdrBytes.Length * 8 < maskLen) throw new FormatException(); - var maskBytes = Bits.GetBitMask(baseAdrBytes.Length, maskLen); - baseAdrBytes = Bits.And(baseAdrBytes, maskBytes); - this.Begin = new IPAddress(baseAdrBytes); - this.End = new IPAddress(Bits.Or(baseAdrBytes, Bits.Not(maskBytes))); - return; - } + if (beginEnd == null) throw new ArgumentNullException("beginEnd"); + if (beginEnd.Count == 0) throw new ArgumentOutOfRangeException("beginEnd", "Array contains no elements"); + + Begin = beginEnd.First(); + End = beginEnd.Last(); - // Pattern 2. Uni address: "127.0.0.1", ":;1" - var m2 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)$", RegexOptions.IgnoreCase); - if (m2.Success) - { - this.Begin = this.End = IPAddress.Parse(ipRangeString); - return; - } + if (Begin.AddressFamily != End.AddressFamily) throw new ArgumentException("Elements must be of the same address family", "beginEnd"); - // Pattern 3. Begin end range: "169.258.0.0-169.258.0.255" - var m3 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)[\-–](?[\da-f\.:]+)$", RegexOptions.IgnoreCase); - if (m3.Success) - { - this.Begin = IPAddress.Parse(m3.Groups["begin"].Value); - this.End = IPAddress.Parse(m3.Groups["end"].Value); - return; - } + var beginBytes = Begin.GetAddressBytes(); + var endBytes = End.GetAddressBytes(); + if (!Bits.LE(endBytes, beginBytes)) throw new ArgumentException("Begin must be smaller than the End", "beginEnd"); + } - // Pattern 4. Bit mask range: "192.168.0.0/255.255.255.0" - var m4 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)/(?[\da-f\.:]+)$", RegexOptions.IgnoreCase); - if (m4.Success) - { - var baseAdrBytes = IPAddress.Parse(m4.Groups["adr"].Value).GetAddressBytes(); - var maskBytes = IPAddress.Parse(m4.Groups["bitmask"].Value).GetAddressBytes(); - baseAdrBytes = Bits.And(baseAdrBytes, maskBytes); - this.Begin = new IPAddress(baseAdrBytes); - this.End = new IPAddress(Bits.Or(baseAdrBytes, Bits.Not(maskBytes))); - return; - } + /// + /// Creates a range from a base address and mask bits + /// + /// + /// + public IPAddressRange(IPAddress baseAddress, int maskLength) + { + var baseAdrBytes = baseAddress.GetAddressBytes(); + if (baseAdrBytes.Length * 8 < maskLength) throw new FormatException(); + var maskBytes = Bits.GetBitMask(baseAdrBytes.Length, maskLength); + baseAdrBytes = Bits.And(baseAdrBytes, maskBytes); + + Begin = new IPAddress(baseAdrBytes); + End = new IPAddress(Bits.Or(baseAdrBytes, Bits.Not(maskBytes))); + } + + /// + /// Creates a range from a base address and subnet mask. + /// + /// + /// + public IPAddressRange(IPAddress baseAddress, IPAddress subnetMask) + { + var baseAdrBytes = baseAddress.GetAddressBytes(); + var maskBytes = subnetMask.GetAddressBytes(); + baseAdrBytes = Bits.And(baseAdrBytes, maskBytes); + + Begin = new IPAddress(baseAdrBytes); + End = new IPAddress(Bits.Or(baseAdrBytes, Bits.Not(maskBytes))); + } - throw new FormatException("Unknown IP range string."); + [Obsolete("Use IPAddressRange.Parse static method instead.")] + public IPAddressRange(string ipRangeString) + { + var parsed = Parse(ipRangeString); + Begin = parsed.Begin; + End = parsed.End; } protected IPAddressRange(SerializationInfo info, StreamingContext context) @@ -113,9 +126,40 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) public static IPAddressRange Parse(string ipRangeString) { -#pragma warning disable 618 - return new IPAddressRange(ipRangeString); -#pragma warning restore + // remove all spaces. + ipRangeString = ipRangeString.Replace(" ", ""); + + // Pattern 1. CIDR range: "192.168.0.0/24", "fe80::/10" + var m1 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)/(?\d+)$", RegexOptions.IgnoreCase); + if (m1.Success) + { + return new IPAddressRange(IPAddress.Parse(m1.Groups["adr"].Value), int.Parse(m1.Groups["maskLen"].Value)); + } + + // Pattern 2. Uni address: "127.0.0.1", ":;1" + var m2 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)$", RegexOptions.IgnoreCase); + if (m2.Success) + { + return new IPAddressRange(IPAddress.Parse(ipRangeString)); + } + + // Pattern 3. Begin end range: "169.258.0.0-169.258.0.255" + var m3 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)[\-–](?[\da-f\.:]+)$", RegexOptions.IgnoreCase); + if (m3.Success) + { + return new IPAddressRange(new[] { IPAddress.Parse(m3.Groups["begin"].Value), IPAddress.Parse(m3.Groups["end"].Value) }); + } + + // Pattern 4. Bit mask range: "192.168.0.0/255.255.255.0" + var m4 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)/(?[\da-f\.:]+)$", RegexOptions.IgnoreCase); + if (m4.Success) + { + return new IPAddressRange( + IPAddress.Parse(m4.Groups["adr"].Value), + IPAddress.Parse(m4.Groups["bitmask"].Value)); + } + + throw new FormatException("Unknown IP range string."); } public static bool TryParse(string ipRangeString, out IPAddressRange ipRange) From d5e33adbbcfc1649dcdc4885327322462d826d31 Mon Sep 17 00:00:00 2001 From: Greg MacLellan Date: Wed, 17 Jun 2015 19:17:12 -0400 Subject: [PATCH 02/10] Use a single regex for parsing --- IPAddressRange/IPAddressRange.cs | 57 ++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/IPAddressRange/IPAddressRange.cs b/IPAddressRange/IPAddressRange.cs index 4a94215..b5728dd 100644 --- a/IPAddressRange/IPAddressRange.cs +++ b/IPAddressRange/IPAddressRange.cs @@ -124,39 +124,48 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) info.AddValue("End", this.End != null ? this.End.ToString() : ""); } + + protected static string ParseRegex = + @"^(?:" + + @"(?[\da-f\.:]+)/(?\d+)$" + // Pattern 1. CIDR range: "192.168.0.0/24", "fe80::/10" + @"|(?[\da-f\.:]+)" + // Pattern 2. Uni address: "127.0.0.1", ":;1" + @"|(?[\da-f\.:]+)[\-–](?[\da-f\.:]+)" + // Pattern 3. Begin end range: "169.258.0.0-169.258.0.255" + @"|(?[\da-f\.:]+)/(?[\da-f\.:]+)" + // Pattern 4. Bit mask range: "192.168.0.0/255.255.255.0" + @")$"; + + /// + /// Parse an IP Adddress range from a string. Accepts CIDR ranges like "192.168.0.0/24", "fe80::/10", + /// single IPs, Begin-end ranges like "169.258.0.0-169.258.0.255" or bitmask ranges like + /// "192.168.0.0/255.255.255.0". + /// + /// + /// public static IPAddressRange Parse(string ipRangeString) { // remove all spaces. ipRangeString = ipRangeString.Replace(" ", ""); - // Pattern 1. CIDR range: "192.168.0.0/24", "fe80::/10" - var m1 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)/(?\d+)$", RegexOptions.IgnoreCase); - if (m1.Success) + var match = Regex.Match(ipRangeString, ParseRegex, RegexOptions.IgnoreCase); + if (match.Success) { - return new IPAddressRange(IPAddress.Parse(m1.Groups["adr"].Value), int.Parse(m1.Groups["maskLen"].Value)); - } + if (!string.IsNullOrEmpty(match.Groups["cidrBase"].Value)) + return new IPAddressRange(IPAddress.Parse(match.Groups["cidrBase"].Value), int.Parse(match.Groups["cidrMask"].Value)); - // Pattern 2. Uni address: "127.0.0.1", ":;1" - var m2 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)$", RegexOptions.IgnoreCase); - if (m2.Success) - { - return new IPAddressRange(IPAddress.Parse(ipRangeString)); - } + if (!string.IsNullOrEmpty(match.Groups["singleAddr"].Value)) + return new IPAddressRange(IPAddress.Parse(ipRangeString)); - // Pattern 3. Begin end range: "169.258.0.0-169.258.0.255" - var m3 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)[\-–](?[\da-f\.:]+)$", RegexOptions.IgnoreCase); - if (m3.Success) - { - return new IPAddressRange(new[] { IPAddress.Parse(m3.Groups["begin"].Value), IPAddress.Parse(m3.Groups["end"].Value) }); - } + if (!string.IsNullOrEmpty(match.Groups["begin"].Value)) + return new IPAddressRange(new[] + { + IPAddress.Parse(match.Groups["begin"].Value), + IPAddress.Parse(match.Groups["end"].Value) + }); - // Pattern 4. Bit mask range: "192.168.0.0/255.255.255.0" - var m4 = Regex.Match(ipRangeString, @"^(?[\da-f\.:]+)/(?[\da-f\.:]+)$", RegexOptions.IgnoreCase); - if (m4.Success) - { - return new IPAddressRange( - IPAddress.Parse(m4.Groups["adr"].Value), - IPAddress.Parse(m4.Groups["bitmask"].Value)); + + if (!string.IsNullOrEmpty(match.Groups["bitmaskAddr"].Value)) + return new IPAddressRange( + IPAddress.Parse(match.Groups["bitmaskAddr"].Value), + IPAddress.Parse(match.Groups["bitmaskMask"].Value)); } throw new FormatException("Unknown IP range string."); From 6ea4a85966b59ed41efc44bf437ad2ce989863c4 Mon Sep 17 00:00:00 2001 From: Greg MacLellan Date: Wed, 17 Jun 2015 19:19:29 -0400 Subject: [PATCH 03/10] Tighten up allowance of spaces: leading/trailing spaces and spaces around range operators are accepted, but spaces within IP address digits are not allowed --- IPAddressRange/IPAddressRange.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/IPAddressRange/IPAddressRange.cs b/IPAddressRange/IPAddressRange.cs index b5728dd..15a6a2d 100644 --- a/IPAddressRange/IPAddressRange.cs +++ b/IPAddressRange/IPAddressRange.cs @@ -126,12 +126,12 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) protected static string ParseRegex = - @"^(?:" + - @"(?[\da-f\.:]+)/(?\d+)$" + // Pattern 1. CIDR range: "192.168.0.0/24", "fe80::/10" - @"|(?[\da-f\.:]+)" + // Pattern 2. Uni address: "127.0.0.1", ":;1" - @"|(?[\da-f\.:]+)[\-–](?[\da-f\.:]+)" + // Pattern 3. Begin end range: "169.258.0.0-169.258.0.255" - @"|(?[\da-f\.:]+)/(?[\da-f\.:]+)" + // Pattern 4. Bit mask range: "192.168.0.0/255.255.255.0" - @")$"; + @"^\s*(?:" + + @"(?[\da-f\.:]+)\s*/\s*(?\d+)$" + // Pattern 1. CIDR range: "192.168.0.0/24", "fe80::/10" + @"|(?[\da-f\.:]+)" + // Pattern 2. Uni address: "127.0.0.1", ":;1" + @"|(?[\da-f\.:]+)\s*[\-–]\s*(?[\da-f\.:]+)" + // Pattern 3. Begin end range: "169.258.0.0-169.258.0.255" + @"|(?[\da-f\.:]+)\s*/\s*(?[\da-f\.:]+)" + // Pattern 4. Bit mask range: "192.168.0.0/255.255.255.0" + @")\s*$"; /// /// Parse an IP Adddress range from a string. Accepts CIDR ranges like "192.168.0.0/24", "fe80::/10", @@ -142,9 +142,6 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) /// public static IPAddressRange Parse(string ipRangeString) { - // remove all spaces. - ipRangeString = ipRangeString.Replace(" ", ""); - var match = Regex.Match(ipRangeString, ParseRegex, RegexOptions.IgnoreCase); if (match.Success) { From b3a67d1f8545f5f17dfecd0a0b8e620808b50c29 Mon Sep 17 00:00:00 2001 From: Greg MacLellan Date: Wed, 17 Jun 2015 19:22:23 -0400 Subject: [PATCH 04/10] Fix mixed up xmldocs on constructors --- IPAddressRange/IPAddressRange.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/IPAddressRange/IPAddressRange.cs b/IPAddressRange/IPAddressRange.cs index 15a6a2d..32f49e5 100644 --- a/IPAddressRange/IPAddressRange.cs +++ b/IPAddressRange/IPAddressRange.cs @@ -16,10 +16,13 @@ public class IPAddressRange : ISerializable, IEnumerable public IPAddress End { get; set; } + /// + /// Creates an empty range object, equivalent to "0.0.0.0/0". + /// public IPAddressRange() : this(new IPAddress(0L)) { } /// - /// Creates an empty range object, equivalent to "0.0.0.0/0". + /// Creates a new range with the same start/end address (range of one) /// /// public IPAddressRange(IPAddress singleAddress) From 63896a339ba4ab1a3d2ebd29f5896c192898d1e8 Mon Sep 17 00:00:00 2001 From: Greg MacLellan Date: Fri, 19 Jun 2015 11:42:46 -0400 Subject: [PATCH 05/10] Fix for Exception when parsing spaces around single IP address --- IPAddressRange/IPAddressRange.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPAddressRange/IPAddressRange.cs b/IPAddressRange/IPAddressRange.cs index 32f49e5..321ecee 100644 --- a/IPAddressRange/IPAddressRange.cs +++ b/IPAddressRange/IPAddressRange.cs @@ -152,7 +152,7 @@ public static IPAddressRange Parse(string ipRangeString) return new IPAddressRange(IPAddress.Parse(match.Groups["cidrBase"].Value), int.Parse(match.Groups["cidrMask"].Value)); if (!string.IsNullOrEmpty(match.Groups["singleAddr"].Value)) - return new IPAddressRange(IPAddress.Parse(ipRangeString)); + return new IPAddressRange(IPAddress.Parse(match.Groups["singleAddr"].Value)); if (!string.IsNullOrEmpty(match.Groups["begin"].Value)) return new IPAddressRange(new[] From 8e4677f4df5555ff3ae83aef1378ce5b8d9654e2 Mon Sep 17 00:00:00 2001 From: Greg MacLellan Date: Fri, 19 Jun 2015 11:43:13 -0400 Subject: [PATCH 06/10] Fix for exception with trailing spaces on CIDR range --- IPAddressRange/IPAddressRange.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IPAddressRange/IPAddressRange.cs b/IPAddressRange/IPAddressRange.cs index 321ecee..ceb5145 100644 --- a/IPAddressRange/IPAddressRange.cs +++ b/IPAddressRange/IPAddressRange.cs @@ -130,7 +130,7 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) protected static string ParseRegex = @"^\s*(?:" + - @"(?[\da-f\.:]+)\s*/\s*(?\d+)$" + // Pattern 1. CIDR range: "192.168.0.0/24", "fe80::/10" + @"(?[\da-f\.:]+)\s*/\s*(?\d+)" + // Pattern 1. CIDR range: "192.168.0.0/24", "fe80::/10" @"|(?[\da-f\.:]+)" + // Pattern 2. Uni address: "127.0.0.1", ":;1" @"|(?[\da-f\.:]+)\s*[\-–]\s*(?[\da-f\.:]+)" + // Pattern 3. Begin end range: "169.258.0.0-169.258.0.255" @"|(?[\da-f\.:]+)\s*/\s*(?[\da-f\.:]+)" + // Pattern 4. Bit mask range: "192.168.0.0/255.255.255.0" From 84944c78dbd94bf73cc46ccc59067f1c915fa2cb Mon Sep 17 00:00:00 2001 From: Greg MacLellan Date: Fri, 19 Jun 2015 11:46:06 -0400 Subject: [PATCH 07/10] Change parsing tests to use TestCase, and add several variations of inputs --- .../IPAddressRangeParseTest.cs | 259 +++++++----------- 1 file changed, 105 insertions(+), 154 deletions(-) diff --git a/IPAddressRange/IPAddressRange.Test/IPAddressRangeParseTest.cs b/IPAddressRange/IPAddressRange.Test/IPAddressRangeParseTest.cs index 63fa478..4ca0cb1 100644 --- a/IPAddressRange/IPAddressRange.Test/IPAddressRangeParseTest.cs +++ b/IPAddressRange/IPAddressRange.Test/IPAddressRangeParseTest.cs @@ -1,4 +1,6 @@ using System; +using System.CodeDom; +using System.Collections.Generic; using Microsoft.VisualStudio.TestTools.UnitTesting; using NetTools; @@ -7,160 +9,109 @@ namespace IPRange.Test [TestClass] public class IPAddressRangeParseTest { - [TestMethod] - public void ParseSucceeds_IPV4() - { - var range = IPAddressRange.Parse("192.168.60.13"); - range.IsNotNull(); - } - - [TestMethod] - public void ParseSucceeds_IPV6() - { - var range = IPAddressRange.Parse("fe80::d503:4ee:3882:c586"); - range.IsNotNull(); - } - - [TestMethod] - public void ParseSucceeds_IPV4_Cipdr() - { - var range = IPAddressRange.Parse("219.165.64.0/19"); - range.IsNotNull(); - } - - [TestMethod] - public void ParseSucceeds_IPV4_Cipdr_Max() - { - var range = IPAddressRange.Parse("219.165.64.73/32"); - range.IsNotNull(); - } - - [TestMethod] - public void ParseSucceeds_IPV4_Cipdr_BitMask() - { - var range = IPAddressRange.Parse("192.168.1.0/255.255.255.0"); - range.IsNotNull(); - } - - [TestMethod] - public void ParseSucceeds_IPV4_Cipdr_Begin_To_End() - { - var range = IPAddressRange.Parse("192.168.60.26-192.168.60.37"); - range.IsNotNull(); - - // with "dash (–)" (0x2013) is also support. - var range2 = IPAddressRange.Parse("192.168.60.26–192.168.60.37"); - range2.IsNotNull(); - range2.Begin.ToString().Is("192.168.60.26"); - range2.End.ToString().Is("192.168.60.37"); - } - - [TestMethod] - [ExpectedException(typeof(FormatException))] - public void Parse_EmptyString_Fails() - { - IPAddressRange.Parse(""); - } - - [TestMethod] - [ExpectedException(typeof(FormatException))] - public void Parse_InValidString_Fails() - { - IPAddressRange.Parse("gvvdv"); - } - - [TestMethod] - [ExpectedException(typeof(FormatException))] - public void Parse_CIDR_OutOfRange() - { - IPAddressRange.Parse("192.168.0.10/48"); - } - - [TestMethod] - public void TryParse_Empty_String() - { - IPAddressRange temp; - var result = IPAddressRange.TryParse("", out temp); - result.Is(false); - temp.IsNull(); - } - - [TestMethod] - public void TryParse_InValid_String() - { - IPAddressRange temp; - var result = IPAddressRange.TryParse("fdfv", out temp); - result.Is(false); - temp.IsNull(); - } - - [TestMethod] - public void TryParse_CIDR_OutOfRange() - { - var ipadr = default(IPAddressRange); - IPAddressRange.TryParse("192.168.0.10/48", out ipadr).Is(false); - ipadr.IsNull(); - } - - [TestMethod] - public void TryParse_IPV4() - { - IPAddressRange temp; - var result = IPAddressRange.TryParse("192.168.60.13", out temp); - result.Is(true); - temp.IsNotNull(); - } - - [TestMethod] - public void TryParse_IPV6() - { - IPAddressRange temp; - var result = IPAddressRange.TryParse("fe80::d503:4ee:3882:c586", out temp); - result.Is(true); - temp.IsNotNull(); - } - - [TestMethod] - public void TryParse_IPV4_Cipdr() - { - IPAddressRange temp; - var result = IPAddressRange.TryParse("219.165.64.0/19", out temp); - result.Is(true); - temp.IsNotNull(); - } - - [TestMethod] - public void TryParse_IPV4_Cipdr_Max() - { - IPAddressRange temp; - var result = IPAddressRange.TryParse("219.165.64.73/32", out temp); - result.Is(true); - temp.IsNotNull(); - } - - [TestMethod] - public void TryParse_IPV4_Cipdr_BitMask() - { - IPAddressRange temp; - var result = IPAddressRange.TryParse("192.168.1.0/255.255.255.0", out temp); - result.Is(true); - temp.IsNotNull(); - } - - [TestMethod] - public void TryParse_IPV4_Cipdr_Begin_To_End() - { - IPAddressRange temp; - var result = IPAddressRange.TryParse("192.168.60.26-192.168.60.37", out temp); - result.Is(true); - temp.IsNotNull(); - - IPAddressRange temp2; - var result2 = IPAddressRange.TryParse("192.168.60.26–192.168.60.37", out temp2); - result2.Is(true); - temp2.IsNotNull(); - temp2.Begin.ToString().Is("192.168.60.26"); - temp2.End.ToString().Is("192.168.60.37"); + public TestContext TestContext { get; set; } + + + [TestMethod] + [TestCase("192.168.60.13", "192.168.60.13", "192.168.60.13")] + [TestCase(" 192.168.60.13 ", "192.168.60.13", "192.168.60.13")] + [TestCase("fe80::d503:4ee:3882:c586", "fe80::d503:4ee:3882:c586", "fe80::d503:4ee:3882:c586")] + [TestCase(" fe80::d503:4ee:3882:c586 ", "fe80::d503:4ee:3882:c586", "fe80::d503:4ee:3882:c586")] + [TestCase("3232252004", "192.168.64.100", "192.168.64.100")] // decimal - new + [TestCase(" 3232252004 ", "192.168.64.100", "192.168.64.100")] // decimal - new + + [TestCase("219.165.64.0/19", "219.165.64.0", "219.165.95.255")] + [TestCase(" 219.165.64.0 / 19 ", "219.165.64.0", "219.165.95.255")] + [TestCase("192.168.1.0/255.255.255.0", "192.168.1.0", "192.168.1.255")] + [TestCase(" 192.168.1.0 / 255.255.255.0 ", "192.168.1.0", "192.168.1.255")] + [TestCase("3232252004/24", "192.168.64.0", "192.168.64.255")] // decimal - new + [TestCase(" 3232252004 / 24 ", "192.168.64.0", "192.168.64.255")] // decimal - new + + [TestCase("192.168.60.26–192.168.60.37", "192.168.60.26", "192.168.60.37")] + [TestCase(" 192.168.60.26 – 192.168.60.37 ", "192.168.60.26", "192.168.60.37")] + [TestCase("fe80::c586-fe80::c600", "fe80::c586", "fe80::c600")] + [TestCase(" fe80::c586 - fe80::c600 ", "fe80::c586", "fe80::c600")] + [TestCase("3232252004-3232252504", "192.168.64.100", "192.168.66.88")] + [TestCase(" 3232252004 - 3232252504 ", "192.168.64.100", "192.168.66.88")] + + // with "dash (–)" (0x2013) is also support. + [TestCase("192.168.61.26–192.168.61.37", "192.168.61.26", "192.168.61.37")] + [TestCase(" 192.168.61.26 – 192.168.61.37 ", "192.168.61.26", "192.168.61.37")] + [TestCase("fe80::c586–fe80::c600", "fe80::c586", "fe80::c600")] + [TestCase(" fe80::c586 – fe80::c600 ", "fe80::c586", "fe80::c600")] + [TestCase("3232252004–3232252504", "192.168.64.100", "192.168.66.88")] + [TestCase(" 3232252004 – 3232252504 ", "192.168.64.100", "192.168.66.88")] + public void ParseSucceeds() + { + TestContext.Run((string input, string expectedBegin, string expectedEnd) => + { + Console.WriteLine("TestCase: \"{0}\", Expected Begin: {1}, End: {2}", input, expectedBegin, expectedEnd); + var range = IPAddressRange.Parse(input); + range.IsNotNull(); + Console.WriteLine(" Result: Begin: {0}, End: {1}", range.Begin, range.End); + range.Begin.ToString().Is(expectedBegin); + range.End.ToString().Is(expectedEnd); + }); + } + + [TestMethod] + [TestCase(null, typeof(ArgumentNullException))] + [TestCase("", typeof(FormatException))] + [TestCase(" ", typeof(FormatException))] + [TestCase("gvvdv", typeof(FormatException))] + [TestCase("192.168.0.10/48", typeof(FormatException))] // out of CIDR range + [TestCase("192.168.0.10-192.168.0.5", typeof(ArgumentException))] // bigger to lower + [TestCase("10.256.1.1", typeof(FormatException))] // invalid ip + public void ParseFails() + { + TestContext.Run((string input, Type expectedException) => + { + Console.WriteLine("TestCase: \"{0}\", Expected Exception: {1}", input, expectedException.Name); + try + { + IPAddressRange.Parse(input); + Assert.Fail("Expected exception of type {0} to be thrown for input \"{1}\"", expectedException.Name,input); + } + catch (AssertFailedException) + { + throw; // allow Assert.Fail to pass through + } + catch (Exception ex) + { + ex.GetType().Is(expectedException); + } + }); + } + + [TestMethod] + [TestCase("", false)] + [TestCase(" ", false)] + [TestCase("fdfv", false)] + [TestCase("192.168.0.10/48", false)] // CIDR out of range + + [TestCase("192.168.60.13", true)] + [TestCase("fe80::d503:4ee:3882:c586", true)] + [TestCase("219.165.64.0/19", true)] + [TestCase("219.165.64.73/32", true)] + [TestCase("192.168.1.0/255.255.255.0", true)] + [TestCase("192.168.60.26-192.168.60.37", true)] + public void TryParse() + { + TestContext.Run((string input, bool expectedReturn) => + { + Console.WriteLine("TestCase: \"{0}\", Expected: {1}", input, expectedReturn); + IPAddressRange temp; + var result = IPAddressRange.TryParse(input, out temp); + result.Is(expectedReturn); + if (expectedReturn) + { + temp.IsNotNull(); + } + else + { + temp.IsNull(); + } + }); } } } From 51f08367c91b31eacf9700cbdc55f77435642cfe Mon Sep 17 00:00:00 2001 From: Greg MacLellan Date: Fri, 19 Jun 2015 11:51:37 -0400 Subject: [PATCH 08/10] Fix TryParse failing on null or other invalid input (fixes #14) --- IPAddressRange/IPAddressRange.Test/IPAddressRangeParseTest.cs | 2 ++ IPAddressRange/IPAddressRange.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/IPAddressRange/IPAddressRange.Test/IPAddressRangeParseTest.cs b/IPAddressRange/IPAddressRange.Test/IPAddressRangeParseTest.cs index 4ca0cb1..16dc93c 100644 --- a/IPAddressRange/IPAddressRange.Test/IPAddressRangeParseTest.cs +++ b/IPAddressRange/IPAddressRange.Test/IPAddressRangeParseTest.cs @@ -84,10 +84,12 @@ public void ParseFails() } [TestMethod] + [TestCase(null, false)] // bug3 [TestCase("", false)] [TestCase(" ", false)] [TestCase("fdfv", false)] [TestCase("192.168.0.10/48", false)] // CIDR out of range + [TestCase("192.168.60.26-192.168.60.22", false)] // big to lower [TestCase("192.168.60.13", true)] [TestCase("fe80::d503:4ee:3882:c586", true)] diff --git a/IPAddressRange/IPAddressRange.cs b/IPAddressRange/IPAddressRange.cs index ceb5145..ccae6b9 100644 --- a/IPAddressRange/IPAddressRange.cs +++ b/IPAddressRange/IPAddressRange.cs @@ -178,7 +178,7 @@ public static bool TryParse(string ipRangeString, out IPAddressRange ipRange) ipRange = IPAddressRange.Parse(ipRangeString); return true; } - catch (FormatException) + catch (Exception) { ipRange = null; return false; From cfd912c8373c9b0e7a17f855615ec5520fc7bdb7 Mon Sep 17 00:00:00 2001 From: Greg MacLellan Date: Thu, 25 Jun 2015 14:05:38 -0400 Subject: [PATCH 09/10] Add GetBitMaskLength method and test --- IPAddressRange/Bits.cs | 40 ++++++++++++++++++- .../IPAddressRange.Test/BitsTest.cs | 24 +++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/IPAddressRange/Bits.cs b/IPAddressRange/Bits.cs index 490ebbe..8d5b1df 100644 --- a/IPAddressRange/Bits.cs +++ b/IPAddressRange/Bits.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; @@ -41,7 +42,7 @@ public static byte[] GetBitMask(int sizeOfBuff, int bitLen) var maskBytes = new byte[sizeOfBuff]; var bytesLen = bitLen / 8; var bitsLen = bitLen % 8; - for (int i = 0; i < bytesLen; i++) + for (var i = 0; i < bytesLen; i++) { maskBytes[i] = 0xff; } @@ -49,6 +50,43 @@ public static byte[] GetBitMask(int sizeOfBuff, int bitLen) return maskBytes; } + /// + /// Counts the number of leading 1's in a bitmask. + /// Returns null if value is invalid as a bitmask. + /// + /// + /// + public static int? GetBitMaskLength(byte[] bytes) + { + var bitLength = 0; + var idx = 0; + + // find beginning 0xFF + for (; idx < bytes.Length && bytes[idx] == 0xff; idx++); + bitLength = 8*idx; + + if (idx < bytes.Length) + { + switch (bytes[idx]) + { + case 0xFE: bitLength += 7; break; + case 0xFC: bitLength += 6; break; + case 0xF8: bitLength += 5; break; + case 0xF0: bitLength += 4; break; + case 0xE0: bitLength += 3; break; + case 0xC0: bitLength += 2; break; + case 0x80: bitLength += 1; break; + case 0x00: break; + default: // invalid bitmask + return null; + } + // remainder must be 0x00 + if (bytes.Skip(idx + 1).Any(x => x != 0x00)) return null; + } + return bitLength; + } + + public static byte[] Increment(byte[] bytes) { var incrementIndex = Array.FindLastIndex(bytes, x => x < byte.MaxValue); diff --git a/IPAddressRange/IPAddressRange.Test/BitsTest.cs b/IPAddressRange/IPAddressRange.Test/BitsTest.cs index b596984..d8f3d30 100644 --- a/IPAddressRange/IPAddressRange.Test/BitsTest.cs +++ b/IPAddressRange/IPAddressRange.Test/BitsTest.cs @@ -84,6 +84,30 @@ public void GetBitMaskTest() Bits.GetBitMask(4, 32).Is(new byte[] { 0xff, 0xff, 0xff, 0xff }); } + [TestMethod] + public void GetBitMaskLengthTest() + { + Bits.GetBitMaskLength(new byte[] { 128, 0, 0, 0 }).Is(1); + Bits.GetBitMaskLength(new byte[] { 255, 0, 0, 0 }).Is(8); + Bits.GetBitMaskLength(new byte[] { 255, 255, 0, 0 }).Is(16); + Bits.GetBitMaskLength(new byte[] { 255, 255, 255, 0 }).Is(24); + Bits.GetBitMaskLength(new byte[] { 255, 255, 254, 0 }).Is(23); + Bits.GetBitMaskLength(new byte[] { 255, 255, 252, 0 }).Is(22); + Bits.GetBitMaskLength(new byte[] { 255, 255, 248, 0 }).Is(21); + Bits.GetBitMaskLength(new byte[] { 255, 255, 240, 0 }).Is(20); + Bits.GetBitMaskLength(new byte[] { 255, 255, 224, 0 }).Is(19); + Bits.GetBitMaskLength(new byte[] { 255, 255, 192, 0 }).Is(18); + Bits.GetBitMaskLength(new byte[] { 255, 255, 128, 0 }).Is(17); + Bits.GetBitMaskLength(new byte[] { 255, 255, 255, 254 }).Is(31); + Bits.GetBitMaskLength(new byte[] { 255, 255, 255, 255 }).Is(32); + + Bits.GetBitMaskLength(new byte[] { 255, 1, 0, 0 }).Is((int?)null); + Bits.GetBitMaskLength(new byte[] { 255, 127, 0, 0 }).Is((int?)null); + + Bits.GetBitMaskLength(new byte[] { 255, 0, 0, 128 }).Is((int?)null); + Bits.GetBitMaskLength(new byte[] { 255, 192, 0, 255 }).Is((int?)null); + } + [TestMethod] public void IncrementTest() { From b411d3879cce4f8ba2db7ec2c09edc7c1df1d751 Mon Sep 17 00:00:00 2001 From: Greg MacLellan Date: Thu, 25 Jun 2015 14:06:27 -0400 Subject: [PATCH 10/10] Constructor changes from https://github.com/jsakamoto/ipaddressrange/pull/13#issuecomment-114384770: * Remove public IPAddressRange(IPAddress baseAddress, IPAddress subnetMask) * Change public IPAddressRange(ICollection beginEnd) to public IPAddressRange(IPAddress begin, IPAddress end) * Add public static int SubnetMaskLength(IPAddress subnetMask) (and tests) * Update XML help for public IPAddressRange(IPAddress baseAddress, int maskLength) to show example use of SubnetMaskLength() --- .../IPAddressRange.Test/IPAddressRangeTest.cs | 76 ++++--------------- IPAddressRange/IPAddressRange.cs | 56 +++++++------- 2 files changed, 40 insertions(+), 92 deletions(-) diff --git a/IPAddressRange/IPAddressRange.Test/IPAddressRangeTest.cs b/IPAddressRange/IPAddressRange.Test/IPAddressRangeTest.cs index c35bc33..38459fa 100644 --- a/IPAddressRange/IPAddressRange.Test/IPAddressRangeTest.cs +++ b/IPAddressRange/IPAddressRange.Test/IPAddressRangeTest.cs @@ -33,57 +33,6 @@ public void CtorTest_Single() range.End.ToString().Is("192.168.0.88"); } - - - [TestMethod] - public void CtorTest_Array_Throws() - { - AssertEx.Throws(() => new IPAddressRange((IPAddress[])null)); - AssertEx.Throws(() => new IPAddressRange(new IPAddress[] {})); - AssertEx.Throws(() => new IPAddressRange(new[] {IPAddress.Loopback, IPAddress.IPv6Loopback}), "Should not allow mixing of ipv4 and v6"); - } - - [TestMethod] - public void CtorTest_Array() - { - var range = new IPAddressRange(new[] { IPAddress.Parse("192.168.0.80"), IPAddress.Parse("192.168.0.88") }); - range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); - range.Begin.ToString().Is("192.168.0.80"); - range.End.AddressFamily.Is(AddressFamily.InterNetwork); - range.End.ToString().Is("192.168.0.88"); - } - - [TestMethod] - public void CtorTest_Array_Single() - { - var range = new IPAddressRange(new[] { IPAddress.Parse("192.168.0.80") }); - range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); - range.Begin.ToString().Is("192.168.0.80"); - range.End.AddressFamily.Is(AddressFamily.InterNetwork); - range.End.ToString().Is("192.168.0.80"); - } - - [TestMethod] - public void CtorTest_Array_Two_Equal() - { - var range = new IPAddressRange(new[] { IPAddress.Parse("192.168.0.80"), IPAddress.Parse("192.168.0.80") }); - range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); - range.Begin.ToString().Is("192.168.0.80"); - range.End.AddressFamily.Is(AddressFamily.InterNetwork); - range.End.ToString().Is("192.168.0.80"); - } - - [TestMethod] - public void CtorTest_Array_Middle_Ignored() - { - var range = new IPAddressRange(new[] { IPAddress.Parse("192.168.0.80"), IPAddress.Parse("192.168.0.254"), IPAddress.Parse("192.168.0.88") }); - range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); - range.Begin.ToString().Is("192.168.0.80"); - range.End.AddressFamily.Is(AddressFamily.InterNetwork); - range.End.ToString().Is("192.168.0.88"); - } - - [TestMethod] public void CtorTest_MaskLength() { @@ -94,17 +43,6 @@ public void CtorTest_MaskLength() range.End.ToString().Is("192.168.0.255"); } - - [TestMethod] - public void CtorTest_SubnetMask() - { - var range = new IPAddressRange(IPAddress.Parse("192.168.0.80"), IPAddress.Parse("255.255.255.0")); - range.Begin.AddressFamily.Is(AddressFamily.InterNetwork); - range.Begin.ToString().Is("192.168.0.0"); - range.End.AddressFamily.Is(AddressFamily.InterNetwork); - range.End.ToString().Is("192.168.0.255"); - } - [TestMethod] public void ParseTest_IPv4_Uniaddress() { @@ -306,6 +244,20 @@ public void DeserializeTest() range3.End.ToString().Is("::6"); } + [TestMethod] + public void SubnetMaskLengthTest_Valid() + { + var range = new IPAddressRange(IPAddress.Parse("192.168.75.23"), IPAddressRange.SubnetMaskLength(IPAddress.Parse("255.255.254.0"))); + range.Begin.ToString().Is("192.168.74.0"); + range.End.ToString().Is("192.168.75.255"); + } + + [TestMethod] + public void SubnetMaskLengthTest_Invalid() + { + AssertEx.Throws(() => + new IPAddressRange(IPAddress.Parse("192.168.75.23"), IPAddressRange.SubnetMaskLength(IPAddress.Parse("255.255.54.0")))); + } [TestMethod] public void Enumerate_IPv4() diff --git a/IPAddressRange/IPAddressRange.cs b/IPAddressRange/IPAddressRange.cs index ccae6b9..d7033f3 100644 --- a/IPAddressRange/IPAddressRange.cs +++ b/IPAddressRange/IPAddressRange.cs @@ -31,18 +31,14 @@ public IPAddressRange(IPAddress singleAddress) } /// - /// Create a new range based on the first and last IPs in an array. - /// Throws an exception if the array is empty or null. All but the first and last values - /// are ignored. Passing a single value is equivalent to a range of one address. + /// Create a new range from a begin and end address. + /// Throws an exception if Begin comes after End, or the + /// addresses are not in the same family. /// - /// - public IPAddressRange(ICollection beginEnd) + public IPAddressRange(IPAddress begin, IPAddress end) { - if (beginEnd == null) throw new ArgumentNullException("beginEnd"); - if (beginEnd.Count == 0) throw new ArgumentOutOfRangeException("beginEnd", "Array contains no elements"); - - Begin = beginEnd.First(); - End = beginEnd.Last(); + Begin = begin; + End = end; if (Begin.AddressFamily != End.AddressFamily) throw new ArgumentException("Elements must be of the same address family", "beginEnd"); @@ -51,8 +47,11 @@ public IPAddressRange(ICollection beginEnd) if (!Bits.LE(endBytes, beginBytes)) throw new ArgumentException("Begin must be smaller than the End", "beginEnd"); } + /// - /// Creates a range from a base address and mask bits + /// Creates a range from a base address and mask bits. + /// This can also be used with to create a + /// range based on a subnet mask. /// /// /// @@ -66,21 +65,6 @@ public IPAddressRange(IPAddress baseAddress, int maskLength) Begin = new IPAddress(baseAdrBytes); End = new IPAddress(Bits.Or(baseAdrBytes, Bits.Not(maskBytes))); } - - /// - /// Creates a range from a base address and subnet mask. - /// - /// - /// - public IPAddressRange(IPAddress baseAddress, IPAddress subnetMask) - { - var baseAdrBytes = baseAddress.GetAddressBytes(); - var maskBytes = subnetMask.GetAddressBytes(); - baseAdrBytes = Bits.And(baseAdrBytes, maskBytes); - - Begin = new IPAddress(baseAdrBytes); - End = new IPAddress(Bits.Or(baseAdrBytes, Bits.Not(maskBytes))); - } [Obsolete("Use IPAddressRange.Parse static method instead.")] public IPAddressRange(string ipRangeString) @@ -155,17 +139,16 @@ public static IPAddressRange Parse(string ipRangeString) return new IPAddressRange(IPAddress.Parse(match.Groups["singleAddr"].Value)); if (!string.IsNullOrEmpty(match.Groups["begin"].Value)) - return new IPAddressRange(new[] - { + return new IPAddressRange( IPAddress.Parse(match.Groups["begin"].Value), IPAddress.Parse(match.Groups["end"].Value) - }); + ); if (!string.IsNullOrEmpty(match.Groups["bitmaskAddr"].Value)) return new IPAddressRange( IPAddress.Parse(match.Groups["bitmaskAddr"].Value), - IPAddress.Parse(match.Groups["bitmaskMask"].Value)); + SubnetMaskLength(IPAddress.Parse(match.Groups["bitmaskMask"].Value))); } throw new FormatException("Unknown IP range string."); @@ -185,6 +168,19 @@ public static bool TryParse(string ipRangeString, out IPAddressRange ipRange) } } + /// + /// Takes a subnetmask (eg, "255.255.254.0") and returns the CIDR bit length of that + /// address. Throws an exception if the passed address is not valid as a subnet mask. + /// + /// The subnet mask to use + /// + public static int SubnetMaskLength(IPAddress subnetMask) + { + var length = Bits.GetBitMaskLength(subnetMask.GetAddressBytes()); + if (length == null) throw new ArgumentException("Not a valid subnet mask", "subnetMask"); + return length.Value; + } + public IEnumerator GetEnumerator() { var first = Begin.GetAddressBytes();