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
57 /// Constructor from a 32-bit constant with its bytes
\r
58 /// in network order.
\r
60 public IPv6Address (ushort [] addr)
\r
63 throw new ArgumentNullException ("addr");
\r
64 if (addr.Length != 8)
\r
65 throw new ArgumentException ("addr");
\r
69 public IPv6Address (ushort [] addr, int prefixLength) : this (addr)
\r
71 if (prefixLength < 0 || prefixLength > 128)
\r
72 throw new ArgumentException ("prefixLength");
\r
73 this.prefixLength = prefixLength;
\r
76 public IPv6Address (ushort [] addr, int prefixLength, int scopeId) : this (addr, prefixLength)
\r
78 this.scopeId = scopeId;
\r
81 public static IPv6Address Parse (string ipString)
\r
83 if (ipString == null)
\r
84 throw new ArgumentNullException ("ipString");
\r
86 if (ipString.Length > 2 &&
\r
87 ipString [0] == '[' &&
\r
88 ipString [ipString.Length - 1] == ']')
\r
89 ipString = ipString.Substring (1, ipString.Length - 2);
\r
91 if (ipString.Length < 2)
\r
92 throw new FormatException ("Not a valid IPv6 address");
\r
96 int pos = ipString.LastIndexOf ('/');
\r
98 string prefix = ipString.Substring (pos + 1);
\r
100 prefixLen = Int32.Parse (prefix);
\r
101 } catch (Exception) {
\r
104 if (prefixLen < 0 || prefixLen > 128)
\r
105 throw new FormatException ("Not a valid prefix length");
\r
106 ipString = ipString.Substring (0, pos);
\r
108 pos = ipString.LastIndexOf ('%');
\r
110 string prefix = ipString.Substring (pos + 1);
\r
112 scopeId = Int32.Parse (prefix);
\r
114 catch (Exception) {
\r
117 ipString = ipString.Substring (0, pos);
\r
121 ushort [] addr = new ushort [8];
\r
124 int pos2 = ipString.LastIndexOf (":");
\r
126 throw new FormatException ("Not a valid IPv6 address");
\r
127 if (pos2 < (ipString.Length - 1)) {
\r
128 string ipv4Str = ipString.Substring (pos2 + 1);
\r
129 if (ipv4Str.IndexOf ('.') != -1) {
\r
131 long a = IPAddress.Parse (ipv4Str).InternalIPv4Address;
\r
132 addr [6] = (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff)));
\r
133 addr [7] = (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff)));
\r
134 if (ipString [pos2 - 1] == ':')
\r
135 ipString = ipString.Substring (0, pos2 + 1);
\r
137 ipString = ipString.Substring (0, pos2);
\r
139 } catch (Exception) {
\r
140 throw new FormatException ("Not a valid IPv6 address");
\r
145 int origLen = ipString.Length;
\r
147 throw new FormatException ("Not a valid IPv6 address");
\r
148 ipString = ipString.Replace ("::", ":!:");
\r
149 int len = ipString.Length;
\r
150 if ((len - origLen) > 1)
\r
151 throw new FormatException ("Not a valid IPv6 address");
\r
153 if (ipString [1] == '!')
\r
154 ipString = ipString.Remove (0, 1);
\r
155 if (ipString [len - 2] == '!')
\r
156 ipString = ipString.Remove (len - 1, 1);
\r
157 if ((ipString.Length > 2) &&
\r
158 ((ipString [0] == ':') || (ipString [ipString.Length - 1] == ':')))
\r
159 throw new FormatException ("Not a valid IPv6 address");
\r
161 string [] pieces = ipString.Split (new char [] {':'});
\r
162 len = pieces.Length;
\r
163 if (len > (ipv4 ? 6 : 8))
\r
164 throw new FormatException ("Not a valid IPv6 address");
\r
165 int piecedouble = -1;
\r
166 for (int i = 0; i < len; i++) {
\r
167 string piece = pieces [i];
\r
171 int plen = piece.Length;
\r
173 throw new FormatException ("Not a valid IPv6 address");
\r
175 for (int j = 0; j < plen; j++)
\r
177 p = (p << 4) + Uri.FromHex (piece [j]);
\r
178 } catch (ArgumentException) {
\r
179 throw new FormatException ("Not a valid IPv6 address");
\r
181 addr [i] = (ushort) p;
\r
185 //expand the :: token
\r
186 if (piecedouble != -1) {
\r
187 int totallen = (ipv4 ? 5 : 7);
\r
189 for (i = totallen; i >= (totallen - (len - piecedouble - 1)); i--) {
\r
190 addr [i] = addr [(len - 1) + i - totallen];
\r
192 for (; i >= piecedouble; i--) {
\r
195 } else if (len != (ipv4 ? 6 : 8))
\r
196 throw new FormatException ("Not a valid IPv6 address");
\r
198 // check IPv4 validity
\r
200 for (int i = 0; i < 5; i++)
\r
202 throw new FormatException ("Not a valid IPv6 address");
\r
203 if (addr [5] != 0 && addr [5] != 0xffff)
\r
204 throw new FormatException ("Not a valid IPv6 address");
\r
207 return new IPv6Address (addr, prefixLen, scopeId);
\r
210 public ushort [] Address {
\r
211 get { return address; }
\r
214 public int PrefixLength {
\r
215 get { return this.prefixLength; }
\r
218 public long ScopeId {
\r
227 public ushort this [int index] {
\r
228 get { return address [index]; }
\r
231 public AddressFamily AddressFamily {
\r
232 get { return AddressFamily.InterNetworkV6; }
\r
236 /// Used to tell whether the given address is the loopback address.
\r
238 public static bool IsLoopback (IPv6Address addr)
\r
240 for (int i = 0; i < 4; i++)
\r
241 if (addr.address [i] != 0)
\r
243 if ((addr.address [5] != 0) && (addr.address [5] != 0xffff))
\r
245 if ((addr.address [6] >> 8) == 0x7f)
\r
247 return ((addr.address [5] == 0) &&
\r
248 (addr.address [6] == 0) &&
\r
249 (addr.address [7] == 1));
\r
252 public bool IsIPv4Compatible ()
\r
254 for (int i = 0; i < 6; i++)
\r
255 if (address [i] != 0)
\r
257 return ( (IPAddress.NetworkToHostOrder(address[7]) << 16) | IPAddress.NetworkToHostOrder(address[6])) > 1;
\r
260 public bool IsIPv4Mapped ()
\r
262 for (int i = 0; i < 5; i++)
\r
263 if (address [i] != 0)
\r
265 return address [5] == 0xffff;
\r
269 /// Overrides System.Object.ToString to return
\r
270 /// this object rendered in a canonicalized notation
\r
272 public override string ToString ()
\r
274 StringBuilder s = new StringBuilder ();
\r
277 if(IsIPv4Compatible() || IsIPv4Mapped())
\r
284 s.Append(new IPAddress( IPAddress.NetworkToHostOrder(address[6]<<16) + IPAddress.NetworkToHostOrder(address[7])).ToString());
\r
286 return s.ToString ();
\r
290 int bestChStart = -1; // Best chain start
\r
291 int bestChLen = 0; // Best chain length
\r
292 int currChLen = 0; // Current chain length
\r
294 // Looks for the longest zero chain
\r
295 for (int i=0; i<8; i++)
\r
297 if (address[i] != 0)
\r
299 if ((currChLen > bestChLen)
\r
300 && (currChLen > 1))
\r
302 bestChLen = currChLen;
\r
303 bestChStart = i - currChLen;
\r
310 if ((currChLen > bestChLen)
\r
311 && (currChLen > 1))
\r
313 bestChLen = currChLen;
\r
314 bestChStart = 8 - currChLen;
\r
317 // makes the string
\r
318 if (bestChStart == 0)
\r
320 for (int i=0; i<8; i++)
\r
322 if (i == bestChStart)
\r
325 i += (bestChLen - 1);
\r
328 s.AppendFormat("{0:x}", address [i]);
\r
329 if (i < 7) s.Append (':');
\r
332 return s.ToString ();
\r
336 /// Whether both objects are equal.
\r
338 public override bool Equals (object other)
\r
340 System.Net.IPv6Address ipv6 = other as System.Net.IPv6Address;
\r
341 if (ipv6 != null) {
\r
342 for (int i = 0; i < 8; i++)
\r
343 if (this.address [i] != ipv6.address [i])
\r
348 System.Net.IPAddress ipv4 = other as System.Net.IPAddress;
\r
349 if (ipv4 != null) {
\r
350 for (int i = 0; i < 5; i++)
\r
351 if (address [i] != 0)
\r
354 if (address [5] != 0 && address [5] != 0xffff)
\r
357 long a = ipv4.InternalIPv4Address;
\r
358 if (address [6] != (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff))) ||
\r
359 address [7] != (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff))))
\r
368 public override int GetHashCode ()
\r
370 return Hash (((((int) address [0]) << 16) + address [1]),
\r
371 ((((int) address [2]) << 16) + address [3]),
\r
372 ((((int) address [4]) << 16) + address [5]),
\r
373 ((((int) address [6]) << 16) + address [7]));
\r
376 private static int Hash (int i, int j, int k, int l)
\r
378 return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);
\r