using System; using System.Linq; using System.Collections.Generic; using ln.type.arithmetics; using System.Net; namespace ln.type { public class IPv6 { public static readonly IPv6 ANY = new IPv6(new byte[17]); public static readonly IPv6 V4Space = Parse("::ffff:0:0/96"); public static readonly IPv6 Loopback = Parse("::1"); readonly UInt16[] value; readonly UInt32 mask; public uint Netmask => mask; public IPv6() { value = new UInt16[8]; mask = 0; } public IPv6(uint netmask) { value = new UInt16[8]; mask = netmask; } public IPv6(IPv6 ip, uint netmask) : this(ip.value, netmask) { } public IPv6(ushort[] words, uint netmask) { if (words.Length > 8) throw new ArgumentOutOfRangeException(nameof(words)); value = words.Slice(0, 8); mask = netmask; } public IPv6(byte[] bytes,uint netmask) :this(bytes) { mask = netmask; } public IPv6(byte[] bytes) :this() { if (bytes.Length == 16) { for (int n = 0; n < 8; n++) { value[n] = (ushort)((bytes[(n << 1) + 0] << 8) | bytes[(n << 1) + 1]); } mask = 128; } else if (bytes.Length == 17) { for (int n = 0; n < 8; n++) { value[n] = (ushort)((bytes[(n << 1) + 0] << 8) | bytes[(n << 1) + 1]); } mask = bytes[16]; } else if (bytes.Length == 4) { value[5] = 0xffff; value[6] = (ushort)((bytes[0] << 8) | bytes[1]); value[7] = (ushort)((bytes[2] << 8) | bytes[3]); mask = 96; } else throw new ArgumentOutOfRangeException(nameof(bytes)); } public IPv6 Network { get { if (mask == 128) return this; ushort[] address = value.Slice(0); uint steps = mask >> 4; uint shift = mask & 0x0F; address[steps] &= (ushort)(0xFFFF << (int)(16 - shift)); for (uint n = steps+1; n < 8; n++) address[n] = 0; return new IPv6(address, mask); } } public bool Contains(IPv6 ip) { if (ip.mask < mask) return false; IPv6 ipNetwork = new IPv6(ip, mask).Network; return Network.Equals(ipNetwork); } public byte[] ToBytes() { byte[] bytes = new byte[16]; for (int n = 0; n < 8; n++) { bytes[(n << 1) + 1] = (byte)((value[n] >> 0) & 0xff); bytes[(n << 1) + 0] = (byte)((value[n] >> 8) & 0xff); } return bytes; } public byte[] ToCIDRBytes() { byte[] bytes = new byte[17]; for (int n = 0; n < 8; n++) { bytes[(n << 1) + 1] = (byte)((value[n] >> 0) & 0xff); bytes[(n << 1) + 0] = (byte)((value[n] >> 8) & 0xff); } bytes[16] = mask.GetBytes()[0]; return bytes; } public byte[] ToPackedBytes() { byte[] bytes = ToBytes(); if (V4Space.Contains(this)) return bytes.Slice(12); return bytes; } public IEnumerable Split(int splitWidth) { List splitted = new List(); if (128 < (mask + splitWidth)) throw new ArgumentOutOfRangeException(nameof(splitWidth),"cannot split to negative host identfier length"); IPv6 ip = new IPv6(value, (uint)(mask + splitWidth)); ushort[] increment = Words.SHL(new ushort[] { 1, 0, 0, 0, 0, 0, 0, 0 }, (int)(128 - ip.mask)); for (int n=0;n<(1< String.Format("{0:x4}", w))); } else if (V4Space.Contains(this)) { return string.Format("::ffff:{0}.{1}.{2}.{3}", ((value[6] >> 8) & 0xFF), ((value[6] >> 0) & 0xFF), ((value[7] >> 8) & 0xFF), ((value[7] >> 0) & 0xFF) ); } else { int zerolen = 0; int zeropos = -1; for (int n=0;n<8;n++) { if (value[n]==0) { int m; for (m = 1; m < (8 - n); m++) { if (value[n + m] != 0) break; } if (m > zerolen) { zerolen = m; zeropos = n; } } } List words = new List(); for (int n = 0; n < 8; n++) { if (n == zeropos) { if (n == 0) words.Add(""); if ((n + zerolen)==8) words.Add(""); words.Add(""); n += zerolen - 1; } else { words.Add(String.Format("{0:x}", value[n])); } } return string.Join(":", words); } } public override string ToString() => ToString(true); public String ToCIDR() { return String.Format("{0}/{1}", ToString(true), mask); } public static IPv6 Parse(string source) { int netmask = 128; ushort[] address = new ushort[8]; int slash = source.IndexOf('/'); if (slash != -1) { netmask = int.Parse(source.Substring(slash + 1)); source = source.Substring(0, slash); } bool hasColons = source.Contains(':'); bool hasDots = source.Contains('.'); if (!hasColons && hasDots) // IPv4 { byte[] v4bytes = source.Split('.').Select((s) => byte.Parse(s)).ToArray(); if (v4bytes.Length != 4) throw new FormatException(); if (netmask != 128) netmask += 96; address[5] = 0xffff; address[6] = v4bytes.GetUShort(true); address[7] = v4bytes.GetUShort(2, true); } else if (hasColons) { string[] words = source.Split(':'); if (string.Empty.Equals(words[0])) words = words.Slice(1); if (string.Empty.Equals(words[words.Length - 1])) words = words.Slice(0, words.Length - 1); if ((words.Length == 1) && (words[0].Equals(String.Empty))) if (netmask == 0) return IPv6.ANY; if (words.Length > 8) throw new FormatException(); if (hasDots) { byte[] v4bytes = words[words.Length-1].Split('.').Select((s) => byte.Parse(s)).ToArray(); if (v4bytes.Length != 4) throw new FormatException(); address[5] = 0xffff; address[6] = v4bytes.GetUShort(true); address[7] = v4bytes.GetUShort(2, true); } else { int fill = 8 - words.Length; int n = 0, m = 0; while (n < words.Length) { if (String.Empty.Equals(words[n])) { m += fill; } else { address[m] = Convert.ToUInt16(words[n], 16); } m++; n++; } } } return new IPv6(address, (uint)netmask); } public static IPv6 operator ++(IPv6 ip) => new IPv6(Words.Add(ip.value.BigEndian(), 1).BigEndian(), ip.mask); public static IPv6 operator --(IPv6 ip) => new IPv6(Words.Del(ip.value.BigEndian(), 1).BigEndian(), ip.mask); public static IPv6 operator +(IPv6 ip, int b) => new IPv6(Words.Add(ip.value.BigEndian(), b).BigEndian(), ip.mask); public static IPv6 operator -(IPv6 ip, int b) => new IPv6(Words.Del(ip.value.BigEndian(), b).BigEndian(), ip.mask); public static implicit operator IPAddress(IPv6 ip) => new IPAddress(ip.ToPackedBytes()); public static implicit operator IPv6(IPAddress ip) => new IPv6(ip.GetAddressBytes()); public static implicit operator IPv6(String s) => IPv6.Parse(s); public override bool Equals(object obj) { if (obj is IPAddress) obj = (IPv6)obj; if (obj is IPv6) { return value.SequenceEqual((obj as IPv6).value) && Equals(mask,(obj as IPv6).mask) ; } return false; } public override int GetHashCode() { int hash = 0; for (int n = 0; n < 8; n++) hash = (hash << 4) | value[n]; return hash; } } }