* TabControl.cs: Fix typo, emilinates an unneeded expose event.
[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 address;
47                 private AddressFamily _family = AddressFamily.InterNetwork;
48                 private ushort[] _numbers = new ushort[8];      /// ip6 Stored in network order (as ip4)
49                 private long _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                         address = addr;
138                 }
139
140 #if NET_1_1
141                 public IPAddress(byte[] address) : this(address, 0)
142                 {
143                 }
144
145                 public IPAddress(byte[] address, long scopeId)
146                 {
147                         if(address.Length != 16)
148                                 throw new ArgumentException("address");
149
150                         Buffer.BlockCopy(address, 0, _numbers, 0, 16);
151                         _family = AddressFamily.InterNetworkV6;
152                         _scopeId = scopeId;
153                 }
154
155                 internal IPAddress(ushort[] address, long scopeId)
156                 {
157                         _numbers = address;
158
159                         for(int i=0; i<8; i++)
160                                 _numbers[i] = (ushort)HostToNetworkOrder((short)_numbers[i]);
161
162                         _family = AddressFamily.InterNetworkV6;
163                         _scopeId = scopeId;
164                 }
165 #endif
166
167                 public static IPAddress Parse (string ip)
168                 {
169                         IPAddress ret;
170
171                         if (ip == null)
172                                 throw new ArgumentNullException ("Value cannot be null.");
173                                 
174 #if NET_1_1
175                         if( (ret = ParseIPV4(ip)) == null)
176                                 if( (ret = ParseIPV6(ip)) == null)
177                                         throw new FormatException("An invalid IP address was specified.");
178 #else
179                         if( (ret = ParseIPV4(ip)) == null)
180                                         throw new FormatException("An invalid IP address was specified.");
181 #endif
182                         return ret;
183                 }
184
185                 private static IPAddress ParseIPV4 (string ip)
186                 {
187                         if (ip.Length == 0 || ip == " ")
188                                 return new IPAddress (0);
189                                 
190                         int pos = ip.IndexOf (' ');
191                         if (pos != -1)
192                                 ip = ip.Substring (0, pos);                             
193
194                         if (ip.Length == 0 || ip [ip.Length - 1] == '.')
195                                 return null;
196
197                         string [] ips = ip.Split (new char [] {'.'});
198                         if (ips.Length > 4)
199                                 return null;
200                         
201                         // Make the number in network order
202                         try 
203                         {
204                                 long a = 0;
205                                 byte val = 0;
206                                 for (int i = 0; i < ips.Length; i++) {
207                                         string subnet = ips [i];
208                                         if ((3 <= subnet.Length && subnet.Length <= 4) &&
209                                             (subnet [0] == '0') &&
210                                             (subnet [1] == 'x' || subnet [2] == 'X')) {
211                                                 if (subnet.Length == 3)
212                                                         val = (byte) Uri.FromHex (subnet [2]);
213                                                 else 
214                                                         val = (byte) ((Uri.FromHex (subnet [2]) << 4) | Uri.FromHex (subnet [3]));
215                                         } else if (subnet.Length == 0)
216                                                 return null;
217                                         else 
218                                                 val = Byte.Parse (subnet, NumberStyles.None);
219
220                                         if (ips.Length < 4 && i == (ips.Length - 1)) 
221                                                 i = 3;
222
223                                         a |= (long) val << (i << 3);
224                                 }
225
226                                 return (new IPAddress (a));
227                         } catch (Exception) {
228                                 return null;
229                         }
230                 }
231                 
232 #if NET_1_1
233                 private static IPAddress ParseIPV6 (string ip)
234                 {
235                         try 
236                         {
237                                 IPv6Address newIPv6Address = IPv6Address.Parse(ip);
238                                 return new IPAddress(newIPv6Address.Address, newIPv6Address.ScopeId);
239                         }
240                         catch (Exception) {
241                                 return null;
242                         }
243                 }
244
245                 [Obsolete("This property is obsolete. Use GetAddressBytes.")]
246 #endif
247                 public long Address 
248                 {
249                         get {
250                                 if(_family != AddressFamily.InterNetwork)
251                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
252
253                                 return address;
254                         }
255                         set {
256                                 /* no need to do this test, ms.net accepts any value.
257                                 if (value < 0 || value > 0x00000000FFFFFFFF)
258                                         throw new ArgumentOutOfRangeException (
259                                                 "the address must be between 0 and 0xFFFFFFFF");
260                                 */
261
262                                 if(_family != AddressFamily.InterNetwork)
263                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
264
265                                 address = value;
266                         }
267                 }
268
269                 internal long InternalIPv4Address {
270                         get { return address; }
271                 }
272                 
273 #if NET_1_1
274                 public long ScopeId {
275                         get {
276                                 if(_family != AddressFamily.InterNetworkV6)
277                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
278
279                                 return _scopeId;
280                         }
281                         set {
282                                 if(_family != AddressFamily.InterNetworkV6)
283                                         throw new Exception("The attempted operation is not supported for the type of object referenced");
284
285                                 _scopeId = value;
286                         }
287                 }
288
289                 public byte [] GetAddressBytes () 
290                 {
291                         if(_family == AddressFamily.InterNetworkV6) {
292                                 byte [] addressBytes = new byte [16];
293                                 Buffer.BlockCopy (_numbers, 0, addressBytes, 0, 16);
294                                 return addressBytes;
295                         } else {
296                                 return new byte [4] { (byte)(address & 0xFF),
297                                                      (byte)((address >> 8) & 0xFF),
298                                                      (byte)((address >> 16) & 0xFF),
299                                                      (byte)(address >> 24) }; 
300                         }
301                 }
302 #endif
303                 public AddressFamily AddressFamily 
304                 {
305                         get {
306                                 return _family;
307                         }
308                 }
309                 
310                 
311                 /// <summary>
312                 ///   Used to tell whether an address is a loopback.
313                 ///   All IP addresses of the form 127.X.Y.Z, where X, Y, and Z are in 
314                 ///   the range 0-255, are loopback addresses.
315                 /// </summary>
316                 /// <param name="addr">Address to compare</param>
317                 /// <returns></returns>
318                 public static bool IsLoopback (IPAddress addr)
319                 {
320                         if(addr._family == AddressFamily.InterNetwork)
321                                 return (addr.address & 0xFF) == 127;
322                         else {
323                                 for(int i=0; i<6; i++) {
324                                         if(addr._numbers[i] != 0)
325                                                 return false;
326                                 }
327
328                                 return NetworkToHostOrder((short)addr._numbers[7]) == 1;
329                         }
330                 }
331
332                 /// <summary>
333                 ///   Overrides System.Object.ToString to return
334                 ///   this object rendered in a quad-dotted notation
335                 /// </summary>
336                 public override string ToString ()
337                 {
338                         if(_family == AddressFamily.InterNetwork)
339                                 return ToString (address);
340                         else
341                         {
342                                 ushort[] numbers = _numbers.Clone() as ushort[];
343
344                                 for(int i=0; i<numbers.Length; i++)
345                                         numbers[i] = (ushort)NetworkToHostOrder((short)numbers[i]);
346
347                                 return new IPv6Address(numbers).ToString();
348                         }
349                 }
350
351                 /// <summary>
352                 ///   Returns this object rendered in a quad-dotted notation
353                 /// </summary>
354                 static string ToString (long addr)
355                 {
356                         // addr is in network order
357                         return  (addr & 0xff).ToString () + "." +
358                                 ((addr >> 8) & 0xff).ToString () + "." +
359                                 ((addr >> 16) & 0xff).ToString () + "." +
360                                 ((addr >> 24) & 0xff).ToString ();
361                 }
362
363                 /// <returns>
364                 ///   Whether both objects are equal.
365                 /// </returns>
366                 public override bool Equals (object other)
367                 {
368                         if (other is System.Net.IPAddress){
369                                 IPAddress otherAddr = other as IPAddress;
370
371                                 if(AddressFamily != otherAddr.AddressFamily)
372                                         return false;
373
374                                 if(AddressFamily == AddressFamily.InterNetwork) {
375                                         return address == otherAddr.address;
376                                 } else {
377                                         ushort[] vals = otherAddr._numbers;
378
379                                         for(int i=0; i<8; i++)
380                                                 if(_numbers[i] != vals[i])
381                                                         return false;
382
383                                         return true;
384                                 }
385                         }
386                         return false;
387                 }
388
389                 public override int GetHashCode ()
390                 {
391                         if(_family == AddressFamily.InterNetwork)
392                                 return (int)address;
393                         else
394                                 return Hash (((((int) _numbers[0]) << 16) + _numbers [1]), 
395                                         ((((int) _numbers [2]) << 16) + _numbers [3]),
396                                         ((((int) _numbers [4]) << 16) + _numbers [5]),
397                                         ((((int) _numbers [6]) << 16) + _numbers [7]));
398                 }
399
400                 private static int Hash (int i, int j, int k, int l) 
401                 {
402                         return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);
403                 }
404         }
405 }