This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[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
14 //
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:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
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.
33 //
34 \r
35 \r
36 using System;\r
37 using System.Net.Sockets;\r
38 using System.Runtime.InteropServices;\r
39 using System.Text;\r
40 \r
41 namespace System.Net {\r
42 \r
43         /// <remarks>\r
44         ///   Encapsulates an IPv6 Address.\r
45         ///   See RFC 2373 for more info on IPv6 addresses.\r
46         /// </remarks>\r
47         [Serializable]\r
48         internal class IPv6Address {\r
49                 private ushort [] address;\r
50                 private int prefixLength;\r
51                 private long scopeId = 0;\r
52 \r
53                 public static readonly IPv6Address Loopback = IPv6Address.Parse ("::1");\r
54                 public static readonly IPv6Address Unspecified = IPv6Address.Parse ("::");\r
55 \r
56                 /// <summary>\r
57                 ///   Constructor from a 32-bit constant with its bytes \r
58                 ///   in network order.\r
59                 /// </summary>\r
60                 public IPv6Address (ushort [] addr)\r
61                 {\r
62                         if (addr == null)\r
63                                 throw new ArgumentNullException ("addr");       \r
64                         if (addr.Length != 8)   \r
65                                 throw new ArgumentException ("addr");\r
66                         address = addr;                 \r
67                 }\r
68                 \r
69                 public IPv6Address (ushort [] addr, int prefixLength) : this (addr)\r
70                 {\r
71                         if (prefixLength < 0 || prefixLength > 128)\r
72                                 throw new ArgumentException ("prefixLength");\r
73                         this.prefixLength = prefixLength;\r
74                 }       \r
75         \r
76                 public IPv6Address (ushort [] addr, int prefixLength, int scopeId) : this (addr, prefixLength)\r
77                 {\r
78             this.scopeId = scopeId;\r
79                 }               \r
80                 \r
81                 public static IPv6Address Parse (string ipString)\r
82                 {\r
83                         if (ipString == null)\r
84                                 throw new ArgumentNullException ("ipString");\r
85 \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
90 \r
91                         if (ipString.Length  < 2)\r
92                                 throw new FormatException ("Not a valid IPv6 address");\r
93 \r
94                         int prefixLen = 0;\r
95                         int scopeId = 0;\r
96                         int pos = ipString.LastIndexOf ('/');\r
97                         if (pos != -1) {\r
98                                 string prefix = ipString.Substring (pos + 1);\r
99                                 try {\r
100                                         prefixLen = Int32.Parse (prefix);\r
101                                 } catch (Exception) {\r
102                                         prefixLen = -1;\r
103                                 }\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
107                         } else {\r
108                                 pos = ipString.LastIndexOf ('%');\r
109                                 if (pos != -1) {\r
110                                         string prefix = ipString.Substring (pos + 1);\r
111                                         try  {\r
112                                                 scopeId = Int32.Parse (prefix);\r
113                                         } \r
114                                         catch (Exception) {\r
115                                                 scopeId = 0;\r
116                                         }\r
117                                         ipString = ipString.Substring (0, pos);\r
118                                 }                       \r
119                         }\r
120                         \r
121                         ushort [] addr = new ushort [8];                        \r
122                         \r
123                         bool ipv4 = false;\r
124                         int pos2 = ipString.LastIndexOf (":");\r
125                         if (pos2 == -1)\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
130                                         try {\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
136                                                 else\r
137                                                         ipString = ipString.Substring (0, pos2);\r
138                                                 ipv4 = true;\r
139                                         } catch (Exception) {\r
140                                                 throw new FormatException ("Not a valid IPv6 address");         \r
141                                         }\r
142                                 }\r
143                         }       \r
144                         \r
145                         int origLen = ipString.Length;\r
146                         if (origLen < 2)\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
152                         \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
160                                 \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
168                                 if (piece == "!")\r
169                                         piecedouble = i;\r
170                                 else {\r
171                                         int plen = piece.Length;\r
172                                         if (plen > 4)\r
173                                                 throw new FormatException ("Not a valid IPv6 address");\r
174                                         int p = 0;\r
175                                         for (int j = 0; j < plen; j++) \r
176                                                 try {\r
177                                                         p = (p << 4) + Uri.FromHex (piece [j]);\r
178                                                 } catch (ArgumentException) {\r
179                                                         throw new FormatException ("Not a valid IPv6 address");\r
180                                                 }\r
181                                         addr [i] = (ushort) p;\r
182                                 }\r
183                         }\r
184 \r
185                         //expand the :: token\r
186                         if (piecedouble != -1) {\r
187                                 int totallen = (ipv4 ? 5 : 7);\r
188                                 int i = totallen;\r
189                                 for (i = totallen; i >= (totallen - (len - piecedouble - 1)); i--) {\r
190                                         addr [i] = addr [(len - 1) + i - totallen];\r
191                                 }\r
192                                 for (; i >= piecedouble; i--) {\r
193                                         addr [i] = 0;\r
194                                 }\r
195                         } else if (len != (ipv4 ? 6 : 8)) \r
196                                 throw new FormatException ("Not a valid IPv6 address");\r
197 \r
198                         // check IPv4 validity\r
199                         if (ipv4) {\r
200                                 for (int i = 0; i < 5; i++) \r
201                                         if (addr [i] != 0)\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
205                         }\r
206                         \r
207                         return new IPv6Address (addr, prefixLen, scopeId);\r
208                 }\r
209                 \r
210                 public ushort [] Address {\r
211                         get { return address; }\r
212                 }\r
213 \r
214                 public int PrefixLength {\r
215                         get { return this.prefixLength; }\r
216                 }\r
217                 \r
218                 public long ScopeId {\r
219                         get {\r
220                                 return scopeId;\r
221                         }\r
222                         set {\r
223                                 scopeId = value;\r
224                         }\r
225                 }\r
226 \r
227                 public ushort this [int index] {\r
228                         get { return address [index]; }\r
229                 }               \r
230 \r
231                 public AddressFamily AddressFamily {\r
232                         get { return AddressFamily.InterNetworkV6; }\r
233                 }\r
234                 \r
235                 /// <summary>\r
236                 ///   Used to tell whether the given address is the loopback address.\r
237                 /// </summary>\r
238                 public static bool IsLoopback (IPv6Address addr)\r
239                 {\r
240                         for (int i = 0; i < 4; i++)\r
241                                 if (addr.address [i] != 0)\r
242                                         return false;\r
243                         if ((addr.address [5] != 0) && (addr.address [5] != 0xffff))\r
244                                 return false;\r
245                         if ((addr.address [6] >> 8) == 0x7f)\r
246                                 return true;\r
247                         return ((addr.address [5] == 0) && \r
248                                 (addr.address [6] == 0) && \r
249                                 (addr.address [7] == 1));\r
250                 }\r
251                 \r
252                 public bool IsIPv4Compatible ()\r
253                 {\r
254                         for (int i = 0; i < 6; i++) \r
255                                 if (address [i] != 0)\r
256                                         return false;\r
257                         return ( (IPAddress.NetworkToHostOrder(address[7]) << 16) | IPAddress.NetworkToHostOrder(address[6])) > 1;\r
258                 }\r
259                 \r
260                 public bool IsIPv4Mapped ()\r
261                 {\r
262                         for (int i = 0; i < 5; i++) \r
263                                 if (address [i] != 0)\r
264                                         return false;\r
265                         return address [5] == 0xffff;\r
266                 }\r
267                 \r
268                 /// <summary>\r
269                 ///   Overrides System.Object.ToString to return\r
270                 ///   this object rendered in a canonicalized notation\r
271                 /// </summary>\r
272                 public override string ToString ()\r
273                 {\r
274                         StringBuilder s = new StringBuilder ();\r
275 \r
276 \r
277                         if(IsIPv4Compatible() || IsIPv4Mapped())\r
278                         {\r
279                                 s.Append("::");\r
280 \r
281                                 if(IsIPv4Mapped())\r
282                                         s.Append("ffff:");\r
283 \r
284                                 s.Append(new IPAddress( IPAddress.NetworkToHostOrder(address[6]<<16) + IPAddress.NetworkToHostOrder(address[7])).ToString());\r
285 \r
286                                 return s.ToString ();\r
287                         }\r
288                         else\r
289                         {\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
293 \r
294                                 // Looks for the longest zero chain\r
295                                 for (int i=0; i<8; i++)\r
296                                 {\r
297                                         if (address[i] != 0)\r
298                                         {\r
299                                                 if ((currChLen > bestChLen) \r
300                                                         && (currChLen > 1))\r
301                                                 {\r
302                                                         bestChLen = currChLen;\r
303                                                         bestChStart = i - currChLen;\r
304                                                 }\r
305                                                 currChLen = 0;\r
306                                         }\r
307                                         else\r
308                                                 currChLen++;\r
309                                 }\r
310                                 if ((currChLen > bestChLen) \r
311                                         && (currChLen > 1))\r
312                                 {\r
313                                         bestChLen = currChLen;\r
314                                         bestChStart = 8 - currChLen;\r
315                                 }\r
316 \r
317                                 // makes the string\r
318                                 if (bestChStart == 0)\r
319                                         s.Append(":");\r
320                                 for (int i=0; i<8; i++)\r
321                                 {\r
322                                         if (i == bestChStart)\r
323                                         {\r
324                                                 s.Append (":");\r
325                                                 i += (bestChLen - 1);\r
326                                                 continue;\r
327                                         }\r
328                                         s.AppendFormat("{0:x}", address [i]);\r
329                                         if (i < 7) s.Append (':');\r
330                                 }\r
331                         }\r
332                         return s.ToString ();\r
333                 }\r
334 \r
335                 /// <returns>\r
336                 ///   Whether both objects are equal.\r
337                 /// </returns>\r
338                 public override bool Equals (object other)\r
339                 {\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
344                                                 return false;\r
345                                 return true;                            \r
346                         } \r
347                         \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
352                                                 return false;                   \r
353 \r
354                                 if (address [5] != 0 && address [5] != 0xffff)\r
355                                         return false;\r
356 \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
360                                         return false;\r
361                                         \r
362                                 return true;\r
363                         }\r
364                         \r
365                         return false;\r
366                 }\r
367 \r
368                 public override int GetHashCode ()\r
369                 {\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
374                 }\r
375                 \r
376                 private static int Hash (int i, int j, int k, int l) \r
377                 {\r
378                         return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);\r
379                 }\r
380         }\r
381 }\r