2 // System.Net.IPv6Address.cs
\r
5 // Lawrence Pit (loz@cable.a2000.nl)
\r
7 // Note I: This class is not defined in the specs of .Net
\r
9 // Note II : The name of this class is perhaps unfortunate as it turns
\r
10 // out that in ms.net there's an internal class called
\r
11 // IPv6Address in namespace System.
\r
15 // Permission is hereby granted, free of charge, to any person obtaining
\r
16 // a copy of this software and associated documentation files (the
\r
17 // "Software"), to deal in the Software without restriction, including
\r
18 // without limitation the rights to use, copy, modify, merge, publish,
\r
19 // distribute, sublicense, and/or sell copies of the Software, and to
\r
20 // permit persons to whom the Software is furnished to do so, subject to
\r
21 // the following conditions:
\r
23 // The above copyright notice and this permission notice shall be
\r
24 // included in all copies or substantial portions of the Software.
\r
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
37 using System.Globalization;
\r
38 using System.Net.Sockets;
\r
39 using System.Runtime.InteropServices;
\r
42 namespace System.Net {
\r
45 /// Encapsulates an IPv6 Address.
\r
46 /// See RFC 2373 for more info on IPv6 addresses.
\r
49 internal class IPv6Address {
\r
50 private ushort [] address;
\r
51 private int prefixLength;
\r
52 private long scopeId = 0;
\r
54 public static readonly IPv6Address Loopback = IPv6Address.Parse ("::1");
\r
55 public static readonly IPv6Address Unspecified = IPv6Address.Parse ("::");
\r
57 public IPv6Address (ushort [] addr)
\r
60 throw new ArgumentNullException ("addr");
\r
61 if (addr.Length != 8)
\r
62 throw new ArgumentException ("addr");
\r
66 public IPv6Address (ushort [] addr, int prefixLength) : this (addr)
\r
68 if (prefixLength < 0 || prefixLength > 128)
\r
69 throw new ArgumentException ("prefixLength");
\r
70 this.prefixLength = prefixLength;
\r
73 public IPv6Address (ushort [] addr, int prefixLength, int scopeId) : this (addr, prefixLength)
\r
75 this.scopeId = scopeId;
\r
78 public static IPv6Address Parse (string ipString)
\r
80 if (ipString == null)
\r
81 throw new ArgumentNullException ("ipString");
\r
84 if (TryParse (ipString, out result))
\r
86 throw new FormatException ("Not a valid IPv6 address");
\r
89 static int Fill (ushort [] addr, string ipString)
\r
94 if (ipString.Length == 0)
\r
97 // Catch double uses of ::
\r
98 if (ipString.IndexOf ("::", StringComparison.Ordinal) != -1)
\r
101 for (int i = 0; i < ipString.Length; i++){
\r
102 char c = ipString [i];
\r
106 // Trailing : is not allowed.
\r
107 if (i == ipString.Length-1)
\r
113 addr [slot++] = (ushort) p;
\r
116 } if ('0' <= c && c <= '9')
\r
117 n = (int) (c - '0');
\r
118 else if ('a' <= c && c <= 'f')
\r
119 n = (int) (c - 'a' + 10);
\r
120 else if ('A' <= c && c <= 'F')
\r
121 n = (int) (c - 'A' + 10);
\r
125 if (p > UInt16.MaxValue)
\r
132 addr [slot++] = (ushort) p;
\r
137 static bool TryParse (string prefix, out int res)
\r
140 return Int32.TryParse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture, out res);
\r
143 res = Int32.Parse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture);
\r
144 } catch (Exception) {
\r
153 public static bool TryParse (string ipString, out IPv6Address result)
\r
156 if (ipString == null)
\r
159 if (ipString.Length > 2 &&
\r
160 ipString [0] == '[' &&
\r
161 ipString [ipString.Length - 1] == ']')
\r
162 ipString = ipString.Substring (1, ipString.Length - 2);
\r
164 if (ipString.Length < 2)
\r
169 int pos = ipString.LastIndexOf ('/');
\r
171 string prefix = ipString.Substring (pos + 1);
\r
172 if (!TryParse (prefix , out prefixLen))
\r
174 if (prefixLen < 0 || prefixLen > 128)
\r
176 ipString = ipString.Substring (0, pos);
\r
178 pos = ipString.LastIndexOf ('%');
\r
180 string prefix = ipString.Substring (pos + 1);
\r
181 if (!TryParse (prefix, out scopeId))
\r
183 ipString = ipString.Substring (0, pos);
\r
188 // At this point the prefix/suffixes have been removed
\r
189 // and we only have to deal with the ipv4 or ipv6 addressed
\r
191 ushort [] addr = new ushort [8];
\r
194 // Is there an ipv4 address at the end?
\r
197 int pos2 = ipString.LastIndexOf (':');
\r
202 if (pos2 < (ipString.Length - 1)) {
\r
203 string ipv4Str = ipString.Substring (pos2 + 1);
\r
204 if (ipv4Str.IndexOf ('.') != -1) {
\r
207 if (!IPAddress.TryParse (ipv4Str, out ip))
\r
210 long a = ip.InternalIPv4Address;
\r
211 addr [6] = (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff)));
\r
212 addr [7] = (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff)));
\r
213 if (pos2 > 0 && ipString [pos2 - 1] == ':')
\r
214 ipString = ipString.Substring (0, pos2 + 1);
\r
216 ipString = ipString.Substring (0, pos2);
\r
223 // Only an ipv6 block remains, either:
\r
224 // "hexnumbers::hexnumbers", "hexnumbers::" or "hexnumbers"
\r
226 int c = ipString.IndexOf ("::", StringComparison.Ordinal);
\r
228 int right_slots = Fill (addr, ipString.Substring (c+2));
\r
229 if (right_slots == -1){
\r
233 if (right_slots + slots > 8){
\r
237 int d = 8-slots-right_slots;
\r
238 for (int i = right_slots; i > 0; i--){
\r
239 addr [i+d-1] = addr [i-1];
\r
243 int left_slots = Fill (addr, ipString.Substring (0, c));
\r
244 if (left_slots == -1)
\r
247 if (left_slots + right_slots + slots > 7)
\r
250 if (Fill (addr, ipString) != 8-slots)
\r
254 // Now check the results in the ipv6-address range only
\r
256 for (int i = 0; i < slots; i++){
\r
257 if (addr [i] != 0 || i == 5 && addr [i] != 0xffff)
\r
261 // check IPv4 validity
\r
262 if (ipv4 && !ipv6) {
\r
263 for (int i = 0; i < 5; i++) {
\r
268 if (addr [5] != 0 && addr [5] != 0xffff)
\r
272 result = new IPv6Address (addr, prefixLen, scopeId);
\r
276 public ushort [] Address {
\r
277 get { return address; }
\r
280 public int PrefixLength {
\r
281 get { return this.prefixLength; }
\r
284 public long ScopeId {
\r
293 public ushort this [int index] {
\r
294 get { return address [index]; }
\r
297 public AddressFamily AddressFamily {
\r
298 get { return AddressFamily.InterNetworkV6; }
\r
301 public static bool IsLoopback (IPv6Address addr)
\r
303 if (addr.address [7] != 1)
\r
306 int x = addr.address [6] >> 8;
\r
307 if (x != 0x7f && x != 0)
\r
310 for (int i = 0; i < 4; i++) {
\r
311 if (addr.address [i] != 0)
\r
315 if (addr.address [5] != 0 && addr.address [5] != 0xffff)
\r
321 private static ushort SwapUShort (ushort number)
\r
323 return (ushort) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );
\r
326 // Convert the address into a format expected by the IPAddress (long) ctor
\r
327 private int AsIPv4Int ()
\r
329 return (SwapUShort (address [7]) << 16) + SwapUShort (address [6]);
\r
332 public bool IsIPv4Compatible ()
\r
334 for (int i = 0; i < 6; i++)
\r
335 if (address [i] != 0)
\r
337 return (AsIPv4Int () > 1);
\r
340 public bool IsIPv4Mapped ()
\r
342 for (int i = 0; i < 5; i++)
\r
343 if (address [i] != 0)
\r
345 return address [5] == 0xffff;
\r
349 /// Overrides System.Object.ToString to return
\r
350 /// this object rendered in a canonicalized notation
\r
352 public override string ToString ()
\r
354 StringBuilder s = new StringBuilder ();
\r
357 if(IsIPv4Compatible() || IsIPv4Mapped())
\r
364 s.Append(new IPAddress( AsIPv4Int ()).ToString ());
\r
366 return s.ToString ();
\r
370 int bestChStart = -1; // Best chain start
\r
371 int bestChLen = 0; // Best chain length
\r
372 int currChLen = 0; // Current chain length
\r
374 // Looks for the longest zero chain
\r
375 for (int i=0; i<8; i++)
\r
377 if (address[i] != 0)
\r
379 if ((currChLen > bestChLen)
\r
380 && (currChLen > 1))
\r
382 bestChLen = currChLen;
\r
383 bestChStart = i - currChLen;
\r
390 if ((currChLen > bestChLen)
\r
391 && (currChLen > 1))
\r
393 bestChLen = currChLen;
\r
394 bestChStart = 8 - currChLen;
\r
397 // makes the string
\r
398 if (bestChStart == 0)
\r
400 for (int i=0; i<8; i++)
\r
402 if (i == bestChStart)
\r
405 i += (bestChLen - 1);
\r
408 s.AppendFormat("{0:x}", address [i]);
\r
409 if (i < 7) s.Append (':');
\r
413 s.Append ('%').Append (scopeId);
\r
414 return s.ToString ();
\r
417 public string ToString (bool fullLength)
\r
420 return ToString ();
\r
422 StringBuilder sb = new StringBuilder ();
\r
423 for (int i=0; i < address.Length - 1; i++) {
\r
424 sb.AppendFormat ("{0:X4}:", address [i]);
\r
426 sb.AppendFormat ("{0:X4}", address [address.Length - 1]);
\r
427 return sb.ToString ();
\r
431 /// Whether both objects are equal.
\r
433 public override bool Equals (object other)
\r
435 System.Net.IPv6Address ipv6 = other as System.Net.IPv6Address;
\r
436 if (ipv6 != null) {
\r
437 for (int i = 0; i < 8; i++)
\r
438 if (this.address [i] != ipv6.address [i])
\r
443 System.Net.IPAddress ipv4 = other as System.Net.IPAddress;
\r
444 if (ipv4 != null) {
\r
445 for (int i = 0; i < 5; i++)
\r
446 if (address [i] != 0)
\r
449 if (address [5] != 0 && address [5] != 0xffff)
\r
452 long a = ipv4.InternalIPv4Address;
\r
453 if (address [6] != (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff))) ||
\r
454 address [7] != (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff))))
\r
463 public override int GetHashCode ()
\r
465 return Hash (((((int) address [0]) << 16) + address [1]),
\r
466 ((((int) address [2]) << 16) + address [3]),
\r
467 ((((int) address [4]) << 16) + address [5]),
\r
468 ((((int) address [6]) << 16) + address [7]));
\r
471 private static int Hash (int i, int j, int k, int l)
\r
473 return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);
\r