@@ -16,6 +16,11 @@ namespace System.Net
1616 [ Serializable ]
1717 public class IPAddress
1818 {
19+ internal const int IPv4AddressBytes = 4 ;
20+ internal const int IPv6AddressBytes = 16 ;
21+
22+ internal const int NumberOfLabels = IPv6AddressBytes / 2 ;
23+
1924 /// <summary>
2025 /// Provides an IP address that indicates that the server must listen for client activity on all network interfaces. This field is read-only.
2126 /// </summary>
@@ -28,16 +33,25 @@ public class IPAddress
2833
2934 internal readonly long Address ;
3035
36+ /// <summary>
37+ /// The Bind(EndPoint) method uses the IPv6Any field to indicate that a Socket must listen for client activity on all network interfaces.
38+ /// </summary>
39+ public static readonly IPAddress IPv6Any = new IPAddress ( new byte [ ] { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } , 0 ) ;
40+
41+ /// <summary>
42+ /// Provides the IP loopback address. This property is read-only.
43+ /// </summary>
44+ public static readonly IPAddress IPv6Loopback = new IPAddress ( new byte [ ] { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 } , 0 ) ;
45+
46+
3147 [ Diagnostics . DebuggerBrowsable ( Diagnostics . DebuggerBrowsableState . Never ) ]
3248 private readonly AddressFamily _family = AddressFamily . InterNetwork ;
3349
3450 [ Diagnostics . DebuggerBrowsable ( Diagnostics . DebuggerBrowsableState . Never ) ]
3551 private readonly ushort [ ] _numbers = new ushort [ NumberOfLabels ] ;
3652
37- internal const int IPv4AddressBytes = 4 ;
38- internal const int IPv6AddressBytes = 16 ;
39-
40- internal const int NumberOfLabels = IPv6AddressBytes / 2 ;
53+ [ Diagnostics . DebuggerBrowsable ( Diagnostics . DebuggerBrowsableState . Never ) ]
54+ private long _scopeid = 0 ;
4155
4256 /// <summary>
4357 /// Gets the address family of the IP address.
@@ -81,7 +95,7 @@ public IPAddress(long newAddress)
8195 /// <summary>
8296 /// Initializes a new instance of the <see cref="IPAddress"/> class with the address specified as a Byte array.
8397 /// </summary>
84- /// <param name="address"></param>
98+ /// <param name="address">The byte array value of the IP address. </param>
8599 /// <exception cref="ArgumentNullException"><paramref name="address"/> is <see langword="null"/>.</exception>
86100 /// <exception cref="ArgumentException"><paramref name="address"/> contains a bad IP address.</exception>
87101 /// <remarks>
@@ -122,6 +136,40 @@ public IPAddress(byte[] address)
122136 }
123137 }
124138
139+ /// <summary>
140+ /// Initializes a new instance of a IPV6 <see cref="IPAddress"/> class with the address specified as a Byte array.
141+ /// </summary>
142+ /// <param name="address">The byte array value of the IP address.</param>
143+ /// <param name="scopeid">The long value of the scope identifier.</param>
144+ /// <exception cref="ArgumentNullException"><paramref name="address"/> is <see langword="null"/>.</exception>
145+ /// <exception cref="ArgumentException"><paramref name="address"/> contains a bad IP address.</exception>
146+ /// <remarks>
147+ /// The IPAddress is created with the <see cref="Address"/> property set to <paramref name="address"/>.
148+ /// If the length of <paramref name="address"/> is 4, <see cref="IPAddress"/>(Byte[]) constructs an IPv4 address; otherwise, an IPv6 address with a scope of 0 is constructed.
149+ /// The <see cref="Byte"/> array is assumed to be in network byte order with the most significant byte first in index position 0.
150+ /// </remarks>
151+ public IPAddress ( byte [ ] address , long scopeid ) : this ( address )
152+ {
153+ if ( address . Length == IPv6AddressBytes )
154+ {
155+ _scopeid = scopeid ;
156+ }
157+ else
158+ {
159+ // Not IPV6 address
160+ #pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
161+ throw new ArgumentException ( ) ;
162+ #pragma warning restore S3928 // OK to throw this here
163+ }
164+ }
165+
166+ private IPAddress ( ushort [ ] address , uint scopeid )
167+ {
168+ _family = AddressFamily . InterNetworkV6 ;
169+ _numbers = address ;
170+ _scopeid = scopeid ;
171+ }
172+
125173 /// <summary>
126174 /// Compares two IP addresses.
127175 /// </summary>
@@ -133,7 +181,31 @@ public override bool Equals(object obj)
133181
134182 if ( obj == null ) return false ;
135183
136- return this . Address == addr . Address ;
184+ // Compare family before address
185+ if ( _family != addr . AddressFamily )
186+ {
187+ return false ;
188+ }
189+
190+ if ( _family == AddressFamily . InterNetworkV6 )
191+ {
192+ // For IPv6 addresses, compare the full 128bit address
193+ for ( int i = 0 ; i < NumberOfLabels ; i ++ )
194+ {
195+ if ( addr . _numbers [ i ] != this . _numbers [ i ] )
196+ return false ;
197+ }
198+
199+ // Also scope must match
200+ if ( addr . _scopeid == this . _scopeid )
201+ return true ;
202+
203+ return false ;
204+ }
205+ else
206+ {
207+ return this . Address == addr . Address ;
208+ }
137209 }
138210
139211 /// <summary>
@@ -142,13 +214,30 @@ public override bool Equals(object obj)
142214 /// <returns>A Byte array.</returns>
143215 public byte [ ] GetAddressBytes ( )
144216 {
145- return new byte [ ]
217+ byte [ ] bytes ;
218+
219+ if ( _family == AddressFamily . InterNetworkV6 )
146220 {
147- ( byte ) ( Address ) ,
148- ( byte ) ( Address >> 8 ) ,
149- ( byte ) ( Address >> 16 ) ,
150- ( byte ) ( Address >> 24 )
151- } ;
221+ bytes = new byte [ NumberOfLabels * 2 ] ;
222+
223+ int j = 0 ;
224+ for ( int i = 0 ; i < NumberOfLabels ; i ++ )
225+ {
226+ bytes [ j ++ ] = ( byte ) ( ( this . _numbers [ i ] >> 8 ) & 0xFF ) ;
227+ bytes [ j ++ ] = ( byte ) ( ( this . _numbers [ i ] ) & 0xFF ) ;
228+ }
229+ return bytes ;
230+ }
231+ else
232+ {
233+ return new byte [ ]
234+ {
235+ ( byte ) ( Address ) ,
236+ ( byte ) ( Address >> 8 ) ,
237+ ( byte ) ( Address >> 16 ) ,
238+ ( byte ) ( Address >> 24 )
239+ } ;
240+ }
152241 }
153242
154243 /// <summary>
@@ -159,7 +248,40 @@ public byte[] GetAddressBytes()
159248 /// </returns>
160249 public static IPAddress Parse ( string ipString )
161250 {
162- return new IPAddress ( NetworkInterface . IPAddressFromString ( ipString ) ) ;
251+ // Check for IPV6 string and use separate parse method
252+ if ( ipString . IndexOf ( ':' ) != - 1 )
253+ {
254+ return new IPAddress ( NetworkInterface . IPV6AddressFromString ( ipString ) , 0 ) ;
255+ }
256+
257+ return new IPAddress ( NetworkInterface . IPV4AddressFromString ( ipString ) ) ;
258+ }
259+
260+ /// <summary>
261+ /// Get or Set IPV6 scope identifier.
262+ /// </summary>
263+ public long ScopeId
264+ {
265+ get
266+ {
267+ // Not valid for IPv4 addresses
268+ if ( _family == AddressFamily . InterNetwork )
269+ {
270+ throw new SocketException ( SocketError . OperationNotSupported ) ;
271+ }
272+
273+ return _scopeid ;
274+ }
275+ set
276+ {
277+ // Not valid for IPv4 addresses
278+ if ( _family == AddressFamily . InterNetwork )
279+ {
280+ throw new SocketException ( SocketError . OperationNotSupported ) ;
281+ }
282+
283+ _scopeid = value ;
284+ }
163285 }
164286
165287 /// <summary>
@@ -172,6 +294,11 @@ public static IPAddress Parse(string ipString)
172294 /// </remarks>
173295 public override string ToString ( )
174296 {
297+ if ( _family == AddressFamily . InterNetworkV6 )
298+ {
299+ return IPv6ToString ( _numbers ) ;
300+ }
301+
175302 return IPv4ToString ( ( uint ) Address ) ;
176303 }
177304
@@ -229,18 +356,100 @@ internal IPAddress Snapshot()
229356 case AddressFamily . InterNetwork :
230357 return new IPAddress ( Address ) ;
231358
232- // case AddressFamily.InterNetworkV6:
233- // return new IPAddress(m_Numbers , (uint)m_ScopeId );
359+ case AddressFamily . InterNetworkV6 :
360+ return new IPAddress ( _numbers , ( uint ) _scopeid ) ;
234361 }
235362
236363 throw new NotSupportedException ( ) ;
237364 }
238365
366+ /// <summary>
367+ /// Gets whether the IP address is an IPv4-mapped IPv6 address.
368+ /// </summary>
369+ /// <returns>
370+ /// true if the IP address is an IPv4-mapped IPv6 address; otherwise, false.
371+ /// </returns>
372+ public bool IsIPv4MappedToIPv6
373+ {
374+ get
375+ {
376+ if ( AddressFamily != AddressFamily . InterNetworkV6 )
377+ {
378+ return false ;
379+ }
380+
381+ for ( int i = 0 ; i < 5 ; i ++ )
382+ {
383+ if ( _numbers [ i ] != 0 )
384+ {
385+ return false ;
386+ }
387+ }
388+
389+ return ( _numbers [ 5 ] == 0xFFFF ) ;
390+ }
391+ }
392+
393+ /// <summary>
394+ /// Maps the <see cref="IPAddress"/> object to an IPv6 address.
395+ /// </summary>
396+ /// <remarks>
397+ /// Dual-stack sockets always require IPv6 addresses. The ability to interact with an IPv4 address
398+ /// requires the use of the IPv4-mapped IPv6 address format. Any IPv4 addresses must be represented
399+ /// in the IPv4-mapped IPv6 address format which enables an IPv6 only application to communicate
400+ /// with an IPv4 node.
401+ /// For example. IPv4 192.168.1.4 maps as IPV6 ::FFFF:192.168.1.4
402+ /// </remarks>
403+ /// <returns>
404+ /// Returns <see cref="IPAddress"/>. An IPV6 address.
405+ /// </returns>
406+ public IPAddress MapToIPv6 ( )
407+ {
408+ if ( AddressFamily == AddressFamily . InterNetworkV6 )
409+ {
410+ return this ;
411+ }
412+
413+ ushort [ ] labels = new ushort [ IPAddress . NumberOfLabels ] ;
414+ labels [ 5 ] = 0xFFFF ;
415+ labels [ 6 ] = ( ushort ) ( ( ( Address & 0x0000FF00 ) >> 8 ) | ( ( Address & 0x000000FF ) << 8 ) ) ;
416+ labels [ 7 ] = ( ushort ) ( ( ( Address & 0xFF000000 ) >> 24 ) | ( ( Address & 0x00FF0000 ) >> 8 ) ) ;
417+
418+ return new IPAddress ( labels , 0 ) ;
419+ }
420+
421+ /// <summary>
422+ /// Maps the <see cref="IPAddress"/> object to an IPv4 address.
423+ /// </summary>
424+ /// <remarks>
425+ /// Dual-stack sockets always require IPv6 addresses. The ability to interact with an IPv4
426+ /// address requires the use of the IPv4-mapped IPv6 address format.
427+ /// </remarks>
428+ /// <returns>
429+ /// Returns <see cref="IPAddress"/>. An IPV4 address.
430+ /// </returns>
431+ public IPAddress MapToIPv4 ( )
432+ {
433+ if ( AddressFamily == AddressFamily . InterNetwork )
434+ {
435+ return this ;
436+ }
437+
438+ long address = ( ( ( ( uint ) _numbers [ 6 ] & 0x0000FF00u ) >> 8 ) |
439+ ( ( ( uint ) _numbers [ 6 ] & 0x000000FFu ) << 8 ) ) |
440+ ( ( ( ( ( uint ) _numbers [ 7 ] & 0x0000FF00u ) >> 8 ) |
441+ ( ( ( uint ) _numbers [ 7 ] & 0x000000FFu ) << 8 ) ) << 16 ) ;
442+
443+ return new IPAddress ( address ) ;
444+ }
445+
239446 #region native methods
240447
241448 [ MethodImpl ( MethodImplOptions . InternalCall ) ]
242449 internal static extern string IPv4ToString ( uint ipv4Address ) ;
243450
451+ [ MethodImpl ( MethodImplOptions . InternalCall ) ]
452+ internal static extern string IPv6ToString ( ushort [ ] ipv6Address ) ;
244453 #endregion
245454 }
246455}
0 commit comments