forked from jsakamoto/ipaddressrange
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathIPAddressRange.cs
More file actions
197 lines (166 loc) · 7.73 KB
/
IPAddressRange.cs
File metadata and controls
197 lines (166 loc) · 7.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Text;
using System.Text.RegularExpressions;
namespace NetTools
{
[Serializable]
public class IPAddressRange : ISerializable, IEnumerable<IPAddress>
{
public IPAddress Begin { get; set; }
public IPAddress End { get; set; }
/// <summary>
/// Creates an empty range object, equivalent to "0.0.0.0/0".
/// </summary>
public IPAddressRange() : this(new IPAddress(0L)) { }
/// <summary>
/// Creates a new range with the same start/end address (range of one)
/// </summary>
/// <param name="singleAddress"></param>
public IPAddressRange(IPAddress singleAddress)
{
Begin = End = singleAddress;
}
/// <summary>
/// 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.
/// </summary>
public IPAddressRange(IPAddress begin, IPAddress end)
{
Begin = begin;
End = end;
if (Begin.AddressFamily != End.AddressFamily) throw new ArgumentException("Elements must be of the same address family", "beginEnd");
var beginBytes = Begin.GetAddressBytes();
var endBytes = End.GetAddressBytes();
if (!Bits.LE(endBytes, beginBytes)) throw new ArgumentException("Begin must be smaller than the End", "beginEnd");
}
/// <summary>
/// Creates a range from a base address and mask bits.
/// This can also be used with <see cref="SubnetMaskLength"/> to create a
/// range based on a subnet mask.
/// </summary>
/// <param name="baseAddress"></param>
/// <param name="maskLength"></param>
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)));
}
[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)
{
var names = new List<string>();
foreach (var item in info) names.Add(item.Name);
Func<string,IPAddress> deserialize = (name) => names.Contains(name) ?
IPAddress.Parse(info.GetValue(name, typeof(object)).ToString()) :
new IPAddress(0L);
this.Begin = deserialize("Begin");
this.End = deserialize("End");
}
public bool Contains(IPAddress ipaddress)
{
if (ipaddress.AddressFamily != this.Begin.AddressFamily) return false;
var adrBytes = ipaddress.GetAddressBytes();
return Bits.GE(this.Begin.GetAddressBytes(), adrBytes) && Bits.LE(this.End.GetAddressBytes(), adrBytes);
}
public bool Contains(IPAddressRange range)
{
if (this.Begin.AddressFamily != range.Begin.AddressFamily) return false;
return
Bits.GE(this.Begin.GetAddressBytes(), range.Begin.GetAddressBytes()) &&
Bits.LE(this.End.GetAddressBytes(), range.End.GetAddressBytes());
throw new NotImplementedException();
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Begin", this.Begin != null ? this.Begin.ToString() : "");
info.AddValue("End", this.End != null ? this.End.ToString() : "");
}
protected static string ParseRegex =
@"^\s*(?:" +
@"(?<cidrBase>[\da-f\.:]+)\s*/\s*(?<cidrMask>\d+)" + // Pattern 1. CIDR range: "192.168.0.0/24", "fe80::/10"
@"|(?<singleAddr>[\da-f\.:]+)" + // Pattern 2. Uni address: "127.0.0.1", ":;1"
@"|(?<begin>[\da-f\.:]+)\s*[\-–]\s*(?<end>[\da-f\.:]+)" + // Pattern 3. Begin end range: "169.258.0.0-169.258.0.255"
@"|(?<bitmaskAddr>[\da-f\.:]+)\s*/\s*(?<bitmaskMask>[\da-f\.:]+)" + // Pattern 4. Bit mask range: "192.168.0.0/255.255.255.0"
@")\s*$";
/// <summary>
/// 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".
/// </summary>
/// <param name="ipRangeString"></param>
/// <returns></returns>
public static IPAddressRange Parse(string ipRangeString)
{
var match = Regex.Match(ipRangeString, ParseRegex, RegexOptions.IgnoreCase);
if (match.Success)
{
if (!string.IsNullOrEmpty(match.Groups["cidrBase"].Value))
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(match.Groups["singleAddr"].Value));
if (!string.IsNullOrEmpty(match.Groups["begin"].Value))
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),
SubnetMaskLength(IPAddress.Parse(match.Groups["bitmaskMask"].Value)));
}
throw new FormatException("Unknown IP range string.");
}
public static bool TryParse(string ipRangeString, out IPAddressRange ipRange)
{
try
{
ipRange = IPAddressRange.Parse(ipRangeString);
return true;
}
catch (Exception)
{
ipRange = null;
return false;
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="subnetMask">The subnet mask to use</param>
/// <returns></returns>
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<IPAddress> GetEnumerator()
{
var first = Begin.GetAddressBytes();
var last = End.GetAddressBytes();
for (var ip = first; Bits.GE(ip, last); ip = Bits.Increment(ip))
yield return new IPAddress(ip);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}