//
// 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.
+//
+using System;
+using System.Globalization;
using System.Net.Sockets;
using System.Runtime.InteropServices;
-using System;
-
namespace System.Net {
/// <remarks>
public class IPAddress {
// Don't change the name of this field without also
// changing socket-io.c in the runtime
- // This will stored in network order
- private long address;
-
- public static readonly IPAddress Any=new IPAddress(0);
- public static readonly IPAddress Broadcast=new IPAddress(0xffffffff);
- public static readonly IPAddress Loopback=new IPAddress(0x7f000001);
- public static readonly IPAddress None=new IPAddress(0xffffffff);
+ // 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;
+
+ 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");
+ public static readonly IPAddress IPv6Any = IPAddress.ParseIPV6 ("::");
+ public static readonly IPAddress IPv6Loopback = IPAddress.ParseIPV6 ("::1");
+ public static readonly IPAddress IPv6None = IPAddress.ParseIPV6 ("::");
private static short SwapShort (short number)
{
byte b5 = (byte) ((number >> 16) & 0xFF);
byte b6 = (byte) ((number >> 8) & 0xFF);
byte b7 = (byte) (number & 0xFF);
- return b0 + (b1 << 8) + (b2 << 16) + (b3 << 24) + (b4 << 32) + (b5 << 40) + (b6 << 48) + (b7 << 56);
+ return (long) b0 + ((long) b1 << 8) + ((long) b2 << 16) + ((long) b3 << 24) + ((long) b4 << 32) + ((long) b5 << 40) + ((long) b6 << 48) + ((long) b7 << 56);
}
public static short HostToNetworkOrder(short host) {
}
/// <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;
}
- public static IPAddress Parse(string ip)
+ public IPAddress (byte[] address)
{
- if(ip == null)
- throw new ArgumentNullException("null ip string");
+ if (address == null)
+ throw new ArgumentNullException ("address");
+
+ int len = address.Length;
+
+#if NET_2_0
+ if (len != 16 && len != 4)
+ throw new ArgumentException ("An invalid IP address was specified.",
+ "address");
+#else
+ if (len != 16)
+ throw new ArgumentException ("address");
+#endif
+
+ if (len == 16) {
+ Buffer.BlockCopy(address, 0, m_Numbers, 0, 16);
+ m_Family = AddressFamily.InterNetworkV6;
+ m_ScopeId = 0;
+ } else {
+ m_Address = (address [3] << 24) + (address [2] << 16) +
+ (address [1] << 8) + address [0];
+ }
+ }
- int pos = 0;
- int ndots = 0;
- char current;
- bool prevDigit = false;
+ public IPAddress(byte[] address, long scopeId)
+ {
+ if (address == null)
+ throw new ArgumentNullException ("address");
+
+ if (address.Length != 16)
+#if NET_2_0
+ throw new ArgumentException ("An invalid IP address was specified.",
+ "address");
+#else
+ throw new ArgumentException("address");
+#endif
+
+ 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;
+ }
- break;
- }
- }
+ public static IPAddress Parse (string ip)
+ {
+ IPAddress ret;
+ if (TryParse (ip, out ret))
+ return ret;
+ throw new FormatException("An invalid IP address was specified.");
+ }
- if (ndots != 3)
- throw new FormatException ("the string is not a valid ip");
+#if NET_2_0
+ public
+#endif
+ static bool TryParse (string ip, out IPAddress address)
+ {
+ if (ip == null)
+ throw new ArgumentNullException ("Value cannot be null.");
+
+ if( (address = ParseIPV4(ip)) == null)
+ if( (address = ParseIPV6(ip)) == null)
+ return false;
+ return true;
+ }
+ 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]));
+ 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));
+ return (new IPAddress (a));
+ } catch (Exception) {
+ return null;
+ }
}
- public long Address {
+ 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.")]
+ 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 {
- if (value < 0 || value > 0x00000000FFFFFFFF)
- throw new ArgumentOutOfRangeException (
- "the address must be between 0 and 0xFFFFFFFF");
+ /* no need to do this test, ms.net accepts any value.
+ if (value < 0 || value > 0x00000000FFFFFFFF)
+ throw new ArgumentOutOfRangeException (
+ "the address must be between 0 and 0xFFFFFFFF");
+ */
+
+ if(m_Family != AddressFamily.InterNetwork)
+ throw new Exception("The attempted operation is not supported for the type of object referenced");
- address = value;
+ m_Address = value;
}
}
- public AddressFamily AddressFamily {
+ internal long InternalIPv4Address {
+ get { return m_Address; }
+ }
+
+#if NET_2_0
+ public bool IsIPv6LinkLocal {
get {
- return(AddressFamily.InterNetwork);
+ if (m_Family == AddressFamily.InterNetwork)
+ return false;
+ int v = NetworkToHostOrder ((short) m_Numbers [0]) & 0xFFF0;
+ return 0xFE80 <= v && v < 0xFEC0;
+ }
+ }
+
+ public bool IsIPv6SiteLocal {
+ get {
+ if (m_Family == AddressFamily.InterNetwork)
+ return false;
+ int v = NetworkToHostOrder ((short) m_Numbers [0]) & 0xFFF0;
+ return 0xFEC0 <= v && v < 0xFF00;
+ }
+ }
+
+ public bool IsIPv6Multicast {
+ get {
+ return m_Family != AddressFamily.InterNetwork &&
+ ((ushort) NetworkToHostOrder ((short) m_Numbers [0]) & 0xFF00) == 0xFF00;
+ }
+ }
+#endif
+
+ 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) };
+ }
+ }
+
+ public AddressFamily AddressFamily
+ {
+ get {
+ return m_Family;
}
}
/// <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>
/// </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>
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);
+ }
+
+#pragma warning disable 169
+ // Added for serialization compatibility with MS.NET
+ private int m_HashCode;
+#pragma warning restore
+
}
}