2003-07-10 Andreas Nahr <ClassDevelopment@A-SoftTech.com>
[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 using System;\r
16 using System.Net.Sockets;\r
17 using System.Runtime.InteropServices;\r
18 using System.Text;\r
19 \r
20 namespace System.Net {\r
21 \r
22         /// <remarks>\r
23         ///   Encapsulates an IPv6 Address.\r
24         ///   See RFC 2373 for more info on IPv6 addresses.\r
25         /// </remarks>\r
26         [Serializable]\r
27         internal class IPv6Address {\r
28                 private ushort [] address;\r
29                 private int prefixLength;\r
30 \r
31                 public static readonly IPv6Address Loopback = IPv6Address.Parse ("::1");\r
32                 public static readonly IPv6Address Unspecified = IPv6Address.Parse ("::");\r
33 \r
34                 /// <summary>\r
35                 ///   Constructor from a 32-bit constant with its bytes \r
36                 ///   in network order.\r
37                 /// </summary>\r
38                 public IPv6Address (ushort [] addr)\r
39                 {\r
40                         if (addr == null)\r
41                                 throw new ArgumentNullException ("addr");       \r
42                         if (addr.Length != 8)   \r
43                                 throw new ArgumentException ("addr");\r
44                         address = addr;                 \r
45                 }\r
46                 \r
47                 public IPv6Address (ushort [] addr, int prefixLength) : this (addr)\r
48                 {\r
49                         if (prefixLength < 0 || prefixLength > 128)\r
50                                 throw new ArgumentException ("prefixLength");\r
51                         this.prefixLength = prefixLength;\r
52                 }               \r
53                 \r
54                 public static IPv6Address Parse (string ipString)\r
55                 {\r
56                         if (ipString == null)\r
57                                 throw new ArgumentNullException ("ipString");\r
58 \r
59                         if (ipString.Length > 2 && \r
60                             ipString [0] == '[' && \r
61                             ipString [ipString.Length - 1] == ']')\r
62                                 ipString = ipString.Substring (1, ipString.Length - 2);                         \r
63 \r
64                         if (ipString.Length  < 2)\r
65                                 throw new FormatException ("Not a valid IPv6 address");\r
66 \r
67                         int prefixLen = 0;\r
68                         int pos = ipString.LastIndexOf ('/');\r
69                         if (pos != -1) {\r
70                                 string prefix = ipString.Substring (pos + 1);\r
71                                 try {\r
72                                         prefixLen = Int32.Parse (prefix);\r
73                                 } catch (Exception) {\r
74                                         prefixLen = -1;\r
75                                 }\r
76                                 if (prefixLen < 0 || prefixLen > 128)\r
77                                         throw new FormatException ("Not a valid prefix length");;\r
78                                 ipString = ipString.Substring (0, pos);\r
79                         }\r
80                         \r
81                         ushort [] addr = new ushort [8];                        \r
82                         \r
83                         bool ipv4 = false;\r
84                         int pos2 = ipString.LastIndexOf (":");\r
85                         if (pos2 == -1)\r
86                                 throw new FormatException ("Not a valid IPv6 address");\r
87                         if (pos2 < (ipString.Length - 1)) {\r
88                                 string ipv4Str = ipString.Substring (pos2 + 1);\r
89                                 if (ipv4Str.IndexOf ('.') != -1) {\r
90                                         try {\r
91                                                 long a = IPAddress.Parse (ipv4Str).Address;\r
92                                                 addr [6] = (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff)));\r
93                                                 addr [7] = (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff)));\r
94                                                 if (ipString [pos2 - 1] == ':') \r
95                                                         ipString = ipString.Substring (0, pos2 + 1);\r
96                                                 else\r
97                                                         ipString = ipString.Substring (0, pos2);\r
98                                                 ipv4 = true;\r
99                                         } catch (Exception) {\r
100                                                 throw new FormatException ("Not a valid IPv6 address");         \r
101                                         }\r
102                                 }\r
103                         }       \r
104                         \r
105                         int origLen = ipString.Length;\r
106                         if (origLen < 2)\r
107                                 throw new FormatException ("Not a valid IPv6 address");                 \r
108                         ipString = ipString.Replace ("::", ":!:");\r
109                         int len = ipString.Length;\r
110                         if ((len - origLen) > 1) \r
111                                 throw new FormatException ("Not a valid IPv6 address");\r
112                         \r
113                         if (ipString [1] == '!') \r
114                                 ipString = ipString.Remove (0, 1);\r
115                         if (ipString [len - 2] == '!')\r
116                                 ipString = ipString.Remove (len - 1, 1);\r
117                         if ((ipString.Length > 2) && \r
118                             ((ipString [0] == ':') || (ipString [ipString.Length - 1] == ':'))) \r
119                                 throw new FormatException ("Not a valid IPv6 address");\r
120                                 \r
121                         string [] pieces = ipString.Split (new char [] {':'});\r
122                         len = pieces.Length;\r
123                         if (len > (ipv4 ? 6 : 8)) \r
124                                 throw new FormatException ("Not a valid IPv6 address");\r
125                         int piecedouble = -1;\r
126                         for (int i = 0; i < len; i++) {\r
127                                 string piece = pieces [i];\r
128                                 if (piece == "!")\r
129                                         piecedouble = i;\r
130                                 else {\r
131                                         int plen = piece.Length;\r
132                                         if (plen > 4)\r
133                                                 throw new FormatException ("Not a valid IPv6 address");\r
134                                         int p = 0;\r
135                                         for (int j = 0; j < plen; j++) \r
136                                                 try {\r
137                                                         p = (p << 4) + Uri.FromHex (piece [j]);\r
138                                                 } catch (ArgumentException) {\r
139                                                         throw new FormatException ("Not a valid IPv6 address");\r
140                                                 }\r
141                                         addr [i] = (ushort) p;\r
142                                 }\r
143                         }\r
144 \r
145                         //expand the :: token\r
146                         if (piecedouble != -1) {\r
147                                 int totallen = (ipv4 ? 5 : 7);\r
148                                 int i = totallen;\r
149                                 for (i = totallen; i >= (totallen - (len - piecedouble - 1)); i--) {\r
150                                         addr [i] = addr [(len - 1) + i - totallen];\r
151                                 }\r
152                                 for (; i >= piecedouble; i--) {\r
153                                         addr [i] = 0;\r
154                                 }\r
155                         } else if (len != (ipv4 ? 6 : 8)) \r
156                                 throw new FormatException ("Not a valid IPv6 address");\r
157 \r
158                         // check IPv4 validity\r
159                         if (ipv4) {\r
160                                 for (int i = 0; i < 5; i++) \r
161                                         if (addr [i] != 0)\r
162                                                 throw new FormatException ("Not a valid IPv6 address");\r
163                                 if (addr [5] != 0 && addr [5] != 0xffff)\r
164                                         throw new FormatException ("Not a valid IPv6 address");\r
165                         }\r
166                         \r
167                         return new IPv6Address (addr, prefixLen);\r
168                 }\r
169                 \r
170                 public ushort [] Address {\r
171                         get { return address; }\r
172                 }\r
173 \r
174                 public int PrefixLength {\r
175                         get { return this.prefixLength; }\r
176                 }\r
177                 \r
178                 public ushort this [int index] {\r
179                         get { return address [index]; }\r
180                 }               \r
181 \r
182                 public AddressFamily AddressFamily {\r
183                         get { return AddressFamily.InterNetworkV6; }\r
184                 }\r
185                 \r
186                 /// <summary>\r
187                 ///   Used to tell whether the given address is the loopback address.\r
188                 /// </summary>\r
189                 public static bool IsLoopback (IPv6Address addr)\r
190                 {\r
191                         for (int i = 0; i < 4; i++)\r
192                                 if (addr.address [i] != 0)\r
193                                         return false;\r
194                         if ((addr.address [5] != 0) && (addr.address [5] != 0xffff))\r
195                                 return false;\r
196                         if ((addr.address [6] >> 8) == 0x7f)\r
197                                 return true;\r
198                         return ((addr.address [5] == 0) && \r
199                                 (addr.address [6] == 0) && \r
200                                 (addr.address [7] == 1));\r
201                 }\r
202                 \r
203                 public bool IsIPv4Compatible ()\r
204                 {\r
205                         for (int i = 0; i < 6; i++) \r
206                                 if (address [i] != 0)\r
207                                         return false;                   \r
208                         return true;\r
209                 }\r
210                 \r
211                 public bool IsIPv4Mapped ()\r
212                 {\r
213                         for (int i = 0; i < 5; i++) \r
214                                 if (address [i] != 0)\r
215                                         return false;\r
216                         return address [5] == 0xffff;\r
217                 }\r
218                 \r
219                 /// <summary>\r
220                 ///   Overrides System.Object.ToString to return\r
221                 ///   this object rendered in a canonicalized notation\r
222                 /// </summary>\r
223                 public override string ToString ()\r
224                 {\r
225                         StringBuilder s = new StringBuilder ();\r
226                         for (int i = 0; i < 7; i++)\r
227                                 s.Append (String.Format ("{0:X4}", address [i])).Append (':');\r
228                         s.Append (String.Format ("{0:X4}", address [7]));\r
229                         return s.ToString ();\r
230                 }\r
231 \r
232                 /// <returns>\r
233                 ///   Whether both objects are equal.\r
234                 /// </returns>\r
235                 public override bool Equals (object other)\r
236                 {\r
237                         System.Net.IPv6Address ipv6 = other as System.Net.IPv6Address;\r
238                         if (ipv6 != null) {\r
239                                 for (int i = 0; i < 8; i++) \r
240                                         if (this.address [i] != ipv6.address [i])\r
241                                                 return false;\r
242                                 return true;                            \r
243                         } \r
244                         \r
245                         System.Net.IPAddress ipv4 = other as System.Net.IPAddress;\r
246                         if (ipv4 != null) {\r
247                                 for (int i = 0; i < 5; i++) \r
248                                         if (address [i] != 0)\r
249                                                 return false;                   \r
250 \r
251                                 if (address [5] != 0 && address [5] != 0xffff)\r
252                                         return false;\r
253 \r
254                                 long a = ipv4.Address;\r
255                                 if (address [6] != (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff))) ||\r
256                                     address [7] != (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff))))\r
257                                         return false;\r
258                                         \r
259                                 return true;\r
260                         }\r
261                         \r
262                         return false;\r
263                 }\r
264 \r
265                 public override int GetHashCode ()\r
266                 {\r
267                         return Hash (((((int) address [0]) << 16) + address [1]), \r
268                                      ((((int) address [2]) << 16) + address [3]),\r
269                                      ((((int) address [4]) << 16) + address [5]),\r
270                                      ((((int) address [6]) << 16) + address [7]));\r
271                 }\r
272                 \r
273                 private static int Hash (int i, int j, int k, int l) \r
274                 {\r
275                         return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);\r
276                 }\r
277         }\r
278 }\r