//
// System.Net.IPv6Address.cs
//
// Author:
// Lawrence Pit (loz@cable.a2000.nl)
//
// Note I: This class is not defined in the specs of .Net
//
// Note II : The name of this class is perhaps unfortunate as it turns
// out that in ms.net there's an internal class called
// IPv6Address in namespace System.
//
//
// 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.Text;
namespace System.Net{
/// <remarks>
/// Encapsulates an IPv6 Address.
/// See RFC 2373 for more info on IPv6 addresses.
/// </remarks>
[Serializable]
internal class IPv6Address {
private ushort [] address;
private int prefixLength;
private long scopeId = 0;
public static readonly IPv6Address Loopback = IPv6Address.Parse ("::1");
public static readonly IPv6Address Unspecified = IPv6Address.Parse ("::");
public IPv6Address (ushort [] addr)
{
if (addr == null)
throw new ArgumentNullException ("addr");
if (addr.Length != 8)
throw new ArgumentException ("addr");
address = addr;
}
public IPv6Address (ushort [] addr, int prefixLength) : this (addr)
{
if (prefixLength < 0 || prefixLength > 128)
throw new ArgumentException ("prefixLength");
this.prefixLength = prefixLength;
}
public IPv6Address (ushort [] addr, int prefixLength, int scopeId) : this (addr, prefixLength)
{
this.scopeId = scopeId;
}
public static IPv6Address Parse (string ipString)
{
if (ipString == null)
throw new ArgumentNullException ("ipString");
IPv6Address result;
if (TryParse (ipString, out result))
return result;
throw new FormatException ("Not a valid IPv6 address");
}
static int Fill (ushort [] addr, string ipString)
{
int p = 0;
int slot = 0;
if (ipString.Length == 0)
return 0;
// Catch double uses of ::
if (ipString.IndexOf ("::") != -1)
return -1;
for (int i = 0; i < ipString.Length; i++){
char c = ipString [i];
int n;
if (c == ':'){
// Trailing : is not allowed.
if (i == ipString.Length-1)
return -1;
if (slot == 8)
return -1;
addr [slot++] = (ushort) p;
p = 0;
continue;
} if ('0' <= c && c <= '9')
n = (int) (c - '0');
else if ('a' <= c && c <= 'f')
n = (int) (c - 'a' + 10);
else if ('A' <= c && c <= 'F')
n = (int) (c - 'A' + 10);
else
return -1;
p = (p << 4) + n;
if (p > UInt16.MaxValue)
return -1;
}
if (slot == 8)
return -1;
addr [slot++] = (ushort) p;
return slot;
}
static bool TryParse (string prefix, out int res)
{
#if NET_2_0
return Int32.TryParse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture, out res);
#else
try {
res = Int32.Parse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture);
} catch (Exception) {
res = -1;
return false;
}
return true;
#endif
}
public static bool TryParse (string ipString, out IPv6Address result)
{
result = null;
if (ipString == null)
return false;
if (ipString.Length > 2 &&
ipString [0] == '[' &&
ipString [ipString.Length - 1] == ']')
ipString = ipString.Substring (1, ipString.Length - 2);
if (ipString.Length < 2)
return false;
int prefixLen = 0;
int scopeId = 0;
int pos = ipString.LastIndexOf ('/');
if (pos != -1) {
string prefix = ipString.Substring (pos + 1);
if (!TryParse (prefix , out prefixLen))
prefixLen = -1;
if (prefixLen < 0 || prefixLen > 128)
return false;
ipString = ipString.Substring (0, pos);
} else {
pos = ipString.LastIndexOf ('%');
if (pos != -1) {
string prefix = ipString.Substring (pos + 1);
if (!TryParse (prefix, out scopeId))
scopeId = 0;
ipString = ipString.Substring (0, pos);
}
}
//
// At this point the prefix/suffixes have been removed
// and we only have to deal with the ipv4 or ipv6 addressed
//
ushort [] addr = new ushort [8];
//
// Is there an ipv4 address at the end?
//
bool ipv4 = false;
int pos2 = ipString.LastIndexOf (':');
if (pos2 == -1)
return false;
int slots = 0;
if (pos2 < (ipString.Length - 1)) {
string ipv4Str = ipString.Substring (pos2 + 1);
if (ipv4Str.IndexOf ('.') != -1) {
IPAddress ip;
if (!IPAddress.TryParse (ipv4Str, out ip))
return false;
long a = ip.InternalIPv4Address;
addr [6] = (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff)));
addr [7] = (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff)));
if (pos2 > 0 && ipString [pos2 - 1] == ':')
ipString = ipString.Substring (0, pos2 + 1);
else
ipString = ipString.Substring (0, pos2);
ipv4 = true;
slots = 2;
}
}
//
// Only an ipv6 block remains, either:
// "hexnumbers::hexnumbers", "hexnumbers::" or "hexnumbers"
//
int c = ipString.IndexOf ("::");
if (c != -1){
int right_slots = Fill (addr, ipString.Substring (c+2));
if (right_slots == -1){
return false;
}
if (right_slots + slots > 8){
return false;
}
int d = 8-slots-right_slots;
for (int i = right_slots; i > 0; i--){
addr [i+d-1] = addr [i-1];
addr [i-1] = 0;
}
int left_slots = Fill (addr, ipString.Substring (0, c));
if (left_slots == -1)
return false;
if (left_slots + right_slots + slots > 7)
return false;
} else {
if (Fill (addr, ipString) != 8-slots)
return false;
}
// Now check the results in the ipv6-address range only
bool ipv6 = false;
for (int i = 0; i < slots; i++){
if (addr [i] != 0 || i == 5 && addr [i] != 0xffff)
ipv6 = true;
}
// check IPv4 validity
if (ipv4 && !ipv6) {
for (int i = 0; i < 5; i++) {
if (addr [i] != 0)
return false;
}
if (addr [5] != 0 && addr [5] != 0xffff)
return false;
}
result = new IPv6Address (addr, prefixLen, scopeId);
return true;
}
public ushort [] Address {
get { return address; }
}
public int PrefixLength {
get { return this.prefixLength; }
}
public long ScopeId {
get {
return scopeId;
}
set {
scopeId = value;
}
}
public ushort this [int index] {
get { return address [index]; }
}
public AddressFamily AddressFamily {
get { return AddressFamily.InterNetworkV6; }
}
public static bool IsLoopback (IPv6Address addr)
{
if (addr.address [7] != 1)
return false;
int x = addr.address [6] >> 8;
if (x != 0x7f && x != 0)
return false;
for (int i = 0; i < 4; i++) {
if (addr.address [i] != 0)
return false;
}
if (addr.address [5] != 0 && addr.address [5] != 0xffff)
return false;
return true;
}
private static ushort SwapUShort (ushort number)
{
return (ushort) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );
}
// Convert the address into a format expected by the IPAddress (long) ctor
private int AsIPv4Int ()
{
return (SwapUShort (address [7]) << 16) + SwapUShort (address [6]);
}
public bool IsIPv4Compatible ()
{
for (int i = 0; i < 6; i++)
if (address [i] != 0)
return false;
return (AsIPv4Int () > 1);
}
public bool IsIPv4Mapped ()
{
for (int i = 0; i < 5; i++)
if (address [i] != 0)
return false;
return address [5] == 0xffff;
}
/// <summary>
/// Overrides System.Object.ToString to return
/// this object rendered in a canonicalized notation
/// </summary>
public override string ToString ()
{
StringBuilder s = new StringBuilder ();
if(IsIPv4Compatible() || IsIPv4Mapped())
{
s.Append("::");
if(IsIPv4Mapped())
s.Append("ffff:");
s.Append(new IPAddress( AsIPv4Int ()).ToString ());
return s.ToString ();
}
else
{
int bestChStart = -1; // Best chain start
int bestChLen = 0; // Best chain length
int currChLen = 0; // Current chain length
// Looks for the longest zero chain
for (int i=0; i<8; i++)
{
if (address[i] != 0)
{
if ((currChLen > bestChLen)
&& (currChLen > 1))
{
bestChLen = currChLen;
bestChStart = i - currChLen;
}
currChLen = 0;
}
else
currChLen++;
}
if ((currChLen > bestChLen)
&& (currChLen > 1))
{
bestChLen = currChLen;
bestChStart = 8 - currChLen;
}
// makes the string
if (bestChStart == 0)
s.Append(":");
for (int i=0; i<8; i++)
{
if (i == bestChStart)
{
s.Append (":");
i += (bestChLen - 1);
continue;
}
s.AppendFormat("{0:x}", address [i]);
if (i < 7) s.Append (':');
}
}
if (scopeId != 0)
s.Append ('%').Append (scopeId);
return s.ToString ();
}
public string ToString (bool fullLength)
{
if (!fullLength)
return ToString ();
StringBuilder sb = new StringBuilder ();
for (int i=0; i < address.Length - 1; i++) {
sb.AppendFormat ("{0:X4}:", address [i]);
}
sb.AppendFormat ("{0:X4}", address [address.Length - 1]);
return sb.ToString ();
}
/// <returns>
/// Whether both objects are equal.
/// </returns>
public override bool Equals (object other)
{
System.Net.IPv6Address ipv6 = other as System.Net.IPv6Address;
if (ipv6 != null) {
for (int i = 0; i < 8; i++)
if (this.address [i] != ipv6.address [i])
return false;
return true;
}
System.Net.IPAddress ipv4 = other as System.Net.IPAddress;
if (ipv4 != null) {
for (int i = 0; i < 5; i++)
if (address [i] != 0)
return false;
if (address [5] != 0 && address [5] != 0xffff)
return false;
long a = ipv4.InternalIPv4Address;
if (address [6] != (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff))) ||
address [7] != (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff))))
return false;
return true;
}
return false;
}
public override int GetHashCode ()
{
return Hash (((((int) address [0]) << 16) + address [1]),
((((int) address [2]) << 16) + address [3]),
((((int) address [4]) << 16) + address [5]),
((((int) address [6]) << 16) + address [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);
}
}
}
|