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
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 using System.Net.Sockets;
\r
38 using System.Runtime.InteropServices;
\r
41 namespace System.Net {
\r
44 /// Encapsulates an IPv6 Address.
\r
45 /// See RFC 2373 for more info on IPv6 addresses.
\r
48 internal class IPv6Address {
\r
49 private ushort [] address;
\r
50 private int prefixLength;
\r
51 private long scopeId = 0;
\r
53 public static readonly IPv6Address Loopback = IPv6Address.Parse ("::1");
\r
54 public static readonly IPv6Address Unspecified = IPv6Address.Parse ("::");
\r
56 public IPv6Address (ushort [] addr)
\r
59 throw new ArgumentNullException ("addr");
\r
60 if (addr.Length != 8)
\r
61 throw new ArgumentException ("addr");
\r
65 public IPv6Address (ushort [] addr, int prefixLength) : this (addr)
\r
67 if (prefixLength < 0 || prefixLength > 128)
\r
68 throw new ArgumentException ("prefixLength");
\r
69 this.prefixLength = prefixLength;
\r
72 public IPv6Address (ushort [] addr, int prefixLength, int scopeId) : this (addr, prefixLength)
\r
74 this.scopeId = scopeId;
\r
77 public static IPv6Address Parse (string ipString)
\r
79 if (ipString == null)
\r
80 throw new ArgumentNullException ("ipString");
\r
82 if (ipString.Length > 2 &&
\r
83 ipString [0] == '[' &&
\r
84 ipString [ipString.Length - 1] == ']')
\r
85 ipString = ipString.Substring (1, ipString.Length - 2);
\r
87 if (ipString.Length < 2)
\r
88 throw new FormatException ("Not a valid IPv6 address");
\r
92 int pos = ipString.LastIndexOf ('/');
\r
94 string prefix = ipString.Substring (pos + 1);
\r
96 prefixLen = Int32.Parse (prefix);
\r
97 } catch (Exception) {
\r
100 if (prefixLen < 0 || prefixLen > 128)
\r
101 throw new FormatException ("Not a valid prefix length");
\r
102 ipString = ipString.Substring (0, pos);
\r
104 pos = ipString.LastIndexOf ('%');
\r
106 string prefix = ipString.Substring (pos + 1);
\r
108 scopeId = Int32.Parse (prefix);
\r
110 catch (Exception) {
\r
113 ipString = ipString.Substring (0, pos);
\r
117 ushort [] addr = new ushort [8];
\r
120 int pos2 = ipString.LastIndexOf (":");
\r
122 throw new FormatException ("Not a valid IPv6 address");
\r
123 if (pos2 < (ipString.Length - 1)) {
\r
124 string ipv4Str = ipString.Substring (pos2 + 1);
\r
125 if (ipv4Str.IndexOf ('.') != -1) {
\r
127 long a = IPAddress.Parse (ipv4Str).InternalIPv4Address;
\r
128 addr [6] = (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff)));
\r
129 addr [7] = (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff)));
\r
130 if (ipString [pos2 - 1] == ':')
\r
131 ipString = ipString.Substring (0, pos2 + 1);
\r
133 ipString = ipString.Substring (0, pos2);
\r
135 } catch (Exception) {
\r
136 throw new FormatException ("Not a valid IPv6 address");
\r
141 int origLen = ipString.Length;
\r
143 throw new FormatException ("Not a valid IPv6 address");
\r
144 ipString = ipString.Replace ("::", ":!:");
\r
145 int len = ipString.Length;
\r
146 if ((len - origLen) > 1)
\r
147 throw new FormatException ("Not a valid IPv6 address");
\r
149 if (ipString [1] == '!')
\r
150 ipString = ipString.Remove (0, 1);
\r
151 if (ipString [len - 2] == '!')
\r
152 ipString = ipString.Remove (len - 1, 1);
\r
153 if ((ipString.Length > 2) &&
\r
154 ((ipString [0] == ':') || (ipString [ipString.Length - 1] == ':')))
\r
155 throw new FormatException ("Not a valid IPv6 address");
\r
157 string [] pieces = ipString.Split (new char [] {':'});
\r
158 len = pieces.Length;
\r
159 if (len > (ipv4 ? 6 : 8))
\r
160 throw new FormatException ("Not a valid IPv6 address");
\r
161 int piecedouble = -1;
\r
163 for (int i = 0; i < len; i++) {
\r
164 string piece = pieces [i];
\r
168 int plen = piece.Length;
\r
170 throw new FormatException ("Not a valid IPv6 address");
\r
172 for (int j = 0; j < plen; j++)
\r
174 p = (p << 4) + Uri.FromHex (piece [j]);
\r
175 } catch (ArgumentException) {
\r
176 throw new FormatException ("Not a valid IPv6 address");
\r
178 addr [i] = (ushort) p;
179 if (p != 0 || (i == 5 && p != 0xffff))
184 //expand the :: token
\r
185 if (piecedouble != -1) {
\r
186 int totallen = (ipv4 ? 5 : 7);
\r
188 for (i = totallen; i >= (totallen - (len - piecedouble - 1)); i--) {
\r
189 addr [i] = addr [(len - 1) + i - totallen];
\r
191 for (; i >= piecedouble; i--) {
\r
194 } else if (len != (ipv4 ? 6 : 8))
\r
195 throw new FormatException ("Not a valid IPv6 address");
\r
197 // check IPv4 validity
199 for (int i = 0; i < 5; i++) {
201 throw new FormatException ("Not a valid IPv6 address");
204 if (addr [5] != 0 && addr [5] != 0xffff)
205 throw new FormatException ("Not a valid IPv6 address");
208 return new IPv6Address (addr, prefixLen, scopeId);
211 public ushort [] Address {
\r
212 get { return address; }
\r
215 public int PrefixLength {
\r
216 get { return this.prefixLength; }
\r
219 public long ScopeId {
\r
228 public ushort this [int index] {
\r
229 get { return address [index]; }
\r
232 public AddressFamily AddressFamily {
\r
233 get { return AddressFamily.InterNetworkV6; }
\r
237 /// Used to tell whether the given address is the loopback address.
\r
239 public static bool IsLoopback (IPv6Address addr)
\r
241 for (int i = 0; i < 4; i++)
\r
242 if (addr.address [i] != 0)
\r
244 if ((addr.address [5] != 0) && (addr.address [5] != 0xffff))
\r
246 if ((addr.address [6] >> 8) == 0x7f)
\r
248 return ((addr.address [5] == 0) &&
\r
249 (addr.address [6] == 0) &&
\r
250 (addr.address [7] == 1));
\r
254 private static ushort SwapUShort (ushort number)
256 return (ushort) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );
259 // Convert the address into a format expected by the IPAddress (long) ctor
260 private int AsIPv4Int ()
262 return (SwapUShort (address [7]) << 16) + SwapUShort (address [6]);
265 public bool IsIPv4Compatible ()
\r
267 for (int i = 0; i < 6; i++)
\r
268 if (address [i] != 0)
\r
270 return (AsIPv4Int () > 1);
273 public bool IsIPv4Mapped ()
\r
275 for (int i = 0; i < 5; i++)
\r
276 if (address [i] != 0)
\r
278 return address [5] == 0xffff;
\r
282 /// Overrides System.Object.ToString to return
\r
283 /// this object rendered in a canonicalized notation
\r
285 public override string ToString ()
\r
287 StringBuilder s = new StringBuilder ();
\r
290 if(IsIPv4Compatible() || IsIPv4Mapped())
\r
297 s.Append(new IPAddress( AsIPv4Int ()).ToString ());
299 return s.ToString ();
\r
303 int bestChStart = -1; // Best chain start
\r
304 int bestChLen = 0; // Best chain length
\r
305 int currChLen = 0; // Current chain length
\r
307 // Looks for the longest zero chain
\r
308 for (int i=0; i<8; i++)
\r
310 if (address[i] != 0)
\r
312 if ((currChLen > bestChLen)
\r
313 && (currChLen > 1))
\r
315 bestChLen = currChLen;
\r
316 bestChStart = i - currChLen;
\r
323 if ((currChLen > bestChLen)
\r
324 && (currChLen > 1))
\r
326 bestChLen = currChLen;
\r
327 bestChStart = 8 - currChLen;
\r
330 // makes the string
\r
331 if (bestChStart == 0)
\r
333 for (int i=0; i<8; i++)
\r
335 if (i == bestChStart)
\r
338 i += (bestChLen - 1);
\r
341 s.AppendFormat("{0:x}", address [i]);
\r
342 if (i < 7) s.Append (':');
\r
345 return s.ToString ();
\r
349 /// Whether both objects are equal.
\r
351 public override bool Equals (object other)
\r
353 System.Net.IPv6Address ipv6 = other as System.Net.IPv6Address;
\r
354 if (ipv6 != null) {
\r
355 for (int i = 0; i < 8; i++)
\r
356 if (this.address [i] != ipv6.address [i])
\r
361 System.Net.IPAddress ipv4 = other as System.Net.IPAddress;
\r
362 if (ipv4 != null) {
\r
363 for (int i = 0; i < 5; i++)
\r
364 if (address [i] != 0)
\r
367 if (address [5] != 0 && address [5] != 0xffff)
\r
370 long a = ipv4.InternalIPv4Address;
\r
371 if (address [6] != (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff))) ||
\r
372 address [7] != (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff))))
\r
381 public override int GetHashCode ()
\r
383 return Hash (((((int) address [0]) << 16) + address [1]),
\r
384 ((((int) address [2]) << 16) + address [3]),
\r
385 ((((int) address [4]) << 16) + address [5]),
\r
386 ((((int) address [6]) << 16) + address [7]));
\r
389 private static int Hash (int i, int j, int k, int l)
\r
391 return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);
\r