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