BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / mcs / class / System / System.Net / IPv6Address.cs
1 //\r
2 // System.Net.IPv6Address.cs\r
3 //\r
4 // Author:\r
5 //   Lawrence Pit (loz@cable.a2000.nl)\r
6 //\r
7 // Note I: This class is not defined in the specs of .Net\r
8 //\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
12 //\r
13 \r
14 //\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
22 // \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
25 // \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
33 //\r
34 \r
35 \r
36 using System;\r
37 using System.Globalization;\r
38 using System.Net.Sockets;\r
39 using System.Runtime.InteropServices;\r
40 using System.Text;\r
41 \r
42 namespace System.Net {\r
43 \r
44         /// <remarks>\r
45         ///   Encapsulates an IPv6 Address.\r
46         ///   See RFC 2373 for more info on IPv6 addresses.\r
47         /// </remarks>\r
48         [Serializable]\r
49         internal class IPv6Address {\r
50                 private ushort [] address;\r
51                 private int prefixLength;\r
52                 private long scopeId = 0;\r
53 \r
54                 public static readonly IPv6Address Loopback = IPv6Address.Parse ("::1");\r
55                 public static readonly IPv6Address Unspecified = IPv6Address.Parse ("::");\r
56 \r
57                 public IPv6Address (ushort [] addr)\r
58                 {\r
59                         if (addr == null)\r
60                                 throw new ArgumentNullException ("addr");       \r
61                         if (addr.Length != 8)   \r
62                                 throw new ArgumentException ("addr");\r
63                         address = addr;                 \r
64                 }\r
65                 \r
66                 public IPv6Address (ushort [] addr, int prefixLength) : this (addr)\r
67                 {\r
68                         if (prefixLength < 0 || prefixLength > 128)\r
69                                 throw new ArgumentException ("prefixLength");\r
70                         this.prefixLength = prefixLength;\r
71                 }\r
72         \r
73                 public IPv6Address (ushort [] addr, int prefixLength, int scopeId) : this (addr, prefixLength)\r
74                 {\r
75                         this.scopeId = scopeId;\r
76                 }\r
77 \r
78                 public static IPv6Address Parse (string ipString)\r
79                 {\r
80                         if (ipString == null)\r
81                                 throw new ArgumentNullException ("ipString");\r
82 \r
83                         IPv6Address result;\r
84                         if (TryParse (ipString, out result))\r
85                                 return result;\r
86                         throw new FormatException ("Not a valid IPv6 address");\r
87                 }\r
88 \r
89                 static int Fill (ushort [] addr, string ipString)\r
90                 {\r
91                         int p = 0;\r
92                         int slot = 0;\r
93 \r
94                         if (ipString.Length == 0)\r
95                                 return 0;\r
96                         \r
97                         // Catch double uses of ::\r
98                         if (ipString.IndexOf ("::", StringComparison.Ordinal) != -1)\r
99                                 return -1;\r
100 \r
101                         for (int i = 0; i < ipString.Length; i++){\r
102                                 char c = ipString [i];\r
103                                 int n;\r
104 \r
105                                 if (c == ':'){\r
106                                         // Trailing : is not allowed.\r
107                                         if (i == ipString.Length-1)\r
108                                                 return -1;\r
109                                         \r
110                                         if (slot == 8)\r
111                                                 return -1;\r
112                                         \r
113                                         addr [slot++] = (ushort) p;\r
114                                         p = 0;\r
115                                         continue;\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
122                                 else \r
123                                         return -1;\r
124                                 p = (p << 4) + n;\r
125                                 if (p > UInt16.MaxValue)\r
126                                         return -1;\r
127                         }\r
128 \r
129                         if (slot == 8)\r
130                                 return -1;\r
131                         \r
132                         addr [slot++] = (ushort) p;\r
133 \r
134                         return slot;\r
135                 }\r
136 \r
137                 static bool TryParse (string prefix, out int res)\r
138                 {\r
139 #if NET_2_0\r
140                         return Int32.TryParse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture, out res);\r
141 #else\r
142                         try {\r
143                                 res = Int32.Parse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture);\r
144                         } catch (Exception) {\r
145                                 res = -1;\r
146                                 return false;\r
147                         }\r
148 \r
149                         return true;\r
150 #endif\r
151                 }\r
152                 \r
153                 public static bool TryParse (string ipString, out IPv6Address result)\r
154                 {\r
155                         result = null;\r
156                         if (ipString == null)\r
157                                 return false;\r
158 \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
163 \r
164                         if (ipString.Length  < 2)\r
165                                 return false;\r
166 \r
167                         int prefixLen = 0;\r
168                         int scopeId = 0;\r
169                         int pos = ipString.LastIndexOf ('/');\r
170                         if (pos != -1) {\r
171                                 string prefix = ipString.Substring (pos + 1);\r
172                                 if (!TryParse (prefix , out prefixLen))\r
173                                         prefixLen = -1;\r
174                                 if (prefixLen < 0 || prefixLen > 128)\r
175                                         return false;\r
176                                 ipString = ipString.Substring (0, pos);\r
177                         } else {\r
178                                 pos = ipString.LastIndexOf ('%');\r
179                                 if (pos != -1) {\r
180                                         string prefix = ipString.Substring (pos + 1);\r
181                                         if (!TryParse (prefix, out scopeId))\r
182                                                 scopeId = 0;\r
183                                         ipString = ipString.Substring (0, pos);\r
184                                 }                       \r
185                         }\r
186 \r
187                         //\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
190                         //\r
191                         ushort [] addr = new ushort [8];\r
192 \r
193                         //\r
194                         // Is there an ipv4 address at the end?\r
195                         //\r
196                         bool ipv4 = false;\r
197                         int pos2 = ipString.LastIndexOf (':');\r
198                         if (pos2 == -1)\r
199                                 return false;\r
200 \r
201                         int slots = 0;\r
202                         if (pos2 < (ipString.Length - 1)) {\r
203                                 string ipv4Str = ipString.Substring (pos2 + 1);\r
204                                 if (ipv4Str.IndexOf ('.') != -1) {\r
205                                         IPAddress ip;\r
206                                         \r
207                                         if (!IPAddress.TryParse (ipv4Str, out ip))\r
208                                                 return false;\r
209                                         \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
215                                         else\r
216                                                 ipString = ipString.Substring (0, pos2);\r
217                                         ipv4 = true;\r
218                                         slots = 2;\r
219                                 }\r
220                         }       \r
221 \r
222                         //\r
223                         // Only an ipv6 block remains, either:\r
224                         // "hexnumbers::hexnumbers", "hexnumbers::" or "hexnumbers"\r
225                         //\r
226                         int c = ipString.IndexOf ("::", StringComparison.Ordinal);\r
227                         if (c != -1){\r
228                                 int right_slots = Fill (addr, ipString.Substring (c+2));\r
229                                 if (right_slots == -1){\r
230                                         return false;\r
231                                 }\r
232 \r
233                                 if (right_slots + slots > 8){\r
234                                         return false;\r
235                                 }\r
236 \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
240                                         addr [i-1] = 0;\r
241                                 }\r
242                                 \r
243                                 int left_slots = Fill (addr, ipString.Substring (0, c));\r
244                                 if (left_slots == -1)\r
245                                         return false;\r
246 \r
247                                 if (left_slots + right_slots + slots > 7)\r
248                                         return false;\r
249                         } else {\r
250                                 if (Fill (addr, ipString) != 8-slots)\r
251                                         return false;\r
252                         }\r
253 \r
254                         // Now check the results in the ipv6-address range only\r
255                         bool ipv6 = false;\r
256                         for (int i = 0; i < slots; i++){\r
257                                 if (addr [i] != 0 || i == 5 && addr [i] != 0xffff)\r
258                                         ipv6 = true;\r
259                         }\r
260                         \r
261                         // check IPv4 validity\r
262                         if (ipv4 && !ipv6) {\r
263                                 for (int i = 0; i < 5; i++) {\r
264                                         if (addr [i] != 0)\r
265                                                 return false;\r
266                                 }\r
267 \r
268                                 if (addr [5] != 0 && addr [5] != 0xffff)\r
269                                         return false;\r
270                         }\r
271 \r
272                         result = new IPv6Address (addr, prefixLen, scopeId);\r
273                         return true;\r
274                 }\r
275 \r
276                 public ushort [] Address {\r
277                         get { return address; }\r
278                 }\r
279 \r
280                 public int PrefixLength {\r
281                         get { return this.prefixLength; }\r
282                 }\r
283                 \r
284                 public long ScopeId {\r
285                         get {\r
286                                 return scopeId;\r
287                         }\r
288                         set {\r
289                                 scopeId = value;\r
290                         }\r
291                 }\r
292 \r
293                 public ushort this [int index] {\r
294                         get { return address [index]; }\r
295                 }               \r
296 \r
297                 public AddressFamily AddressFamily {\r
298                         get { return AddressFamily.InterNetworkV6; }\r
299                 }\r
300 \r
301                 public static bool IsLoopback (IPv6Address addr)\r
302                 {\r
303                         if (addr.address [7] != 1)\r
304                                 return false;\r
305 \r
306                         int x = addr.address [6] >> 8;\r
307                         if (x != 0x7f && x != 0)\r
308                                 return false;\r
309 \r
310                         for (int i = 0; i < 4; i++) {\r
311                                 if (addr.address [i] != 0)\r
312                                         return false;\r
313                         }\r
314 \r
315                         if (addr.address [5] != 0 && addr.address [5] != 0xffff)\r
316                                 return false;\r
317 \r
318                         return true;\r
319                 }\r
320 \r
321                 private static ushort SwapUShort (ushort number)\r
322                 {\r
323                         return (ushort) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );\r
324                 }\r
325 \r
326                 // Convert the address into a format expected by the IPAddress (long) ctor\r
327                 private int AsIPv4Int ()\r
328                 {\r
329                         return (SwapUShort (address [7]) << 16) + SwapUShort (address [6]);\r
330                 }                       \r
331 \r
332                 public bool IsIPv4Compatible ()\r
333                 {\r
334                         for (int i = 0; i < 6; i++) \r
335                                 if (address [i] != 0)\r
336                                         return false;\r
337                         return (AsIPv4Int () > 1);\r
338                 }\r
339                 \r
340                 public bool IsIPv4Mapped ()\r
341                 {\r
342                         for (int i = 0; i < 5; i++) \r
343                                 if (address [i] != 0)\r
344                                         return false;\r
345                         return address [5] == 0xffff;\r
346                 }\r
347                 \r
348                 /// <summary>\r
349                 ///   Overrides System.Object.ToString to return\r
350                 ///   this object rendered in a canonicalized notation\r
351                 /// </summary>\r
352                 public override string ToString ()\r
353                 {\r
354                         StringBuilder s = new StringBuilder ();\r
355 \r
356 \r
357                         if(IsIPv4Compatible() || IsIPv4Mapped())\r
358                         {\r
359                                 s.Append("::");\r
360 \r
361                                 if(IsIPv4Mapped())\r
362                                         s.Append("ffff:");\r
363 \r
364                                 s.Append(new IPAddress( AsIPv4Int ()).ToString ());\r
365 \r
366                                 return s.ToString ();\r
367                         }\r
368                         else\r
369                         {\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
373 \r
374                                 // Looks for the longest zero chain\r
375                                 for (int i=0; i<8; i++)\r
376                                 {\r
377                                         if (address[i] != 0)\r
378                                         {\r
379                                                 if ((currChLen > bestChLen) \r
380                                                         && (currChLen > 1))\r
381                                                 {\r
382                                                         bestChLen = currChLen;\r
383                                                         bestChStart = i - currChLen;\r
384                                                 }\r
385                                                 currChLen = 0;\r
386                                         }\r
387                                         else\r
388                                                 currChLen++;\r
389                                 }\r
390                                 if ((currChLen > bestChLen) \r
391                                         && (currChLen > 1))\r
392                                 {\r
393                                         bestChLen = currChLen;\r
394                                         bestChStart = 8 - currChLen;\r
395                                 }\r
396 \r
397                                 // makes the string\r
398                                 if (bestChStart == 0)\r
399                                         s.Append(":");\r
400                                 for (int i=0; i<8; i++)\r
401                                 {\r
402                                         if (i == bestChStart)\r
403                                         {\r
404                                                 s.Append (":");\r
405                                                 i += (bestChLen - 1);\r
406                                                 continue;\r
407                                         }\r
408                                         s.AppendFormat("{0:x}", address [i]);\r
409                                         if (i < 7) s.Append (':');\r
410                                 }\r
411                         }\r
412                         if (scopeId != 0)\r
413                                 s.Append ('%').Append (scopeId);\r
414                         return s.ToString ();\r
415                 }\r
416 \r
417                 public string ToString (bool fullLength)\r
418                 {\r
419                         if (!fullLength)\r
420                                 return ToString ();\r
421 \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
425                         }\r
426                         sb.AppendFormat ("{0:X4}", address [address.Length - 1]);\r
427                         return sb.ToString ();\r
428                 }\r
429 \r
430                 /// <returns>\r
431                 ///   Whether both objects are equal.\r
432                 /// </returns>\r
433                 public override bool Equals (object other)\r
434                 {\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
439                                                 return false;\r
440                                 return true;\r
441                         }\r
442                         \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
447                                                 return false;\r
448 \r
449                                 if (address [5] != 0 && address [5] != 0xffff)\r
450                                         return false;\r
451 \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
455                                         return false;\r
456 \r
457                                 return true;\r
458                         }\r
459                         \r
460                         return false;\r
461                 }\r
462 \r
463                 public override int GetHashCode ()\r
464                 {\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
469                 }\r
470                 \r
471                 private static int Hash (int i, int j, int k, int l) \r
472                 {\r
473                         return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);\r
474                 }\r
475         }\r
476 }\r