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
95 if (ipString.Length == 0)
\r
98 // Catch double uses of ::
\r
99 if (ipString.IndexOf ("::", StringComparison.Ordinal) != -1)
\r
102 for (int i = 0; i < ipString.Length; i++){
\r
103 char c = ipString [i];
\r
107 // Leading : is not allowed.
\r
111 // Trailing : is not allowed.
\r
112 if (i == ipString.Length-1)
\r
118 addr [slot++] = (ushort) p;
\r
126 if ('0' <= c && c <= '9')
\r
127 n = (int) (c - '0');
\r
128 else if ('a' <= c && c <= 'f')
\r
129 n = (int) (c - 'a' + 10);
\r
130 else if ('A' <= c && c <= 'F')
\r
131 n = (int) (c - 'A' + 10);
\r
135 if (p > UInt16.MaxValue)
\r
142 addr [slot++] = (ushort) p;
\r
147 static bool TryParse (string prefix, out int res)
\r
149 return Int32.TryParse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture, out res);
\r
152 public static bool TryParse (string ipString, out IPv6Address result)
\r
155 if (ipString == null)
\r
158 if (ipString.Length > 2 &&
\r
159 ipString [0] == '[' &&
\r
160 ipString [ipString.Length - 1] == ']')
\r
161 ipString = ipString.Substring (1, ipString.Length - 2);
\r
163 if (ipString.Length < 2)
\r
168 int pos = ipString.LastIndexOf ('/');
\r
170 string prefix = ipString.Substring (pos + 1);
\r
171 if (!TryParse (prefix , out prefixLen))
\r
173 if (prefixLen < 0 || prefixLen > 128)
\r
175 ipString = ipString.Substring (0, pos);
\r
177 pos = ipString.LastIndexOf ('%');
\r
179 string prefix = ipString.Substring (pos + 1);
\r
180 if (!TryParse (prefix, out scopeId))
\r
182 ipString = ipString.Substring (0, pos);
\r
187 // At this point the prefix/suffixes have been removed
\r
188 // and we only have to deal with the ipv4 or ipv6 addressed
\r
190 ushort [] addr = new ushort [8];
\r
193 // Is there an ipv4 address at the end?
\r
195 int pos2 = ipString.LastIndexOf (':');
\r
200 if (pos2 < (ipString.Length - 1)) {
\r
201 string ipv4Str = ipString.Substring (pos2 + 1);
\r
202 if (ipv4Str.IndexOf ('.') != -1) {
\r
205 if (!IPAddress.TryParse (ipv4Str, out ip))
\r
208 long a = ip.InternalIPv4Address;
\r
209 addr [6] = (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff)));
\r
210 addr [7] = (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff)));
\r
211 if (pos2 > 0 && ipString [pos2 - 1] == ':')
\r
212 ipString = ipString.Substring (0, pos2 + 1);
\r
214 ipString = ipString.Substring (0, pos2);
\r
220 // Only an ipv6 block remains, either:
\r
221 // "hexnumbers::hexnumbers", "hexnumbers::" or "hexnumbers"
\r
223 int c = ipString.IndexOf ("::", StringComparison.Ordinal);
\r
225 int right_slots = Fill (addr, ipString.Substring (c+2));
\r
226 if (right_slots == -1){
\r
230 if (right_slots + slots > 8){
\r
234 int d = 8-slots-right_slots;
\r
235 for (int i = right_slots; i > 0; i--){
\r
236 addr [i+d-1] = addr [i-1];
\r
240 int left_slots = Fill (addr, ipString.Substring (0, c));
\r
241 if (left_slots == -1)
\r
244 if (left_slots + right_slots + slots > 7)
\r
247 if (Fill (addr, ipString) != 8-slots)
\r
251 result = new IPv6Address (addr, prefixLen, scopeId);
\r
255 public ushort [] Address {
\r
256 get { return address; }
\r
259 public int PrefixLength {
\r
260 get { return this.prefixLength; }
\r
263 public long ScopeId {
\r
272 public ushort this [int index] {
\r
273 get { return address [index]; }
\r
276 public AddressFamily AddressFamily {
\r
277 get { return AddressFamily.InterNetworkV6; }
\r
280 public static bool IsLoopback (IPv6Address addr)
\r
282 if (addr.address [7] != 1)
\r
285 int x = addr.address [6] >> 8;
\r
286 if (x != 0x7f && x != 0)
\r
289 for (int i = 0; i < 4; i++) {
\r
290 if (addr.address [i] != 0)
\r
294 if (addr.address [5] != 0 && addr.address [5] != 0xffff)
\r
300 private static ushort SwapUShort (ushort number)
\r
302 return (ushort) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );
\r
305 // Convert the address into a format expected by the IPAddress (long) ctor
\r
306 // This needs to be unsigned to satisfy the '> 1' test in IsIPv4Compatible()
\r
307 private uint AsIPv4Int ()
\r
309 return (uint)(SwapUShort (address [7]) << 16) + SwapUShort (address [6]);
\r
312 public bool IsIPv4Compatible ()
\r
314 for (int i = 0; i < 6; i++)
\r
315 if (address [i] != 0)
\r
317 /* MS .net only seems to format the last 4
\r
318 * bytes as an IPv4 address if address[6] is
\r
321 if (address[6] == 0)
\r
323 return (AsIPv4Int () > 1);
\r
326 public bool IsIPv4Mapped ()
\r
328 for (int i = 0; i < 5; i++)
\r
329 if (address [i] != 0)
\r
331 /* MS .net only seems to format the last 4
\r
332 * bytes as an IPv4 address if address[6] is
\r
335 if (address[6] == 0)
\r
338 return address [5] == 0xffff;
\r
342 /// Overrides System.Object.ToString to return
\r
343 /// this object rendered in a canonicalized notation
\r
345 public override string ToString ()
\r
347 StringBuilder s = new StringBuilder ();
\r
350 if(IsIPv4Compatible() || IsIPv4Mapped())
\r
357 s.Append(new IPAddress( AsIPv4Int ()).ToString ());
\r
359 return s.ToString ();
\r
363 int bestChStart = -1; // Best chain start
\r
364 int bestChLen = 0; // Best chain length
\r
365 int currChLen = 0; // Current chain length
\r
367 // Looks for the longest zero chain
\r
368 for (int i=0; i<8; i++)
\r
370 if (address[i] != 0)
\r
372 if ((currChLen > bestChLen)
\r
373 && (currChLen > 1))
\r
375 bestChLen = currChLen;
\r
376 bestChStart = i - currChLen;
\r
383 if ((currChLen > bestChLen)
\r
384 && (currChLen > 1))
\r
386 bestChLen = currChLen;
\r
387 bestChStart = 8 - currChLen;
\r
390 // makes the string
\r
391 if (bestChStart == 0)
\r
393 for (int i=0; i<8; i++)
\r
395 if (i == bestChStart)
\r
398 i += (bestChLen - 1);
\r
401 s.AppendFormat("{0:x}", address [i]);
\r
402 if (i < 7) s.Append (':');
\r
406 s.Append ('%').Append (scopeId);
\r
407 return s.ToString ();
\r
410 public string ToString (bool fullLength)
\r
413 return ToString ();
\r
415 StringBuilder sb = new StringBuilder ();
\r
416 for (int i=0; i < address.Length - 1; i++) {
\r
417 sb.AppendFormat ("{0:X4}:", address [i]);
\r
419 sb.AppendFormat ("{0:X4}", address [address.Length - 1]);
\r
420 return sb.ToString ();
\r
424 /// Whether both objects are equal.
\r
426 public override bool Equals (object other)
\r
428 System.Net.IPv6Address ipv6 = other as System.Net.IPv6Address;
\r
429 if (ipv6 != null) {
\r
430 for (int i = 0; i < 8; i++)
\r
431 if (this.address [i] != ipv6.address [i])
\r
436 System.Net.IPAddress ipv4 = other as System.Net.IPAddress;
\r
437 if (ipv4 != null) {
\r
438 for (int i = 0; i < 5; i++)
\r
439 if (address [i] != 0)
\r
442 if (address [5] != 0 && address [5] != 0xffff)
\r
445 long a = ipv4.InternalIPv4Address;
\r
446 if (address [6] != (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff))) ||
\r
447 address [7] != (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff))))
\r
456 public override int GetHashCode ()
\r
458 return Hash (((((int) address [0]) << 16) + address [1]),
\r
459 ((((int) address [2]) << 16) + address [3]),
\r
460 ((((int) address [4]) << 16) + address [5]),
\r
461 ((((int) address [6]) << 16) + address [7]));
\r
464 private static int Hash (int i, int j, int k, int l)
\r
466 return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);
\r