1
+ // <copyright file="IPNetwork2Parse.cs" company="IPNetwork">
2
+ // Copyright (c) IPNetwork. All rights reserved.
3
+ // </copyright>
4
+
5
+ using System . Collections . Generic ;
6
+ using System . Net . Sockets ;
7
+ using System . Numerics ;
8
+
9
+ namespace System . Net ;
10
+
11
+ /// <summary>
12
+ /// the parse methods.
13
+ /// </summary>
14
+ public partial class IPNetwork2
15
+ {
16
+ /// <summary>
17
+ /// 192.168.1.45 - 192.168.1.65
18
+ ///
19
+ /// ```
20
+ /// 192.168.1.45/32 (covers: 192.168.1.45)
21
+ /// 192.168.1.46/31 (covers: 192.168.1.46 - 192.168.1.47)
22
+ /// 192.168.1.48/28 (covers: 192.168.1.48 - 192.168.1.63)
23
+ /// 192.168.1.64/31 (covers: 192.168.1.64 - 192.168.1.65)
24
+ /// ```
25
+ ///
26
+ /// </summary>
27
+ /// <param name="range">A string containing an ip range to convert (192.168.1.45 - 192.168.1.65).</param>
28
+ /// <param name="ipnetworks">An IPNetwork List equivalent to the network contained in the range</param>
29
+ /// <returns>true if parse was successful, false if the parse failed.</returns>
30
+ public static bool TryParseRange ( string range , out IEnumerable < IPNetwork2 > ipnetworks )
31
+ {
32
+ return InternalParseRange ( true , range , out ipnetworks ) ;
33
+ }
34
+
35
+ /// <summary>
36
+ /// 192.168.1.45 - 192.168.1.65
37
+ ///
38
+ /// ```
39
+ /// 192.168.1.45/32 (covers: 192.168.1.45)
40
+ /// 192.168.1.46/31 (covers: 192.168.1.46 - 192.168.1.47)
41
+ /// 192.168.1.48/28 (covers: 192.168.1.48 - 192.168.1.63)
42
+ /// 192.168.1.64/31 (covers: 192.168.1.64 - 192.168.1.65)
43
+ /// ```
44
+ ///
45
+ /// </summary>
46
+ /// <param name="start">A string containing an ip range start (**192.168.1.45** - 192.168.1.65).</param>
47
+ /// <param name="end">A string containing an ip range end (192.168.1.45 - **192.168.1.65**).</param>
48
+ /// <param name="ipnetworks">An IPNetwork List equivalent to the network contained in the range</param>
49
+ /// <returns>true if parse was successful, false if the parse failed.</returns>
50
+ public static bool TryParseRange ( string start , string end , out IEnumerable < IPNetwork2 > ipnetworks )
51
+ {
52
+ return InternalParseRange ( true , start , end , out ipnetworks ) ;
53
+ }
54
+
55
+ /// <summary>
56
+ /// 192.168.1.45 - 192.168.1.65
57
+ ///
58
+ /// ```
59
+ /// 192.168.1.45/32 (covers: 192.168.1.45)
60
+ /// 192.168.1.46/31 (covers: 192.168.1.46 - 192.168.1.47)
61
+ /// 192.168.1.48/28 (covers: 192.168.1.48 - 192.168.1.63)
62
+ /// 192.168.1.64/31 (covers: 192.168.1.64 - 192.168.1.65)
63
+ /// ```
64
+ ///
65
+ /// </summary>
66
+ /// <param name="range">A string containing an ip range to convert (192.168.1.45 - 192.168.1.65).</param>
67
+ /// <returns>An IPNetwork List equivalent to the network contained in the range.</returns>
68
+ public static IEnumerable < IPNetwork2 > ParseRange ( string range )
69
+ {
70
+ InternalParseRange ( false , range , out IEnumerable < IPNetwork2 > ipnetworks ) ;
71
+ return ipnetworks ;
72
+ }
73
+
74
+ /// <summary>
75
+ /// 192.168.1.45, 192.168.1.65
76
+ ///
77
+ /// ```
78
+ /// 192.168.1.45/32 (covers: 192.168.1.45)
79
+ /// 192.168.1.46/31 (covers: 192.168.1.46 - 192.168.1.47)
80
+ /// 192.168.1.48/28 (covers: 192.168.1.48 - 192.168.1.63)
81
+ /// 192.168.1.64/31 (covers: 192.168.1.64 - 192.168.1.65)
82
+ /// ```
83
+ /// </summary>
84
+ /// <param name="start">A string containing a start range ip address.</param>
85
+ /// <param name="end">A string containing a end range ip address.</param>
86
+ /// <returns>An IPNetwork List equivalent to the network contained in the range.</returns>
87
+ public static IEnumerable < IPNetwork2 > ParseRange ( string start , string end )
88
+ {
89
+ InternalParseRange ( false , start , end , out IEnumerable < IPNetwork2 > ipnetworks ) ;
90
+ return ipnetworks ;
91
+ }
92
+
93
+ /// <summary>
94
+ /// 192.168.1.45 - 192.168.1.65
95
+ ///
96
+ /// ```
97
+ /// 192.168.1.45/32 (covers: 192.168.1.45)
98
+ /// 192.168.1.46/31 (covers: 192.168.1.46 - 192.168.1.47)
99
+ /// 192.168.1.48/28 (covers: 192.168.1.48 - 192.168.1.63)
100
+ /// 192.168.1.64/31 (covers: 192.168.1.64 - 192.168.1.65)
101
+ /// ```
102
+ /// </summary>
103
+ /// <param name="tryParse">Whether to throw exception or not during conversion.</param>
104
+ /// <param name="start">A string containing a start range ip address.</param>
105
+ /// <param name="end">A string containing a end range ip address.</param>
106
+ /// <param name="ipnetworks">The resulting IPNetworks.</param>
107
+ internal static bool InternalParseRange ( bool tryParse , string start , string end , out IEnumerable < IPNetwork2 > ipnetworks )
108
+ {
109
+ bool startParsed = IPAddress . TryParse ( start , out IPAddress startIp ) ;
110
+ if ( ! startParsed )
111
+ {
112
+ if ( ! tryParse )
113
+ {
114
+ throw new ArgumentException ( "Invalid start IPAddress" , nameof ( start ) ) ;
115
+ }
116
+
117
+ ipnetworks = null ;
118
+ return false ;
119
+ }
120
+
121
+ bool endParsed = IPAddress . TryParse ( end , out IPAddress endIp ) ;
122
+ if ( ! endParsed )
123
+ {
124
+ if ( ! tryParse )
125
+ {
126
+ throw new ArgumentException ( "Invalid end IPAddress" , nameof ( end ) ) ;
127
+ }
128
+
129
+ ipnetworks = null ;
130
+ return false ;
131
+ }
132
+
133
+ bool parsed = InternalParseRange ( tryParse , startIp , endIp , out ipnetworks ) ;
134
+ return parsed ;
135
+ }
136
+
137
+ /// <summary>
138
+ /// Internal parse an IPNetwork2.
139
+ /// </summary>
140
+ /// <param name="tryParse">Prevent exception.</param>
141
+ /// <param name="range">The network range parse.</param>
142
+ /// <param name="ipnetworks">The resulting IPNetworks.</param>
143
+ /// <exception cref="ArgumentNullException">When network is null.</exception>
144
+ /// <exception cref="ArgumentException">When network is not valid.</exception>
145
+ /// <returns>true if parsed, otherwise false</returns>
146
+ internal static bool InternalParseRange ( bool tryParse , string range , out IEnumerable < IPNetwork2 > ipnetworks )
147
+ {
148
+ if ( string . IsNullOrEmpty ( range ) )
149
+ {
150
+ if ( ! tryParse )
151
+ {
152
+ throw new ArgumentNullException ( nameof ( range ) ) ;
153
+ }
154
+
155
+ ipnetworks = null ;
156
+ return false ;
157
+ }
158
+
159
+ string [ ] args = range . Split ( [ ' ' , '-' ] , StringSplitOptions . RemoveEmptyEntries ) ;
160
+ if ( args . Length == 2 )
161
+ {
162
+ bool parsed3 = InternalParseRange ( tryParse , args [ 0 ] , args [ 1 ] , out ipnetworks ) ;
163
+ return parsed3 ;
164
+ }
165
+
166
+ if ( ! tryParse )
167
+ {
168
+ throw new ArgumentOutOfRangeException ( nameof ( range ) ) ;
169
+ }
170
+ ipnetworks = null ;
171
+ return false ;
172
+ }
173
+
174
+ /// <summary>
175
+ /// 192.168.168.100 255.255.255.0
176
+ ///
177
+ /// Network : 192.168.168.0
178
+ /// Netmask : 255.255.255.0
179
+ /// Cidr : 24
180
+ /// Start : 192.168.168.1
181
+ /// End : 192.168.168.254
182
+ /// Broadcast : 192.168.168.255.
183
+ /// </summary>
184
+ /// <param name="tryParse">Whether to throw exception or not during conversion.</param>
185
+ /// <param name="start">A start range ip address.</param>
186
+ /// <param name="end">An end range ip address.</param>
187
+ /// <param name="ipnetworks">The resulting IPNetworks.</param>
188
+ internal static bool InternalParseRange ( bool tryParse , IPAddress start , IPAddress end , out IEnumerable < IPNetwork2 > ipnetworks )
189
+ {
190
+ if ( start == null )
191
+ {
192
+ if ( ! tryParse )
193
+ {
194
+ throw new ArgumentNullException ( nameof ( start ) ) ;
195
+ }
196
+
197
+ ipnetworks = null ;
198
+ return false ;
199
+ }
200
+
201
+ if ( end == null )
202
+ {
203
+ if ( ! tryParse )
204
+ {
205
+ throw new ArgumentNullException ( nameof ( end ) ) ;
206
+ }
207
+ ipnetworks = null ;
208
+ return false ;
209
+ }
210
+
211
+ if ( end . AddressFamily != start . AddressFamily )
212
+ {
213
+ if ( ! tryParse )
214
+ {
215
+ throw new ArgumentException ( nameof ( AddressFamily ) ) ;
216
+ }
217
+ ipnetworks = null ;
218
+ return false ;
219
+ }
220
+
221
+ var result = new List < IPNetwork2 > ( ) ;
222
+
223
+ var startValue = ToBigInteger ( start ) ;
224
+ var endValue = ToBigInteger ( end ) ;
225
+
226
+ if ( startValue > endValue )
227
+ {
228
+ throw new ArgumentException ( "Start IP must be less than or equal to end IP" , nameof ( end ) ) ;
229
+ }
230
+
231
+ var addressFamily = start . AddressFamily ;
232
+ byte addressBits = addressFamily == AddressFamily . InterNetworkV6 ? ( byte ) 128 : ( byte ) 32 ;
233
+
234
+ var current = startValue ;
235
+ while ( current <= endValue )
236
+ {
237
+ // Find the largest CIDR block that starts at current and doesn't exceed endValue
238
+ byte prefixLength = FindOptimalPrefixLength ( current , endValue , addressBits ) ;
239
+
240
+ var network = new IPNetwork2 ( current , addressFamily , prefixLength ) ;
241
+ result . Add ( network ) ;
242
+
243
+ // Move to the next IP after this block
244
+ uint blockSize = ( uint ) ( 1 << ( addressBits - prefixLength ) ) ;
245
+ current += blockSize ;
246
+ }
247
+
248
+ ipnetworks = result ;
249
+ return true ;
250
+ }
251
+
252
+ private static byte FindOptimalPrefixLength ( BigInteger startIp , BigInteger endIp , int addressBits )
253
+ {
254
+ BigInteger remainingIps = endIp - startIp + 1 ;
255
+
256
+ // Find the number of trailing zeros in startIp (alignment)
257
+ int alignment = startIp . IsZero ? addressBits : CountTrailingZeros ( startIp ) ;
258
+
259
+ // Find the largest power of 2 that fits in the remaining range
260
+ int maxBlockSizeBits = remainingIps . IsZero ? 0 : GetHighestBitPosition ( remainingIps ) ;
261
+
262
+ // Take the minimum of alignment and what fits in range
263
+ int blockSizeBits = Math . Min ( alignment , maxBlockSizeBits ) ;
264
+
265
+ // Convert to prefix length
266
+ return ( byte ) ( addressBits - blockSizeBits ) ;
267
+ }
268
+
269
+ private static int CountTrailingZeros ( BigInteger value )
270
+ {
271
+ if ( value . IsZero ) return 0 ;
272
+
273
+ int count = 0 ;
274
+ while ( ( value & BigInteger . One ) == 0 )
275
+ {
276
+ value >>= 1 ;
277
+ count ++ ;
278
+ }
279
+ return count ;
280
+ }
281
+
282
+ private static int GetHighestBitPosition ( BigInteger value )
283
+ {
284
+ if ( value . IsZero ) return 0 ;
285
+
286
+ int position = 0 ;
287
+ while ( value > 1 )
288
+ {
289
+ value >>= 1 ;
290
+ position ++ ;
291
+ }
292
+ return position ;
293
+ }
294
+ }
0 commit comments