74e67f25f2fc6383a8e3de05ac3643d3d012b566
[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 //
11 // Note: the address is stored in host order
12
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Globalization;
36 using System.Net.Sockets;
37 using System.Runtime.InteropServices;
38
39 namespace System.Net {
40
41         /// <remarks>
42         ///   Encapsulates an IP Address.
43         /// </remarks>
44         [Serializable]
45         public class IPAddress {
46                 // Don't change the name of this field without also
47                 // changing socket-io.c in the runtime
48                 private long address;
49                 private AddressFamily _family = AddressFamily.InterNetwork;
50                 private ushort[] _numbers = new ushort[8];      /// ip6 Stored in network order (as ip4)
51                 private long _scopeId = 0;
52
53                 public static readonly IPAddress Any = new IPAddress(0);
54                 public static readonly IPAddress Broadcast = IPAddress.Parse ("255.255.255.255");
55                 public static readonly IPAddress Loopback = IPAddress.Parse ("127.0.0.1");
56                 public static readonly IPAddress None = IPAddress.Parse ("255.255.255.255");
57
58 #if NET_1_1
59                 public static readonly IPAddress IPv6Any = IPAddress.ParseIPV6 ("::");
60                 public static readonly IPAddress IPv6Loopback = IPAddress.ParseIPV6 ("::1");
61                 public static readonly IPAddress IPv6None = IPAddress.ParseIPV6 ("::");
62 #endif
63
64                 private static short SwapShort (short number)
65                 {
66                         return (short) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );
67                 }
68
69                 private static int SwapInt (int number)
70                 {
71                         byte b0 = (byte) ((number >> 24) & 0xFF);
72                         byte b1 = (byte) ((number >> 16) & 0xFF);
73                         byte b2 = (byte) ((number >> 8) & 0xFF);
74                         byte b3 = (byte) (number & 0xFF);
75                         return b0 + (b1 << 8) + (b2 << 16) + (b3 << 24);
76                 }
77
78                 private static long SwapLong (long number)
79                 {
80                         byte b0 = (byte) ((number >> 56) & 0xFF);
81                         byte b1 = (byte) ((number >> 48) & 0xFF);
82                         byte b2 = (byte) ((number >> 40) & 0xFF);
83                         byte b3 = (byte) ((number >> 32) & 0xFF);
84                         byte b4 = (byte) ((number >> 24) & 0xFF);
85                         byte b5 = (byte) ((number >> 16) & 0xFF);
86                         byte b6 = (byte) ((number >> 8) & 0xFF);
87                         byte b7 = (byte) (number & 0xFF);
88                         return (long) b0 + ((long) b1 << 8) + ((long) b2 << 16) + ((long) b3 << 24) + ((long) b4 << 32) + ((long) b5 << 40) + ((long) b6 << 48) + ((long) b7 << 56);
89                 }
90
91                 public static short HostToNetworkOrder(short host) {
92                         if (!BitConverter.IsLittleEndian)
93                                 return(host);
94
95                         return SwapShort (host);
96                 }
97
98                 public static int HostToNetworkOrder(int host) {
99                         if (!BitConverter.IsLittleEndian)
100                                 return(host);
101
102                         return SwapInt (host);
103                 }
104                 
105                 public static long HostToNetworkOrder(long host) {
106                         if (!BitConverter.IsLittleEndian)
107                                 return(host);
108
109                         return SwapLong (host);
110                 }
111
112                 public static short NetworkToHostOrder(short network) {
113                         if (!BitConverter.IsLittleEndian)
114                                 return(network);
115
116                         return SwapShort (network);
117                 }
118
119                 public static int NetworkToHostOrder(int network) {
120                         if (!BitConverter.IsLittleEndian)
121                                 return(network);
122
123                         return SwapInt (network);
124                 }
125
126                 public static long NetworkToHostOrder(long network) {
127                         if (!BitConverter.IsLittleEndian)
128                                 return(network);
129
130                         return SwapLong (network);
131                 }
132                 
133                 /// <summary>
134                 ///   Constructor from a 32-bit constant with its bytes 
135                 ///   in network order.
136                 /// </summary>
137                 public IPAddress (long addr)
138                 {
139                         address = addr;
140                 }
141
142 #if NET_1_1
143                 public IPAddress(byte[] address) : this(address, 0)
144                 {
145                 }
146
147                 public IPAddress(byte[] address, long scopeId)
148                 {
149                         if(address.Length != 16)
150                                 throw new ArgumentException("address");
151
152                         Buffer.BlockCopy(address, 0, _numbers, 0, 16);
153                         _family = AddressFamily.InterNetworkV6;
154                         _scopeId = scopeId;
155                 }
156
157                 internal IPAddress(ushort[] address, long scopeId)
158                 {
159                         _numbers = address;
160
161                         for(int i=0; i<8; i++)
162                                 _numbers[i] = (ushort)HostToNetworkOrder((short)_numbers[i]);
163
164                         _family = AddressFamily.InterNetworkV6;
165                         _scopeId = scopeId;
166                 }
167 #endif
168
169                 public static IPAddress Parse (string ip)
170                 {
171                         IPAddress ret;
172
173                         if (ip == null)
174                                 throw new ArgumentNullException ("Value cannot be null.");
175                                 
176 #if NET_1_1
177                         if( (ret = ParseIPV4(ip)) == null)
178                                 if( (ret = ParseIPV6(ip)) == null)
179                                         throw new FormatException("An invalid IP address was specified.");
180 #else
181                         if( (ret = ParseIPV4(ip)) == null)
182                                         throw new FormatException("An invalid IP address was specified.");
183 #endif
184                         return ret;
185                 }
186
187                 private static IPAddress ParseIPV4 (string ip)
188                 {
189                         if (ip.Length == 0 || ip [0] == ' ')
190                                 return new IPAddress (0);
191                                 
192                         int pos = ip.IndexOf (' ');
193                         if (pos != -1)
194                                 ip = ip.Substring (0, pos);                             
195                         else if (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                                                 val = 0;
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(_family != AddressFamily.InterNetwork)
252                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
253
254                                 return 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(_family != AddressFamily.InterNetwork)
264                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
265
266                                 address = value;
267                         }
268                 }
269
270                 internal long InternalIPv4Address {
271                         get { return address; }
272                 }
273                 
274 #if NET_1_1
275                 public long ScopeId {
276                         get {
277                                 if(_family != AddressFamily.InterNetworkV6)
278                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
279
280                                 return _scopeId;
281                         }
282                         set {
283                                 if(_family != AddressFamily.InterNetworkV6)
284                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
285
286                                 _scopeId = value;
287                         }
288                 }
289
290                 public byte [] GetAddressBytes () 
291                 {
292                         if(_family == AddressFamily.InterNetworkV6) {
293                                 byte [] addressBytes = new byte [16];
294                                 Buffer.BlockCopy (_numbers, 0, addressBytes, 0, 16);
295                                 return addressBytes;
296                         } else {
297                                 return new byte [4] { (byte)(address & 0xFF),
298                                                      (byte)((address >> 8) & 0xFF),
299                                                      (byte)((address >> 16) & 0xFF),
300                                                      (byte)(address >> 24) }; 
301                         }
302                 }
303 #endif
304                 public AddressFamily AddressFamily 
305                 {
306                         get {
307                                 return _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._family == AddressFamily.InterNetwork)
322                                 return (addr.address & 0xFF) == 127;
323                         else {
324                                 for(int i=0; i<6; i++) {
325                                         if(addr._numbers[i] != 0)
326                                                 return false;
327                                 }
328
329                                 return NetworkToHostOrder((short)addr._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(_family == AddressFamily.InterNetwork)
340                                 return ToString (address);
341                         else
342                         {
343                                 ushort[] numbers = _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 address == otherAddr.address;
377                                 } else {
378                                         ushort[] vals = otherAddr._numbers;
379
380                                         for(int i=0; i<8; i++)
381                                                 if(_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(_family == AddressFamily.InterNetwork)
393                                 return (int)address;
394                         else
395                                 return Hash (((((int) _numbers[0]) << 16) + _numbers [1]), 
396                                         ((((int) _numbers [2]) << 16) + _numbers [3]),
397                                         ((((int) _numbers [4]) << 16) + _numbers [5]),
398                                         ((((int) _numbers [6]) << 16) + _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 }