svn path=/branches/mono-1-1-9/mcs/; revision=51206
[mono.git] / mcs / class / System / System.Net / IPAddress.cs
old mode 100755 (executable)
new mode 100644 (file)
index 758e239..ca2b12d
@@ -3,17 +3,35 @@
 //
 // Author:
 //   Miguel de Icaza (miguel@ximian.com)
+//   Lawrence Pit (loz@cable.a2000.nl)
 //
 // (C) Ximian, Inc.  http://www.ximian.com
 //
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-// Note: the address is stored in host order
 
+using System;
+using System.Globalization;
 using System.Net.Sockets;
 using System.Runtime.InteropServices;
 
-using System;
-
 namespace System.Net {
 
        /// <remarks>
@@ -23,13 +41,25 @@ namespace System.Net {
        public class IPAddress {
                // Don't change the name of this field without also
                // changing socket-io.c in the runtime
-               private long address;
+               // The IP address is stored in little-endian order inside the int, 
+               // meaning the lower order bytes contain the netid
+               private long m_Address;
+               private AddressFamily m_Family = AddressFamily.InterNetwork;
+               private ushort[] m_Numbers = new ushort[8];     /// ip6 Stored in network order (as ip4)
+               private long m_ScopeId = 0;
+               private int m_HashCode; // Added for serialization compatibility with MS.NET
 
                public static readonly IPAddress Any = new IPAddress(0);
                public static readonly IPAddress Broadcast = IPAddress.Parse ("255.255.255.255");
                public static readonly IPAddress Loopback = IPAddress.Parse ("127.0.0.1");
                public static readonly IPAddress None = IPAddress.Parse ("255.255.255.255");
 
+#if NET_1_1
+               public static readonly IPAddress IPv6Any = IPAddress.ParseIPV6 ("::");
+               public static readonly IPAddress IPv6Loopback = IPAddress.ParseIPV6 ("::1");
+               public static readonly IPAddress IPv6None = IPAddress.ParseIPV6 ("::");
+#endif
+
                private static short SwapShort (short number)
                {
                        return (short) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );
@@ -100,63 +130,128 @@ namespace System.Net {
                }
                
                /// <summary>
-               ///   Constructor from a 32-bit constant with its bytes 
-               ///   in network order.
+               ///   Constructor from a 32-bit constant with the address bytes in
+               ///   little-endian order (the lower order bytes contain the netid)
                /// </summary>
                public IPAddress (long addr)
                {
-                       Address = addr;
+                       m_Address = addr;
+               }
+
+#if NET_1_1
+               public IPAddress(byte[] address) : this(address, 0)
+               {
                }
 
-               public static IPAddress Parse(string ip)
+               public IPAddress(byte[] address, long scopeId)
                {
-                       if(ip == null)
-                               throw new ArgumentNullException("null ip string");
+                       if(address.Length != 16)
+                               throw new ArgumentException("address");
 
-                       int pos = 0;
-                       int ndots = 0;
-                       char current;
-                       bool prevDigit = false;
+                       Buffer.BlockCopy(address, 0, m_Numbers, 0, 16);
+                       m_Family = AddressFamily.InterNetworkV6;
+                       m_ScopeId = scopeId;
+               }
 
-                       while (pos < ip.Length) {
-                               current  = ip [pos++];
-                               if (Char.IsDigit (current))
-                                       prevDigit = true;
-                               else
-                               if (current == '.') {
-                                       // No more than 3 dots. Doesn't allow ending with a dot.
-                                       if (++ndots > 3 || pos == ip.Length || prevDigit == false)
-                                               throw new FormatException ("the string is not a valid ip");
+               internal IPAddress(ushort[] address, long scopeId)
+               {
+                       m_Numbers = address;
 
-                                       prevDigit = false;
-                               }
-                               else if (!Char.IsDigit (current)) {
-                                       if (!Char.IsWhiteSpace (current))
-                                               throw new FormatException ("the string is not a valid ip");
+                       for(int i=0; i<8; i++)
+                               m_Numbers[i] = (ushort)HostToNetworkOrder((short)m_Numbers[i]);
 
-                                       // The same as MS does
-                                       if (pos == 1) 
-                                               return new IPAddress (0);
+                       m_Family = AddressFamily.InterNetworkV6;
+                       m_ScopeId = scopeId;
+               }
+#endif
 
-                                       break;
-                               }
-                       }
+               public static IPAddress Parse (string ip)
+               {
+                       IPAddress ret;
+
+                       if (ip == null)
+                               throw new ArgumentNullException ("Value cannot be null.");
+                               
+#if NET_1_1
+                       if( (ret = ParseIPV4(ip)) == null)
+                               if( (ret = ParseIPV6(ip)) == null)
+                                       throw new FormatException("An invalid IP address was specified.");
+#else
+                       if( (ret = ParseIPV4(ip)) == null)
+                                       throw new FormatException("An invalid IP address was specified.");
+#endif
+                       return ret;
+               }
 
-                       if (ndots != 3)
-                               throw new FormatException ("the string is not a valid ip");
+               private static IPAddress ParseIPV4 (string ip)
+               {
+                       if (ip.Length == 0 || ip == " ")
+                               return new IPAddress (0);
+                               
+                       int pos = ip.IndexOf (' ');
+                       if (pos != -1)
+                               ip = ip.Substring (0, pos);                             
 
+                       if (ip.Length == 0 || ip [ip.Length - 1] == '.')
+                               return null;
 
-                       long a = 0;
                        string [] ips = ip.Split (new char [] {'.'});
+                       if (ips.Length > 4)
+                               return null;
+                       
                        // Make the number in network order
-                       for (int i = ips.Length - 1; i >= 0; i--)
-                               a = (a << 8) |  (Byte.Parse(ips [i]));
-                       return (new IPAddress (a));
+                       try 
+                       {
+                               long a = 0;
+                               byte val = 0;
+                               for (int i = 0; i < ips.Length; i++) {
+                                       string subnet = ips [i];
+                                       if ((3 <= subnet.Length && subnet.Length <= 4) &&
+                                           (subnet [0] == '0') &&
+                                           (subnet [1] == 'x' || subnet [2] == 'X')) {
+                                               if (subnet.Length == 3)
+                                                       val = (byte) Uri.FromHex (subnet [2]);
+                                               else 
+                                                       val = (byte) ((Uri.FromHex (subnet [2]) << 4) | Uri.FromHex (subnet [3]));
+                                       } else if (subnet.Length == 0)
+                                               return null;
+                                       else 
+                                               val = Byte.Parse (subnet, NumberStyles.None);
+
+                                       if (ips.Length < 4 && i == (ips.Length - 1)) 
+                                               i = 3;
+
+                                       a |= (long) val << (i << 3);
+                               }
+
+                               return (new IPAddress (a));
+                       } catch (Exception) {
+                               return null;
+                       }
                }
                
-               public long Address {
+#if NET_1_1
+               private static IPAddress ParseIPV6 (string ip)
+               {
+                       try 
+                       {
+                               IPv6Address newIPv6Address = IPv6Address.Parse(ip);
+                               return new IPAddress(newIPv6Address.Address, newIPv6Address.ScopeId);
+                       }
+                       catch (Exception) {
+                               return null;
+                       }
+               }
+
+               [Obsolete("This property is obsolete. Use GetAddressBytes.")]
+#endif
+               public long Address 
+               {
                        get {
-                               return address;
+                               if(m_Family != AddressFamily.InterNetwork)
+                                       throw new Exception("The attempted operation is not supported for the type of object referenced");
+
+                               return m_Address;
                        }
                        set {
                                /* no need to do this test, ms.net accepts any value.
@@ -165,13 +260,51 @@ namespace System.Net {
                                                "the address must be between 0 and 0xFFFFFFFF");
                                */
 
-                               address = value;
+                               if(m_Family != AddressFamily.InterNetwork)
+                                       throw new Exception("The attempted operation is not supported for the type of object referenced");
+
+                               m_Address = value;
                        }
                }
 
-               public AddressFamily AddressFamily {
+               internal long InternalIPv4Address {
+                       get { return m_Address; }
+               }
+               
+#if NET_1_1
+               public long ScopeId {
+                       get {
+                               if(m_Family != AddressFamily.InterNetworkV6)
+                                       throw new Exception("The attempted operation is not supported for the type of object referenced");
+
+                               return m_ScopeId;
+                       }
+                       set {
+                               if(m_Family != AddressFamily.InterNetworkV6)
+                                       throw new Exception("The attempted operation is not supported for the type of object referenced");
+
+                               m_ScopeId = value;
+                       }
+               }
+
+               public byte [] GetAddressBytes () 
+               {
+                       if(m_Family == AddressFamily.InterNetworkV6) {
+                               byte [] addressBytes = new byte [16];
+                               Buffer.BlockCopy (m_Numbers, 0, addressBytes, 0, 16);
+                               return addressBytes;
+                       } else {
+                               return new byte [4] { (byte)(m_Address & 0xFF),
+                                                    (byte)((m_Address >> 8) & 0xFF),
+                                                    (byte)((m_Address >> 16) & 0xFF),
+                                                    (byte)(m_Address >> 24) }; 
+                       }
+               }
+#endif
+               public AddressFamily AddressFamily 
+               {
                        get {
-                               return(AddressFamily.InterNetwork);
+                               return m_Family;
                        }
                }
                
@@ -185,7 +318,16 @@ namespace System.Net {
                /// <returns></returns>
                public static bool IsLoopback (IPAddress addr)
                {
-                       return (addr.address & 0xFF) == 127;
+                       if(addr.m_Family == AddressFamily.InterNetwork)
+                               return (addr.m_Address & 0xFF) == 127;
+                       else {
+                               for(int i=0; i<6; i++) {
+                                       if(addr.m_Numbers[i] != 0)
+                                               return false;
+                               }
+
+                               return NetworkToHostOrder((short)addr.m_Numbers[7]) == 1;
+                       }
                }
 
                /// <summary>
@@ -194,7 +336,17 @@ namespace System.Net {
                /// </summary>
                public override string ToString ()
                {
-                       return ToString (address);
+                       if(m_Family == AddressFamily.InterNetwork)
+                               return ToString (m_Address);
+                       else
+                       {
+                               ushort[] numbers = m_Numbers.Clone() as ushort[];
+
+                               for(int i=0; i<numbers.Length; i++)
+                                       numbers[i] = (ushort)NetworkToHostOrder((short)numbers[i]);
+
+                               return new IPv6Address(numbers).ToString();
+                       }
                }
 
                /// <summary>
@@ -215,14 +367,40 @@ namespace System.Net {
                public override bool Equals (object other)
                {
                        if (other is System.Net.IPAddress){
-                               return Address == ((System.Net.IPAddress) other).Address;
+                               IPAddress otherAddr = other as IPAddress;
+
+                               if(AddressFamily != otherAddr.AddressFamily)
+                                       return false;
+
+                               if(AddressFamily == AddressFamily.InterNetwork) {
+                                       return m_Address == otherAddr.m_Address;
+                               } else {
+                                       ushort[] vals = otherAddr.m_Numbers;
+
+                                       for(int i=0; i<8; i++)
+                                               if(m_Numbers[i] != vals[i])
+                                                       return false;
+
+                                       return true;
+                               }
                        }
                        return false;
                }
 
                public override int GetHashCode ()
                {
-                       return (int)Address;
+                       if(m_Family == AddressFamily.InterNetwork)
+                               return (int)m_Address;
+                       else
+                               return Hash (((((int) m_Numbers[0]) << 16) + m_Numbers [1]), 
+                                       ((((int) m_Numbers [2]) << 16) + m_Numbers [3]),
+                                       ((((int) m_Numbers [4]) << 16) + m_Numbers [5]),
+                                       ((((int) m_Numbers [6]) << 16) + m_Numbers [7]));
+               }
+
+               private static int Hash (int i, int j, int k, int l) 
+               {
+                       return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);
                }
        }
 }