* IPAddress.cs: On 1.1, 4-byte addresses are not supported in the
[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                 public static readonly IPAddress IPv6Any = IPAddress.ParseIPV6 ("::");
56                 public static readonly IPAddress IPv6Loopback = IPAddress.ParseIPV6 ("::1");
57                 public static readonly IPAddress IPv6None = IPAddress.ParseIPV6 ("::");
58
59                 private static short SwapShort (short number)
60                 {
61                         return (short) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );
62                 }
63
64                 private static int SwapInt (int number)
65                 {
66                         byte b0 = (byte) ((number >> 24) & 0xFF);
67                         byte b1 = (byte) ((number >> 16) & 0xFF);
68                         byte b2 = (byte) ((number >> 8) & 0xFF);
69                         byte b3 = (byte) (number & 0xFF);
70                         return b0 + (b1 << 8) + (b2 << 16) + (b3 << 24);
71                 }
72
73                 private static long SwapLong (long number)
74                 {
75                         byte b0 = (byte) ((number >> 56) & 0xFF);
76                         byte b1 = (byte) ((number >> 48) & 0xFF);
77                         byte b2 = (byte) ((number >> 40) & 0xFF);
78                         byte b3 = (byte) ((number >> 32) & 0xFF);
79                         byte b4 = (byte) ((number >> 24) & 0xFF);
80                         byte b5 = (byte) ((number >> 16) & 0xFF);
81                         byte b6 = (byte) ((number >> 8) & 0xFF);
82                         byte b7 = (byte) (number & 0xFF);
83                         return (long) b0 + ((long) b1 << 8) + ((long) b2 << 16) + ((long) b3 << 24) + ((long) b4 << 32) + ((long) b5 << 40) + ((long) b6 << 48) + ((long) b7 << 56);
84                 }
85
86                 public static short HostToNetworkOrder(short host) {
87                         if (!BitConverter.IsLittleEndian)
88                                 return(host);
89
90                         return SwapShort (host);
91                 }
92
93                 public static int HostToNetworkOrder(int host) {
94                         if (!BitConverter.IsLittleEndian)
95                                 return(host);
96
97                         return SwapInt (host);
98                 }
99                 
100                 public static long HostToNetworkOrder(long host) {
101                         if (!BitConverter.IsLittleEndian)
102                                 return(host);
103
104                         return SwapLong (host);
105                 }
106
107                 public static short NetworkToHostOrder(short network) {
108                         if (!BitConverter.IsLittleEndian)
109                                 return(network);
110
111                         return SwapShort (network);
112                 }
113
114                 public static int NetworkToHostOrder(int network) {
115                         if (!BitConverter.IsLittleEndian)
116                                 return(network);
117
118                         return SwapInt (network);
119                 }
120
121                 public static long NetworkToHostOrder(long network) {
122                         if (!BitConverter.IsLittleEndian)
123                                 return(network);
124
125                         return SwapLong (network);
126                 }
127                 
128                 /// <summary>
129                 ///   Constructor from a 32-bit constant with the address bytes in
130                 ///   little-endian order (the lower order bytes contain the netid)
131                 /// </summary>
132                 public IPAddress (long addr)
133                 {
134                         m_Address = addr;
135                 }
136
137                 public IPAddress (byte[] address)
138                 {
139                         if (address == null)
140                                 throw new ArgumentNullException ("address");
141
142                         int len = address.Length;
143
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
152                         if (len == 16) {
153                                 Buffer.BlockCopy(address, 0, m_Numbers, 0, 16);
154                                 m_Family = AddressFamily.InterNetworkV6;
155                                 m_ScopeId = 0;
156                         } else {
157                                 m_Address = (address [3] << 24) + (address [2] << 16) +
158                                         (address [1] << 8) + address [0];
159                         }
160                 }
161
162                 public IPAddress(byte[] address, long scopeId)
163                 {
164                         if (address == null)
165                                 throw new ArgumentNullException ("address");
166
167                         if (address.Length != 16)
168                                 throw new ArgumentException("address");
169
170                         Buffer.BlockCopy(address, 0, m_Numbers, 0, 16);
171                         m_Family = AddressFamily.InterNetworkV6;
172                         m_ScopeId = scopeId;
173                 }
174
175                 internal IPAddress(ushort[] address, long scopeId)
176                 {
177                         m_Numbers = address;
178
179                         for(int i=0; i<8; i++)
180                                 m_Numbers[i] = (ushort)HostToNetworkOrder((short)m_Numbers[i]);
181
182                         m_Family = AddressFamily.InterNetworkV6;
183                         m_ScopeId = scopeId;
184                 }
185
186                 public static IPAddress Parse (string ip)
187                 {
188                         IPAddress ret;
189                         if (TryParse (ip, out ret))
190                                 return ret;
191                         throw new FormatException("An invalid IP address was specified.");
192                 }
193
194 #if NET_2_0
195                 public
196 #endif
197                 static bool TryParse (string ip, out IPAddress address)
198                 {
199                         if (ip == null)
200                                 throw new ArgumentNullException ("Value cannot be null.");
201                                 
202                         if( (address = ParseIPV4(ip)) == null)
203                                 if( (address = ParseIPV6(ip)) == null)
204                                         return false;
205                         return true;
206                 }
207
208                 private static IPAddress ParseIPV4 (string ip)
209                 {
210                         if (ip.Length == 0 || ip == " ")
211                                 return new IPAddress (0);
212                                 
213                         int pos = ip.IndexOf (' ');
214                         if (pos != -1)
215                                 ip = ip.Substring (0, pos);
216
217                         if (ip.Length == 0 || ip [ip.Length - 1] == '.')
218                                 return null;
219
220                         string [] ips = ip.Split (new char [] {'.'});
221                         if (ips.Length > 4)
222                                 return null;
223                         
224                         // Make the number in network order
225                         try 
226                         {
227                                 long a = 0;
228                                 byte val = 0;
229                                 for (int i = 0; i < ips.Length; i++) {
230                                         string subnet = ips [i];
231                                         if ((3 <= subnet.Length && subnet.Length <= 4) &&
232                                             (subnet [0] == '0') &&
233                                             (subnet [1] == 'x' || subnet [2] == 'X')) {
234                                                 if (subnet.Length == 3)
235                                                         val = (byte) Uri.FromHex (subnet [2]);
236                                                 else 
237                                                         val = (byte) ((Uri.FromHex (subnet [2]) << 4) | Uri.FromHex (subnet [3]));
238                                         } else if (subnet.Length == 0)
239                                                 return null;
240                                         else 
241                                                 val = Byte.Parse (subnet, NumberStyles.None);
242
243                                         if (ips.Length < 4 && i == (ips.Length - 1)) 
244                                                 i = 3;
245
246                                         a |= (long) val << (i << 3);
247                                 }
248
249                                 return (new IPAddress (a));
250                         } catch (Exception) {
251                                 return null;
252                         }
253                 }
254                 
255                 private static IPAddress ParseIPV6 (string ip)
256                 {
257                         try 
258                         {
259                                 IPv6Address newIPv6Address = IPv6Address.Parse(ip);
260                                 return new IPAddress(newIPv6Address.Address, newIPv6Address.ScopeId);
261                         }
262                         catch (Exception) {
263                                 return null;
264                         }
265                 }
266
267                 [Obsolete("This property is obsolete. Use GetAddressBytes.")]
268                 public long Address 
269                 {
270                         get {
271                                 if(m_Family != AddressFamily.InterNetwork)
272                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
273
274                                 return m_Address;
275                         }
276                         set {
277                                 /* no need to do this test, ms.net accepts any value.
278                                 if (value < 0 || value > 0x00000000FFFFFFFF)
279                                         throw new ArgumentOutOfRangeException (
280                                                 "the address must be between 0 and 0xFFFFFFFF");
281                                 */
282
283                                 if(m_Family != AddressFamily.InterNetwork)
284                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
285
286                                 m_Address = value;
287                         }
288                 }
289
290                 internal long InternalIPv4Address {
291                         get { return m_Address; }
292                 }
293
294 #if NET_2_0
295                 public bool IsIPv6LinkLocal {
296                         get {
297                                 if (m_Family == AddressFamily.InterNetwork)
298                                         return false;
299                                 int v = NetworkToHostOrder ((short) m_Numbers [0]) & 0xFFF0;
300                                 return 0xFE80 <= v && v < 0xFEC0;
301                         }
302                 }
303
304                 public bool IsIPv6SiteLocal {
305                         get {
306                                 if (m_Family == AddressFamily.InterNetwork)
307                                         return false;
308                                 int v = NetworkToHostOrder ((short) m_Numbers [0]) & 0xFFF0;
309                                 return 0xFEC0 <= v && v < 0xFF00;
310                         }
311                 }
312
313                 public bool IsIPv6Multicast {
314                         get {
315                                 return m_Family != AddressFamily.InterNetwork &&
316                                         ((ushort) NetworkToHostOrder ((short) m_Numbers [0]) & 0xFF00) == 0xFF00;
317                         }
318                 }
319 #endif
320
321                 public long ScopeId {
322                         get {
323                                 if(m_Family != AddressFamily.InterNetworkV6)
324                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
325
326                                 return m_ScopeId;
327                         }
328                         set {
329                                 if(m_Family != AddressFamily.InterNetworkV6)
330                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
331
332                                 m_ScopeId = value;
333                         }
334                 }
335
336                 public byte [] GetAddressBytes () 
337                 {
338                         if(m_Family == AddressFamily.InterNetworkV6) {
339                                 byte [] addressBytes = new byte [16];
340                                 Buffer.BlockCopy (m_Numbers, 0, addressBytes, 0, 16);
341                                 return addressBytes;
342                         } else {
343                                 return new byte [4] { (byte)(m_Address & 0xFF),
344                                                      (byte)((m_Address >> 8) & 0xFF),
345                                                      (byte)((m_Address >> 16) & 0xFF),
346                                                      (byte)(m_Address >> 24) }; 
347                         }
348                 }
349
350                 public AddressFamily AddressFamily 
351                 {
352                         get {
353                                 return m_Family;
354                         }
355                 }
356                 
357                 
358                 /// <summary>
359                 ///   Used to tell whether an address is a loopback.
360                 ///   All IP addresses of the form 127.X.Y.Z, where X, Y, and Z are in 
361                 ///   the range 0-255, are loopback addresses.
362                 /// </summary>
363                 /// <param name="addr">Address to compare</param>
364                 /// <returns></returns>
365                 public static bool IsLoopback (IPAddress addr)
366                 {
367                         if(addr.m_Family == AddressFamily.InterNetwork)
368                                 return (addr.m_Address & 0xFF) == 127;
369                         else {
370                                 for(int i=0; i<6; i++) {
371                                         if(addr.m_Numbers[i] != 0)
372                                                 return false;
373                                 }
374
375                                 return NetworkToHostOrder((short)addr.m_Numbers[7]) == 1;
376                         }
377                 }
378
379                 /// <summary>
380                 ///   Overrides System.Object.ToString to return
381                 ///   this object rendered in a quad-dotted notation
382                 /// </summary>
383                 public override string ToString ()
384                 {
385                         if(m_Family == AddressFamily.InterNetwork)
386                                 return ToString (m_Address);
387                         else
388                         {
389                                 ushort[] numbers = m_Numbers.Clone() as ushort[];
390
391                                 for(int i=0; i<numbers.Length; i++)
392                                         numbers[i] = (ushort)NetworkToHostOrder((short)numbers[i]);
393
394                                 return new IPv6Address(numbers).ToString();
395                         }
396                 }
397
398                 /// <summary>
399                 ///   Returns this object rendered in a quad-dotted notation
400                 /// </summary>
401                 static string ToString (long addr)
402                 {
403                         // addr is in network order
404                         return  (addr & 0xff).ToString () + "." +
405                                 ((addr >> 8) & 0xff).ToString () + "." +
406                                 ((addr >> 16) & 0xff).ToString () + "." +
407                                 ((addr >> 24) & 0xff).ToString ();
408                 }
409
410                 /// <returns>
411                 ///   Whether both objects are equal.
412                 /// </returns>
413                 public override bool Equals (object other)
414                 {
415                         if (other is System.Net.IPAddress){
416                                 IPAddress otherAddr = other as IPAddress;
417
418                                 if(AddressFamily != otherAddr.AddressFamily)
419                                         return false;
420
421                                 if(AddressFamily == AddressFamily.InterNetwork) {
422                                         return m_Address == otherAddr.m_Address;
423                                 } else {
424                                         ushort[] vals = otherAddr.m_Numbers;
425
426                                         for(int i=0; i<8; i++)
427                                                 if(m_Numbers[i] != vals[i])
428                                                         return false;
429
430                                         return true;
431                                 }
432                         }
433                         return false;
434                 }
435
436                 public override int GetHashCode ()
437                 {
438                         if(m_Family == AddressFamily.InterNetwork)
439                                 return (int)m_Address;
440                         else
441                                 return Hash (((((int) m_Numbers[0]) << 16) + m_Numbers [1]), 
442                                         ((((int) m_Numbers [2]) << 16) + m_Numbers [3]),
443                                         ((((int) m_Numbers [4]) << 16) + m_Numbers [5]),
444                                         ((((int) m_Numbers [6]) << 16) + m_Numbers [7]));
445                 }
446
447                 private static int Hash (int i, int j, int k, int l) 
448                 {
449                         return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);
450                 }
451
452 #pragma warning disable 169
453                 // Added for serialization compatibility with MS.NET
454                 private int m_HashCode; 
455 #pragma warning restore
456                 
457         }
458 }