[Mono.Security]: Add new MonoTlsProviderFactory.CreateHttpListener() API to create...
[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 pdigits = 0;\r
93                         int slot = 0;\r
94 \r
95                         if (ipString.Length == 0)\r
96                                 return 0;\r
97                         \r
98                         // Catch double uses of ::\r
99                         if (ipString.IndexOf ("::", StringComparison.Ordinal) != -1)\r
100                                 return -1;\r
101 \r
102                         for (int i = 0; i < ipString.Length; i++){\r
103                                 char c = ipString [i];\r
104                                 int n;\r
105 \r
106                                 if (c == ':'){\r
107                                         // Leading : is not allowed.\r
108                                         if (i == 0)\r
109                                                return -1;\r
110                                        \r
111                                         // Trailing : is not allowed.\r
112                                         if (i == ipString.Length-1)\r
113                                                 return -1;\r
114                                         \r
115                                         if (slot == 8)\r
116                                                 return -1;\r
117                                         \r
118                                         addr [slot++] = (ushort) p;\r
119                                         p = 0;\r
120                                         pdigits = 0;\r
121                                         continue;\r
122                                 }\r
123                                 pdigits++;\r
124                                 if (pdigits > 4)\r
125                                         return -1;\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
132                                 else \r
133                                         return -1;\r
134                                 p = (p << 4) + n;\r
135                                 if (p > UInt16.MaxValue)\r
136                                         return -1;\r
137                         }\r
138 \r
139                         if (slot == 8)\r
140                                 return -1;\r
141                         \r
142                         addr [slot++] = (ushort) p;\r
143 \r
144                         return slot;\r
145                 }\r
146 \r
147                 static bool TryParse (string prefix, out int res)\r
148                 {\r
149                         return Int32.TryParse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture, out res);\r
150                 }\r
151                 \r
152                 public static bool TryParse (string ipString, out IPv6Address result)\r
153                 {\r
154                         result = null;\r
155                         if (ipString == null)\r
156                                 return false;\r
157 \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
162 \r
163                         if (ipString.Length  < 2)\r
164                                 return false;\r
165 \r
166                         int prefixLen = 0;\r
167                         int scopeId = 0;\r
168                         int pos = ipString.LastIndexOf ('/');\r
169                         if (pos != -1) {\r
170                                 string prefix = ipString.Substring (pos + 1);\r
171                                 if (!TryParse (prefix , out prefixLen))\r
172                                         prefixLen = -1;\r
173                                 if (prefixLen < 0 || prefixLen > 128)\r
174                                         return false;\r
175                                 ipString = ipString.Substring (0, pos);\r
176                         } else {\r
177                                 pos = ipString.LastIndexOf ('%');\r
178                                 if (pos != -1) {\r
179                                         string prefix = ipString.Substring (pos + 1);\r
180                                         if (!TryParse (prefix, out scopeId))\r
181                                                 scopeId = 0;\r
182                                         ipString = ipString.Substring (0, pos);\r
183                                 }                       \r
184                         }\r
185 \r
186                         //\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
189                         //\r
190                         ushort [] addr = new ushort [8];\r
191 \r
192                         //\r
193                         // Is there an ipv4 address at the end?\r
194                         //\r
195                         int pos2 = ipString.LastIndexOf (':');\r
196                         if (pos2 == -1)\r
197                                 return false;\r
198 \r
199                         int slots = 0;\r
200                         if (pos2 < (ipString.Length - 1)) {\r
201                                 string ipv4Str = ipString.Substring (pos2 + 1);\r
202                                 if (ipv4Str.IndexOf ('.') != -1) {\r
203                                         IPAddress ip;\r
204                                         \r
205                                         if (!IPAddress.TryParse (ipv4Str, out ip))\r
206                                                 return false;\r
207                                         \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
213                                         else\r
214                                                 ipString = ipString.Substring (0, pos2);\r
215                                         slots = 2;\r
216                                 }\r
217                         }       \r
218 \r
219                         //\r
220                         // Only an ipv6 block remains, either:\r
221                         // "hexnumbers::hexnumbers", "hexnumbers::" or "hexnumbers"\r
222                         //\r
223                         int c = ipString.IndexOf ("::", StringComparison.Ordinal);\r
224                         if (c != -1){\r
225                                 int right_slots = Fill (addr, ipString.Substring (c+2));\r
226                                 if (right_slots == -1){\r
227                                         return false;\r
228                                 }\r
229 \r
230                                 if (right_slots + slots > 8){\r
231                                         return false;\r
232                                 }\r
233 \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
237                                         addr [i-1] = 0;\r
238                                 }\r
239                                 \r
240                                 int left_slots = Fill (addr, ipString.Substring (0, c));\r
241                                 if (left_slots == -1)\r
242                                         return false;\r
243 \r
244                                 if (left_slots + right_slots + slots > 7)\r
245                                         return false;\r
246                         } else {\r
247                                 if (Fill (addr, ipString) != 8-slots)\r
248                                         return false;\r
249                         }\r
250 \r
251                         result = new IPv6Address (addr, prefixLen, scopeId);\r
252                         return true;\r
253                 }\r
254 \r
255                 public ushort [] Address {\r
256                         get { return address; }\r
257                 }\r
258 \r
259                 public int PrefixLength {\r
260                         get { return this.prefixLength; }\r
261                 }\r
262                 \r
263                 public long ScopeId {\r
264                         get {\r
265                                 return scopeId;\r
266                         }\r
267                         set {\r
268                                 scopeId = value;\r
269                         }\r
270                 }\r
271 \r
272                 public ushort this [int index] {\r
273                         get { return address [index]; }\r
274                 }               \r
275 \r
276                 public AddressFamily AddressFamily {\r
277                         get { return AddressFamily.InterNetworkV6; }\r
278                 }\r
279 \r
280                 public static bool IsLoopback (IPv6Address addr)\r
281                 {\r
282                         if (addr.address [7] != 1)\r
283                                 return false;\r
284 \r
285                         int x = addr.address [6] >> 8;\r
286                         if (x != 0x7f && x != 0)\r
287                                 return false;\r
288 \r
289                         for (int i = 0; i < 4; i++) {\r
290                                 if (addr.address [i] != 0)\r
291                                         return false;\r
292                         }\r
293 \r
294                         if (addr.address [5] != 0 && addr.address [5] != 0xffff)\r
295                                 return false;\r
296 \r
297                         return true;\r
298                 }\r
299 \r
300                 private static ushort SwapUShort (ushort number)\r
301                 {\r
302                         return (ushort) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );\r
303                 }\r
304 \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
308                 {\r
309                         return (uint)(SwapUShort (address [7]) << 16) + SwapUShort (address [6]);\r
310                 }                       \r
311 \r
312                 public bool IsIPv4Compatible ()\r
313                 {\r
314                         for (int i = 0; i < 6; i++) \r
315                                 if (address [i] != 0)\r
316                                         return false;\r
317                         /* MS .net only seems to format the last 4\r
318                          * bytes as an IPv4 address if address[6] is\r
319                          * non-zero\r
320                          */\r
321                         if (address[6] == 0)\r
322                                 return false;\r
323                         return (AsIPv4Int () > 1);\r
324                 }\r
325                 \r
326                 public bool IsIPv4Mapped ()\r
327                 {\r
328                         for (int i = 0; i < 5; i++) \r
329                                 if (address [i] != 0)\r
330                                         return false;\r
331                         /* MS .net only seems to format the last 4\r
332                          * bytes as an IPv4 address if address[6] is\r
333                          * non-zero\r
334                          */\r
335                         if (address[6] == 0)\r
336                                 return false;\r
337                         \r
338                         return address [5] == 0xffff;\r
339                 }\r
340                 \r
341                 /// <summary>\r
342                 ///   Overrides System.Object.ToString to return\r
343                 ///   this object rendered in a canonicalized notation\r
344                 /// </summary>\r
345                 public override string ToString ()\r
346                 {\r
347                         StringBuilder s = new StringBuilder ();\r
348 \r
349 \r
350                         if(IsIPv4Compatible() || IsIPv4Mapped())\r
351                         {\r
352                                 s.Append("::");\r
353 \r
354                                 if(IsIPv4Mapped())\r
355                                         s.Append("ffff:");\r
356 \r
357                                 s.Append(new IPAddress( AsIPv4Int ()).ToString ());\r
358 \r
359                                 return s.ToString ();\r
360                         }\r
361                         else\r
362                         {\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
366 \r
367                                 // Looks for the longest zero chain\r
368                                 for (int i=0; i<8; i++)\r
369                                 {\r
370                                         if (address[i] != 0)\r
371                                         {\r
372                                                 if ((currChLen > bestChLen) \r
373                                                         && (currChLen > 1))\r
374                                                 {\r
375                                                         bestChLen = currChLen;\r
376                                                         bestChStart = i - currChLen;\r
377                                                 }\r
378                                                 currChLen = 0;\r
379                                         }\r
380                                         else\r
381                                                 currChLen++;\r
382                                 }\r
383                                 if ((currChLen > bestChLen) \r
384                                         && (currChLen > 1))\r
385                                 {\r
386                                         bestChLen = currChLen;\r
387                                         bestChStart = 8 - currChLen;\r
388                                 }\r
389 \r
390                                 // makes the string\r
391                                 if (bestChStart == 0)\r
392                                         s.Append(":");\r
393                                 for (int i=0; i<8; i++)\r
394                                 {\r
395                                         if (i == bestChStart)\r
396                                         {\r
397                                                 s.Append (":");\r
398                                                 i += (bestChLen - 1);\r
399                                                 continue;\r
400                                         }\r
401                                         s.AppendFormat("{0:x}", address [i]);\r
402                                         if (i < 7) s.Append (':');\r
403                                 }\r
404                         }\r
405                         if (scopeId != 0)\r
406                                 s.Append ('%').Append (scopeId);\r
407                         return s.ToString ();\r
408                 }\r
409 \r
410                 public string ToString (bool fullLength)\r
411                 {\r
412                         if (!fullLength)\r
413                                 return ToString ();\r
414 \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
418                         }\r
419                         sb.AppendFormat ("{0:X4}", address [address.Length - 1]);\r
420                         return sb.ToString ();\r
421                 }\r
422 \r
423                 /// <returns>\r
424                 ///   Whether both objects are equal.\r
425                 /// </returns>\r
426                 public override bool Equals (object other)\r
427                 {\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
432                                                 return false;\r
433                                 return true;\r
434                         }\r
435                         \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
440                                                 return false;\r
441 \r
442                                 if (address [5] != 0 && address [5] != 0xffff)\r
443                                         return false;\r
444 \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
448                                         return false;\r
449 \r
450                                 return true;\r
451                         }\r
452                         \r
453                         return false;\r
454                 }\r
455 \r
456                 public override int GetHashCode ()\r
457                 {\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
462                 }\r
463                 \r
464                 private static int Hash (int i, int j, int k, int l) \r
465                 {\r
466                         return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);\r
467                 }\r
468         }\r
469 }\r