New test.
[mono.git] / mcs / class / System / System.Net / IPAddress.cs
1 //
2 // System.Net.IPAddress.cs
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Lawrence Pit (loz@cable.a2000.nl)
7 //
8 // (C) Ximian, Inc.  http://www.ximian.com
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System;
31 using System.Globalization;
32 using System.Net.Sockets;
33 using System.Runtime.InteropServices;
34
35 namespace System.Net {
36
37         /// <remarks>
38         ///   Encapsulates an IP Address.
39         /// </remarks>
40         [Serializable]
41         public class IPAddress {
42                 // Don't change the name of this field without also
43                 // changing socket-io.c in the runtime
44                 // The IP address is stored in little-endian order inside the int, 
45                 // meaning the lower order bytes contain the netid
46                 private long m_Address;
47                 private AddressFamily m_Family = AddressFamily.InterNetwork;
48                 private ushort[] m_Numbers = new ushort[8];     /// ip6 Stored in network order (as ip4)
49                 private long m_ScopeId = 0;
50
51                 public static readonly IPAddress Any = new IPAddress(0);
52                 public static readonly IPAddress Broadcast = IPAddress.Parse ("255.255.255.255");
53                 public static readonly IPAddress Loopback = IPAddress.Parse ("127.0.0.1");
54                 public static readonly IPAddress None = IPAddress.Parse ("255.255.255.255");
55
56 #if NET_1_1
57                 public static readonly IPAddress IPv6Any = IPAddress.ParseIPV6 ("::");
58                 public static readonly IPAddress IPv6Loopback = IPAddress.ParseIPV6 ("::1");
59                 public static readonly IPAddress IPv6None = IPAddress.ParseIPV6 ("::");
60 #endif
61
62                 private static short SwapShort (short number)
63                 {
64                         return (short) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );
65                 }
66
67                 private static int SwapInt (int number)
68                 {
69                         byte b0 = (byte) ((number >> 24) & 0xFF);
70                         byte b1 = (byte) ((number >> 16) & 0xFF);
71                         byte b2 = (byte) ((number >> 8) & 0xFF);
72                         byte b3 = (byte) (number & 0xFF);
73                         return b0 + (b1 << 8) + (b2 << 16) + (b3 << 24);
74                 }
75
76                 private static long SwapLong (long number)
77                 {
78                         byte b0 = (byte) ((number >> 56) & 0xFF);
79                         byte b1 = (byte) ((number >> 48) & 0xFF);
80                         byte b2 = (byte) ((number >> 40) & 0xFF);
81                         byte b3 = (byte) ((number >> 32) & 0xFF);
82                         byte b4 = (byte) ((number >> 24) & 0xFF);
83                         byte b5 = (byte) ((number >> 16) & 0xFF);
84                         byte b6 = (byte) ((number >> 8) & 0xFF);
85                         byte b7 = (byte) (number & 0xFF);
86                         return (long) b0 + ((long) b1 << 8) + ((long) b2 << 16) + ((long) b3 << 24) + ((long) b4 << 32) + ((long) b5 << 40) + ((long) b6 << 48) + ((long) b7 << 56);
87                 }
88
89                 public static short HostToNetworkOrder(short host) {
90                         if (!BitConverter.IsLittleEndian)
91                                 return(host);
92
93                         return SwapShort (host);
94                 }
95
96                 public static int HostToNetworkOrder(int host) {
97                         if (!BitConverter.IsLittleEndian)
98                                 return(host);
99
100                         return SwapInt (host);
101                 }
102                 
103                 public static long HostToNetworkOrder(long host) {
104                         if (!BitConverter.IsLittleEndian)
105                                 return(host);
106
107                         return SwapLong (host);
108                 }
109
110                 public static short NetworkToHostOrder(short network) {
111                         if (!BitConverter.IsLittleEndian)
112                                 return(network);
113
114                         return SwapShort (network);
115                 }
116
117                 public static int NetworkToHostOrder(int network) {
118                         if (!BitConverter.IsLittleEndian)
119                                 return(network);
120
121                         return SwapInt (network);
122                 }
123
124                 public static long NetworkToHostOrder(long network) {
125                         if (!BitConverter.IsLittleEndian)
126                                 return(network);
127
128                         return SwapLong (network);
129                 }
130                 
131                 /// <summary>
132                 ///   Constructor from a 32-bit constant with the address bytes in
133                 ///   little-endian order (the lower order bytes contain the netid)
134                 /// </summary>
135                 public IPAddress (long addr)
136                 {
137                         m_Address = addr;
138                 }
139
140 #if NET_1_1
141                 public IPAddress (byte[] address)
142                 {
143                         int len = address.Length;
144 #if NET_2_0
145                         if (len != 16 && len != 4)
146                                 throw new ArgumentException ("address");
147 #else
148                         if (len != 16)
149                                 throw new ArgumentException ("address");
150 #endif
151                         if (len == 16) {
152                                 Buffer.BlockCopy(address, 0, m_Numbers, 0, 16);
153                                 m_Family = AddressFamily.InterNetworkV6;
154                                 m_ScopeId = 0;
155                         } else {
156                                 m_Address = (address [3] << 24) + (address [2] << 16) +
157                                         (address [1] << 8) + address [0];
158                         }
159                 }
160
161                 public IPAddress(byte[] address, long scopeId)
162                 {
163                         if (address.Length != 16)
164                                 throw new ArgumentException("address");
165
166                         Buffer.BlockCopy(address, 0, m_Numbers, 0, 16);
167                         m_Family = AddressFamily.InterNetworkV6;
168                         m_ScopeId = scopeId;
169                 }
170
171                 internal IPAddress(ushort[] address, long scopeId)
172                 {
173                         m_Numbers = address;
174
175                         for(int i=0; i<8; i++)
176                                 m_Numbers[i] = (ushort)HostToNetworkOrder((short)m_Numbers[i]);
177
178                         m_Family = AddressFamily.InterNetworkV6;
179                         m_ScopeId = scopeId;
180                 }
181 #endif
182
183                 public static IPAddress Parse (string ip)
184                 {
185                         IPAddress ret;
186                         if (TryParse (ip, out ret))
187                                 return ret;
188                         throw new FormatException("An invalid IP address was specified.");
189                 }
190
191 #if NET_2_0
192                 public
193 #endif
194                 static bool TryParse (string ip, out IPAddress address)
195                 {
196                         if (ip == null)
197                                 throw new ArgumentNullException ("Value cannot be null.");
198                                 
199 #if NET_1_1
200                         if( (address = ParseIPV4(ip)) == null)
201                                 if( (address = ParseIPV6(ip)) == null)
202                                         return false;
203 #else
204                         if( (address = ParseIPV4(ip)) == null)
205                                         return false;
206 #endif
207                         return true;
208                 }
209
210                 private static IPAddress ParseIPV4 (string ip)
211                 {
212                         if (ip.Length == 0 || ip == " ")
213                                 return new IPAddress (0);
214                                 
215                         int pos = ip.IndexOf (' ');
216                         if (pos != -1)
217                                 ip = ip.Substring (0, pos);                             
218
219                         if (ip.Length == 0 || ip [ip.Length - 1] == '.')
220                                 return null;
221
222                         string [] ips = ip.Split (new char [] {'.'});
223                         if (ips.Length > 4)
224                                 return null;
225                         
226                         // Make the number in network order
227                         try 
228                         {
229                                 long a = 0;
230                                 byte val = 0;
231                                 for (int i = 0; i < ips.Length; i++) {
232                                         string subnet = ips [i];
233                                         if ((3 <= subnet.Length && subnet.Length <= 4) &&
234                                             (subnet [0] == '0') &&
235                                             (subnet [1] == 'x' || subnet [2] == 'X')) {
236                                                 if (subnet.Length == 3)
237                                                         val = (byte) Uri.FromHex (subnet [2]);
238                                                 else 
239                                                         val = (byte) ((Uri.FromHex (subnet [2]) << 4) | Uri.FromHex (subnet [3]));
240                                         } else if (subnet.Length == 0)
241                                                 return null;
242                                         else 
243                                                 val = Byte.Parse (subnet, NumberStyles.None);
244
245                                         if (ips.Length < 4 && i == (ips.Length - 1)) 
246                                                 i = 3;
247
248                                         a |= (long) val << (i << 3);
249                                 }
250
251                                 return (new IPAddress (a));
252                         } catch (Exception) {
253                                 return null;
254                         }
255                 }
256                 
257 #if NET_1_1
258                 private static IPAddress ParseIPV6 (string ip)
259                 {
260                         try 
261                         {
262                                 IPv6Address newIPv6Address = IPv6Address.Parse(ip);
263                                 return new IPAddress(newIPv6Address.Address, newIPv6Address.ScopeId);
264                         }
265                         catch (Exception) {
266                                 return null;
267                         }
268                 }
269
270                 [Obsolete("This property is obsolete. Use GetAddressBytes.")]
271 #endif
272                 public long Address 
273                 {
274                         get {
275                                 if(m_Family != AddressFamily.InterNetwork)
276                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
277
278                                 return m_Address;
279                         }
280                         set {
281                                 /* no need to do this test, ms.net accepts any value.
282                                 if (value < 0 || value > 0x00000000FFFFFFFF)
283                                         throw new ArgumentOutOfRangeException (
284                                                 "the address must be between 0 and 0xFFFFFFFF");
285                                 */
286
287                                 if(m_Family != AddressFamily.InterNetwork)
288                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
289
290                                 m_Address = value;
291                         }
292                 }
293
294                 internal long InternalIPv4Address {
295                         get { return m_Address; }
296                 }
297
298 #if NET_2_0
299                 public bool IsIPv6LinkLocal {
300                         get {
301                                 if (m_Family == AddressFamily.InterNetwork)
302                                         return false;
303                                 int v = NetworkToHostOrder ((short) m_Numbers [0]) & 0xFFF0;
304                                 return 0xFE80 <= v && v < 0xFEC0;
305                         }
306                 }
307
308                 public bool IsIPv6SiteLocal {
309                         get {
310                                 if (m_Family == AddressFamily.InterNetwork)
311                                         return false;
312                                 int v = NetworkToHostOrder ((short) m_Numbers [0]) & 0xFFF0;
313                                 return 0xFEC0 <= v && v < 0xFF00;
314                         }
315                 }
316
317                 public bool IsIPv6Multicast {
318                         get {
319                                 return m_Family != AddressFamily.InterNetwork &&
320                                         ((ushort) NetworkToHostOrder ((short) m_Numbers [0]) & 0xFF00) == 0xFF00;
321                         }
322                 }
323 #endif
324
325 #if NET_1_1
326                 public long ScopeId {
327                         get {
328                                 if(m_Family != AddressFamily.InterNetworkV6)
329                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
330
331                                 return m_ScopeId;
332                         }
333                         set {
334                                 if(m_Family != AddressFamily.InterNetworkV6)
335                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
336
337                                 m_ScopeId = value;
338                         }
339                 }
340
341                 public byte [] GetAddressBytes () 
342                 {
343                         if(m_Family == AddressFamily.InterNetworkV6) {
344                                 byte [] addressBytes = new byte [16];
345                                 Buffer.BlockCopy (m_Numbers, 0, addressBytes, 0, 16);
346                                 return addressBytes;
347                         } else {
348                                 return new byte [4] { (byte)(m_Address & 0xFF),
349                                                      (byte)((m_Address >> 8) & 0xFF),
350                                                      (byte)((m_Address >> 16) & 0xFF),
351                                                      (byte)(m_Address >> 24) }; 
352                         }
353                 }
354 #endif
355                 public AddressFamily AddressFamily 
356                 {
357                         get {
358                                 return m_Family;
359                         }
360                 }
361                 
362                 
363                 /// <summary>
364                 ///   Used to tell whether an address is a loopback.
365                 ///   All IP addresses of the form 127.X.Y.Z, where X, Y, and Z are in 
366                 ///   the range 0-255, are loopback addresses.
367                 /// </summary>
368                 /// <param name="addr">Address to compare</param>
369                 /// <returns></returns>
370                 public static bool IsLoopback (IPAddress addr)
371                 {
372                         if(addr.m_Family == AddressFamily.InterNetwork)
373                                 return (addr.m_Address & 0xFF) == 127;
374                         else {
375                                 for(int i=0; i<6; i++) {
376                                         if(addr.m_Numbers[i] != 0)
377                                                 return false;
378                                 }
379
380                                 return NetworkToHostOrder((short)addr.m_Numbers[7]) == 1;
381                         }
382                 }
383
384                 /// <summary>
385                 ///   Overrides System.Object.ToString to return
386                 ///   this object rendered in a quad-dotted notation
387                 /// </summary>
388                 public override string ToString ()
389                 {
390                         if(m_Family == AddressFamily.InterNetwork)
391                                 return ToString (m_Address);
392                         else
393                         {
394                                 ushort[] numbers = m_Numbers.Clone() as ushort[];
395
396                                 for(int i=0; i<numbers.Length; i++)
397                                         numbers[i] = (ushort)NetworkToHostOrder((short)numbers[i]);
398
399                                 return new IPv6Address(numbers).ToString();
400                         }
401                 }
402
403                 /// <summary>
404                 ///   Returns this object rendered in a quad-dotted notation
405                 /// </summary>
406                 static string ToString (long addr)
407                 {
408                         // addr is in network order
409                         return  (addr & 0xff).ToString () + "." +
410                                 ((addr >> 8) & 0xff).ToString () + "." +
411                                 ((addr >> 16) & 0xff).ToString () + "." +
412                                 ((addr >> 24) & 0xff).ToString ();
413                 }
414
415                 /// <returns>
416                 ///   Whether both objects are equal.
417                 /// </returns>
418                 public override bool Equals (object other)
419                 {
420                         if (other is System.Net.IPAddress){
421                                 IPAddress otherAddr = other as IPAddress;
422
423                                 if(AddressFamily != otherAddr.AddressFamily)
424                                         return false;
425
426                                 if(AddressFamily == AddressFamily.InterNetwork) {
427                                         return m_Address == otherAddr.m_Address;
428                                 } else {
429                                         ushort[] vals = otherAddr.m_Numbers;
430
431                                         for(int i=0; i<8; i++)
432                                                 if(m_Numbers[i] != vals[i])
433                                                         return false;
434
435                                         return true;
436                                 }
437                         }
438                         return false;
439                 }
440
441                 public override int GetHashCode ()
442                 {
443                         if(m_Family == AddressFamily.InterNetwork)
444                                 return (int)m_Address;
445                         else
446                                 return Hash (((((int) m_Numbers[0]) << 16) + m_Numbers [1]), 
447                                         ((((int) m_Numbers [2]) << 16) + m_Numbers [3]),
448                                         ((((int) m_Numbers [4]) << 16) + m_Numbers [5]),
449                                         ((((int) m_Numbers [6]) << 16) + m_Numbers [7]));
450                 }
451
452                 private static int Hash (int i, int j, int k, int l) 
453                 {
454                         return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);
455                 }
456
457 #pragma warning disable 169
458                 // Added for serialization compatibility with MS.NET
459                 private int m_HashCode; 
460 #pragma warning restore
461                 
462         }
463 }