using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Numerics; using System.Text.Json.Serialization; using cloud.insecurity.docker.ipam.schema; using cloud.insecurity.docker.ipam.schema; using LiteDB; using Newtonsoft.Json; namespace cloud.insecurity.docker.ipam { public class Scope { private Scope? ParentScope; private bool _Locked; public Guid Id { get { return GetUniqueId(Net, PrefixLen); } } public string Net { get; set; } public int PrefixLen { get; set; } public List? Tags { get; set; } public int? TcpIpVersion { get; set; } public bool Allocated { get; set; } private static LiteDatabase Db = new LiteDatabase(@"IPAM.db"); private static log4net.ILog Log { get { return log4net.LogManager.GetLogger(typeof(Scope)); } } private schema.Scope Schema; public bool Locked { get { return _Locked; } internal set { _Locked = value; } } public Guid Parent { get { if (ParentScope == null) return System.Guid.Empty; return ParentScope.Id; } } private bool Tagged { get { throw new NotImplementedException(); } } private IPNetwork Network { get { return IPNetwork.Parse(string.Format("{0}/{1}", Net, PrefixLen.ToString())); } } public Scope LockScope() { if (Locked) throw new InvalidOperationException("scope is already locked"); _Locked = true; return this; } public Scope() { } public static IPNetwork GetNetworkObject(string network, int prefixLen) { return IPNetwork.Parse(string.Format("{0}/{1}", network, prefixLen.ToString())); } public static Guid GetUniqueId(string network, int prefixLen) { var test = GetNetworkObject(network, prefixLen); var bytes = GetNetworkObject(network, prefixLen) .LastUsable .GetAddressBytes(); if (bytes == null || bytes.Length < 4) throw new ArgumentException("need at least 4 bytes"); if (bytes.Length == 4) { bytes = new byte[] {0, 0, 0, 0}.Concat(bytes).ToArray() .Concat(new byte[] {0, 0, 0, 0, 0, 0, 0, 0}).ToArray(); } //bytes = bytes.Select(b => Convert.ToByte(b ^ prefixLen)).ToArray(); using (MemoryStream ms = new MemoryStream(bytes)) { using (BinaryReader b = new BinaryReader(ms)) { // https://en.wikipedia.org/wiki/Universally_unique_identifier#History return new Guid( b.ReadUInt32(), b.ReadUInt16(), b.ReadUInt16(), b.ReadByte(), b.ReadByte(), b.ReadByte(), b.ReadByte(), b.ReadByte(), b.ReadByte(), b.ReadByte(), b.ReadByte()); // however this is RFC4122 (g)UUID and the information // is definitely not relevant but a nice piece of // nostalgia. } } } public Scope GetUnassignedScope() { if (Locked) throw new InvalidOperationException("scope is locked"); return Network.Subnet(Convert.ToByte(Schema.child_prefix)).Where(s => { return !Db.GetCollection("Scopes") .Find(scope => scope.Id == GetUniqueId(s.Network.ToString(), s.Cidr) && scope.Allocated) .Any(); }).Select(s => new Scope() { Net = s.Network.ToString(), PrefixLen = Schema.child_prefix.GetValueOrDefault(), ParentScope = this, }).Single(); } public IPAddress LeaseNetworkAddress() { throw new NotImplementedException(); } public static IEnumerable GetParentScopes() { var enumerable = JsonConvert.DeserializeObject( new StreamReader(System.IO.File .Open(@"schema.json", FileMode.Open)) .ReadToEnd()) ?.scopes; if (enumerable == null) throw new NotImplementedException(); return EnumerateSchemaScopes(enumerable); } private static IEnumerable EnumerateSchemaScopes(IEnumerable enumerable, Scope? parent = null) { foreach (var scope in enumerable) { Scope newScope = null; Db.GetCollection("Scopes").EnsureIndex(s => s.Id,true); try { newScope = Db.GetCollection("Scopes") .Find(s => s.Id == GetUniqueId(scope.network, (scope.prefix != 0) ? scope.prefix : (int)parent.Schema.child_prefix)).SingleOrDefault(); } catch(Exception ex) { Log.Debug("DB not initialized?", ex); } if (newScope == null) { newScope = new Scope() { Net = scope.network, PrefixLen = (scope.prefix == 0) ? (int)parent.Schema.child_prefix : scope.prefix, Tags = scope.tags, Schema = scope, TcpIpVersion = scope.tcp_ip_version, Locked = (scope.lock_down.GetValueOrDefault()) ? true : false, ParentScope = parent }; Db.GetCollection("Scopes") .Insert(new BsonValue(newScope.Id), newScope); newScope = newScope; } else { newScope.ParentScope = parent; newScope.Schema = scope; } yield return newScope; if (newScope?.Schema.scopes.Count > 0) foreach (var s in EnumerateSchemaScopes(newScope.Schema.scopes, newScope)) { yield return s; } } } } }