2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2025-06-26 21:13:13 +00:00

Add project files.

This commit is contained in:
Ahmed Zamil
2017-09-15 23:40:03 +03:00
parent 4c95cb1cc6
commit 7ae722ab51
99 changed files with 14687 additions and 0 deletions

253
Esiur/Data/AutoList.cs Normal file
View File

@ -0,0 +1,253 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Esiur.Engine;
using System.Reflection;
namespace Esiur.Data
{
public class AutoList<T, ST> : IEnumerable
{
private readonly object syncRoot = new object();
private List<T> list = new List<T>();
public delegate void Modified(ST sender, int index, T oldValue, T newValue);
public delegate void Added(ST sender, T value);
public delegate void Removed(ST sender, T value);
public delegate void Cleared(ST sender);
public event Modified OnModified;
public event Removed OnRemoved;
public event Cleared OnCleared;
public event Added OnAdd;
ST state;
bool removableList;
/*
IOrderedEnumerable<T> OrderBy<T, TK>(Func<T, TK> keySelector)
{
return list.OrderBy<T,TK>(keySelector);
}
*/
public void Sort()
{
list.Sort();
}
public void Sort(IComparer<T> comparer)
{
list.Sort(comparer);
}
public IEnumerable<T> Where(Func<T, bool> predicate)
{
return list.Where(predicate);
}
/// <summary>
/// Convert AutoList to array
/// </summary>
/// <returns>Array</returns>
public T[] ToArray()
{
// list.OrderBy()
return list.ToArray();
}
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="state">State object to be included when an event is raised.</param>
public AutoList(ST state)
{
this.state = state;
#if NETSTANDARD1_5
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
#else
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
#endif
}
/// <summary>
/// Create a new instance of AutoList
/// </summary>
/// <param name="values">Populate the list with items</param>
/// <returns></returns>
public AutoList(T[] values)
{
#if NETSTANDARD1_5
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
#else
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
#endif
AddRange(values);
}
/// <summary>
/// Synchronization lock of the list
/// </summary>
public object SyncRoot
{
get
{
return syncRoot;
}
}
/// <summary>
/// First item in the list
/// </summary>
public T First()
{
return list.First();
}
/// <summary>
/// Get an item at a specified index
/// </summary>
public T this[int index]
{
get
{
return list[index];
}
set
{
var oldValue = list[index];
if (removableList)
{
if (oldValue != null)
((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
if (value != null)
((IDestructible)value).OnDestroy += ItemDestroyed;
}
lock (syncRoot)
list[index] = value;
OnModified?.Invoke(state, index, oldValue, value);
}
}
/// <summary>
/// Add item to the list
/// </summary>
public void Add(T value)
{
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy += ItemDestroyed;
lock (syncRoot)
list.Add(value);
OnAdd?.Invoke(state, value);
}
/// <summary>
/// Add an array of items to the list
/// </summary>
public void AddRange(T[] values)
{
foreach (var v in values)
Add(v);
}
private void ItemDestroyed(object sender)
{
Remove((T)sender);
}
public IEnumerator GetEnumerator()
{
return list.GetEnumerator();
}
/// <summary>
/// Clear the list
/// </summary>
public void Clear()
{
if (removableList)
foreach(IDestructible v in list)
if (v!=null)
v.OnDestroy -= ItemDestroyed;
lock (syncRoot)
list.Clear();
OnCleared?.Invoke(state);
}
/// <summary>
/// Remove an item from the list
/// <param name="value">Item to remove</param>
/// </summary>
public void Remove(T value)
{
if (!list.Contains(value))
return;
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy -= ItemDestroyed;
lock (syncRoot)
list.Remove(value);
OnRemoved?.Invoke(state, value);
}
/// <summary>
/// Number of items in the list
/// </summary>
public int Count
{
get { return list.Count; }
}
/// <summary>
/// Check if an item exists in the list
/// </summary>
/// <param name="value">Item to check if exists</param>
public bool Contains(T value)
{
return list.Contains(value);
}
/// <summary>
/// Check if any item of the given array is in the list
/// </summary>
/// <param name="values">Array of items</param>
public bool ContainsAny(T[] values)
{
foreach (var v in values)
if (list.Contains(v))
return true;
return false;
}
/// <summary>
/// Check if any item of the given list is in the list
/// </summary>
/// <param name="values">List of items</param>
public bool ContainsAny(AutoList<T, ST> values)
{
foreach (var v in values)
if (list.Contains((T)v))
return true;
return false;
}
}
}

196
Esiur/Data/BinaryList.cs Normal file
View File

@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Misc;
using System.Reflection;
namespace Esiur.Data
{
/// <summary>
/// BinaryList holds a list of items to be converted to binary for storage and transmission
/// </summary>
public class BinaryList
{
private List<byte> held = new List<byte>();
/// <summary>
/// Create an instance of BinaryList
/// </summary>
public BinaryList()
{
}
/// <summary>
/// Converts parameters to binary in same order
/// </summary>
/// <param name="values">Variables to convert</param>
public static byte[] ToBytes(params object[] values)
{
var held = new List<byte>();
foreach (var i in values)
{
if (i is byte)
held.Add((byte)i);
else
{
#if NETSTANDARD1_5
MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() });
#else
MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() });
#endif
if (mi != null)
{
var b = (byte[])mi.Invoke(null, new object[] { i });
held.AddRange(b);
}
}
}
return held.ToArray();
}
/// <summary>
/// Create a new instance of BinaryList
/// </summary>
/// <param name="values">Populate the list items</param>
public BinaryList(params object[] values)
{
AddRange(values);
}
/// <summary>
/// Add an array of items at the end of the list
/// </summary>
/// <param name="values">Array of items</param>
public void AddRange(object[] values)
{
foreach (var i in values)
{
if (i is byte)
held.Add((byte)i);
else
{
#if NETSTANDARD1_5
MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() });
#else
MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() });
#endif
if (mi != null)
{
var b = (byte[])mi.Invoke(null, new object[] {i});
held.AddRange(b);
}
}
}
}
/// <summary>
/// Add multiple items at the end of the list
/// </summary>
/// <param name="values">Parameters of items</param>
public void Append(params object[] values)
{
AddRange(values);
}
/// <summary>
/// Insert new items to the list at a specified index
/// </summary>
/// <param name="offset">Position in the list</param>
/// <param name="values">Items to insert</param>
public void Insert(int offset, params object[] values)
{
foreach (var i in values)
{
if (i is byte)
{
held.Insert(offset++, (byte)i);
}
else
{
#if NETSTANDARD1_5
MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() });
#else
MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() });
#endif
if (mi != null)
{
var b = (byte[])mi.Invoke(null, new object[] { i });
held.InsertRange(offset, b);
offset += b.Length;
}
}
}
}
/// <summary>
/// Number of the items in the list
/// </summary>
public int Length
{
get
{
return held.Count;
}
}
/*
public void Append(byte data)
{
held.Add(data);
}
public void Append(byte[] data)
{
held.AddRange(data);
}
public void Append(int data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(uint data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(float data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(short data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(ushort data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(double data)
{
held.AddRange(DC.ToBytes(data));
}
public void Append(sbyte data)
{
held.Add((byte)data);
}
*/
/// <summary>
/// Convert the list to an array of bytes
/// </summary>
/// <returns>Bytes array</returns>
public byte[] ToArray()
{
return held.ToArray();
}
}
}

991
Esiur/Data/Codec.cs Normal file
View File

@ -0,0 +1,991 @@
using System;
using System.Collections.Generic;
using System.Text;
using Esiur.Misc;
using System.ComponentModel;
using Esiur.Data;
using Esiur.Engine;
using Esiur.Net.IIP;
using Esiur.Resource;
using System.Linq;
using System.Reflection;
namespace Esiur.Data
{
public static class Codec
{
/// <summary>
/// Check if a DataType is an array
/// </summary>
/// <param name="type">DataType to check</param>
/// <returns>True if DataType is an array, otherwise false</returns>
public static bool IsArray(this DataType type)
{
return (((byte)type & 0x80) == 0x80) && (type != DataType.NotModified);
}
/// <summary>
/// Get the element DataType
/// </summary>
/// <example>
/// Passing UInt8Array will return UInt8
/// </example>
/// <param name="type">DataType to get its element DataType</param>
public static DataType GetElementType(this DataType type)
{
return (DataType)((byte)type & 0x7F);
}
/// <summary>
/// Get DataType array of a given Structure
/// </summary>
/// <param name="structure">Structure to get its DataTypes</param>
/// <param name="connection">Distributed connection is required in case a type is at the other end</param>
private static DataType[] GetStructureDateTypes(Structure structure, DistributedConnection connection)
{
var keys = structure.GetKeys();
var types = new DataType[keys.Length];
for (var i = 0; i < keys.Length; i++)
types[i] = Codec.GetDataType(structure[keys[i]], connection);
return types;
}
/// <summary>
/// Compare two structures
/// </summary>
/// <param name="initial">Initial structure to compare with</param>
/// <param name="next">Next structure to compare with the initial</param>
/// <param name="connection">DistributedConnection is required in case a structure holds items at the other end</param>
public static StructureComparisonResult Compare(Structure initial, Structure next, DistributedConnection connection)
{
if (next == null)
return StructureComparisonResult.Null;
if (initial == null)
return StructureComparisonResult.Structure;
if (next == initial)
return StructureComparisonResult.Same;
if (initial.Length != next.Length)
return StructureComparisonResult.Structure;
var previousKeys = initial.GetKeys();
var nextKeys = next.GetKeys();
for (var i = 0; i < previousKeys.Length; i++)
if (previousKeys[i] != nextKeys[i])
return StructureComparisonResult.Structure;
var previousTypes = GetStructureDateTypes(initial, connection);
var nextTypes = GetStructureDateTypes(next, connection);
for (var i = 0; i < previousTypes.Length; i++)
if (previousTypes[i] != nextTypes[i])
return StructureComparisonResult.StructureSameKeys;
return StructureComparisonResult.StructureSameTypes;
}
/// <summary>
/// Compose an array of structures into an array of bytes
/// </summary>
/// <param name="structures">Array of Structure to compose</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end</param>
/// <param name="prependLength">If true, prepend the length as UInt32 at the beginning of the returned bytes array</param>
/// <returns>Array of bytes in the network byte order</returns>
public static byte[] ComposeStructureArray(Structure[] structures, DistributedConnection connection, bool prependLength = false)
{
if (structures == null || structures?.Length == 0)
return new byte[0];
var rt = new BinaryList();
var comparsion = StructureComparisonResult.Structure;
rt.Append((byte)comparsion);
rt.Append(ComposeStructure(structures[0], connection));
for (var i = 1; i < structures.Length; i++)
{
comparsion = Compare(structures[i - 1], structures[i], connection);
rt.Append((byte)comparsion);
if (comparsion == StructureComparisonResult.Structure)
rt.Append(ComposeStructure(structures[i], connection));
else if (comparsion == StructureComparisonResult.StructureSameKeys)
rt.Append(ComposeStructure(structures[i], connection, false));
else if (comparsion == StructureComparisonResult.StructureSameTypes)
rt.Append(ComposeStructure(structures[i], connection, false, false));
}
if (prependLength)
rt.Insert(0, rt.Length);
return rt.ToArray();
}
/// <summary>
/// Parse an array of structures
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset</param>
/// <param name="length">Number of bytes to parse</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end</param>
/// <returns>Array of structures</returns>
public static AsyncBag<Structure> ParseStructureArray(byte[] data, uint offset, uint length, DistributedConnection connection)
{
var reply = new AsyncBag<Structure>();
if (length == 0)
{
reply.Seal();
return reply;
}
var end = offset + length;
var result = (StructureComparisonResult)data[offset++];
AsyncReply previous = null;
string[] previousKeys = null;
DataType[] previousTypes = null;
if (result == StructureComparisonResult.Null)
previous = new AsyncReply<Structure>(null);
else if (result == StructureComparisonResult.Structure)
{
uint cs = data.GetUInt32(offset);
cs += 4;
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes);
offset += cs;
}
reply.Add(previous);
while (offset < end)
{
result = (StructureComparisonResult)data[offset++];
if (result == StructureComparisonResult.Null)
previous = new AsyncReply<Structure>(null);
else if (result == StructureComparisonResult.Structure)
{
uint cs = data.GetUInt32(offset);
cs += 4;
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes);
offset += cs;
}
else if (result == StructureComparisonResult.StructureSameKeys)
{
uint cs = data.GetUInt32(offset);
cs += 4;
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes, previousKeys);
offset += cs;
}
else if (result == StructureComparisonResult.StructureSameTypes)
{
uint cs = data.GetUInt32(offset);
cs += 4;
previous = ParseStructure(data, offset, cs, connection, out previousKeys, out previousTypes, previousKeys, previousTypes);
offset += cs;
}
reply.Add(previous);
}
reply.Seal();
return reply;
}
/// <summary>
/// Compose a structure into an array of bytes
/// </summary>
/// <param name="value">Structure to compose</param>
/// <param name="connection">DistributedConnection is required in case an item in the structure is at the other end</param>
/// <param name="includeKeys">Whether to include the structure keys</param>
/// <param name="includeTypes">Whether to include each item DataType</param>
/// <param name="prependLength">If true, prepend the length as UInt32 at the beginning of the returned bytes array</param>
/// <returns>Array of bytes in the network byte order</returns>
public static byte[] ComposeStructure(Structure value, DistributedConnection connection, bool includeKeys = true, bool includeTypes = true, bool prependLength = false)
{
var rt = new BinaryList();
if (includeKeys)
{
foreach (var i in value)
{
var key = DC.ToBytes(i.Key);
rt.Append((byte)key.Length, key, Compose(i.Value, connection));
}
}
else
{
foreach (var i in value)
rt.Append(Compose(i.Value, connection, includeTypes));
}
if (prependLength)
rt.Insert(0, rt.Length);
return rt.ToArray();
}
/// <summary>
/// Parse a structure
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="length">Number of bytes to parse.</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
/// <returns>Value</returns>
public static AsyncReply<Structure> ParseStructure(byte[] data, uint offset, uint contentLength, DistributedConnection connection)
{
string[] pk;
DataType[] pt;
return ParseStructure(data, offset, contentLength, connection, out pk, out pt);
}
/// <summary>
/// Parse a structure
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="length">Number of bytes to parse.</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
/// <param name="parsedKeys">Array to store keys in.</param>
/// <param name="parsedTypes">Array to store DataTypes in.</param>
/// <param name="keys">Array of keys, in case the data doesn't include keys</param>
/// <param name="types">Array of DataTypes, in case the data doesn't include DataTypes</param>
/// <returns>Structure</returns>
public static AsyncReply<Structure> ParseStructure(byte[] data, uint offset, uint length, DistributedConnection connection, out string[] parsedKeys, out DataType[] parsedTypes, string[] keys = null, DataType[] types = null)
{
var reply = new AsyncReply<Structure>();
var bag = new AsyncBag<object>();
var keylist = new List<string>();
var typelist = new List<DataType>();
if (keys == null)
{
while (length > 0)
{
var len = data[offset++];
keylist.Add(data.GetString(offset, len));
offset += len;
typelist.Add((DataType)data[offset]);
uint rt;
bag.Add(Codec.Parse(data, offset, out rt, connection));
length -= rt + len + 1;
offset += rt;
}
}
else if (types == null)
{
keylist.AddRange(keys);
while (length > 0)
{
typelist.Add((DataType)data[offset]);
uint rt;
bag.Add(Codec.Parse(data, offset, out rt, connection));
length -= rt + 1;
offset += rt + 1;
}
}
else
{
keylist.AddRange(keys);
typelist.AddRange(types);
var i = 0;
while (length > 0)
{
uint rt;
bag.Add(Codec.Parse(data, offset, out rt, connection, types[i]));
length -= rt;
offset += rt;
i++;
}
}
bag.Seal();
bag.Then((res) =>
{
// compose the list
var s = new Structure();
for (var i = 0; i < keylist.Count; i++)
s[keylist[i]] = res[i];
reply.Trigger(s);
});
parsedKeys = keylist.ToArray();
parsedTypes = typelist.ToArray();
return reply;
}
/// <summary>
/// Parse a value
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
/// <param name="dataType">DataType, in case the data is not prepended with DataType</param>
/// <returns>Structure</returns>
public static AsyncReply Parse(byte[] data, uint offset, DistributedConnection connection, DataType dataType = DataType.Unspecified)
{
uint size;
return Parse(data, offset, out size, connection);
}
/// <summary>
/// Parse a value
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="size">Output the number of bytes parsed</param>
/// <param name="connection">DistributedConnection is required in case a structure in the array holds items at the other end.</param>
/// <param name="dataType">DataType, in case the data is not prepended with DataType</param>
/// <returns>Value</returns>
public static AsyncReply Parse(byte[] data, uint offset, out uint size, DistributedConnection connection, DataType dataType = DataType.Unspecified)
{
var reply = new AsyncReply();
bool isArray;
DataType t;
if (dataType == DataType.Unspecified)
{
size = 1;
dataType = (DataType)data[offset++];
}
else
size = 0;
t = (DataType)((byte)dataType & 0x7F);
isArray = ((byte)dataType & 0x80) == 0x80;
var payloadSize = dataType.Size();// SizeOf();
uint contentLength = 0;
// check if we have the enough data
if (payloadSize == -1)
{
contentLength = data.GetUInt32(offset);
offset += 4;
size += 4 + contentLength;
}
else
size += (uint)payloadSize;
if (isArray)
{
switch (t)
{
// VarArray ?
case DataType.Void:
return ParseVarArray(data, offset, contentLength, connection);
case DataType.Bool:
return new AsyncReply<bool[]>(data.GetBooleanArray(offset, contentLength));
case DataType.UInt8:
return new AsyncReply<byte[]>(data.GetUInt8Array(offset, contentLength));
case DataType.Int8:
return new AsyncReply<sbyte[]>(data.GetInt8Array(offset, contentLength));
case DataType.Char:
return new AsyncReply<char[]>(data.GetCharArray(offset, contentLength));
case DataType.Int16:
return new AsyncReply<short[]>(data.GetInt16Array( offset, contentLength));
case DataType.UInt16:
return new AsyncReply<ushort[]>(data.GetUInt16Array(offset, contentLength));
case DataType.Int32:
return new AsyncReply<int[]>(data.GetInt32Array(offset, contentLength));
case DataType.UInt32:
return new AsyncReply<uint[]>(data.GetUInt32Array(offset, contentLength));
case DataType.Int64:
return new AsyncReply<long[]>(data.GetInt64Array(offset, contentLength));
case DataType.UInt64:
return new AsyncReply<ulong[]>(data.GetUInt64Array(offset, contentLength));
case DataType.Float32:
return new AsyncReply<float[]>(data.GetFloat32Array(offset, contentLength));
case DataType.Float64:
return new AsyncReply<double[]>(data.GetFloat64Array(offset, contentLength));
case DataType.String:
return new AsyncReply<string[]>(data.GetStringArray(offset, contentLength));
case DataType.Resource:
case DataType.DistributedResource:
return ParseResourceArray(data, offset, contentLength, connection);
case DataType.DateTime:
return new AsyncReply<DateTime[]>(data.GetDateTimeArray(offset, contentLength));
case DataType.Structure:
return ParseStructureArray(data, offset, contentLength, connection);
}
}
else
{
switch (t)
{
case DataType.NotModified:
return new AsyncReply<object>(new NotModified());
case DataType.Void:
return new AsyncReply<object>(null);
case DataType.Bool:
return new AsyncReply<bool>(data.GetBoolean(offset));
case DataType.UInt8:
return new AsyncReply<byte>(data[offset]);
case DataType.Int8:
return new AsyncReply<sbyte>((sbyte)data[offset]);
case DataType.Char:
return new AsyncReply<char>(data.GetChar(offset));
case DataType.Int16:
return new AsyncReply<short>(data.GetInt16(offset));
case DataType.UInt16:
return new AsyncReply<ushort>(data.GetUInt16(offset));
case DataType.Int32:
return new AsyncReply<int>(data.GetInt32(offset));
case DataType.UInt32:
return new AsyncReply<uint>(data.GetUInt32(offset));
case DataType.Int64:
return new AsyncReply<long>(data.GetInt64(offset));
case DataType.UInt64:
return new AsyncReply<ulong>(data.GetUInt64(offset));
case DataType.Float32:
return new AsyncReply<float>(data.GetFloat32(offset));
case DataType.Float64:
return new AsyncReply<double>(data.GetFloat64(offset));
case DataType.String:
return new AsyncReply<string>(data.GetString(offset, contentLength));
case DataType.Resource:
return ParseResource(data, offset);
case DataType.DistributedResource:
return ParseDistributedResource(data, offset, connection);
case DataType.DateTime:
return new AsyncReply<DateTime>(data.GetDateTime(offset));
case DataType.Structure:
return ParseStructure(data, offset, contentLength, connection);
}
}
return null;
}
/// <summary>
/// Parse a resource
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <returns>Resource</returns>
public static AsyncReply<IResource> ParseResource(byte[] data, uint offset)
{
return Warehouse.Get(data.GetUInt32(offset));
}
/// <summary>
/// Parse a DistributedResource
/// </summary>
/// <param name="data">Bytes array</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="connection">DistributedConnection is required.</param>
/// <returns>DistributedResource</returns>
public static AsyncReply<DistributedResource> ParseDistributedResource(byte[] data, uint offset, DistributedConnection connection)
{
//var g = data.GetGuid(offset);
//offset += 16;
// find the object
var iid = data.GetUInt32(offset);
return connection.Fetch(iid);// Warehouse.Get(iid);
}
public enum ResourceComparisonResult
{
Null,
Distributed,
DistributedSameClass,
Local,
Same
}
public enum StructureComparisonResult : byte
{
Null,
Structure,
StructureSameKeys,
StructureSameTypes,
Same
}
/// <summary>
/// Check if a resource is local to a given connection.
/// </summary>
/// <param name="resource">Resource to check.</param>
/// <param name="connection">DistributedConnection to check if the resource is local to it.</param>
/// <returns>True, if the resource owner is the given connection, otherwise False.</returns>
static bool IsLocalResource(IResource resource, DistributedConnection connection)
{
if (resource is DistributedResource)
if ((resource as DistributedResource).Connection == connection)
return true;
return false;
}
/// <summary>
/// Compare two resources
/// </summary>
/// <param name="initial">Initial resource to make comparison with.</param>
/// <param name="next">Next resource to compare with the initial.</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <returns>Null, same, local, distributed or same class distributed.</returns>
public static ResourceComparisonResult Compare(IResource initial, IResource next, DistributedConnection connection)
{
if (next == null)
return ResourceComparisonResult.Null;
if (next == initial)
return ResourceComparisonResult.Same;
if (IsLocalResource(next, connection))
return ResourceComparisonResult.Local;
if (initial == null)
return ResourceComparisonResult.Distributed;
if (initial.Instance.Template.ClassId == next.Instance.Template.ClassId)
return ResourceComparisonResult.DistributedSameClass;
return ResourceComparisonResult.Distributed;
}
/// <summary>
/// Compose a resource
/// </summary>
/// <param name="resource">Resource to compose.</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <returns>Array of bytes in the network byte order.</returns>
public static byte[] ComposeResource(IResource resource, DistributedConnection connection)
{
if (IsLocalResource(resource, connection))
return DC.ToBytes((resource as DistributedResource).Id);
else
{
return BinaryList.ToBytes(resource.Instance.Template.ClassId, resource.Instance.Id);
}
}
/// <summary>
/// Compose an array of resources
/// </summary>
/// <param name="resources">Array of resources.</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <param name="prependLength">If True, prepend the length of the output at the beginning.</param>
/// <returns>Array of bytes in the network byte order.</returns>
public static byte[] ComposeResourceArray(IResource[] resources, DistributedConnection connection, bool prependLength = false)
{
if (resources == null || resources?.Length == 0)
return new byte[0];
var rt = new BinaryList();
var comparsion = Compare(null, resources[0], connection);
rt.Append((byte)comparsion);
if (comparsion == ResourceComparisonResult.Local)
rt.Append((resources[0] as DistributedResource).Id);
else if (comparsion == ResourceComparisonResult.Distributed)
{
rt.Append(resources[0].Instance.Template.ClassId);
rt.Append(resources[0].Instance.Id);
}
for (var i = 1; i < resources.Length; i++)
{
comparsion = Compare(resources[i - 1], resources[i], connection);
rt.Append((byte)comparsion);
if (comparsion == ResourceComparisonResult.Local)
rt.Append((resources[0] as DistributedResource).Id);
else if (comparsion == ResourceComparisonResult.Distributed)
{
rt.Append(resources[0].Instance.Template.ClassId);
rt.Append(resources[0].Instance.Id);
}
else if (comparsion == ResourceComparisonResult.DistributedSameClass)
{
rt.Append(resources[0].Instance.Id);
}
}
if (prependLength)
rt.Insert(0, rt.Length);
return rt.ToArray();
}
/// <summary>
/// Parse an array of bytes into array of resources
/// </summary>
/// <param name="data">Array of bytes.</param>
/// <param name="length">Number of bytes to parse.</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="connection">DistributedConnection is required to fetch resources.</param>
/// <returns>Array of resources.</returns>
public static AsyncBag<IResource> ParseResourceArray(byte[] data, uint offset, uint length, DistributedConnection connection)
{
var reply = new AsyncBag<IResource>();
if (length == 0)
{
reply.Seal();
return reply;
}
var end = offset + length;
//
var result = (ResourceComparisonResult)data[offset++];
AsyncReply previous = null;
Guid previousGuid = Guid.Empty;
if (result == ResourceComparisonResult.Null)
previous = new AsyncReply<IResource>(null);
else if (result == ResourceComparisonResult.Local)
{
previous = Warehouse.Get(data.GetUInt32(offset));
offset += 4;
}
else if (result == ResourceComparisonResult.Distributed)
{
previousGuid = data.GetGuid(offset);
offset += 16;
//previous = connection.Fetch(previousGuid, data.GetUInt32(offset));
offset += 4;
}
reply.Add(previous);
while (offset < end)
{
result = (ResourceComparisonResult)data[offset++];
if (result == ResourceComparisonResult.Null)
previous = new AsyncReply<IResource>(null);
//else if (result == ResourceComparisonResult.Same)
// reply.Add(previous);
else if (result == ResourceComparisonResult.Local)
{
// overwrite previous
previous = Warehouse.Get(data.GetUInt32(offset));
offset += 4;
}
else if (result == ResourceComparisonResult.Distributed)
{
// overwrite previous
previousGuid = data.GetGuid(offset);
offset += 16;
//previous = connection.Fetch(previousGuid, data.GetUInt32(offset));
offset += 4;
}
else if (result == ResourceComparisonResult.DistributedSameClass)
{
// overwrite previous
//previous = connection.Fetch(previousGuid, data.GetUInt32(offset));
offset += 4;
}
reply.Add(previous);
}
reply.Seal();
return reply;
}
/// <summary>
/// Compose an array of variables
/// </summary>
/// <param name="array">Variables.</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <param name="prependLength">If True, prepend the length as UInt32 at the beginning of the output.</param>
/// <returns>Array of bytes in the network byte order.</returns>
public static byte[] ComposeVarArray(object[] array, DistributedConnection connection, bool prependLength = false)
{
var rt = new List<byte>();
for (var i = 0; i < array.Length; i++)
rt.AddRange(Compose(array[i], connection));
if (prependLength)
rt.InsertRange(0, DC.ToBytes(rt.Count));
return rt.ToArray();
}
public static AsyncBag<object> ParseVarArray(byte[] data, DistributedConnection connection)
{
return ParseVarArray(data, 0, (uint)data.Length, connection);
}
/// <summary>
/// Parse an array of bytes into an array of varialbes.
/// </summary>
/// <param name="data">Array of bytes.</param>
/// <param name="offset">Zero-indexed offset.</param>
/// <param name="length">Number of bytes to parse.</param>
/// <param name="connection">DistributedConnection is required to fetch resources.</param>
/// <returns>Array of variables.</returns>
public static AsyncBag<object> ParseVarArray(byte[] data, uint offset, uint length, DistributedConnection connection)
{
var rt = new AsyncBag<object>();
while (length > 0)
{
uint cs;
rt.Add(Parse(data, offset, out cs, connection));
if (cs > 0)
{
offset += (uint)cs;
length -= (uint)cs;
}
else
throw new Exception("Error while parsing structured data");
}
rt.Seal();
return rt;
}
/// <summary>
/// Compose a variable
/// </summary>
/// <param name="value">Value to compose.</param>
/// <param name="connection">DistributedConnection is required to check locality.</param>
/// <param name="prependType">If True, prepend the DataType at the beginning of the output.</param>
/// <returns>Array of bytes in the network byte order.</returns>
public static byte[] Compose(object value, DistributedConnection connection, bool prependType = true)
{
var type = GetDataType(value, connection);
var rt = new BinaryList();
switch (type)
{
case DataType.Void:
// nothing to do;
break;
case DataType.String:
var st = DC.ToBytes((string)value);
rt.Append(st.Length, st);
break;
case DataType.Resource:
rt.Append((value as DistributedResource).Id);
break;
case DataType.DistributedResource:
//rt.Append((value as IResource).Instance.Template.ClassId, (value as IResource).Instance.Id);
rt.Append((value as IResource).Instance.Id);
break;
case DataType.Structure:
rt.Append(ComposeStructure((Structure)value, connection, true, true, true));
break;
case DataType.VarArray:
rt.Append(ComposeVarArray((object[])value, connection, true));
break;
case DataType.ResourceArray:
if (value is IResource[])
rt.Append(ComposeResourceArray((IResource[])value, connection, true));
else
rt.Append(ComposeResourceArray((IResource[])DC.CastConvert(value, typeof(IResource[])), connection, true));
break;
case DataType.StructureArray:
rt.Append(ComposeStructureArray((Structure[])value, connection, true));
break;
default:
rt.Append(value);
if (type.IsArray())
rt.Insert(0, rt.Length);
break;
}
if (prependType)
rt.Insert(0, (byte)type);
return rt.ToArray();
}
/// <summary>
/// Check if a type implements an interface
/// </summary>
/// <param name="type">Sub-class type.</param>
/// <param name="iface">Super-interface type.</param>
/// <returns>True, if <paramref name="type"/> implements <paramref name="iface"/>.</returns>
private static bool ImplementsInterface(Type type, Type iface)
{
while (type != null)
{
#if NETSTANDARD1_5
if (type.GetTypeInfo().GetInterfaces().Contains(iface))
return true;
type = type.GetTypeInfo().BaseType;
#else
if (type.GetInterfaces().Contains(iface))
return true;
type = type.BaseType;
#endif
}
return false;
}
/// <summary>
/// Check if a type inherits another type.
/// </summary>
/// <param name="childType">Child type.</param>
/// <param name="parentType">Parent type.</param>
/// <returns>True, if <paramref name="childType"/> inherits <paramref name="parentType"/>.</returns>
private static bool HasParentType(Type childType, Type parentType)
{
while (childType != null)
{
if (childType == parentType)
return true;
#if NETSTANDARD1_5
childType = childType.GetTypeInfo().BaseType;
#else
childType = childType.BaseType;
#endif
}
return false;
}
/// <summary>
/// Get the DataType of a given value.
/// This function is needed to compose a value.
/// </summary>
/// <param name="value">Value to find its DataType.</param>
/// <param name="connection">DistributedConnection is required to check locality of resources.</param>
/// <returns>DataType.</returns>
public static DataType GetDataType(object value, DistributedConnection connection)
{
if (value == null)
return DataType.Void;
var t = value.GetType();
var isArray = t.IsArray;
if (isArray)
t = t.GetElementType();
DataType type;
if (t == typeof(bool))
type = DataType.Bool;
else if (t == typeof(char))
type = DataType.Char;
else if (t == typeof(byte))
type = DataType.UInt8;
else if (t == typeof(sbyte))
type = DataType.Int8;
else if (t == typeof(short))
type = DataType.Int16;
else if (t == typeof(ushort))
type = DataType.UInt16;
else if (t == typeof(int))
type = DataType.Int32;
else if (t == typeof(uint))
type = DataType.UInt32;
else if (t == typeof(long))
type = DataType.Int64;
else if (t == typeof(ulong))
type = DataType.UInt64;
else if (t == typeof(float))
type = DataType.Float32;
else if (t == typeof(double))
type = DataType.Float64;
else if (t == typeof(decimal))
type = DataType.Decimal;
else if (t == typeof(string))
type = DataType.String;
else if (t == typeof(DateTime))
type = DataType.DateTime;
else if (t == typeof(Structure))
type = DataType.Structure;
//else if (t == typeof(DistributedResource))
// type = DataType.DistributedResource;
else if (ImplementsInterface(t, typeof(IResource)))
{
if (isArray)
return DataType.ResourceArray;
else
{
return IsLocalResource((IResource)value, connection) ? DataType.Resource : DataType.DistributedResource;
}
}
else
return DataType.Void;
if (isArray)
return (DataType)((byte)type | 0x80);
else
return type;
}
}
}

1069
Esiur/Data/DataConverter.cs Normal file

File diff suppressed because it is too large Load Diff

95
Esiur/Data/DataType.cs Normal file
View File

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Data
{
public enum DataType : byte
{
Void = 0x0,
//Variant,
Bool,
Int8,
UInt8,
Char,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
Float32,
Float64,
Decimal,
DateTime,
Resource,
DistributedResource,
ResourceLink,
String,
Structure,
//Stream,
//Array = 0x80,
VarArray = 0x80,
BoolArray,
UInt8Array,
Int8Array,
CharArray,
Int16Array,
UInt16Array,
Int32Array,
UInt32Array,
Int64Array,
UInt64Array,
Float32Array,
Float64Array,
DecimalArray,
DateTimeArray,
ResourceArray,
DistributedResourceArray,
ResourceLinkArray,
StringArray,
StructureArray,
NotModified = 0x7f,
Unspecified = 0xff,
}
public static class DataTypeExpansions
{
public static int Size(this DataType t)
{
switch (t)
{
case DataType.Void:
case DataType.NotModified:
return 0;
case DataType.Bool:
case DataType.UInt8:
case DataType.Int8:
return 1;
case DataType.Char:
case DataType.UInt16:
case DataType.Int16:
return 2;
case DataType.Int32:
case DataType.UInt32:
case DataType.Float32:
case DataType.Resource:
return 4;
case DataType.Int64:
case DataType.UInt64:
case DataType.Float64:
case DataType.DateTime:
return 8;
case DataType.DistributedResource:
return 4;
default:
return -1;
}
}
}
}

204
Esiur/Data/KeyList.cs Normal file
View File

@ -0,0 +1,204 @@
using System;
using System.IO;
using System.Collections;
using System.Security.Cryptography;
using System.Text;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Esiur.Engine;
namespace Esiur.Data
{
public class KeyList<KT, T> : IEnumerable
{
private readonly object syncRoot = new object();
private Dictionary<KT, T> dic;
public delegate void Modified(KT key, T oldValue, T newValue);
public delegate void Added(T value);
public delegate void Removed(KT key, T value);
public delegate void Cleared();
public event Modified OnModified;
public event Removed OnRemoved;
public event Cleared OnCleared;
public event Added OnAdd;
bool removableList;
public object SyncRoot
{
get
{
return syncRoot;
}
}
public T Take(KT key)
{
if (dic.ContainsKey(key))
{
var v = dic[key];
Remove(key);
return v;
}
else
return default(T);
}
public void Sort(Func<KeyValuePair<KT, T>, object> keySelector)
{
dic = dic.OrderBy(keySelector).ToDictionary(x => x.Key, x => x.Value);
}
public T[] ToArray()
{
var a = new T[Count];
dic.Values.CopyTo(a, 0);
return a;
}
public void Add(KT key, T value)
{
lock (syncRoot)
{
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy += ItemDestroyed;
if (dic.ContainsKey(key))
{
var oldValue = dic[key];
if (removableList)
if (oldValue != null)
((IDestructible)oldValue).OnDestroy -= ItemDestroyed;
dic[key] = value;
if (OnModified != null)
OnModified(key, oldValue, value);
}
else
{
dic.Add(key, value);
if (OnAdd != null)
OnAdd(value);
}
}
}
private void ItemDestroyed(object sender)
{
RemoveValue((T)sender);
}
public void RemoveValue(T value)
{
var toRemove = new List<KT>();
foreach (var kv in dic)
if (kv.Value.Equals(value))
toRemove.Add(kv.Key);
foreach (var k in toRemove)
Remove(k);
}
public T this[KT key]
{
get
{
if (dic.ContainsKey(key))
return dic[key];
else
return default(T);
}
set
{
Add(key, value);
}
}
public IEnumerator GetEnumerator()
{
return dic.GetEnumerator();
}
public void Clear()
{
if (removableList)
foreach (IDestructible v in dic.Values)
if (v != null)
v.OnDestroy -= ItemDestroyed;
lock (syncRoot)
dic.Clear();
if (OnCleared != null)
OnCleared();
}
public Dictionary<KT, T>.KeyCollection Keys
{
get { return dic.Keys; }
}
public Dictionary<KT, T>.ValueCollection Values
{
get
{
return dic.Values;
}
}
public void Remove(KT key)
{
if (!dic.ContainsKey(key))
return;
var value = dic[key];
if (removableList)
if (value != null)
((IDestructible)value).OnDestroy -= ItemDestroyed;
lock (syncRoot)
dic.Remove(key);
if (OnRemoved != null)
OnRemoved(key, value);
}
public int Count
{
get { return dic.Count; }
}
public bool Contains(KT Key)
{
return dic.ContainsKey(Key);
}
public bool ContainsKey(KT Key)
{
return dic.ContainsKey(Key);
}
public bool ContainsValue(T Value)
{
return dic.ContainsValue(Value);
}
public KeyList()
{
#if NETSTANDARD1_5
removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()));
#else
removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T)));
#endif
if (typeof(KT) == typeof(string))
dic = (Dictionary<KT, T>)(object)new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
else
dic = new Dictionary<KT, T>();
}
}
}

13
Esiur/Data/NotModified.cs Normal file
View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Data
{
public class NotModified
{
}
}

194
Esiur/Data/StringKeyList.cs Normal file
View File

@ -0,0 +1,194 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Reflection;
namespace Esiur.Data
{
public class StringKeyList : IEnumerable<KeyValuePair<string, string>>
{
//private List<string> m_keys = new List<string>();
//private List<string> m_values = new List<string>();
private List<KeyValuePair<string, string>> m_Variables = new List<KeyValuePair<string, string>>();
private bool allowMultiple;
public delegate void Modified(string Key, string NewValue);
public event Modified OnModified;
public StringKeyList(bool AllowMultipleValues = false)
{
allowMultiple = AllowMultipleValues;
}
public void Add(string Key, string Value)
{
if (OnModified != null)
OnModified(Key, Value);
var key = Key.ToLower();
if (!allowMultiple)
{
foreach(var kv in m_Variables)
{
if (kv.Key.ToLower() == key)
{
m_Variables.Remove(kv);
break;
}
}
}
m_Variables.Add(new KeyValuePair<string, string>(Key, Value));
}
public string this[string Key]
{
get
{
var key = Key.ToLower();
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
return kv.Value;
return null;
}
set
{
var key = Key.ToLower();
if (OnModified != null)
OnModified(Key, value);
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
m_Variables.Remove(kv);
m_Variables.Add(new KeyValuePair<string, string>(Key, value));
}
}
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
{
//return m_keys.GetEnumerator();
return m_Variables.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return m_Variables.GetEnumerator();
}
public void Clear()
{
if (OnModified != null)
OnModified(null, null);
m_Variables.Clear();
}
/*
public string[] Keys
{
get
{
return m_keys.ToArray();
}
}
//public Dictionary<string, string>.ValueCollection Values
public string[] Values
{
get
{
//return m_Variables.Values;
return m_values.ToArray();
}
}
*/
public List<string> GetValues(string Key)
{
var key = Key.ToLower();
List<string> values = new List<string>();
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
values.Add(kv.Value);
return values;
}
public void RemoveAll(string Key)
{
while (Remove(Key)){}
}
public bool Remove(string Key)
{
var key = Key.ToLower();
foreach(var kv in m_Variables)
{
if (kv.Key.ToLower() == key)
{
if (OnModified != null)
OnModified(Key, null);
m_Variables.Remove(kv);
return true;
}
}
return false;
}
public int Count
{
get { return m_Variables.Count; }
}
public bool ContainsKey(string Key)
{
var key = Key.ToLower();
foreach (var kv in m_Variables)
if (kv.Key.ToLower() == key)
return true;
return false;
}
/*
public bool ContainsKey(string Key)
{
//return m_Variables.ContainsKey(Key);
return m_keys.Contains(Key.ToLower());
}
*/
public bool ContainsValue(string Value)
{
var value = Value.ToLower();
foreach (var kv in m_Variables)
if (kv.Value.ToLower() == value)
return true;
return false;
}
//internal KeyList()
//{
// m_Session = Session;
// m_Server = Server;
//}
}
}

83
Esiur/Data/Structure.cs Normal file
View File

@ -0,0 +1,83 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Engine;
namespace Esiur.Data
{
public class Structure : IEnumerable<KeyValuePair<string, object>>
{
private Dictionary<string, object> dic = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
private object syncRoot = new object();
public bool ContainsKey(string key)
{
return dic.ContainsKey(key);
}
public override string ToString()
{
var rt = "";
foreach (var kv in dic)
rt += kv.Key + ": " + kv.Value.ToString() + "\r\n";
return rt.TrimEnd('\r', '\n');
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return dic.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return dic.GetEnumerator();
}
public int Length
{
get { return dic.Count; }
}
public KeyValuePair<string, object> At(int index)
{
return dic.ElementAt(index);
}
public object SyncRoot
{
get { return syncRoot; }
}
public string[] GetKeys()
{
return dic.Keys.ToArray();
}
public object this[string index]
{
get
{
if (dic.ContainsKey(index))
return dic[index];
else
return null;
}
set
{
if (dic.ContainsKey(index))
dic[index] = value;
else
dic.Add(index, value);
}
}
}
}

64
Esiur/Engine/AsyncBag.cs Normal file
View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Engine
{
public class AsyncBag<T>:AsyncReply
{
//List<AsyncReply> replies = new List<AsyncReply>();
//List<T> results = new List<T>();
Dictionary<AsyncReply, T> results = new Dictionary<AsyncReply, T>();
int count = 0;
bool sealedBag = false;
public void Then(Action<T[]> callback)
{
base.Then(new Action<object>(o => callback((T[])o)));
}
/*
public void Trigger(T[] result)
{
Trigger((object)result);
}
*/
public void Seal()
{
sealedBag = true;
if (results.Count == 0)
Trigger(new T[0]);
for(var i = 0; i < results.Count; i++)
//foreach(var reply in results.Keys)
results.Keys.ElementAt(i).Then((r) => {
results[results.Keys.ElementAt(i)] = (T)r;
count++;
if (count == results.Count)
Trigger(results.Values.ToArray());
});
}
public void Add(AsyncReply reply)
{
if (!sealedBag)
results.Add(reply, default(T));
}
public AsyncBag()
{
}
/*
public AsyncBag(T[] result)
{
this.result = result;
}
*/
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Engine
{
public class AsyncQueue<T> : AsyncReply
{
List<AsyncReply<T>> list = new List<AsyncReply<T>>();
//Action<T> callback;
object queueLock = new object();
public void Then(Action<T> callback)
{
base.Then(new Action<object>(o => callback((T)o)));
}
public void Add(AsyncReply<T> reply)
{
lock (queueLock)
list.Add(reply);
resultReady = false;
reply.Then(processQueue);
}
public void Remove(AsyncReply<T> reply)
{
lock (queueLock)
list.Remove(reply);
processQueue(default(T));
}
void processQueue(T o)
{
lock (queueLock)
for (var i = 0; i < list.Count; i++)
if (list[i].Ready)
{
Trigger(list[i].Result);
list.RemoveAt(i);
i--;
}
else
break;
resultReady = (list.Count == 0);
}
public AsyncQueue()
{
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Engine
{
public class AsyncReply
{
protected List<Action<object>> callbacks = new List<Action<object>>();
protected object result;
object callbacksLock = new object();
//bool fired = false;
protected bool resultReady = false;
public bool Ready
{
get { return resultReady; }
}
public object Result
{
get { return result; }
}
public void Then(Action<object> callback)
{
callbacks.Add(callback);
if (resultReady)
callback(result);
// Trigger(this.result);
}
public void Trigger(object result)
{
//if (!fired)
//{
this.result = result;
resultReady = true;
lock (callbacksLock)
{
foreach (var cb in callbacks)
cb(result);
//callbacks.Clear();
}
/*
if (callback == null)
{
fireAtChance = true;
}
else
{
callback(result);
fired = true;
}
*/
//}
}
public AsyncReply()
{
}
public AsyncReply(object result)
{
resultReady = true;
this.result = result;
}
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Esiur.Resource;
namespace Esiur.Engine
{
public class AsyncReply<T>: AsyncReply
{
public void Then(Action<T> callback)
{
base.Then(new Action<object>(o => callback((T)o)));
}
public void Trigger(T result)
{
Trigger((object)result);
}
public AsyncReply()
{
}
public AsyncReply(T result)
: base(result)
{
}
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Engine
{
public delegate void DestroyedEvent(object sender);
public interface IDestructible
{
event DestroyedEvent OnDestroy;
void Destroy();
}
}

15
Esiur/Engine/LogType.cs Normal file
View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Engine
{
public enum LogType
{
Debug,
Warning,
Error,
}
}

32
Esiur/Esiur.csproj Normal file
View File

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.5</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<DefineConstants>TRACE;DEBUG;NETSTANDARD1_5</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Net\UDP\UDPServer.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Net\DataLink\Sources\" />
</ItemGroup>
<ItemGroup>
<None Include="Net\UDP\UDPServer.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Diagnostics.StackTrace" Version="4.3.0" />
<PackageReference Include="System.Dynamic.Runtime" Version="4.3.0" />
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
<PackageReference Include="System.Net.Security" Version="4.3.1" />
</ItemGroup>
</Project>

435
Esiur/Misc/Global.cs Normal file
View File

@ -0,0 +1,435 @@
using System;
using System.IO;
using System.Collections;
using System.Xml;
using System.Security.Cryptography;
using System.Text;
using System.Reflection;
using Esiur.Data;
using System.Collections.Generic;
//using Esiur.Net.Packets;
using System.Text.RegularExpressions;
using System.Net.NetworkInformation;
using System.Linq;
using Esiur.Engine;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Esiur.Misc
{
public static class Global
{
private static KeyList<string, object> variables = new KeyList<string, object>();
// private static Hashtable m_Cached = new Hashtable();
//internal static bool SystemIsWorking = false;
private static Random rand = new Random(System.Environment.TickCount);
//public static Encoding DefaultEncoding = Encoding.GetEncoding(1252);// .GetEncoding("windows-1252");
public static KeyList<string, long> Counters = new KeyList<string, long>();
public delegate void LogEvent(string service, LogType type, string message);
public static LogEvent SystemLog;
static Random random = new Random();
/*
public static char GetDirectorySeparator()
{
return System.IO.Path.DirectorySeparatorChar;
}
*/
public static void Log(Exception ex, params object[] arguments)
{
try
{
var stack = new StackTrace(ex, true);
var frame = stack.GetFrames().First();
var method = frame.GetMethod();
var parameters = method.GetParameters();
var service = method.DeclaringType.Name;
var message = "";
if (arguments.Length > 0 && parameters.Length > 0)
{
message = "Arguments ( ";
for (int i = 0; i < parameters.Length && i < arguments.Length; i++)
{
message += parameters[i].Name + ": " + arguments[i].ToString() + " ";
}
message += ")" + Environment.NewLine + "------------------------------------------------";
}
message += ex.ToString();
Log(service, LogType.Error, message);
Log(service, LogType.Error, ex.ToString());
}
catch
{
}
}
public static void Log(string service, LogType type, string message, bool appendHeader = true)
{
//if (type != LogType.Debug)
Console.WriteLine(service + " " + message);
if (SystemLog != null)
SystemLog(service, type, message);
}
/*
public static string GetTempPath()
{
return System.IO.Path.GetTempPath();
}
*/
public static string RemoveControlCharacters(string inString)
{
if (inString == null) return null;
StringBuilder newString = new StringBuilder();
char ch;
for (int i = 0; i < inString.Length; i++)
{
ch = inString[i];
if (!char.IsControl(ch))
{
newString.Append(ch);
}
}
return newString.ToString();
}
public static void PrintCounters()
{
string[] keys = new string[Counters.Keys.Count];
Counters.Keys.CopyTo(keys, 0);
foreach (string k in keys)
{
Console.WriteLine(k + ":" + Counters[k]);
}
}
// Encoding ANSI = Encoding.GetEncoding(1252);
/*
public static Hashtable Cached
{
get
{
return m_Cached;
}
}*/
/*
public static string ByteArrayToMAC(byte[] array)
{
string rt="";
if (array == null)
return "00:00:00:00:00:00";
else
{
//for (int i = 0; i < array.Length - 1; i++)
// rt += Convert.ToString(array[i], 16) + ":";
//rt += Convert.ToString(array[array.Length - 1], 16);
rt = BitConverter.ToString(array);
rt = rt.Replace('-', ':');
return rt;
}
}
*/
/*
public static string IPAddressFromInt32(UInt32 IP)
{
//var dIP = DC.ToBytes(IP);
return (IP >> 24) + "." + ((IP >> 16) & 0xFF) + "." + ((IP >> 8) & 0xFF) + "." + (IP & 0xFF);
}
*/
public static KeyList<string, object> Variables
{
get
{
return variables;
}
}
public static uint CurrentUnixTime()
{
return (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
}
public static void SetConsoleColors(ConsoleColor ForegroundColor, ConsoleColor BackgroundColor)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
switch (ForegroundColor)
{
case ConsoleColor.Black:
Console.Write("\u001B[30m");
break;
case ConsoleColor.Blue:
Console.Write("\u001B[1;34m");
break;
case ConsoleColor.Cyan:
Console.Write("\u001B[1;36m");
break;
case ConsoleColor.Gray:
case ConsoleColor.DarkGray:
Console.Write("\u001B[1;30m");
break;
case ConsoleColor.Green:
Console.Write("\u001B[1;32m");
break;
case ConsoleColor.Magenta:
Console.Write("\u001B[1;35m");
break;
case ConsoleColor.Red:
Console.Write("\u001B[1;31m");
break;
case ConsoleColor.White:
Console.Write("\u001B[1;37m");
break;
case ConsoleColor.Yellow:
Console.Write("\u001B[1;33m");
break;
case ConsoleColor.DarkBlue:
Console.Write("\u001B[34m");
break;
case ConsoleColor.DarkCyan:
Console.Write("\u001B[36m");
break;
case ConsoleColor.DarkGreen:
Console.Write("\u001B[32m");
break;
case ConsoleColor.DarkMagenta:
Console.Write("\u001B[35m");
break;
case ConsoleColor.DarkRed:
Console.Write("\u001B[31m");
break;
case ConsoleColor.DarkYellow:
Console.Write("\u001B[33m");
break;
}
switch (BackgroundColor)
{
case ConsoleColor.Black:
Console.Write("\u001B[40m");
break;
case ConsoleColor.Blue:
Console.Write("\u001B[1;44m");
break;
case ConsoleColor.Cyan:
Console.Write("\u001B[1;46m");
break;
case ConsoleColor.Gray:
case ConsoleColor.DarkGray:
Console.Write("\u001B[1;40m");
break;
case ConsoleColor.Green:
Console.Write("\u001B[1;42m");
break;
case ConsoleColor.Magenta:
Console.Write("\u001B[1;45m");
break;
case ConsoleColor.Red:
Console.Write("\u001B[1;41m");
break;
case ConsoleColor.White:
Console.Write("\u001B[1;47m");
break;
case ConsoleColor.Yellow:
Console.Write("\u001B[1;43m");
break;
case ConsoleColor.DarkBlue:
Console.Write("\u001B[44m");
break;
case ConsoleColor.DarkCyan:
Console.Write("\u001B[46m");
break;
case ConsoleColor.DarkGreen:
Console.Write("\u001B[42m");
break;
case ConsoleColor.DarkMagenta:
Console.Write("\u001B[45m");
break;
case ConsoleColor.DarkRed:
Console.Write("\u001B[41m");
break;
case ConsoleColor.DarkYellow:
Console.Write("\u001B[43m");
break;
}
}
else
{
Console.ForegroundColor = ForegroundColor;
Console.BackgroundColor = BackgroundColor;
}
}
public static string GetUserPart(string strAddress)
{
return strAddress.Substring(0, strAddress.IndexOf("@", 0));
}
public static byte[][] GetBytesFromChunk(byte[] Data, int ChunkSize)
{
if (ChunkSize == 1)
{
byte[][] ar = new byte[0][];
int ptr = 0;
while (ptr < Data.Length)
{
Array.Resize<byte[]>(ref ar, ar.Length + 1);
ar[ar.Length - 1] = new byte[Data[ptr]];
Buffer.BlockCopy(Data, ++ptr, ar[ar.Length - 1], 0, Data[ptr]);
ptr += Data[ptr] + 1;
}
return ar;
}
return null;
}
public static string GetFileTitle(string Filename)
{
string[] s = Filename.Split(Path.DirectorySeparatorChar);
return s[s.Length - 1];
}
public static string GetNewFileName(string FileDir)
{
string tempGetNewFileName = null;
short i = 0;
string NewFile = null;
NewFile = FileDir;
Begin:
FileInfo FF = new FileInfo(NewFile);
if (FF.Exists)
{
//If FSO.FileExists(NewFile) Then
i++; //= i + 1;
NewFile = FileDir.Substring(0, FileDir.Length - 4) + "_" + i + "." + FileDir.Substring(FileDir.Length - 3);
goto Begin;
}
else
{
tempGetNewFileName = NewFile;
}
return tempGetNewFileName;
}
/////////////////////////////////////
public static string TrimEx(string strIn)
{
return strIn.Replace("\r", "").Replace("\n", "");
}
/*
public static bool IsUnix()
{
// Linux OSs under Mono 1.2 uses unknown integer numbers so this should identify all non windows as unix
return (Environment.OSVersion.Platform != PlatformID.Win32NT
&& Environment.OSVersion.Platform != PlatformID.Win32Windows); // || Environment.OSVersion.Platform == PlatformID.Linux;
}
*/
public static string GenerateCode()
{
return GenerateCode(16);
}
public static byte[] GenerateBytes(int Length)
{
var b = new byte[Length];
rand.NextBytes(b);
return b;
}
public static string GenerateCode(int length)
{
return GenerateCode(length, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()_-+=\\?/");
}
public static string GenerateCode(int length, string chars)
//public static string GenerateCode(int Length)
{
//var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()_-+=\\?/";
var result = new string(
Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)])
.ToArray());
if (result.Length < length)
Console.WriteLine();
return result;
/*
int len = 0;
string code = "";
while(len < Length)
{
var c = Convert.ToChar((byte)(rand.NextDouble() * 255));
if (Char.IsLetterOrDigit(c))
{
code += c;
len++;
}
}
return code;
*/
}
public static string ReplaceOnce(string Expression, string Find, string Replacement)
{
int pos = Expression.IndexOf(Find);
if (pos != -1)
return Expression.Substring(0, pos) + Replacement + Expression.Substring(pos + Find.Length);
else
return Expression;
}
//public void Replace(string Expression, string Find, string Replacement, int Start, int Count)
//{
// Expression.IndexOf(
//}
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Engine;
using Esiur.Data;
using Esiur.Net.Packets;
using Esiur.Resource;
namespace Esiur.Net.DataLink
{
public abstract class PacketFilter : IResource
{
public Instance Instance
{
get;
set;
}
public event DestroyedEvent OnDestroy;
public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
public abstract bool Execute(Packet packet);
public void Destroy()
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Engine;
using Esiur.Data;
using System.Runtime.InteropServices;
using Esiur.Net.Packets;
using Esiur.Resource;
namespace Esiur.Net.DataLink
{
public class PacketServer:IResource
{
List<PacketSource> sources = new List<PacketSource>();
List<PacketFilter> filters = new List<PacketFilter>();
[Storable]
public string Mode
{
get;
set;
}
public Instance Instance
{
get;
set;
}
public List<PacketSource> Sources
{
get
{
return sources;
}
}
public event DestroyedEvent OnDestroy;
public void Destroy()
{
throw new NotImplementedException();
}
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
if (trigger == ResourceTrigger.Initialize)
{
foreach (Instance instance in Instance.Children)
{
if (instance.Resource is PacketFilter)
{
filters.Add(instance.Resource as PacketFilter);
}
else if (instance.Resource is PacketSource)
{
sources.Add(instance.Resource as PacketSource);
}
}
foreach (var src in sources)
{
src.OnNewPacket += PacketReceived;
src.Open();
}
}
else if (trigger == ResourceTrigger.Terminate)
{
// foreach (var src in sources)
// src.Close();
}
else if (trigger == ResourceTrigger.SystemReload)
{
foreach (var src in sources)
{
src.Close();
src.Open();
}
}
return new AsyncReply<bool>( true);
}
void PacketReceived(Packet Packet)
{
foreach (var f in filters)
{
if (f.Execute(Packet))
{
break;
}
}
}
}
}

View File

@ -0,0 +1,71 @@
using Esiur.Net.Packets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Engine;
using Esiur.Resource;
namespace Esiur.Net.DataLink
{
public abstract class PacketSource: IResource
{
public delegate void NewPacket(Packet Packet);
public abstract event NewPacket OnNewPacket;
public event DestroyedEvent OnDestroy;
public Instance Instance
{
get;
set;
}
public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
public abstract bool RawMode
{
set;
get;
}
//public PacketSource(PacketServer Server, bool RawMode)
//{
// this.RawMode = RawMode;
//}
public abstract bool Open();
public abstract bool Close();
public abstract bool Write(Packet packet);
public void Destroy()
{
throw new NotImplementedException();
}
/*
public virtual string TypeName
{
get
{
return "Raw";
}
}
*/
public abstract byte[] Address
{
get;
}
public abstract string DeviceId
{
get;
}
}
}

View File

@ -0,0 +1,346 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net;
using System.Collections;
using System.Collections.Generic;
using Esiur.Net.Sockets;
using Esiur.Data;
using Esiur.Net.Packets;
using Esiur.Misc;
using System.Security.Cryptography;
namespace Esiur.Net.HTTP
{
//[Serializable]
public class HTTPConnection : NetworkConnection
{
/*
public enum SendOptions : int
{
AllCalculateLength,
AllDontCalculateLength,
SpecifiedHeadersOnly,
DataOnly
}
*/
public HTTPConnection()
{
Response = new HTTPResponsePacket();
variables = new KeyList<string, object>();
}
public void SetParent(HTTPServer Parent)
{
Server = Parent;
}
//public bool HeadersSent;
private KeyList<string, object> variables;
private bool Busy = false;
private DateTime RequestTime = DateTime.MinValue;
public bool WSMode;
private HTTPServer Server;
public WebsocketPacket WSRequest;
public HTTPRequestPacket Request;
public HTTPResponsePacket Response;
HTTPSession session;
public KeyList<string, object> Variables
{
get
{
return variables;
}
}
public bool IsBusy()
{
return Busy;
}
internal long Parse(byte[] data)
{
if (WSMode)
{
// now parse WS protocol
WebsocketPacket ws = new WebsocketPacket();
var pSize = ws.Parse(data, 0, (uint)data.Length);
if (pSize > 0)
{
WSRequest = ws;
return 0;
}
else
{
return pSize;
}
}
else
{
HTTPRequestPacket rp = new HTTPRequestPacket();
var pSize = rp.Parse(data, 0, (uint)data.Length);
if (pSize > 0)
{
Request = rp;
return 0;
}
else
{
return pSize;
}
}
}
/*
public override void Send(string Response)
{
Send(Response, SendOptions.AllCalculateLength);
}
public void Send(string Message, SendOptions Options)
{
if (Response.Handled)
return;
if (Response != null)
Send(Encoding.Default.GetBytes(Response), Options);
else
Send((byte[])null, Options);
}
public void Send(MemoryStream ms)
{
Send(ms.ToArray(), SendOptions.AllCalculateLength);
}
*/
public void Flush()
{
// close the connection
if (Request.Headers["connection"].ToLower() != "keep-alive" & Connected)
Close();
}
public bool Upgrade()
{
if (IsWebsocketRequest())
{
string magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
string ret = Request.Headers["Sec-WebSocket-Key"] + magicString;
// Compute the SHA1 hash
SHA1 sha = SHA1.Create();
byte[] sha1Hash = sha.ComputeHash(Encoding.UTF8.GetBytes(ret));
Response.Headers["Upgrade"] = Request.Headers["Upgrade"];
Response.Headers["Connection"] = Request.Headers["Connection"];// "Upgrade";
Response.Headers["Sec-WebSocket-Accept"] = Convert.ToBase64String(sha1Hash);
if (Request.Headers.ContainsKey("Sec-WebSocket-Protocol"))
Response.Headers["Sec-WebSocket-Protocol"] = Request.Headers["Sec-WebSocket-Protocol"];
//Response.Headers["Sec-WebSocket-Protocol"] = Request.Headers["Sec-WebSocket-Protocol"];
//Response.Headers["Origin"] = Request.Headers["Origin"];
Response.Number = HTTPResponsePacket.ResponseCode.HTTP_SWITCHING;
Response.Text = "Switching Protocols";
WSMode = true;
//Send((byte[])null, SendOptions.AllDontCalculateLength);
Send();
return true;
}
return false;
}
public HTTPServer Parent
{
get
{
return Server;
}
}
public override void Send(string data)
{
Response.Message = Encoding.UTF8.GetBytes(data);
Send();
}
public override void Send(byte[] message)
{
Response.Message = message;
Send();
}
public void Send(HTTPResponsePacket.ComposeOptions Options = HTTPResponsePacket.ComposeOptions.AllCalculateLength)
{
if (Response.Handled)
return;
Busy = true;
try
{
Response.Compose(Options);
base.Send(Response.Data);
// Refresh the current session
if (session != null)
session.Refresh();
}
catch
{
try
{
Close();// Server.CloseClient(Connection);
}
finally { }
}
finally
{
Busy = false;
}
}
public void CreateNewSession()
{
if (session == null)
{
// Create a new one
session = Server.CreateSession(Global.GenerateCode(12), 60 * 20);
HTTPResponsePacket.HTTPCookie cookie = new HTTPResponsePacket.HTTPCookie("SID", session.Id);
cookie.Expires = DateTime.MaxValue;
cookie.Path = "/";
cookie.HttpOnly = true;
Response.Cookies.Add(cookie);
}
}
public bool IsWebsocketRequest()
{
if (Request.Headers.ContainsKey("connection")
&& Request.Headers["connection"].ToLower().Contains("upgrade")
&& Request.Headers.ContainsKey("upgrade")
&& Request.Headers["upgrade"].ToLower() == "websocket"
&& Request.Headers.ContainsKey("Sec-WebSocket-Version")
&& Request.Headers["Sec-WebSocket-Version"] == "13"
&& Request.Headers.ContainsKey("Sec-WebSocket-Key"))
//&& Request.Headers.ContainsKey("Sec-WebSocket-Protocol"))
{
return true;
}
else
{
return false;
}
}
public void SendFile(string filename)
{
if (Response.Handled == true)
return;
try
{
//HTTP/1.1 200 OK
//Server: Microsoft-IIS/5.0
//Content-Location: http://127.0.0.1/index.html
//Date: Wed, 10 Dec 2003 19:10:25 GMT
//Content-Type: text/html
//Accept-Ranges: bytes
//Last-Modified: Mon, 22 Sep 2003 22:36:56 GMT
//Content-Length: 1957
if (!File.Exists(filename))
{
Response.Number = HTTPResponsePacket.ResponseCode.HTTP_NOTFOUND;
Send("File Not Found");//, SendOptions.AllCalculateLength);
return;
}
Busy = true;
System.DateTime FWD = File.GetLastWriteTime(filename);
if (Request.Headers.ContainsKey("if-modified-since"))// != DateTime.Parse("12:00:00 AM"))
{
try
{
DateTime IMS = DateTime.Parse(Request.Headers["if-modified-since"]);
if (FWD <= IMS)
{
Response.Number = HTTPResponsePacket.ResponseCode.HTTP_NOTMODIFIED;
Response.Text = "Not Modified";
}
}
catch
{
}
}
if (Response.Number == HTTPResponsePacket.ResponseCode.HTTP_NOTMODIFIED)
{
Send((byte[])null);
}
else
{
// Fri, 30 Oct 2007 14:19:41 GMT
Response.Headers["Last-Modified"] = FWD.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss");
FileInfo fi = new FileInfo(filename);
Response.Headers["Content-Length"] = fi.Length.ToString();
Send(HTTPResponsePacket.ComposeOptions.SpecifiedHeadersOnly);
using (var fs = new FileStream(filename, FileMode.Open))
{
var buffer = new byte[5000];
var offset = 0;
while (offset < fs.Length)
{
var n = fs.Read(buffer, offset, buffer.Length);
offset += n;
base.Send(buffer);
}
}
}
Busy = false;
return;
}
catch
{
Busy = false;
try
{
Close();
}
finally { }
}
}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net;
using System.Collections;
using System.Collections.Generic;
using Esiur.Data;
using Esiur.Engine;
using Esiur.Resource;
namespace Esiur.Net.HTTP
{
public abstract class HTTPFilter : IResource
{
public Instance Instance
{
get;
set;
}
public event DestroyedEvent OnDestroy;
public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
/*
public virtual void SessionModified(HTTPSession session, string key, object oldValue, object newValue)
{
}
public virtual void SessionExpired(HTTPSession session)
{
}
*/
public abstract bool Execute(HTTPConnection sender);
public virtual void ClientConnected(HTTPConnection HTTP)
{
//return false;
}
public virtual void ClientDisconnected(HTTPConnection HTTP)
{
//return false;
}
public void Destroy()
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,416 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net;
using System.Collections;
using System.Collections.Generic;
using Esiur.Net.Sockets;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Engine;
using Esiur.Net.Packets;
using System.Security.Cryptography.X509Certificates;
using Esiur.Resource;
namespace Esiur.Net.HTTP
{
public class HTTPServer : NetworkServer<HTTPConnection>, IResource
{
Dictionary<string, HTTPSession> sessions= new Dictionary<string, HTTPSession>();
public Instance Instance
{
get;
set;
}
[Storable]
string ip
{
get;
set;
}
[Storable]
ushort port
{
get;
set;
}
[Storable]
uint timeout
{
get;
set;
}
[Storable]
uint clock
{
get;
set;
}
[Storable]
uint maxPost
{
get;
set;
}
[Storable]
bool ssl
{
get;
set;
}
[Storable]
string certificate
{
get;
set;
}
//public override void ClientConnected(TClient Sender)
//{
//}
/*
public DStringDictionary Configurations
{
get { return config; }
}
*/
public enum ResponseCodes : int
{
HTTP_OK = 200,
HTTP_NOTFOUND = 404,
HTTP_SERVERERROR = 500,
HTTP_MOVED = 301,
HTTP_NOTMODIFIED = 304,
HTTP_REDIRECT = 307
}
public HTTPSession CreateSession(string id, int timeout)
{
var s = new HTTPSession();
s.Set(id, timeout);
sessions.Add(id, s);
return s;
}
/*
protected override void SessionModified(NetworkSession session, string key, object oldValue, object newValue)
{
foreach (var instance in Instance.Children)
{
var f = (HTTPFilter)instance;
f.SessionModified(session as HTTPSession, key, oldValue, newValue);
}
}
*/
//public override object InitializeLifetimeService()
//{
// return null;
//}
public static string MakeCookie(string Item, string Value, DateTime Expires, string Domain, string Path, bool HttpOnly)
{
//Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2001 21:00:00 GMT; domain=.com.au; path=/
//Set-Cookie: SessionID=another; expires=Fri, 29 Jun 2006 20:47:11 UTC; path=/
string Cookie = Item + "=" + Value;
if (Expires.Ticks != 0)
{
Cookie += "; expires=" + Expires.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss") + " GMT";
}
if (Domain != null)
{
Cookie += "; domain=" + Domain;
}
if (Path != null)
{
Cookie += "; path=" + Path;
}
if (HttpOnly)
{
Cookie += "; HttpOnly";
}
return Cookie;
}
protected override void ClientDisconnected(HTTPConnection sender)
{
Console.WriteLine("OUT: " + this.Connections.Count);
foreach (IResource resource in Instance.Children)
{
if (resource is HTTPFilter)
{
(resource as HTTPFilter).ClientDisconnected(sender);
}
}
}
protected override void DataReceived(HTTPConnection sender, NetworkBuffer data)
{
//Console.WriteLine(Data);
// Initialize a new session
//HTTPConnection HTTP = (HTTPConnection)sender.ExtraObject;
//string Data = System.Text.Encoding.Default.GetString(ReceivedData);
byte[] msg = data.Read();
var BL = sender.Parse(msg);
if (BL == 0)
{
if (sender.Request.Method == HTTPRequestPacket.HTTPMethod.UNKNOWN)
{
sender.Close();
return;
}
if (sender.Request.URL == "")
{
sender.Close();
return;
}
}
else if (BL == -1)
{
data.HoldForNextWrite(msg);
return;
}
else if (BL < 0)
{
data.HoldFor(msg, (uint) (msg.Length - BL));
return;
}
else if (BL > 0)
{
if (BL > maxPost)
{
sender.Send(
"<html><body>POST method content is larger than "
+ maxPost
+ " bytes.</body></html>");
sender.Close();
}
else
{
data.HoldFor(msg, (uint)(msg.Length + BL));
}
return;
}
else if (BL < 0) // for security
{
sender.Close();
return;
}
if (sender.IsWebsocketRequest() & !sender.WSMode)
{
sender.Upgrade();
//return;
}
//return;
try
{
foreach (IResource resource in Instance.Children)
{
if (resource is HTTPFilter)
{
if ((resource as HTTPFilter).Execute(sender))
return;
}
}
sender.Send("Bad Request");
sender.Close();
}
catch (Exception ex)
{
if (ex.Message != "Thread was being aborted.")
{
Global.Log("HTTPServer", LogType.Error, ex.ToString());
//Console.WriteLine(ex.ToString());
//EventLog.WriteEntry("HttpServer", ex.ToString(), EventLogEntryType.Error);
sender.Send(Return500(ex.Message));
}
}
}
private string Return500(string sMessage)
{
string sTMP = null;
sTMP = "<HTML><HEAD><TITLE>500 Internal Server Error</TITLE></HEAD><br>\r\n";
sTMP = sTMP + "<BODY BGCOLOR=" + (char)(34) + "#FFFFFF" + (char)(34) + " Text=" + (char)(34) + "#000000" + (char)(34) + " LINK=" + (char)(34) + "#0000FF" + (char)(34) + " VLINK=" + (char)(34) + "#000080" + (char)(34) + " ALINK=" + (char)(34) + "#008000" + (char)(34) + "><br>\r\n";
sTMP = sTMP + "<b>500</b> Sorry - Internal Server Error<br>" + sMessage + "\r\n";
sTMP = sTMP + "</BODY><br>\r\n";
sTMP = sTMP + "</HTML><br>\r\n";
return sTMP;
}
/*
protected override void SessionEnded(NetworkSession session)
{
// verify wether there are no active connections related to the session
foreach (HTTPConnection c in Connections)//.Values)
{
if (c.Session == session)
{
session.Refresh();
return;
}
}
foreach (Instance instance in Instance.Children)
{
var f = (HTTPFilter)instance.Resource;
f.SessionExpired((HTTPSession)session);
}
base.SessionEnded((HTTPSession)session);
//Sessions.Remove(Session.ID);
//Session.Dispose();
}
*/
/*
public int TTL
{
get
{
return Timeout;// mTimeout;
}
}
*/
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
if (trigger == ResourceTrigger.Initialize)
{
//var ip = (IPAddress)Instance.Attributes["ip"];
//var port = (int)Instance.Attributes["port"];
//var ssl = (bool)Instance.Attributes["ssl"];
//var cert = (string)Instance.Attributes["certificate"];
//if (ip == null) ip = IPAddress.Any;
ISocket listener;
IPAddress ipAdd;
if (ip == null)
ipAdd = IPAddress.Any;
else
ipAdd = IPAddress.Parse(ip);
// if (ssl)
// listener = new SSLSocket(new IPEndPoint(ipAdd, port), new X509Certificate2(certificate));
// else
listener = new TCPSocket(new IPEndPoint(ipAdd, port));
Start(listener,
timeout,
clock);
}
else if (trigger == ResourceTrigger.Terminate)
{
Stop();
}
else if (trigger == ResourceTrigger.SystemReload)
{
Trigger(ResourceTrigger.Terminate);
Trigger(ResourceTrigger.Initialize);
}
return new AsyncReply<bool>(true);
}
protected override void ClientConnected(HTTPConnection sender)
{
//sender.SessionModified += SessionModified;
//sender.SessionEnded += SessionExpired;
sender.SetParent(this);
Console.WriteLine("IN: " + this.Connections.Count);
foreach (IResource resource in Instance.Children)
{
if (resource is HTTPFilter)
{
(resource as HTTPFilter).ClientConnected(sender);
}
}
}
public void Destroy()
{
throw new NotImplementedException();
}
/*
public int LocalPort
{
get
{
return cServer.LocalPort;
}
}
*/
/*
public HTTPServer(int Port)
{
cServer = new TServer();
cServer.LocalPort = Port;
cServer.StartServer();
cServer.ClientConnected += new TServer.eClientConnected(ClientConnected);
cServer.ClientDisConnected += new TServer.eClientDisConnected(ClientDisConnected);
cServer.ClientIsSwitching += new TServer.eClientIsSwitching(ClientIsSwitching);
cServer.DataReceived += new TServer.eDataReceived(DataReceived);
}*/
//~HTTPServer()
//{
// cServer.StopServer();
//}
}
}

View File

@ -0,0 +1,106 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net;
using System.Collections;
using System.Collections.Generic;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Engine;
namespace Esiur.Net.HTTP
{
public class HTTPSession : IDestructible //<T> where T : TClient
{
public delegate void SessionModifiedEvent(HTTPSession session, string key, object oldValue, object newValue);
public delegate void SessionEndedEvent(HTTPSession session);
private string id;
private Timer timer;
private int timeout;
DateTime creation;
DateTime lastAction;
private KeyList<string, object> variables;
public event SessionEndedEvent OnEnd;
public event SessionModifiedEvent OnModify;
public event DestroyedEvent OnDestroy;
public KeyList<string, object> Variables
{
get { return variables; }
}
public HTTPSession()
{
variables = new KeyList<string, object>();
variables.OnModified += new KeyList<string, object>.Modified(VariablesModified);
creation = DateTime.Now;
}
internal void Set(string id, int timeout)
{
//modified = sessionModifiedEvent;
//ended = sessionEndEvent;
this.id = id;
if (this.timeout != 0)
{
this.timeout = timeout;
timer = new Timer(OnSessionEndTimerCallback, null, TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
creation = DateTime.Now;
}
}
private void OnSessionEndTimerCallback(object o)
{
OnEnd?.Invoke(this);
}
void VariablesModified(string key, object oldValue, object newValue)
{
OnModify?.Invoke(this, key, oldValue, newValue);
}
public void Destroy()
{
OnDestroy?.Invoke(this);
timer.Dispose();
timer = null;
}
internal void Refresh()
{
lastAction = DateTime.Now;
timer.Change(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
}
public int Timeout // Seconds
{
get
{
return timeout;
}
set
{
timeout = value;
Refresh();
}
}
public string Id
{
get { return id; }
}
public DateTime LastAction
{
get { return lastAction; }
}
}
}

51
Esiur/Net/HTTP/IIPoWS.cs Normal file
View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Esiur.Resource;
using Esiur.Net.IIP;
using Esiur.Net.Sockets;
using Esiur.Engine;
namespace Esiur.Net.HTTP
{
public class IIPoWS: HTTPFilter
{
public override bool Execute(HTTPConnection sender)
{
if (sender.Request.Filename.StartsWith("/iip/"))
{
// find the service
var path = sender.Request.Filename.Substring(5);// sender.Request.Query["path"];
Warehouse.Get(path).Then((r) =>
{
if (r is DistributedServer)
{
var httpServer = sender.Parent;
var iipServer = r as DistributedServer;
var tcpSocket = sender.Unassign();
var wsSocket = new WSSocket(tcpSocket);
httpServer.Connections.Remove(sender);
var iipConnection = new DistributedConnection();
iipConnection.Server = iipServer;
iipConnection.Assign(wsSocket);
}
});
return true;
}
return false;
}
public override AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
return new AsyncReply<bool>(true);
}
}
}

View File

@ -0,0 +1,514 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using Esiur.Net.Sockets;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Engine;
using Esiur.Net.Packets;
using Esiur.Resource;
using Esiur.Security.Authority;
using Esiur.Resource.Template;
using System.Linq;
using System.Diagnostics;
namespace Esiur.Net.IIP
{
public partial class DistributedConnection : NetworkConnection, IStore
{
public delegate void ReadyEvent(DistributedConnection sender);
public delegate void ErrorEvent(DistributedConnection sender, byte errorCode, string errorMessage);
/// <summary>
/// Ready event is raised when the connection is fully established.
/// </summary>
public event ReadyEvent OnReady;
/// <summary>
/// Error event
/// </summary>
public event ErrorEvent OnError;
IIPPacket packet = new IIPPacket();
IIPAuthPacket authPacket = new IIPAuthPacket();
byte[] sessionId;
AuthenticationType hostType;
string domain;
string localUsername, remoteUsername;
byte[] localPassword;
byte[] localNonce, remoteNonce;
bool ready, readyToEstablish;
DateTime loginDate;
KeyList<string, object> variables = new KeyList<string, object>();
/// <summary>
/// Local username to authenticate ourselves.
/// </summary>
public string LocalUsername { get; set; }
/// <summary>
/// Peer's username.
/// </summary>
public string RemoteUsername { get; set; }
/// <summary>
/// Working domain.
/// </summary>
public string Domain { get { return domain; } }
/// <summary>
/// Distributed server responsible for this connection, usually for incoming connections.
/// </summary>
public DistributedServer Server
{
get;
set;
}
/// <summary>
/// Send data to the other end as parameters
/// </summary>
/// <param name="values">Values will be converted to bytes then sent.</param>
internal void SendParams(params object[] values)
{
var ar = BinaryList.ToBytes(values);
Send(ar);
//StackTrace stackTrace = new StackTrace(;
// Get calling method name
//Console.WriteLine("TX " + hostType + " " + ar.Length + " " + stackTrace.GetFrame(1).GetMethod().ToString());
}
/// <summary>
/// Send raw data through the connection.
/// </summary>
/// <param name="data">Data to send.</param>
public override void Send(byte[] data)
{
//Console.WriteLine("Client: {0}", Data.Length);
Global.Counters["IIP Sent Packets"]++;
base.Send(data);
}
/// <summary>
/// KeyList to store user variables related to this connection.
/// </summary>
public KeyList<string, object> Variables
{
get
{
return variables;
}
}
/// <summary>
/// IResource interface.
/// </summary>
public Instance Instance
{
get;
set;
}
/// <summary>
/// Assign a socket to the connection.
/// </summary>
/// <param name="socket">Any socket that implements ISocket.</param>
public override void Assign(ISocket socket)
{
base.Assign(socket);
if (hostType == AuthenticationType.Client)
{
// declare (Credentials -> No Auth, No Enctypt)
var un = DC.ToBytes(localUsername);
var dmn = DC.ToBytes(domain);
if (socket.State == SocketState.Established)
{
SendParams((byte)0x60, (byte)dmn.Length, dmn, localNonce, (byte)un.Length, un);
}
else
{
socket.OnConnect += () =>
{ // declare (Credentials -> No Auth, No Enctypt)
SendParams((byte)0x60, (byte)dmn.Length, dmn, localNonce, (byte)un.Length, un);
};
}
}
}
/// <summary>
/// Create a new distributed connection.
/// </summary>
/// <param name="socket">Socket to transfer data through.</param>
/// <param name="domain">Working domain.</param>
/// <param name="username">Username.</param>
/// <param name="password">Password.</param>
public DistributedConnection(ISocket socket, string domain, string username, string password)
{
//Instance.Name = Global.GenerateCode(12);
this.hostType = AuthenticationType.Client;
this.domain = domain;
this.localUsername = username;
this.localPassword = DC.ToBytes(password);
init();
Assign(socket);
}
/// <summary>
/// Create a new instance of a distributed connection
/// </summary>
public DistributedConnection()
{
//myId = Global.GenerateCode(12);
// localParams.Host = DistributedParameters.HostType.Host;
init();
}
public string Link(IResource resource)
{
if (resource is DistributedConnection)
{
var r = resource as DistributedResource;
if (r.Instance.Store == this)
return this.Instance.Name + "/" + r.Id;
}
return null;
}
void init()
{
queue.Then((x) =>
{
if (x.Type == DistributedResourceQueueItem.DistributedResourceQueueItemType.Event)
x.Resource._EmitEventByIndex(x.Index, (object[])x.Value);
else
x.Resource.UpdatePropertyByIndex(x.Index, x.Value);
});
var r = new Random();
localNonce = new byte[32];
r.NextBytes(localNonce);
}
protected override void DataReceived(NetworkBuffer data)
{
// Console.WriteLine("DR " + hostType + " " + data.Available + " " + RemoteEndPoint.ToString());
var msg = data.Read();
uint offset = 0;
uint ends = (uint)msg.Length;
while (offset < ends)
{
if (ready)
{
var rt = packet.Parse(msg, offset, ends);
if (rt <= 0)
{
data.HoldFor(msg, offset, ends - offset, (uint)(-rt));
return;
}
else
{
offset += (uint)rt;
if (packet.Command == IIPPacket.IIPPacketCommand.Event)
{
switch (packet.Event)
{
case IIPPacket.IIPPacketEvent.ResourceReassigned:
IIPEventResourceReassigned(packet.ResourceId, packet.NewResourceId);
break;
case IIPPacket.IIPPacketEvent.ResourceDestroyed:
IIPEventResourceDestroyed(packet.ResourceId);
break;
case IIPPacket.IIPPacketEvent.PropertyUpdated:
IIPEventPropertyUpdated(packet.ResourceId, packet.MethodIndex, packet.Content);
break;
case IIPPacket.IIPPacketEvent.EventOccured:
IIPEventEventOccured(packet.ResourceId, packet.MethodIndex, packet.Content);
break;
}
}
else if (packet.Command == IIPPacket.IIPPacketCommand.Request)
{
switch (packet.Action)
{
case IIPPacket.IIPPacketAction.AttachResource:
IIPRequestAttachResource(packet.CallbackId, packet.ResourceId);
break;
case IIPPacket.IIPPacketAction.ReattachResource:
IIPRequestReattachResource(packet.CallbackId, packet.ResourceId, packet.ResourceAge);
break;
case IIPPacket.IIPPacketAction.DetachResource:
IIPRequestDetachResource(packet.CallbackId, packet.ResourceId);
break;
case IIPPacket.IIPPacketAction.CreateResource:
IIPRequestCreateResource(packet.CallbackId, packet.ClassName);
break;
case IIPPacket.IIPPacketAction.DeleteResource:
IIPRequestDeleteResource(packet.CallbackId, packet.ResourceId);
break;
case IIPPacket.IIPPacketAction.TemplateFromClassName:
IIPRequestTemplateFromClassName(packet.CallbackId, packet.ClassName);
break;
case IIPPacket.IIPPacketAction.TemplateFromClassId:
IIPRequestTemplateFromClassId(packet.CallbackId, packet.ClassId);
break;
case IIPPacket.IIPPacketAction.TemplateFromResourceLink:
IIPRequestTemplateFromResourceLink(packet.CallbackId, packet.ResourceLink);
break;
case IIPPacket.IIPPacketAction.TemplateFromResourceId:
IIPRequestTemplateFromResourceId(packet.CallbackId, packet.ResourceId);
break;
case IIPPacket.IIPPacketAction.ResourceIdFromResourceLink:
IIPRequestResourceIdFromResourceLink(packet.CallbackId, packet.ResourceLink);
break;
case IIPPacket.IIPPacketAction.InvokeFunction:
IIPRequestInvokeFunction(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.Content);
break;
case IIPPacket.IIPPacketAction.GetProperty:
IIPRequestGetProperty(packet.CallbackId, packet.ResourceId, packet.MethodIndex);
break;
case IIPPacket.IIPPacketAction.GetPropertyIfModified:
IIPRequestGetPropertyIfModifiedSince(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.ResourceAge);
break;
case IIPPacket.IIPPacketAction.SetProperty:
IIPRequestSetProperty(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.Content);
break;
}
}
else if (packet.Command == IIPPacket.IIPPacketCommand.Reply)
{
switch (packet.Action)
{
case IIPPacket.IIPPacketAction.AttachResource:
IIPReply(packet.CallbackId, packet.ClassId, packet.ResourceAge, packet.ResourceLink, packet.Content);
//IIPReplyAttachResource(packet.CallbackId, packet.ResourceAge, Codec.ParseValues(packet.Content));
break;
case IIPPacket.IIPPacketAction.ReattachResource:
//IIPReplyReattachResource(packet.CallbackId, packet.ResourceAge, Codec.ParseValues(packet.Content));
IIPReply(packet.CallbackId, packet.ResourceAge, packet.Content);
break;
case IIPPacket.IIPPacketAction.DetachResource:
//IIPReplyDetachResource(packet.CallbackId);
IIPReply(packet.CallbackId);
break;
case IIPPacket.IIPPacketAction.CreateResource:
//IIPReplyCreateResource(packet.CallbackId, packet.ClassId, packet.ResourceId);
IIPReply(packet.CallbackId, packet.ClassId, packet.ResourceId);
break;
case IIPPacket.IIPPacketAction.DeleteResource:
//IIPReplyDeleteResource(packet.CallbackId);
IIPReply(packet.CallbackId);
break;
case IIPPacket.IIPPacketAction.TemplateFromClassName:
//IIPReplyTemplateFromClassName(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
IIPReply(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
break;
case IIPPacket.IIPPacketAction.TemplateFromClassId:
//IIPReplyTemplateFromClassId(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
IIPReply(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
break;
case IIPPacket.IIPPacketAction.TemplateFromResourceLink:
//IIPReplyTemplateFromResourceLink(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
IIPReply(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
break;
case IIPPacket.IIPPacketAction.TemplateFromResourceId:
//IIPReplyTemplateFromResourceId(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
IIPReply(packet.CallbackId, ResourceTemplate.Parse(packet.Content));
break;
case IIPPacket.IIPPacketAction.ResourceIdFromResourceLink:
//IIPReplyResourceIdFromResourceLink(packet.CallbackId, packet.ClassId, packet.ResourceId, packet.ResourceAge);
IIPReply(packet.CallbackId, packet.ClassId, packet.ResourceId, packet.ResourceAge);
break;
case IIPPacket.IIPPacketAction.InvokeFunction:
//IIPReplyInvokeFunction(packet.CallbackId, Codec.Parse(packet.Content, 0));
IIPReply(packet.CallbackId, packet.Content);
break;
case IIPPacket.IIPPacketAction.GetProperty:
//IIPReplyGetProperty(packet.CallbackId, Codec.Parse(packet.Content, 0));
IIPReply(packet.CallbackId, packet.Content);
break;
case IIPPacket.IIPPacketAction.GetPropertyIfModified:
//IIPReplyGetPropertyIfModifiedSince(packet.CallbackId, Codec.Parse(packet.Content, 0));
IIPReply(packet.CallbackId, packet.Content);
break;
case IIPPacket.IIPPacketAction.SetProperty:
//IIPReplySetProperty(packet.CallbackId);
IIPReply(packet.CallbackId);
break;
}
}
}
}
else
{
var rt = authPacket.Parse(msg, offset, ends);
Console.WriteLine(hostType.ToString() + " " + offset + " " + ends + " " + rt + " " + authPacket.ToString());
if (rt <= 0)
{
data.HoldFor(msg, ends + (uint)(-rt));
return;
}
else
{
offset += (uint)rt;
if (hostType == AuthenticationType.Host)
{
if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Declare)
{
if (authPacket.RemoteMethod == IIPAuthPacket.IIPAuthPacketMethod.Credentials && authPacket.LocalMethod == IIPAuthPacket.IIPAuthPacketMethod.None)
{
remoteUsername = authPacket.RemoteUsername;
remoteNonce = authPacket.RemoteNonce;
domain = authPacket.Domain;
SendParams((byte)0xa0, localNonce);
}
}
else if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Action)
{
if (authPacket.Action == IIPAuthPacket.IIPAuthPacketAction.AuthenticateHash)
{
var remoteHash = authPacket.Hash;
Server.Membership.GetPassword(remoteUsername, domain).Then((pw) =>
{
if (pw != null)
{
var hashFunc = SHA256.Create();
var hash = hashFunc.ComputeHash(BinaryList.ToBytes(pw, remoteNonce, localNonce));
if (hash.SequenceEqual(remoteHash))
{
// send our hash
var localHash = hashFunc.ComputeHash(BinaryList.ToBytes(localNonce, remoteNonce, pw));
SendParams((byte)0, localHash);
readyToEstablish = true;
}
else
{
Console.WriteLine("Incorrect password");
SendParams((byte)0xc0, (byte)1, (ushort)5, DC.ToBytes("Error"));
}
}
});
}
else if (authPacket.Action == IIPAuthPacket.IIPAuthPacketAction.NewConnection)
{
if (readyToEstablish)
{
var r = new Random();
sessionId = new byte[32];
r.NextBytes(sessionId);
SendParams((byte)0x28, sessionId);
ready = true;
OnReady?.Invoke(this);
}
}
}
}
else if (hostType == AuthenticationType.Client)
{
if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Acknowledge)
{
remoteNonce = authPacket.RemoteNonce;
// send our hash
var hashFunc = SHA256.Create();
var localHash = hashFunc.ComputeHash(BinaryList.ToBytes(localPassword, localNonce, remoteNonce));
SendParams((byte)0, localHash);
}
else if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Action)
{
if (authPacket.Action == IIPAuthPacket.IIPAuthPacketAction.AuthenticateHash)
{
// check if the server knows my password
var hashFunc = SHA256.Create();
var remoteHash = hashFunc.ComputeHash(BinaryList.ToBytes(remoteNonce, localNonce, localPassword));
if (remoteHash.SequenceEqual(authPacket.Hash))
{
// send establish request
SendParams((byte)0x20, (ushort)0);
}
else
{
SendParams((byte)0xc0, 1, (ushort)5, DC.ToBytes("Error"));
}
}
else if (authPacket.Action == IIPAuthPacket.IIPAuthPacketAction.ConnectionEstablished)
{
sessionId = authPacket.SessionId;
ready = true;
OnReady?.Invoke(this);
}
}
else if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Error)
{
OnError?.Invoke(this, authPacket.ErrorCode, authPacket.ErrorMessage);
Close();
}
}
}
}
}
}
/// <summary>
/// Resource interface
/// </summary>
/// <param name="trigger">Resource trigger.</param>
/// <returns></returns>
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
return new AsyncReply<bool>();
}
/// <summary>
/// Store interface.
/// </summary>
/// <param name="resource">Resource.</param>
/// <returns></returns>
public bool Put(IResource resource)
{
resources.Add(Convert.ToUInt32(resource.Instance.Name), (DistributedResource)resource);
return true;
}
}
}

View File

@ -0,0 +1,802 @@
using Esiur.Data;
using Esiur.Engine;
using Esiur.Net.Packets;
using Esiur.Resource;
using Esiur.Resource.Template;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Net.IIP
{
partial class DistributedConnection
{
KeyList<uint, DistributedResource> resources = new KeyList<uint, DistributedResource>();
KeyList<uint, AsyncReply<DistributedResource>> resourceRequests = new KeyList<uint, AsyncReply<DistributedResource>>();
KeyList<Guid, AsyncReply<ResourceTemplate>> templateRequests = new KeyList<Guid, AsyncReply<ResourceTemplate>>();
KeyList<string, AsyncReply<IResource>> pathRequests = new KeyList<string, AsyncReply<IResource>>();
Dictionary<Guid, ResourceTemplate> templates = new Dictionary<Guid, ResourceTemplate>();
KeyList<uint, AsyncReply<object[]>> requests = new KeyList<uint, AsyncReply<object[]>>();
uint callbackCounter = 0;
AsyncQueue<DistributedResourceQueueItem> queue = new AsyncQueue<DistributedResourceQueueItem>();
/// <summary>
/// Send IIP request.
/// </summary>
/// <param name="action">Packet action.</param>
/// <param name="args">Arguments to send.</param>
/// <returns></returns>
internal AsyncReply<object[]> SendRequest(IIPPacket.IIPPacketAction action, params object[] args)
{
var reply = new AsyncReply<object[]>();
callbackCounter++;
var bl = new BinaryList((byte)(0x40 | (byte)action), callbackCounter);
bl.AddRange(args);
Send(bl.ToArray());
requests.Add(callbackCounter, reply);
return reply;
}
void IIPReply(uint callbackId, params object[] results)
{
var req = requests.Take(callbackId);
req?.Trigger(results);
}
void IIPEventResourceReassigned(uint resourceId, uint newResourceId)
{
}
void IIPEventResourceDestroyed(uint resourceId)
{
if (resources.Contains(resourceId))
{
var r = resources[resourceId];
resources.Remove(resourceId);
r.Destroy();
}
}
void IIPEventPropertyUpdated(uint resourceId, byte index, byte[] content)
{
if (resources.Contains(resourceId))
{
// push to the queue to gaurantee serialization
var reply = new AsyncReply<DistributedResourceQueueItem>();
queue.Add(reply);
var r = resources[resourceId];
Codec.Parse(content, 0, this).Then((arguments) =>
{
var pt = r.Template.GetPropertyTemplate(index);
if (pt != null)
{
reply.Trigger(new DistributedResourceQueueItem((DistributedResource)r, DistributedResourceQueueItem.DistributedResourceQueueItemType.Propery, arguments, index));
}
else
{ // ft found, fi not found, this should never happen
queue.Remove(reply);
}
});
}
}
void IIPEventEventOccured(uint resourceId, byte index, byte[] content)
{
if (resources.Contains(resourceId))
{
// push to the queue to gaurantee serialization
var reply = new AsyncReply<DistributedResourceQueueItem>();
var r = resources[resourceId];
queue.Add(reply);
Codec.ParseVarArray(content, this).Then((arguments) =>
{
var et = r.Template.GetEventTemplate(index);
if (et != null)
{
reply.Trigger(new DistributedResourceQueueItem((DistributedResource)r, DistributedResourceQueueItem.DistributedResourceQueueItemType.Event, arguments, index));
}
else
{ // ft found, fi not found, this should never happen
queue.Remove(reply);
}
});
}
}
void IIPRequestAttachResource(uint callback, uint resourceId)
{
Warehouse.Get(resourceId).Then((res) =>
{
if (res != null)
{
var r = res as IResource;
r.Instance.ResourceEventOccured += Instance_EventOccured;
r.Instance.ResourceModified += Instance_PropertyModified;
r.Instance.ResourceDestroyed += Instance_ResourceDestroyed;
var link = DC.ToBytes(r.Instance.Link);
// reply ok
SendParams((byte)0x80, callback, r.Instance.Template.ClassId, r.Instance.Age, (ushort)link.Length, link, Codec.ComposeVarArray(r.Instance.Serialize(), this, true));
}
else
{
// reply failed
//SendParams(0x80, r.Instance.Id, r.Instance.Age, r.Instance.Serialize(false, this));
}
});
}
void IIPRequestReattachResource(uint callback, uint resourceId, uint resourceAge)
{
Warehouse.Get(resourceId).Then((res) =>
{
if (res != null)
{
var r = res as IResource;
r.Instance.ResourceEventOccured += Instance_EventOccured;
r.Instance.ResourceModified += Instance_PropertyModified;
r.Instance.ResourceDestroyed += Instance_ResourceDestroyed;
// reply ok
SendParams((byte)0x81, callback, r.Instance.Age, Codec.ComposeVarArray(r.Instance.Serialize(), this, true));
}
else
{
// reply failed
}
});
}
void IIPRequestDetachResource(uint callback, uint resourceId)
{
Warehouse.Get(resourceId).Then((res) =>
{
if (res != null)
{
var r = res as IResource;
r.Instance.ResourceEventOccured -= Instance_EventOccured;
r.Instance.ResourceModified -= Instance_PropertyModified;
r.Instance.ResourceDestroyed -= Instance_ResourceDestroyed;
// reply ok
SendParams((byte)0x82, callback);
}
else
{
// reply failed
}
});
}
void IIPRequestCreateResource(uint callback, string className)
{
// not implemented
}
void IIPRequestDeleteResource(uint callback, uint resourceId)
{
// not implemented
}
void IIPRequestTemplateFromClassName(uint callback, string className)
{
Warehouse.GetTemplate(className).Then((t) =>
{
if (t != null)
SendParams((byte)0x88, callback, t.Content);
else
{
// reply failed
}
});
}
void IIPRequestTemplateFromClassId(uint callback, Guid classId)
{
Warehouse.GetTemplate(classId).Then((t) =>
{
if (t != null)
SendParams((byte)0x89, callback, (uint)t.Content.Length, t.Content);
else
{
// reply failed
}
});
}
void IIPRequestTemplateFromResourceLink(uint callback, string resourceLink)
{
Warehouse.GetTemplate(resourceLink).Then((t) =>
{
if (t != null)
SendParams((byte)0x8A, callback, t.Content);
else
{
// reply failed
}
});
}
void IIPRequestTemplateFromResourceId(uint callback, uint resourceId)
{
Warehouse.Get(resourceId).Then((r) =>
{
if (r != null)
SendParams((byte)0x8B, callback, r.Instance.Template.Content);
else
{
// reply failed
}
});
}
void IIPRequestResourceIdFromResourceLink(uint callback, string resourceLink)
{
Warehouse.Get(resourceLink).Then((r) =>
{
if (r != null)
SendParams((byte)0x8C, callback, r.Instance.Template.ClassId, r.Instance.Id, r.Instance.Age);
else
{
// reply failed
}
});
}
void IIPRequestInvokeFunction(uint callback, uint resourceId, byte index, byte[] content)
{
Warehouse.Get(resourceId).Then((r) =>
{
if (r != null)
{
Codec.ParseVarArray(content, this).Then(async (arguments) =>
{
var ft = r.Instance.Template.GetFunctionTemplate(index);
if (ft != null)
{
if (r is DistributedResource)
{
var rt = (r as DistributedResource)._Invoke(index, arguments);
if (rt != null)
{
rt.Then(res =>
{
SendParams((byte)0x90, callback, Codec.Compose(res, this));
});
}
else
{
// function not found on a distributed object
}
}
else
{
#if NETSTANDARD1_5
var fi = r.GetType().GetTypeInfo().GetMethod(ft.Name);
#else
var fi = r.GetType().GetMethod(ft.Name);
#endif
if (fi != null)
{
// cast shit
ParameterInfo[] pi = fi.GetParameters();
object[] args = null;
if (pi.Length > 0)
{
int argsCount = pi.Length;
args = new object[pi.Length];
if (pi[pi.Length - 1].ParameterType == typeof(DistributedConnection))
{
args[--argsCount] = this;
}
if (arguments != null)
{
for (int i = 0; i < argsCount && i < arguments.Length; i++)
{
args[i] = DC.CastConvert(arguments[i], pi[i].ParameterType);
}
}
}
var rt = fi.Invoke(r, args);
if (rt is Task)
{
var t = (Task)rt;
//Console.WriteLine(t.IsCompleted);
await t;
#if NETSTANDARD1_5
var res = t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t);
#else
var res = t.GetType().GetProperty("Result").GetValue(t);
#endif
SendParams((byte)0x90, callback, Codec.Compose(res, this));
}
else if (rt is AsyncReply) //(rt.GetType().IsGenericType && (rt.GetType().GetGenericTypeDefinition() == typeof(AsyncReply<>)))
{
(rt as AsyncReply).Then(res =>
{
SendParams((byte)0x90, callback, Codec.Compose(res, this));
});
}
else
{
SendParams((byte)0x90, callback, Codec.Compose(rt, this));
}
}
else
{
// ft found, fi not found, this should never happen
}
}
}
else
{
// no function at this index
}
});
}
else
{
// no resource with this id
}
});
}
void IIPRequestGetProperty(uint callback, uint resourceId, byte index)
{
Warehouse.Get(resourceId).Then((r) =>
{
if (r != null)
{
var pt = r.Instance.Template.GetFunctionTemplate(index);
if (pt != null)
{
if (r is DistributedResource)
{
SendParams((byte)0x91, callback, Codec.Compose((r as DistributedResource)._Get(pt.Index), this));
}
else
{
#if NETSTANDARD1_5
var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name);
#else
var pi = r.GetType().GetProperty(pt.Name);
#endif
if (pi != null)
{
SendParams((byte)0x91, callback, Codec.Compose(pi.GetValue(r), this));
}
else
{
// pt found, pi not found, this should never happen
}
}
}
else
{
// pt not found
}
}
else
{
// resource not found
}
});
}
void IIPRequestGetPropertyIfModifiedSince(uint callback, uint resourceId, byte index, uint age)
{
Warehouse.Get(resourceId).Then((r) =>
{
if (r != null)
{
var pt = r.Instance.Template.GetFunctionTemplate(index);
if (pt != null)
{
if (r.Instance.GetAge(index) > age)
{
#if NETSTANDARD1_5
var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name);
#else
var pi = r.GetType().GetProperty(pt.Name);
#endif
if (pi != null)
{
SendParams((byte)0x92, callback, Codec.Compose(pi.GetValue(r), this));
}
else
{
// pt found, pi not found, this should never happen
}
}
else
{
SendParams((byte)0x92, callback, (byte)DataType.NotModified);
}
}
else
{
// pt not found
}
}
else
{
// resource not found
}
});
}
void IIPRequestSetProperty(uint callback, uint resourceId, byte index, byte[] content)
{
Warehouse.Get(resourceId).Then((r) =>
{
if (r != null)
{
var pt = r.Instance.Template.GetPropertyTemplate(index);
if (pt != null)
{
Codec.Parse(content, 0, this).Then((value) =>
{
if (r is DistributedResource)
{
// propagation
(r as DistributedResource)._Set(index, value).Then((x) =>
{
SendParams((byte)0x93, callback);
});
}
else
{
#if NETSTANDARD1_5
var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name);
#else
var pi = r.GetType().GetProperty(pt.Name);
#endif
if (pi != null)
{
// cast new value type to property type
var v = DC.CastConvert(value, pi.PropertyType);
pi.SetValue(r, v);
SendParams((byte)0x93, callback);
}
else
{
// pt found, pi not found, this should never happen
}
}
});
}
else
{
// property not found
}
}
else
{
// resource not found
}
});
}
/*
void IIPReplyAttachResource(uint callback, uint resourceAge, object[] properties)
{
if (requests.ContainsKey(callback))
{
var req = requests[callback];
var r = resources[(uint)req.Arguments[0]];
if (r == null)
{
r.Instance.Deserialize(properties);
r.Instance.Age = resourceAge;
r.Attached();
// process stack
foreach (var rr in resources.Values)
rr.Stack.ProcessStack();
}
else
{
// resource not found
}
}
}
void IIPReplyReattachResource(uint callback, uint resourceAge, object[] properties)
{
var req = requests.Take(callback);
if (req != null)
{
var r = resources[(uint)req.Arguments[0]];
if (r == null)
{
r.Instance.Deserialize(properties);
r.Instance.Age = resourceAge;
r.Attached();
// process stack
foreach (var rr in resources.Values)
rr.Stack.ProcessStack();
}
else
{
// resource not found
}
}
}
void IIPReplyDetachResource(uint callback)
{
var req = requests.Take(callback);
// nothing to do
}
void IIPReplyCreateResource(uint callback, Guid classId, uint resourceId)
{
var req = requests.Take(callback);
// nothing to do
}
void IIPReplyDeleteResource(uint callback)
{
var req = requests.Take(callback);
// nothing to do
}
void IIPReplyTemplateFromClassName(uint callback, ResourceTemplate template)
{
// cache
if (!templates.ContainsKey(template.ClassId))
templates.Add(template.ClassId, template);
var req = requests.Take(callback);
req?.Trigger(template);
}
void IIPReplyTemplateFromClassId(uint callback, ResourceTemplate template)
{
// cache
if (!templates.ContainsKey(template.ClassId))
templates.Add(template.ClassId, template);
var req = requests.Take(callback);
req?.Trigger(template);
}
void IIPReplyTemplateFromResourceLink(uint callback, ResourceTemplate template)
{
// cache
if (!templates.ContainsKey(template.ClassId))
templates.Add(template.ClassId, template);
var req = requests.Take(callback);
req?.Trigger(template);
}
void IIPReplyTemplateFromResourceId(uint callback, ResourceTemplate template)
{
// cache
if (!templates.ContainsKey(template.ClassId))
templates.Add(template.ClassId, template);
var req = requests.Take(callback);
req?.Trigger(template);
}
void IIPReplyResourceIdFromResourceLink(uint callback, Guid classId, uint resourceId, uint resourceAge)
{
var req = requests.Take(callback);
req?.Trigger(template);
}
void IIPReplyInvokeFunction(uint callback, object returnValue)
{
}
void IIPReplyGetProperty(uint callback, object value)
{
}
void IIPReplyGetPropertyIfModifiedSince(uint callback, object value)
{
}
void IIPReplySetProperty(uint callback)
{
}
*/
/// <summary>
/// Get the ResourceTemplate for a given class Id.
/// </summary>
/// <param name="classId">Class GUID.</param>
/// <returns>ResourceTemplate.</returns>
public AsyncReply<ResourceTemplate> GetTemplate(Guid classId)
{
if (templates.ContainsKey(classId))
return new AsyncReply<ResourceTemplate>(templates[classId]);
else if (templateRequests.ContainsKey(classId))
return templateRequests[classId];
var reply = new AsyncReply<ResourceTemplate>();
templateRequests.Add(classId, reply);
SendRequest(IIPPacket.IIPPacketAction.TemplateFromClassId, classId).Then((rt) =>
{
templateRequests.Remove(classId);
templates.Add(((ResourceTemplate)rt[0]).ClassId, (ResourceTemplate)rt[0]);
reply.Trigger(rt[0]);
});
return reply;
}
// IStore interface
/// <summary>
/// Get a resource by its path.
/// </summary>
/// <param name="path">Path to the resource.</param>
/// <returns>Resource</returns>
public AsyncReply<IResource> Get(string path)
{
if (pathRequests.ContainsKey(path))
return pathRequests[path];
var reply = new AsyncReply<IResource>();
pathRequests.Add(path, reply);
var bl = new BinaryList(path);
bl.Insert(0, (ushort)bl.Length);
SendRequest(IIPPacket.IIPPacketAction.ResourceIdFromResourceLink, bl.ToArray()).Then((rt) =>
{
pathRequests.Remove(path);
//(Guid)rt[0],
Fetch( (uint)rt[1]).Then((r) =>
{
reply.Trigger(r);
});
});
return reply;
}
/// <summary>
/// Retrive a resource by its instance Id.
/// </summary>
/// <param name="iid">Instance Id</param>
/// <returns>Resource</returns>
public AsyncReply<IResource> Retrieve(uint iid)
{
foreach (var r in resources.Values)
if (r.Instance.Id == iid)
return new AsyncReply<IResource>(r);
return new AsyncReply<IResource>(null);
}
/// <summary>
/// Fetch a resource from the other end
/// </summary>
/// <param name="classId">Class GUID</param>
/// <param name="id">Resource Id</param>Guid classId
/// <returns>DistributedResource</returns>
public AsyncReply<DistributedResource> Fetch( uint id)
{
if (resourceRequests.ContainsKey(id) && resources.ContainsKey(id))
{
// dig for dead locks
return resourceRequests[id];
}
else if (resourceRequests.ContainsKey(id))
return resourceRequests[id];
else if (resources.ContainsKey(id))
return new AsyncReply<DistributedResource>(resources[id]);
var reply = new AsyncReply<DistributedResource>();
SendRequest(IIPPacket.IIPPacketAction.AttachResource, id).Then((rt) =>
{
GetTemplate((Guid)rt[0]).Then((tmp) =>
{
// ClassId, ResourceAge, ResourceLink, Content
//var dr = Warehouse.New<DistributedResource>(id.ToString(), this);
//var dr = nInitialize(this, tmp, id, (uint)rt[0]);
var dr = new DistributedResource(this, tmp, id, (uint)rt[1], (string)rt[2]);
Warehouse.Put(dr, id.ToString(), this);
Codec.ParseVarArray((byte[])rt[3], this).Then((ar) =>
{
dr._Attached(ar);
resourceRequests.Remove(id);
reply.Trigger(dr);
});
});
});
return reply;
}
private void Instance_ResourceDestroyed(IResource resource)
{
// compose the packet
SendParams((byte)0x1, resource.Instance.Id);
}
private void Instance_PropertyModified(IResource resource, string name, object newValue, object oldValue)
{
var pt = resource.Instance.Template.GetPropertyTemplate(name);
if (pt == null)
return;
// compose the packet
if (newValue is Func<DistributedConnection, object>)
SendParams((byte)0x10, resource.Instance.Id, pt.Index, Codec.Compose((newValue as Func<DistributedConnection, object>)(this), this));
else
SendParams((byte)0x10, resource.Instance.Id, pt.Index, Codec.Compose(newValue, this));
}
private void Instance_EventOccured(IResource resource, string name, string[] receivers, object[] args)
{
var et = resource.Instance.Template.GetEventTemplate(name);
if (et == null)
return;
if (receivers != null)
if (!receivers.Contains(RemoteUsername))
return;
var clientArgs = new object[args.Length];
for (var i = 0; i < args.Length; i++)
if (args[i] is Func<DistributedConnection, object>)
clientArgs[i] = (args[i] as Func<DistributedConnection, object>)(this);
else
clientArgs[i] = args[i];
// compose the packet
SendParams((byte)0x11, resource.Instance.Id, (byte)et.Index, Codec.ComposeVarArray(args, this, true));
}
}
}

View File

@ -0,0 +1,374 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.IO;
using System.Collections;
using System.ComponentModel;
using Esiur.Misc;
using Esiur.Data;
using System.Dynamic;
using System.Security.Cryptography;
using Esiur.Engine;
using System.Runtime.CompilerServices;
using System.Reflection.Emit;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Esiur.Resource;
using Esiur.Resource.Template;
namespace Esiur.Net.IIP
{
//[System.Runtime.InteropServices.ComVisible(true)]
public class DistributedResource : DynamicObject, IResource
{
/// <summary>
/// Raised when the distributed resource is destroyed.
/// </summary>
public event DestroyedEvent OnDestroy;
uint instanceId;
DistributedConnection connection;
bool isAttached = false;
bool isReady = false;
//Structure properties = new Structure();
string link;
uint age;
uint[] ages;
object[] properties;
DistributedResourceEvent[] events;
ResourceTemplate template;
//DistributedResourceStack stack;
bool destroyed;
/// <summary>
/// Resource template for the remotely located resource.
/// </summary>
public ResourceTemplate Template
{
get { return template; }
}
/// <summary>
/// Connection responsible for the distributed resource.
/// </summary>
public DistributedConnection Connection
{
get { return connection; }
}
/// <summary>
/// Resource link
/// </summary>
public string Link
{
get { return link; }
}
/// <summary>
/// Instance Id given by the other end.
/// </summary>
public uint Id
{
get { return instanceId; }
}
/// <summary>
/// IDestructible interface.
/// </summary>
public void Destroy()
{
destroyed = true;
OnDestroy?.Invoke(this);
}
/// <summary>
/// Resource is ready when all its properties are attached.
/// </summary>
internal bool IsReady
{
get
{
return isReady;
}
}
/// <summary>
/// Resource is attached when all its properties are received.
/// </summary>
internal bool IsAttached
{
get
{
return isAttached;
}
}
// public DistributedResourceStack Stack
//{
// get { return stack; }
//}
/// <summary>
/// Create a new distributed resource.
/// </summary>
/// <param name="connection">Connection responsible for the distributed resource.</param>
/// <param name="template">Resource template.</param>
/// <param name="instanceId">Instance Id given by the other end.</param>
/// <param name="age">Resource age.</param>
public DistributedResource(DistributedConnection connection, ResourceTemplate template, uint instanceId, uint age, string link)
{
this.link = link;
this.connection = connection;
this.instanceId = instanceId;
this.template = template;
this.age = age;
}
internal void _Ready()
{
isReady = true;
}
internal bool _Attached(object[] properties)
{
if (isAttached)
return false;
else
{
this.properties = properties;
ages = new uint[properties.Length];
this.events = new DistributedResourceEvent[template.Events.Length];
isAttached = true;
}
return true;
}
internal void _EmitEventByIndex(byte index, object[] args)
{
var et = template.GetEventTemplate(index);
events[index]?.Invoke(this, args);
Instance.EmitResourceEvent(et.Name, null, args);
}
public AsyncReply _Invoke(byte index, object[] args)
{
if (destroyed)
throw new Exception("Trying to access destroyed object");
if (index >= template.Functions.Length)
throw new Exception("Function index is incorrect");
var reply = new AsyncReply();
var parameters = Codec.ComposeVarArray(args, connection, true);
connection.SendRequest(Packets.IIPPacket.IIPPacketAction.InvokeFunction, instanceId, index, parameters).Then((res) =>
{
Codec.Parse((byte[])res[0], 0, connection).Then((rt) =>
{
reply.Trigger(rt);
});
});
return reply;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
var ft = template.GetFunctionTemplate(binder.Name);
var reply = new AsyncReply();
if (isAttached && ft!=null)
{
result = _Invoke(ft.Index, args);
return true;
}
else
{
result = null;
return false;
}
}
/// <summary>
/// Get a property value.
/// </summary>
/// <param name="index">Zero-based property index.</param>
/// <returns>Value</returns>
internal object _Get(byte index)
{
if (index >= properties.Length)
return null;
return properties[index];
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (destroyed)
throw new Exception("Trying to access destroyed object");
result = null;
if (!isAttached)
return false;
var pt = template.GetPropertyTemplate(binder.Name);
if (pt != null)
{
result = properties[pt.Index];
return true;
}
else
{
var et = template.GetEventTemplate(binder.Name);
if (et == null)
return false;
result = events[et.Index];
return true;
}
}
internal void UpdatePropertyByIndex(byte index, object value)
{
var pt = template.GetPropertyTemplate(index);
properties[index] = value;
Instance.Modified(pt.Name, value);
}
/// <summary>
/// Set property value.
/// </summary>
/// <param name="index">Zero-based property index.</param>
/// <param name="value">Value</param>
/// <returns>Indicator when the property is set.</returns>
internal AsyncReply _Set(byte index, object value)
{
if (index >= properties.Length)
return null;
var reply = new AsyncReply();
var parameters = Codec.Compose(value, connection);
connection.SendRequest(Packets.IIPPacket.IIPPacketAction.SetProperty, instanceId, index, parameters).Then((res) =>
{
// not really needed, server will always send property modified, this only happens if the programmer forgot to emit in property setter
//Update(index, value);
reply.Trigger(null);
// nothing to do here
});
return reply;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (destroyed)
throw new Exception("Trying to access destroyed object");
if (!isAttached)
return false;
var pt = template.GetPropertyTemplate(binder.Name);
if (pt != null)
{
_Set(pt.Index, value);
return true;
}
else
{
var et = template.GetEventTemplate(binder.Name);
if (et == null)
return false;
events[et.Index] = (DistributedResourceEvent)value;
return true;
}
}
/*
public async void InvokeMethod(byte index, object[] arguments, DistributedConnection sender)
{
// get function parameters
Type t = this.GetType();
MethodInfo mi = t.GetMethod(GetFunctionName(index), BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance | BindingFlags.InvokeMethod);
if (mi != null)
{
try
{
var res = await invokeMethod(mi, arguments, sender);
object rt = Codec.Compose(res);
sender.SendParams((byte)0x80, instanceId, index, rt);
}
catch(Exception ex)
{
var msg = ex.InnerException != null ? ex.InnerException.Message : ex.Message;
sender.SendParams((byte)0x8E, instanceId, index, Codec.Compose(msg));
}
}
}
*/
/// <summary>
/// Resource interface.
/// </summary>
public Instance Instance
{
get;
set;
}
/// <summary>
/// Create a new instance of distributed resource.
/// </summary>
public DistributedResource()
{
//stack = new DistributedResourceStack(this);
}
/// <summary>
/// Resource interface.
/// </summary>
/// <param name="trigger"></param>
/// <returns></returns>
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
// do nothing.
return new AsyncReply<bool>(true);
}
}
}

View File

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Net.IIP
{
public delegate void DistributedResourceEvent(DistributedResource sender, params object[] arguments);
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Net.IIP
{
public class DistributedResourceQueueItem
{
public enum DistributedResourceQueueItemType
{
Propery,
Event
}
DistributedResourceQueueItemType type;
byte index;
object value;
DistributedResource resource;
public DistributedResourceQueueItem(DistributedResource resource, DistributedResourceQueueItemType type, object value, byte index)
{
this.resource = resource;
this.index = index;
this.type = type;
this.value = value;
}
public DistributedResource Resource
{
get { return resource; }
}
public DistributedResourceQueueItemType Type
{
get { return type; }
}
public byte Index
{
get { return index; }
}
public object Value
{
get { return value; }
}
}
}

View File

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Net.Sockets;
using Esiur.Misc;
using System.Threading;
using Esiur.Data;
using Esiur.Engine;
using System.Net;
using Esiur.Resource;
using Esiur.Security.Membership;
namespace Esiur.Net.IIP
{
public class DistributedServer : NetworkServer<DistributedConnection>, IResource
{
[Storable]
[ResourceProperty]
public string ip
{
get;
set;
}
[Storable]
[ResourceProperty]
public IMembership Membership
{
get;
set;
}
[Storable]
[ResourceProperty]
public ushort port
{
get;
set;
}
[Storable]
[ResourceProperty]
public uint timeout
{
get;
set;
}
[Storable]
[ResourceProperty]
public uint clock
{
get;
set;
}
public Instance Instance
{
get;
set;
}
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
if (trigger == ResourceTrigger.Initialize)
{
TCPSocket listener;
if (ip != null)
listener = new TCPSocket(new IPEndPoint(IPAddress.Parse(ip), port));
else
listener = new TCPSocket(new IPEndPoint(IPAddress.Any, port));
Start(listener, timeout, clock);
}
else if (trigger == ResourceTrigger.Terminate)
{
Stop();
}
else if (trigger == ResourceTrigger.SystemReload)
{
Trigger(ResourceTrigger.Terminate);
Trigger(ResourceTrigger.Initialize);
}
return new AsyncReply<bool>(true);
}
protected override void DataReceived(DistributedConnection sender, NetworkBuffer data)
{
//throw new NotImplementedException();
}
private void SessionModified(DistributedConnection Session, string Key, object NewValue)
{
}
protected override void ClientConnected(DistributedConnection sender)
{
Console.WriteLine("DistributedConnection Client Connected");
sender.Server = this;
}
protected override void ClientDisconnected(DistributedConnection sender)
{
Console.WriteLine("DistributedConnection Client Disconnected");
}
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
using Esiur.Net.Sockets;
using Esiur.Security.Authority;
namespace Esiur.Net.IIP
{
public class DistributedSession : NetworkSession
{
Source Source { get; }
Authentication Authentication;
}
}

167
Esiur/Net/NetworkBuffer.cs Normal file
View File

@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Data;
using Esiur.Misc;
namespace Esiur.Net
{
public class NetworkBuffer
{
byte[] data;
uint neededDataLength = 0;
//bool trim;
public NetworkBuffer()
{
data = new byte[0];
}
public bool Protected
{
get
{
return neededDataLength > data.Length;
}
}
public uint Available
{
get
{
return (uint)data.Length;
}
}
//public void HoldForAtLeast(byte[] src, uint offset, uint size, uint needed)
//{
// HoldFor(src, offset, size, needed);
// //trim = false;
//}
//public void HoldForAtLeast(byte[] src, uint needed)
//{
// HoldForAtLeast(src, 0, (uint)src.Length, needed);
//}
public void HoldForNextWrite(byte[] src)
{
//HoldForAtLeast(src, (uint)src.Length + 1);
HoldFor(src, (uint)src.Length + 1);
}
public void HoldForNextWrite(byte[] src, uint offset, uint size)
{
//HoldForAtLeast(src, offset, size, size + 1);
HoldFor(src, offset, size, size + 1);
}
public void HoldFor(byte[] src, uint offset, uint size, uint needed)
{
if (size >= needed)
throw new Exception("Size >= Needed !");
//trim = true;
data = DC.Combine(src, offset, size, data, 0, (uint)data.Length);
neededDataLength = needed;
// Console.WriteLine("Hold StackTrace: '{0}'", Environment.StackTrace);
Console.WriteLine("Holded {0} {1} {2} {3} - {4}", offset, size, needed, data.Length, GetHashCode());
}
public void HoldFor(byte[] src, uint needed)
{
HoldFor(src, 0, (uint)src.Length, needed);
}
public bool Protect(byte[] data, uint offset, uint needed)//, bool exact = false)
{
uint dataLength = (uint)data.Length - offset;
// protection
if (dataLength < needed)
{
//if (exact)
// HoldFor(data, offset, dataLength, needed);
//else
//HoldForAtLeast(data, offset, dataLength, needed);
HoldFor(data, offset, dataLength, needed);
return true;
}
else
return false;
}
public void Write(byte[] src)
{
Write(src, 0, (uint)src.Length);
}
public void Write(byte[] src, uint offset, uint length)
{
DC.Append(ref data, src, offset, length);
}
public bool CanRead
{
get
{
if (data.Length == 0)
return false;
if (data.Length < neededDataLength)
return false;
return true;
}
}
public byte[] Read()
{
if (data.Length == 0)
return null;
byte[] rt = null;
if (neededDataLength == 0)
{
rt = data;
data = new byte[0];
}
else
{
//Console.WriteLine("P STATE:" + data.Length + " " + neededDataLength);
if (data.Length >= neededDataLength)
{
//Console.WriteLine("data.Length >= neededDataLength " + data.Length + " >= " + neededDataLength + " " + trim);
//if (trim)
//{
// rt = DC.Clip(data, 0, neededDataLength);
// data = DC.Clip(data, neededDataLength, (uint)data.Length - neededDataLength);
//}
//else
//{
// return all data
rt = data;
data = new byte[0];
//}
neededDataLength = 0;
return rt;
}
else
{
return null;
}
}
return rt;
}
}
}

View File

@ -0,0 +1,248 @@
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net;
using System.Collections;
using System.Collections.Generic;
using Esiur.Misc;
using Esiur.Engine;
using Esiur.Data;
using Esiur.Net.Sockets;
using Esiur.Resource;
namespace Esiur.Net
{
public class NetworkConnection: IDestructible// <TS>: IResource where TS : NetworkSession
{
private ISocket sock;
// private bool connected;
private DateTime lastAction;
public delegate void DataReceivedEvent(NetworkConnection sender, NetworkBuffer data);
public delegate void ConnectionClosedEvent(NetworkConnection sender);
public delegate void ConnectionEstablishedEvent(NetworkConnection sender);
public event ConnectionEstablishedEvent OnConnect;
public event DataReceivedEvent OnDataReceived;
public event ConnectionClosedEvent OnClose;
public event DestroyedEvent OnDestroy;
public void Destroy()
{
// if (connected)
Close();
OnDestroy?.Invoke(this);
}
public NetworkConnection()
{
}
public ISocket Socket
{
get
{
return sock;
}
}
public virtual void Assign(ISocket socket)
{
lastAction = DateTime.Now;
sock = socket;
//connected = true;
socket.OnReceive += Socket_OnReceive;
socket.OnClose += Socket_OnClose;
socket.OnConnect += Socket_OnConnect;
if (socket.State == SocketState.Established)
socket.Begin();
}
private void Socket_OnConnect()
{
OnConnect?.Invoke(this);
}
private void Socket_OnClose()
{
OnClose?.Invoke(this);
}
private void Socket_OnReceive(NetworkBuffer buffer)
{
try
{
// Unassigned ?
if (sock == null)
return;
// Closed ?
if (sock.State == SocketState.Closed || sock.State == SocketState.Terminated) // || !connected)
return;
lastAction = DateTime.Now;
while (buffer.Available > 0 && !buffer.Protected)
DataReceived(buffer);
}
catch (Exception ex)
{
Global.Log("NetworkConnection", LogType.Warning, ex.ToString());
}
}
public ISocket Unassign()
{
if (sock != null)
{
// connected = false;
sock.OnClose -= Socket_OnClose;
sock.OnConnect -= Socket_OnConnect;
sock.OnReceive -= Socket_OnReceive;
var rt = sock;
sock = null;
return rt;
}
else
return null;
}
protected virtual void DataReceived(NetworkBuffer data)
{
if (OnDataReceived != null)
{
try
{
OnDataReceived?.Invoke(this, data);
}
catch (Exception ex)
{
Global.Log("NetworkConenction:DataReceived", LogType.Error, ex.ToString());
}
}
}
public void Close()
{
//if (!connected)
// return;
try
{
if (sock != null)
sock.Close();
}
catch(Exception ex)
{
Global.Log("NetworkConenction:Close", LogType.Error, ex.ToString());
}
//finally
//{
//connected = false;
//}
}
public DateTime LastAction
{
get { return lastAction; }
}
public IPEndPoint RemoteEndPoint
{
get
{
if (sock != null)
return (IPEndPoint)sock.RemoteEndPoint;
else
return null;
}
}
public IPEndPoint LocalEndPoint
{
get
{
if (sock != null)
return (IPEndPoint)sock.LocalEndPoint;
else
return null;
}
}
public bool Connected
{
get
{
return sock.State == SocketState.Established;// connected;
}
}
/*
public void CloseAndWait()
{
try
{
if (!connected)
return;
if (sock != null)
sock.Close();
while (connected)
{
Thread.Sleep(100);
}
}
finally
{
}
}
*/
public virtual void Send(byte[] msg)
{
//Console.WriteLine("TXX " + msg.Length);
try
{
//if (!connected)
//{
//Console.WriteLine("not connected");
// return;
//}
if (sock != null)
{
lastAction = DateTime.Now;
sock.Send(msg);
}
}
catch
{
}
}
public virtual void Send(string data)
{
Send(Encoding.UTF8.GetBytes(data));
}
}
}

346
Esiur/Net/NetworkServer.cs Normal file
View File

@ -0,0 +1,346 @@
using System;
using System.Threading;
using System.Collections.Generic;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Engine;
using Esiur.Net.Sockets;
using Esiur.Resource;
using System.Threading.Tasks;
namespace Esiur.Net
{
//public abstract class NetworkServer<TConnection, TSession> : IResource where TSession : NetworkSession, new() where TConnection : NetworkConnection<TSession>, new()
public abstract class NetworkServer<TConnection>: IDestructible where TConnection : NetworkConnection, new()
{
//private bool isRunning;
uint clock;
private ISocket listener;
private AutoList<TConnection, NetworkServer<TConnection>> connections;
//private Thread thread;
protected abstract void DataReceived(TConnection sender, NetworkBuffer data);
protected abstract void ClientConnected(TConnection sender);
protected abstract void ClientDisconnected(TConnection sender);
// private int port;
// private IPAddress ip = null;
private uint timeout;
private Timer timer;
//public KeyList<string, TSession> Sessions = new KeyList<string, TSession>();
public event DestroyedEvent OnDestroy;
public AutoList<TConnection, NetworkServer<TConnection>> Connections
{
get
{
return connections;
}
}
/*
public void RemoveSession(string ID)
{
Sessions.Remove(ID);
}
public void RemoveSession(TSession Session)
{
if (Session != null)
Sessions.Remove(Session.Id);
}
*/
/*
public TSession CreateSession(string ID, int Timeout)
{
TSession s = new TSession();
s.SetSession(ID, Timeout, new NetworkSession.SessionModifiedEvent(SessionModified)
, new NetworkSession.SessionEndedEvent(SessionEnded));
Sessions.Add(ID, s);
return s;
}
*/
/*
private void pSessionModified(TSession session, string key, object oldValue, object newValue)
{
SessionModified((TSession)session, key, oldValue, newValue);
}
private void pSessionEnded(NetworkSession session)
{
SessionEnded((TSession)session);
}
*/
/*
protected virtual void SessionModified(NetworkSession session, string key, object oldValue, object newValue)
{
}
protected virtual void SessionEnded(NetworkSession session)
{
Sessions.Remove(session.Id);
session.Destroy();
}
*/
private void MinuteThread(object state)
{
List<TConnection> ToBeClosed = null;
lock (connections.SyncRoot)
{
foreach (TConnection c in connections)
{
if (DateTime.Now.Subtract(c.LastAction).TotalSeconds >= timeout)
{
if (ToBeClosed == null)
ToBeClosed = new List<TConnection>();
ToBeClosed.Add(c);
}
}
}
//Console.WriteLine("UnLock MinuteThread");
if (ToBeClosed != null)
{
//Console.WriteLine("Term: " + ToBeClosed.Count + " " + this.listener.LocalEndPoint.ToString());
foreach (TConnection c in ToBeClosed)
c.Close();// CloseAndWait();
ToBeClosed.Clear();
ToBeClosed = null;
}
}
public void Start(ISocket socket, uint timeout, uint clock)
{
if (listener != null)
return;
//if (socket.State == SocketState.Listening)
// return;
//if (isRunning)
// return;
connections = new AutoList<TConnection, NetworkServer<TConnection>>(this);
if (timeout > 0 & clock > 0)
{
timer = new Timer(MinuteThread, null, TimeSpan.FromMinutes(0), TimeSpan.FromSeconds(clock));
this.timeout = timeout;
}
//this.ip = ip;
//this.port = port;
this.clock = clock;
// start a new thread for the server to live on
//isRunning = true;
listener = socket;
// Start accepting
listener.Accept().Then(NewConnection);
//var rt = listener.Accept().Then()
//thread = new Thread(new System.Threading.ThreadStart(ListenForConnections));
//thread.Start();
}
/*
public int LocalPort
{
get
{
return port;
}
}
*/
public uint Clock
{
get { return clock; }
}
public void Stop()
{
var port = 0;
try
{
if (listener != null)
{
port = listener.LocalEndPoint.Port;
listener.Close();
}
// wait until the listener stops
//while (isRunning)
//{
// Thread.Sleep(100);
//}
Console.WriteLine("Listener stopped");
var cons = connections.ToArray();
//lock (connections.SyncRoot)
//{
foreach (TConnection con in cons)
con.Close();
//}
Console.WriteLine("Sockets Closed");
while (connections.Count > 0)
{
Console.WriteLine("Waiting... " + connections.Count);
//Thread.Sleep(1000);
}
}
finally
{
Console.WriteLine("Server@{0} is down", port);
}
}
private void NewConnection(ISocket sock)
{
try
{
/*
if (listener.State == SocketState.Closed || listener.State == SocketState.Terminated)
{
Console.WriteLine("Listen socket break ");
Console.WriteLine(listener.LocalEndPoint.Port);
break;
}
*/
if (sock == null)
{
Console.Write("sock == null");
return;
}
//sock.ReceiveBufferSize = 102400;
//sock.SendBufferSize = 102400;
TConnection c = new TConnection();
c.OnDataReceived += OnDataReceived;
c.OnConnect += OnClientConnect;
c.OnClose += OnClientClose;
connections.Add(c);
c.Assign(sock);
ClientConnected(c);
// Accept more
listener.Accept().Then(NewConnection);
}
catch (Exception ex)
{
Console.WriteLine("TSERVER " + ex.ToString());
Global.Log("TServer", LogType.Error, ex.ToString());
}
//isRunning = false;
}
public bool IsRunning
{
get
{
return listener.State == SocketState.Listening;
//isRunning;
}
}
public void OnDataReceived(NetworkConnection sender, NetworkBuffer data)
{
DataReceived((TConnection)sender, data);
}
public void OnClientConnect(NetworkConnection sender)
{
if (sender == null)
return;
if (sender.RemoteEndPoint == null || sender.LocalEndPoint == null)
{ }
//Console.WriteLine("NULL");
else
Global.Log("Connections", LogType.Debug, sender.RemoteEndPoint.Address.ToString()
+ "->" + sender.LocalEndPoint.Port + " at " + DateTime.UtcNow.ToString("d")
+ " " + DateTime.UtcNow.ToString("d"), false);
// Console.WriteLine("Connected " + sender.RemoteEndPoint.ToString());
ClientConnected((TConnection)sender);
}
public void OnClientClose(NetworkConnection sender)
{
try
{
sender.Destroy();
ClientDisconnected((TConnection)sender);
}
catch (Exception ex)
{
Global.Log("NetworkServer:OnClientDisconnect", LogType.Error, ex.ToString());
}
sender = null;
GC.Collect();
}
public void Destroy()
{
Stop();
OnDestroy?.Invoke(this);
}
~NetworkServer()
{
Stop();
//Connections = null;
listener = null;
}
}
}

106
Esiur/Net/NetworkSession.cs Normal file
View File

@ -0,0 +1,106 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net;
using System.Collections;
using System.Collections.Generic;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Engine;
namespace Esiur.Net
{
public class NetworkSession:IDestructible //<T> where T : TClient
{
public delegate void SessionModifiedEvent(NetworkSession session, string key, object oldValue, object newValue);
public delegate void SessionEndedEvent(NetworkSession session);
private string id;
private Timer timer;
private int timeout;
DateTime creation;
DateTime lastAction;
private KeyList<string, object> variables;
public event SessionEndedEvent OnEnd;
public event SessionModifiedEvent OnModify;
public event DestroyedEvent OnDestroy;
public KeyList<string, object> Variables
{
get { return variables; }
}
public NetworkSession()
{
variables = new KeyList<string, object>();
variables.OnModified += new KeyList<string, object>.Modified(VariablesModified);
creation = DateTime.Now;
}
internal void Set(string id, int timeout )
{
//modified = sessionModifiedEvent;
//ended = sessionEndEvent;
this.id = id;
if (this.timeout != 0)
{
this.timeout = timeout;
timer = new Timer(OnSessionEndTimerCallback, null, TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
creation = DateTime.Now;
}
}
private void OnSessionEndTimerCallback(object o)
{
OnEnd?.Invoke(this);
}
void VariablesModified(string key, object oldValue, object newValue)
{
OnModify?.Invoke(this, key, oldValue, newValue);
}
public void Destroy()
{
OnDestroy?.Invoke(this);
timer.Dispose();
timer = null;
}
internal void Refresh()
{
lastAction = DateTime.Now;
timer.Change(TimeSpan.FromSeconds(timeout), TimeSpan.FromSeconds(0));
}
public int Timeout // Seconds
{
get
{
return timeout;
}
set
{
timeout = value;
Refresh();
}
}
public string Id
{
get { return id; }
}
public DateTime LastAction
{
get { return lastAction; }
}
}
}

View File

@ -0,0 +1,287 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Misc;
using Esiur.Data;
using System.Net;
namespace Esiur.Net.Packets
{
public class HTTPRequestPacket : Packet
{
public enum HTTPMethod:byte
{
GET,
POST,
HEAD,
PUT,
DELETE,
OPTIONS,
TRACE,
CONNECT,
UNKNOWN
}
public StringKeyList Query;
public HTTPMethod Method;
public StringKeyList Headers;
public bool WSMode;
public string Version;
public StringKeyList Cookies; // String
public string URL; /// With query
public string Filename; /// Without query
//public byte[] PostContents;
public KeyList<string, object> PostForms;
public byte[] Message;
private HTTPMethod getMethod(string method)
{
switch (method.ToLower())
{
case "get":
return HTTPMethod.GET;
case "post":
return HTTPMethod.POST;
case "head":
return HTTPMethod.HEAD;
case "put":
return HTTPMethod.PUT;
case "delete":
return HTTPMethod.DELETE;
case "options":
return HTTPMethod.OPTIONS;
case "trace":
return HTTPMethod.TRACE;
case "connect":
return HTTPMethod.CONNECT;
default:
return HTTPMethod.UNKNOWN;
}
}
public override string ToString()
{
return "HTTPRequestPacket"
+ "\n\tVersion: " + Version
+ "\n\tMethod: " + Method
+ "\n\tURL: " + URL
+ "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL");
}
public override long Parse(byte[] data, uint offset, uint ends)
{
string[] sMethod = null;
string[] sLines = null;
uint headerSize = 0;
for (uint i = offset; i < ends - 3; i++)
{
if (data[i] == '\r' && data[i + 1] == '\n'
&& data[i + 2] == '\r' && data[i + 3] == '\n')
{
sLines = Encoding.ASCII.GetString(data, (int)offset,(int)( i - offset)).Split(new string[] { "\r\n" },
StringSplitOptions.None);
headerSize = i + 4;
break;
}
}
if (headerSize == 0)
return -1;
Cookies = new StringKeyList();
PostForms = new KeyList<string, object>();
Query = new StringKeyList();
Headers = new StringKeyList();
sMethod = sLines[0].Split(' ');
Method = getMethod(sMethod[0].Trim());
if (sMethod.Length == 3)
{
sMethod[1] = WebUtility.UrlDecode(sMethod[1]);
if (sMethod[1].Length >= 7)
{
if (sMethod[1].Substring(0, 7) == "http://")
{
sMethod[1] = sMethod[1].Substring(sMethod[1].IndexOf("/", 7));
}
}
URL = sMethod[1].Trim();
if (URL.IndexOf("?", 0) != -1)
{
Filename = URL.Split(new char[] { '?' }, 2)[0];
}
else
{
Filename = URL;
}
if (Filename.IndexOf("%", 0) != -1)
{
Filename = WebUtility.UrlDecode(Filename);
}
Version = sMethod[2].Trim();
}
// Read all headers
for (int i = 1; i < sLines.Length; i++)
{
if (sLines[i] == String.Empty)
{
// Invalid header
return 0;
}
if (sLines[i].IndexOf(':') == -1)
{
// Invalid header
return 0;
}
string[] header = sLines[i].Split(new char[] { ':' }, 2);
header[0] = header[0].ToLower();
Headers[header[0]] = header[1].Trim();
if (header[0] == "cookie")
{
string[] cookies = header[1].Split(';');
foreach (string cookie in cookies)
{
if (cookie.IndexOf('=') != -1)
{
string[] splitCookie = cookie.Split('=');
splitCookie[0] = splitCookie[0].Trim();
splitCookie[1] = splitCookie[1].Trim();
if (!(Cookies.ContainsKey(splitCookie[0].Trim())))
Cookies.Add(splitCookie[0], splitCookie[1]);
}
else
{
if (!(Cookies.ContainsKey(cookie.Trim())))
{
Cookies.Add(cookie.Trim(), String.Empty);
}
}
}
}
}
// Query String
if (URL.IndexOf("?", 0) != -1)
{
string[] SQ = URL.Split(new char[] { '?' }, 2)[1].Split('&');
foreach (string S in SQ)
{
if (S.IndexOf("=", 0) != -1)
{
string[] qp = S.Split(new char[] { '=' }, 2);
if (!Query.ContainsKey(WebUtility.UrlDecode(qp[0])))
{
Query.Add(WebUtility.UrlDecode(qp[0]), WebUtility.UrlDecode(qp[1]));
}
}
else
{
if (!(Query.ContainsKey(WebUtility.UrlDecode(S))))
{
Query.Add(WebUtility.UrlDecode(S), null);
}
}
}
}
// Post Content-Length
if (Method == HTTPMethod.POST)
{
try
{
uint postSize = uint.Parse((string)Headers["content-length"]);
// check limit
if (postSize > data.Length - headerSize)
return postSize - (data.Length - headerSize);
if (Headers["content-type"] == "application/x-www-form-urlencoded"
|| Headers["content-type"] == ""
|| Headers["content-type"] == null)
{
string[] PostVars = null;
PostVars = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split('&');
for (int J = 0; J < PostVars.Length; J++)
{
if (PostVars[J].IndexOf("=") != -1)
{
string key = WebUtility.HtmlDecode(
WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[0]));
if (PostForms.Contains(key))
PostForms[key] = WebUtility.HtmlDecode(
WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[1]));
else
PostForms.Add(key, WebUtility.HtmlDecode(
WebUtility.UrlDecode(PostVars[J].Split(new char[] { '=' }, 2)[1])));
}
else
if (PostForms.Contains("unknown"))
PostForms["unknown"] = PostForms["unknown"]
+ "&" + WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J]));
else
PostForms.Add("unknown", WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J])));
}
}
else if (Headers["content-type"].StartsWith("multipart/form-data"))
{
int st = 1;
int ed = 0;
string strBoundry = "--" + Headers["content-type"].Substring(
Headers["content-type"].IndexOf("boundary=", 0) + 9);
string[] sc = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split(
new string[] { strBoundry }, StringSplitOptions.None);
for (int j = 1; j < sc.Length - 1; j++)
{
string[] ps = sc[j].Split(new string[] { "\r\n\r\n" }, 2, StringSplitOptions.None);
ps[1] = ps[1].Substring(0, ps[1].Length - 2); // remove the empty line
st = ps[0].IndexOf("name=", 0) + 6;
ed = ps[0].IndexOf("\"", st);
PostForms.Add(ps[0].Substring(st, ed - st), ps[1]);
}
}
else
{
//PostForms.Add(Headers["content-type"], Encoding.Default.GetString( ));
Message = DC.Clip(data, headerSize, postSize);
}
return headerSize + postSize;
}
catch
{
return 0;
}
}
return headerSize;
}
}
}

View File

@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Misc;
using Esiur.Data;
namespace Esiur.Net.Packets
{
public class HTTPResponsePacket : Packet
{
public enum ComposeOptions : int
{
AllCalculateLength,
AllDontCalculateLength,
SpecifiedHeadersOnly,
DataOnly
}
public enum ResponseCode : int
{
HTTP_SWITCHING = 101,
HTTP_OK = 200,
HTTP_NOTFOUND = 404,
HTTP_SERVERERROR = 500,
HTTP_MOVED = 301,
HTTP_NOTMODIFIED = 304,
HTTP_REDIRECT = 307
}
public class HTTPCookie
{
public string Name;
public string Value;
public DateTime Expires;
public string Path;
public bool HttpOnly;
public string Domain;
public HTTPCookie(string Name, string Value)
{
this.Name = Name;
this.Value = Value;
}
public HTTPCookie(string Name, string Value, DateTime Expires)
{
this.Name = Name;
this.Value = Value;
this.Expires = Expires;
}
public override string ToString()
{
//Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2001 21:00:00 GMT; domain=.com.au; path=/
//Set-Cookie: SessionID=another; expires=Fri, 29 Jun 2006 20:47:11 UTC; path=/
string Cookie = Name + "=" + Value;
if (Expires.Ticks != 0)
{
Cookie += "; expires=" + Expires.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss") + " GMT";
}
if (Domain != null)
{
Cookie += "; domain=" + Domain;
}
if (Path != null)
{
Cookie += "; path=" + Path;
}
if (HttpOnly)
{
Cookie += "; HttpOnly";
}
return Cookie;
}
}
public StringKeyList Headers = new StringKeyList(true);
public string Version = "HTTP/1.1";
public byte[] Message;
public ResponseCode Number;
public string Text;
//public DStringDictionary Cookies;
public List<HTTPCookie> Cookies = new List<HTTPCookie>();
public bool Handled;
//public bool ResponseHandled; //flag this as true if you are handling it yourself
//private bool createSession;
//private HTTPServer Server;
//public HTTPSession Session;
public override string ToString()
{
return "HTTPResponsePacket"
+ "\n\tVersion: " + Version
//+ "\n\tMethod: " + Method
//+ "\n\tURL: " + URL
+ "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL");
}
private string MakeHeader(ComposeOptions Options)
{
string header = Version + " " + (int)Number + " " + Text + "\r\n"
+ "Server: Delta Web Server\r\n"
//Fri, 30 Oct 2007 14:19:41 GMT"
+ "Date: " + DateTime.Now.ToUniversalTime().ToString("r") + "\r\n";
if (Options == ComposeOptions.AllCalculateLength && Message != null)
{
Headers["Content-Length"] = Message.Length.ToString();
}
foreach (var kv in Headers)
{
header += kv.Key + ": " + kv.Value + "\r\n";
}
// Set-Cookie: ckGeneric=CookieBody; expires=Sun, 30-Dec-2007 21:00:00 GMT; path=/
// Set-Cookie: ASPSESSIONIDQABBDSQA=IPDPMMMALDGFLMICEJIOCIPM; path=/
foreach (var Cookie in Cookies)
{
header += "Set-Cookie: " + Cookie.ToString() + "\r\n";
}
header += "\r\n";
return header;
}
public bool Compose(ComposeOptions options)
{
List<byte> msg = new List<byte>();
if (options != ComposeOptions.DataOnly)
{
msg.AddRange(Encoding.UTF8.GetBytes(MakeHeader(options)));
}
if (options != ComposeOptions.SpecifiedHeadersOnly)
{
if (Message != null)
msg.AddRange(Message);
}
Data = msg.ToArray();
return true;
}
public override bool Compose()
{
return Compose(ComposeOptions.AllDontCalculateLength);
}
public override long Parse(byte[] data, uint offset, uint ends)
{
string[] sMethod = null;
string[] sLines = null;
uint headerSize = 0;
for (uint i = offset; i < ends - 3; i++)
{
if (data[i] == '\r' && data[i + 1] == '\n'
&& data[i + 2] == '\r' && data[i + 3] == '\n')
{
sLines = Encoding.ASCII.GetString(data, (int)offset, (int)(i - offset)).Split(new string[] { "\r\n" },
StringSplitOptions.None);
headerSize = i + 4;
break;
}
}
if (headerSize == 0)
return -1;
//Cookies = new DStringDictionary();
//Headers = new DStringDictionary(true);
sMethod = sLines[0].Split(' ');
if (sMethod.Length == 3)
{
Version = sMethod[0].Trim();
Number = (ResponseCode)(Convert.ToInt32(sMethod[1].Trim()));
Text = sMethod[2];
}
// Read all headers
for (int i = 1; i < sLines.Length; i++)
{
if (sLines[i] == String.Empty)
{
// Invalid header
return 0;
}
if (sLines[i].IndexOf(':') == -1)
{
// Invalid header
return 0;
}
string[] header = sLines[i].Split(new char[] { ':' }, 2);
header[0] = header[0].ToLower();
Headers[header[0]] = header[1].Trim();
//Set-Cookie: NAME=VALUE; expires=DATE;
if (header[0] == "set-cookie")
{
string[] cookie = header[1].Split(';');
if (cookie.Length >= 1)
{
string[] splitCookie = cookie[0].Split('=');
HTTPCookie c = new HTTPCookie(splitCookie[0], splitCookie[1]);
for (int j = 1; j < cookie.Length; j++)
{
splitCookie = cookie[j].Split('=');
switch (splitCookie[0].ToLower())
{
case "domain":
c.Domain = splitCookie[1];
break;
case "path":
c.Path = splitCookie[1];
break;
case "httponly":
c.HttpOnly = true;
break;
case "expires":
// Wed, 13-Jan-2021 22:23:01 GMT
c.Expires = DateTime.Parse(splitCookie[1]);
break;
}
}
}
}
}
// Content-Length
try
{
uint contentLength = uint.Parse((string)Headers["content-length"]);
// check limit
if (contentLength > data.Length - headerSize)
{
return contentLength - (data.Length - headerSize);
}
Message = DC.Clip(data, offset, contentLength);
return headerSize + contentLength;
}
catch
{
return 0;
}
}
}
}

View File

@ -0,0 +1,382 @@
 using Esiur.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Net.Packets
{
class IIPAuthPacket : Packet
{
public enum IIPAuthPacketCommand: byte
{
Action = 0,
Declare,
Acknowledge,
Error,
}
public enum IIPAuthPacketAction: byte
{
// Authenticate
AuthenticateHash,
//Challenge,
//CertificateRequest,
//CertificateReply,
//EstablishRequest,
//EstablishReply
NewConnection = 0x20,
ResumeConnection,
ConnectionEstablished = 0x28
}
public enum IIPAuthPacketMethod: byte
{
None,
Certificate,
Credentials,
Token
}
public IIPAuthPacketCommand Command
{
get;
set;
}
public IIPAuthPacketAction Action
{
get;
set;
}
public byte ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public IIPAuthPacketMethod LocalMethod
{
get;
set;
}
public byte[] SourceInfo
{
get;
set;
}
public byte[] Hash
{
get;
set;
}
public byte[] SessionId
{
get;
set;
}
public IIPAuthPacketMethod RemoteMethod
{
get;
set;
}
public string Domain
{
get;
set;
}
public long CertificateId
{
get;set;
}
public string LocalUsername
{
get;
set;
}
public string RemoteUsername
{
get;
set;
}
public byte[] LocalPassword
{
get;
set;
}
public byte[] RemotePassword
{
get;
set;
}
public byte[] LocalToken
{
get;
set;
}
public byte[] RemoteToken
{
get;
set;
}
public byte[] AsymetricEncryptionKey
{
get;
set;
}
public byte[] LocalNonce
{
get;
set;
}
public byte[] RemoteNonce
{
get;
set;
}
private uint dataLengthNeeded;
bool NotEnough(uint offset, uint ends, uint needed)
{
if (offset + needed > ends)
{
dataLengthNeeded = needed - (ends - offset);
return true;
}
else
return false;
}
public override string ToString()
{
return Command.ToString() + " " + Action.ToString();
}
public override long Parse(byte[] data, uint offset, uint ends)
{
var oOffset = offset;
if (NotEnough(offset, ends, 1))
return -dataLengthNeeded;
Command = (IIPAuthPacketCommand)(data[offset] >> 6);
if (Command == IIPAuthPacketCommand.Action)
{
Action = (IIPAuthPacketAction)(data[offset++] & 0x3f);
if (Action == IIPAuthPacketAction.AuthenticateHash)
{
if (NotEnough(offset, ends, 32))
return -dataLengthNeeded;
Hash = data.Clip(offset, 32);
//var hash = new byte[32];
//Buffer.BlockCopy(data, (int)offset, hash, 0, 32);
//Hash = hash;
offset += 32;
}
else if (Action == IIPAuthPacketAction.NewConnection)
{
if (NotEnough(offset, ends, 2))
return -dataLengthNeeded;
var length = data.GetUInt16(offset);
offset += 2;
if (NotEnough(offset, ends, length))
return -dataLengthNeeded;
SourceInfo = data.Clip(offset, length);
//var sourceInfo = new byte[length];
//Buffer.BlockCopy(data, (int)offset, sourceInfo, 0, length);
//SourceInfo = sourceInfo;
offset += 32;
}
else if (Action == IIPAuthPacketAction.ResumeConnection
|| Action == IIPAuthPacketAction.ConnectionEstablished)
{
//var sessionId = new byte[32];
if (NotEnough(offset, ends, 32))
return -dataLengthNeeded;
SessionId = data.Clip(offset, 32);
//Buffer.BlockCopy(data, (int)offset, sessionId, 0, 32);
//SessionId = sessionId;
offset += 32;
}
}
else if (Command == IIPAuthPacketCommand.Declare)
{
RemoteMethod = (IIPAuthPacketMethod)((data[offset] >> 4) & 0x3);
LocalMethod = (IIPAuthPacketMethod)((data[offset] >> 2) & 0x3);
var encrypt = ((data[offset++] & 0x2) == 0x2);
if (NotEnough(offset, ends, 1))
return -dataLengthNeeded;
var domainLength = data[offset++];
if (NotEnough(offset, ends, domainLength))
return -dataLengthNeeded;
var domain = data.GetString(offset, domainLength);
Domain = domain;
offset += domainLength;
if (RemoteMethod == IIPAuthPacketMethod.Credentials)
{
if (LocalMethod == IIPAuthPacketMethod.None)
{
if (NotEnough(offset, ends, 33))
return -dataLengthNeeded;
//var remoteNonce = new byte[32];
//Buffer.BlockCopy(data, (int)offset, remoteNonce, 0, 32);
//RemoteNonce = remoteNonce;
RemoteNonce = data.Clip(offset, 32);
offset += 32;
var length = data[offset++];
if (NotEnough(offset, ends, length))
return -dataLengthNeeded;
RemoteUsername = data.GetString(offset, length);
offset += length;
}
}
if (encrypt)
{
if (NotEnough(offset, ends, 2))
return -dataLengthNeeded;
var keyLength = data.GetUInt16(offset);
offset += 2;
if (NotEnough(offset, ends, keyLength))
return -dataLengthNeeded;
//var key = new byte[keyLength];
//Buffer.BlockCopy(data, (int)offset, key, 0, keyLength);
//AsymetricEncryptionKey = key;
AsymetricEncryptionKey = data.Clip(offset, keyLength);
offset += keyLength;
}
}
else if (Command == IIPAuthPacketCommand.Acknowledge)
{
RemoteMethod = (IIPAuthPacketMethod)((data[offset] >> 4) & 0x3);
LocalMethod = (IIPAuthPacketMethod)((data[offset] >> 2) & 0x3);
var encrypt = ((data[offset++] & 0x2) == 0x2);
if (NotEnough(offset, ends, 1))
return -dataLengthNeeded;
if (RemoteMethod == IIPAuthPacketMethod.Credentials)
{
if (LocalMethod == IIPAuthPacketMethod.None)
{
if (NotEnough(offset, ends, 32))
return -dataLengthNeeded;
/*
var remoteNonce = new byte[32];
Buffer.BlockCopy(data, (int)offset, remoteNonce, 0, 32);
RemoteNonce = remoteNonce;
*/
RemoteNonce = data.Clip(offset, 32);
offset += 32;
}
}
if (encrypt)
{
if (NotEnough(offset, ends, 2))
return -dataLengthNeeded;
var keyLength = data.GetUInt16(offset);
offset += 2;
if (NotEnough(offset, ends, keyLength))
return -dataLengthNeeded;
//var key = new byte[keyLength];
//Buffer.BlockCopy(data, (int)offset, key, 0, keyLength);
//AsymetricEncryptionKey = key;
AsymetricEncryptionKey = data.Clip(offset, keyLength);
offset += keyLength;
}
}
else if (Command == IIPAuthPacketCommand.Error)
{
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
offset++;
ErrorCode = data[offset++];
var cl = data.GetUInt16(offset);
offset += 2;
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
ErrorMessage = data.GetString(offset, cl);
offset += cl;
}
return offset - oOffset;
}
}
}

View File

@ -0,0 +1,550 @@
 using Esiur.Data;
using Esiur.Engine;
using Esiur.Misc;
using Esiur.Net.Packets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Net.Packets
{
class IIPPacket : Packet
{
public enum IIPPacketCommand : byte
{
Event = 0,
Request,
Reply,
Error,
}
public enum IIPPacketEvent: byte
{
// Event Manage
ResourceReassigned = 0,
ResourceDestroyed,
// Event Invoke
PropertyUpdated = 0x10,
EventOccured,
}
public enum IIPPacketAction : byte
{
// Request Manage
AttachResource = 0,
ReattachResource,
DetachResource,
CreateResource,
DeleteResource,
// Request Inquire
TemplateFromClassName = 0x8,
TemplateFromClassId,
TemplateFromResourceLink,
TemplateFromResourceId,
ResourceIdFromResourceLink,
// Request Invoke
InvokeFunction = 0x10,
GetProperty,
GetPropertyIfModified,
SetProperty,
}
public IIPPacketCommand Command
{
get;
set;
}
public IIPPacketAction Action
{
get;
set;
}
public IIPPacketEvent Event
{
get;
set;
}
public uint ResourceId { get; set; }
public uint NewResourceId { get; set; }
public uint ResourceAge { get; set; }
public byte[] Content { get; set; }
public byte ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public string ClassName { get; set; }
public string ResourceLink { get; set; }
public Guid ClassId { get; set; }
public byte MethodIndex { get; set; }
public string MethodName { get; set; }
public uint CallbackId { get; set; }
private uint dataLengthNeeded;
public override bool Compose()
{
return base.Compose();
}
bool NotEnough(uint offset, uint ends, uint needed)
{
if (offset + needed > ends)
{
dataLengthNeeded = needed - (ends - offset);
return true;
}
else
return false;
}
public override long Parse(byte[] data, uint offset, uint ends)
{
var oOffset = offset;
if (NotEnough(offset, ends, 1))
return -dataLengthNeeded;
Command = (IIPPacketCommand)(data[offset] >> 6);
if (Command == IIPPacketCommand.Event)
{
Event = (IIPPacketEvent)(data[offset++] & 0x3f);
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
ResourceId = data.GetUInt32(offset);
offset += 4;
}
else
{
Action = (IIPPacketAction)(data[offset++] & 0x3f);
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
CallbackId = data.GetUInt32(offset);
offset += 4;
}
if (Command == IIPPacketCommand.Event)
{
if (Event == IIPPacketEvent.ResourceReassigned)
{
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
NewResourceId = data.GetUInt32( offset);
offset += 4;
}
else if (Event == IIPPacketEvent.ResourceDestroyed)
{
// nothing to parse
}
else if (Event == IIPPacketEvent.PropertyUpdated)
{
if (NotEnough(offset, ends, 2))
return -dataLengthNeeded;
MethodIndex = data[offset++];
var dt = (DataType)data[offset++];
var size = dt.Size();// Codec.SizeOf(dt);
if (size < 0)
{
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
var cl = data.GetUInt32( offset);
offset += 4;
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
Content = data.Clip( offset - 5, cl + 5);
offset += cl;
}
else
{
if (NotEnough(offset, ends, (uint)size))
return -dataLengthNeeded;
Content = data.Clip(offset - 1, (uint)size + 1);
offset += (uint)size;
}
}
else if (Event == IIPPacketEvent.EventOccured)
{
if (NotEnough(offset, ends, 5))
return -dataLengthNeeded;
MethodIndex = data[offset++];
var cl = data.GetUInt32( offset);
offset += 4;
Content = data.Clip( offset, cl);
offset += cl;
}
}
else if (Command == IIPPacketCommand.Request)
{
if (Action == IIPPacketAction.AttachResource)
{
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
ResourceId = data.GetUInt32(offset);
offset += 4;
}
else if (Action == IIPPacketAction.ReattachResource)
{
if (NotEnough(offset, ends, 8))
return -dataLengthNeeded;
ResourceId = data.GetUInt32(offset);
offset += 4;
ResourceAge = data.GetUInt32(offset);
offset += 4;
}
else if (Action == IIPPacketAction.DetachResource)
{
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
ResourceId = data.GetUInt32(offset);
offset += 4;
}
else if (Action == IIPPacketAction.CreateResource)
{
if (NotEnough(offset, ends, 1))
return -dataLengthNeeded;
var cl = data[offset++];
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
ClassName = data.GetString(offset, cl);
offset += cl;
}
else if (Action == IIPPacketAction.DeleteResource)
{
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
ResourceId = data.GetUInt32(offset);
offset += 4;
}
else if (Action == IIPPacketAction.TemplateFromClassName)
{
if (NotEnough(offset, ends, 1))
return -dataLengthNeeded;
var cl = data[offset++];
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
ClassName = data.GetString(offset, cl);
offset += cl;
}
else if (Action == IIPPacketAction.TemplateFromClassId)
{
if (NotEnough(offset, ends, 16))
return -dataLengthNeeded;
ClassId = data.GetGuid(offset);
offset += 16;
}
else if (Action == IIPPacketAction.TemplateFromResourceLink)
{
if (NotEnough(offset, ends, 2))
return -dataLengthNeeded;
var cl = data.GetUInt16(offset);
offset += 2;
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
ResourceLink = data.GetString(offset, cl);
offset += cl;
}
else if (Action == IIPPacketAction.TemplateFromResourceId)
{
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
ResourceId = data.GetUInt32(offset);
offset += 4;
}
else if (Action == IIPPacketAction.ResourceIdFromResourceLink)
{
if (NotEnough(offset, ends, 2))
return -dataLengthNeeded;
var cl = data.GetUInt16(offset);
offset += 2;
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
ResourceLink = data.GetString(offset, cl);
offset += cl;
}
else if (Action == IIPPacketAction.InvokeFunction)
{
if (NotEnough(offset, ends, 9))
return -dataLengthNeeded;
ResourceId = data.GetUInt32(offset);
offset += 4;
MethodIndex = data[offset++];
var cl = data.GetUInt32(offset);
offset += 4;
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
Content = data.Clip(offset, cl);
offset += cl;
}
else if (Action == IIPPacketAction.GetProperty)
{
if (NotEnough(offset, ends, 5))
return -dataLengthNeeded;
ResourceId = data.GetUInt32(offset);
offset += 4;
MethodIndex = data[offset++];
}
else if (Action == IIPPacketAction.GetPropertyIfModified)
{
if (NotEnough(offset, ends, 9))
return -dataLengthNeeded;
ResourceId = data.GetUInt32(offset);
offset += 4;
MethodIndex = data[offset++];
ResourceAge = data.GetUInt32(offset);
offset += 4;
}
else if (Action == IIPPacketAction.SetProperty)
{
if (NotEnough(offset, ends, 6))
return -dataLengthNeeded;
ResourceId = data.GetUInt32(offset);
offset += 4;
MethodIndex = data[offset++];
var dt = (DataType)data[offset++];
var size = dt.Size();// Codec.SizeOf(dt);
if (size < 0)
{
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
var cl = data.GetUInt32(offset);
offset += 4;
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
Content = data.Clip(offset-5, cl + 5);
offset += cl;
}
else
{
if (NotEnough(offset, ends, (uint)size))
return -dataLengthNeeded;
Content = data.Clip(offset-1, (uint)size + 1);
offset += (uint)size;
}
}
}
else if (Command == IIPPacketCommand.Reply)
{
if (Action == IIPPacketAction.AttachResource
|| Action == IIPPacketAction.ReattachResource)
{
if (NotEnough(offset, ends, 26))
return -dataLengthNeeded;
ClassId = data.GetGuid(offset);
offset += 16;
ResourceAge = data.GetUInt32(offset);
offset += 4;
uint cl = data.GetUInt16(offset);
offset += 2;
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
ResourceLink = data.GetString(offset, cl);
offset += cl;
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
cl = data.GetUInt32(offset);
offset += 4;
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
Content = data.Clip(offset, cl);
offset += cl;
}
else if (Action == IIPPacketAction.DetachResource)
{
// nothing to do
}
else if (Action == IIPPacketAction.CreateResource)
{
if (NotEnough(offset, ends, 20))
return -dataLengthNeeded;
ClassId = data.GetGuid(offset);
offset += 16;
ResourceId = data.GetUInt32(offset);
offset += 4;
}
else if (Action == IIPPacketAction.DetachResource)
{
// nothing to do
}
else if (Action == IIPPacketAction.TemplateFromClassName
|| Action == IIPPacketAction.TemplateFromClassId
|| Action == IIPPacketAction.TemplateFromResourceLink
|| Action == IIPPacketAction.TemplateFromResourceId)
{
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
var cl = data.GetUInt32(offset);
offset += 4;
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
Content = data.Clip(offset, cl);
offset += cl;
}
else if (Action == IIPPacketAction.ResourceIdFromResourceLink)
{
if (NotEnough(offset, ends, 24))
return -dataLengthNeeded;
ClassId = data.GetGuid(offset);
offset += 16;
ResourceId = data.GetUInt32(offset);
offset += 4;
ResourceAge = data.GetUInt32(offset);
offset += 4;
}
else if (Action == IIPPacketAction.InvokeFunction
|| Action == IIPPacketAction.GetProperty
|| Action == IIPPacketAction.GetPropertyIfModified)
{
if (NotEnough(offset, ends, 1))
return -dataLengthNeeded;
var dt = (DataType)data[offset++];
var size = dt.Size();// Codec.SizeOf(dt);
if (size < 0)
{
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
var cl = data.GetUInt32(offset);
offset += 4;
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
Content = data.Clip(offset - 5, cl + 5);
offset += cl;
}
else
{
if (NotEnough(offset, ends, (uint)size))
return -dataLengthNeeded;
Content = data.Clip(offset - 1, (uint)size + 1);
offset += (uint)size;
}
}
else if (Action == IIPPacketAction.SetProperty)
{
// nothing to do
}
}
else if (Command == IIPPacketCommand.Error)
{
// Error
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
CallbackId = data.GetUInt32(offset);
if (NotEnough(offset, ends, 1))
return -dataLengthNeeded;
ErrorCode = data[offset++];
if (NotEnough(offset, ends, 4))
return -dataLengthNeeded;
var cl = data.GetUInt32(offset);
offset += 4;
if (NotEnough(offset, ends, cl))
return -dataLengthNeeded;
ErrorMessage = data.GetString(offset, cl);
offset += cl;
}
return offset - oOffset;
}
}
}

367
Esiur/Net/Packets/Packet.cs Normal file
View File

@ -0,0 +1,367 @@
/******************************************************************************\
* Uruky Sniffer Project *
* *
* Copyright (C) 2006 Ahmed Khalaf - ahmed@uruky.com *
* ahmed_baghdad@yahoo.com *
* http://www.uruky.com *
* http://www.dijlh.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2, or (at your option) *
* any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
* *
* File: Packet.cs *
* Description: Ethernet/ARP/IPv4/TCP/UDP Packet Decoding & Encoding Class *
* Compatibility: .Net Framework 2.0 / Mono 1.1.8 *
* *
\******************************************************************************/
using System;
using System.Text;
using Esiur.Misc;
using Esiur.Net.DataLink;
using System.Net.NetworkInformation;
using Esiur.Data;
namespace Esiur.Net.Packets
{
internal static class Functions
{
public static void AddData(ref byte[] dest, byte[] src)
{
int I = 0;
if (src == null)
{
return;
}
if (dest != null)
{
I = dest.Length;
Array.Resize(ref dest, dest.Length + src.Length);
//dest = (byte[])Resize(dest, dest.Length + src.Length);
}
else
{
dest = new byte[src.Length];
}
Array.Copy(src, 0, dest, I, src.Length);
}
/*
public static Array Resize(Array array, int newSize)
{
Type myType = Type.GetType(array.GetType().FullName.TrimEnd('[', ']'));
Array nA = Array.CreateInstance(myType, newSize);
Array.Copy(array, nA, (newSize > array.Length ? array.Length : newSize));
return nA;
} */
//Computes the checksum used in IP, ARP..., ie the
// "The 16 bit one's complement of the one 's complement sum
//of all 16 bit words" as seen in RFCs
// Returns a 4 characters hex string
// data's lenght must be multiple of 4, else zero padding
public static ushort IP_CRC16(byte[] data)
{
ulong Sum = 0;
bool Padding = false;
/// * Padding if needed
if (data.Length % 2 != 0)
{
Array.Resize(ref data, data.Length + 1);
//data = (byte[])Resize(data, data.Length + 1);
Padding = true;
}
int count = data.Length;
///* add 16-bit words */
while (count > 0) //1)
{
///* this is the inner loop */
Sum += GetInteger(data[count - 2], data[count - 1]);
///* Fold 32-bit sum to 16-bit */
while (Sum >> 16 != 0)
{
Sum = (Sum & 0XFFFF) + (Sum >> 16);
}
count -= 2;
}
/// * reverse padding
if (Padding)
{
Array.Resize(ref data, data.Length - 1);
//data = (byte[])Resize(data, data.Length - 1);
}
///* Return one's compliment of final sum.
//return (ushort)(ushort.MaxValue - (ushort)Sum);
return (ushort)(~Sum);
}
public static ushort GetInteger(byte B1, byte B2)
{
return BitConverter.ToUInt16(new byte[] { B2, B1 }, 0);
//return System.Convert.ToUInt16("&h" + GetHex(B1) + GetHex(B2));
}
public static uint GetLong(byte B1, byte B2, byte B3, byte B4)
{
return BitConverter.ToUInt32(new byte[] { B4, B3, B2, B1 }, 0);
//return System.Convert.ToUInt32("&h" + GetHex(B1) + GetHex(B2) + GetHex(B3) + GetHex(B4));
}
public static string GetHex(byte B)
{
return (((B < 15) ? 0 + System.Convert.ToString(B, 16).ToUpper() : System.Convert.ToString(B, 16).ToUpper()));
}
public static bool GetBit(uint B, byte Pos)
{
//return BitConverter.ToBoolean(BitConverter.GetBytes(B), Pos + 1);
return (B & (uint)(Math.Pow(2, (Pos - 1)))) == (Math.Pow(2, (Pos - 1)));
}
public static ushort RemoveBit(ushort I, byte Pos)
{
return (ushort)RemoveBit((uint)I, Pos);
}
public static uint RemoveBit(uint I, byte Pos)
{
if (GetBit(I, Pos))
{
return I - (uint)(Math.Pow(2, (Pos - 1)));
}
else
{
return I;
}
}
public static void SplitInteger(ushort I, ref byte BLeft, ref byte BRight)
{
byte[] b = BitConverter.GetBytes(I);
BLeft = b[1];
BRight = b[0];
//BLeft = I >> 8;
//BRight = (I << 8) >> 8;
}
public static void SplitLong(uint I, ref byte BLeft, ref byte BLeftMiddle, ref byte BRightMiddle, ref byte BRight)
{
byte[] b = BitConverter.GetBytes(I);
BLeft = b[3];
BLeftMiddle = b[2];
BRightMiddle = b[1];
BRight = b[0];
//BLeft = I >> 24;
//BLeftMiddle = (I << 8) >> 24;
//BRightMiddle = (I << 16) >> 24;
//BRight = (I << 24) >> 24;
}
}
public class PosixTime
{
ulong seconds;
ulong microseconds;
PosixTime(ulong Seconds, ulong Microseconds)
{
seconds = Seconds;
microseconds = Microseconds;
}
public override string ToString()
{
return seconds + "." + microseconds;
}
}
public class Packet
{
//public EtherServer2.EthernetSource Source;
public PacketSource Source;
public DateTime Timestamp;
public enum PPPType : ushort
{
IP = 0x0021, // Internet Protocol version 4 [RFC1332]
SDTP = 0x0049, // Serial Data Transport Protocol (PPP-SDTP) [RFC1963]
IPv6HeaderCompression = 0x004f, // IPv6 Header Compression
IPv6 = 0x0057, // Internet Protocol version 6 [RFC5072]
W8021dHelloPacket = 0x0201, // 802.1d Hello Packets [RFC3518]
IPv6ControlProtocol = 0x8057, // IPv6 Control Protocol [RFC5072]
}
public enum ProtocolType : ushort
{
IP = 0x800, // IPv4
ARP = 0x806, // Address Resolution Protocol
IPv6 = 0x86DD, // IPv6
FrameRelayARP = 0x0808, // Frame Relay ARP [RFC1701]
VINESLoopback = 0x0BAE, // VINES Loopback [RFC1701]
VINESEcho = 0x0BAF, // VINES ECHO [RFC1701]
TransEtherBridging = 0x6558, // TransEther Bridging [RFC1701]
RawFrameRelay = 0x6559, // Raw Frame Relay [RFC1701]
IEE8021QVLAN = 0x8100, // IEEE 802.1Q VLAN-tagged frames (initially Wellfleet)
SNMP = 0x814C, // SNMP [JKR1]
TCPIP_Compression = 0x876B, // TCP/IP Compression [RFC1144]
IPAutonomousSystems = 0x876C, // IP Autonomous Systems [RFC1701]
SecureData = 0x876D, // Secure Data [RFC1701]
PPP = 0x880B, // PPP [IANA]
MPLS = 0x8847, // MPLS [RFC5332]
MPLS_UpstreamAssignedLabel = 0x8848, // MPLS with upstream-assigned label [RFC5332]
PPPoEDiscoveryStage = 0x8863, // PPPoE Discovery Stage [RFC2516]
PPPoESessionStage = 0x8864, // PPPoE Session Stage [RFC2516]
}
/*
public static void GetPacketMACAddresses(Packet packet, out byte[] srcMAC, out byte[] dstMAC)
{
// get the node address
Packet root = packet.RootPacket;
if (root is TZSPPacket)
{
TZSPPacket tp = (TZSPPacket)root;
if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.Ethernet)
{
EthernetPacket ep = (EthernetPacket)tp.SubPacket;
srcMAC = ep.SourceMAC;
dstMAC = ep.DestinationMAC;
}
else if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.IEEE802_11)
{
W802_11Packet wp = (W802_11Packet)tp.SubPacket;
srcMAC = wp.SA;
dstMAC = wp.DA;
}
else
{
srcMAC = null;
dstMAC = null;
}
}
else if (root is EthernetPacket)
{
EthernetPacket ep = (EthernetPacket)root;
srcMAC = ep.SourceMAC;
dstMAC = ep.DestinationMAC;
}
else if (root is W802_11Packet)
{
W802_11Packet wp = (W802_11Packet)root;
srcMAC = wp.SA;
dstMAC = wp.DA;
}
else
{
srcMAC = null;
dstMAC = null;
}
}
public static void GetPacketAddresses(Packet packet, ref string srcMAC, ref string dstMAC, ref string srcIP, ref string dstIP)
{
if (packet is TCPv4Packet)
{
if (packet.ParentPacket is IPv4Packet)
{
IPv4Packet ip = (IPv4Packet)packet.ParentPacket;
srcIP = ip.SourceIP.ToString();
dstIP = ip.DestinationIP.ToString();
}
}
// get the node address
Packet root = packet.RootPacket;
if (root is TZSPPacket)
{
TZSPPacket tp = (TZSPPacket)root;
if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.Ethernet)
{
EthernetPacket ep = (EthernetPacket)tp.SubPacket;
srcMAC = DC.GetPhysicalAddress(ep.SourceMAC, 0).ToString();
dstMAC = DC.GetPhysicalAddress(ep.DestinationMAC, 0).ToString();
}
else if (tp.Protocol == TZSPPacket.TZSPEncapsulatedProtocol.IEEE802_11)
{
W802_11Packet wp = (W802_11Packet)tp.SubPacket;
srcMAC = DC.GetPhysicalAddress(wp.SA, 0).ToString();
dstMAC = DC.GetPhysicalAddress(wp.DA, 0).ToString();
}
}
else if (root is EthernetPacket)
{
EthernetPacket ep = (EthernetPacket)root;
srcMAC = DC.GetPhysicalAddress(ep.SourceMAC, 0).ToString();
dstMAC = DC.GetPhysicalAddress(ep.DestinationMAC, 0).ToString();
}
else if (root is W802_11Packet)
{
W802_11Packet wp = (W802_11Packet)root;
srcMAC = DC.GetPhysicalAddress(wp.SA, 0).ToString();
dstMAC = DC.GetPhysicalAddress(wp.DA, 0).ToString();
}
}
*/
PosixTime Timeval;
public byte[] Header;
public byte[] Preamble;
//public byte[] Payload;
public byte[] Data;
public Packet SubPacket;
public Packet ParentPacket;
public virtual long Parse(byte[] data, uint offset, uint ends) { return 0; }
public virtual bool Compose() { return false; }
public Packet RootPacket
{
get
{
Packet root = this;
while (root.ParentPacket != null)
root = root.ParentPacket;
return root;
}
}
public Packet LeafPacket
{
get
{
Packet leaf = this;
while (leaf.SubPacket != null)
leaf = leaf.SubPacket;
return leaf;
}
}
}
}
/************************************ EOF *************************************/

View File

@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Misc;
using Esiur.Data;
namespace Esiur.Net.Packets
{
public class WebsocketPacket : Packet
{
public enum WSOpcode : byte
{
ContinuationFrame = 0x0, // %x0 denotes a continuation frame
TextFrame = 0x1, // %x1 denotes a text frame
BinaryFrame = 0x2, // %x2 denotes a binary frame
// %x3-7 are reserved for further non-control frames
ConnectionClose = 0x8, // %x8 denotes a connection close
Ping = 0x9, // %x9 denotes a ping
Pong = 0xA, // %xA denotes a pong
//* %xB-F are reserved for further control frames
}
public bool FIN;
public bool RSV1;
public bool RSV2;
public bool RSV3;
public WSOpcode Opcode;
public bool Mask;
public long PayloadLength;
// public UInt32 MaskKey;
public byte[] MaskKey;
public byte[] Message;
public override string ToString()
{
return "WebsocketPacket"
+ "\n\tFIN: " + FIN
+ "\n\tOpcode: " + Opcode
+ "\n\tPayload: " + PayloadLength
+ "\n\tMaskKey: " + MaskKey
+ "\n\tMessage: " + (Message != null ? Message.Length.ToString() : "NULL");
}
public override bool Compose()
{
var pkt = new List<byte>();
pkt.Add((byte)((FIN ? 0x80 : 0x0) |
(RSV1 ? 0x40 : 0x0) |
(RSV2 ? 0x20 : 0x0) |
(RSV3 ? 0x10 : 0x0) |
(byte)Opcode));
// calculate length
if (Message.Length > UInt16.MaxValue)
// 4 bytes
{
pkt.Add((byte)((Mask ? 0x80 : 0x0) | 127));
pkt.AddRange(DC.ToBytes((UInt64)Message.LongCount()));
}
else if (Message.Length > 125)
// 2 bytes
{
pkt.Add((byte)((Mask ? 0x80 : 0x0) | 126));
pkt.AddRange(DC.ToBytes((UInt16)Message.Length));
}
else
{
pkt.Add((byte)((Mask ? 0x80 : 0x0) | Message.Length));
}
if (Mask)
{
pkt.AddRange(MaskKey);
}
pkt.AddRange(Message);
Data = pkt.ToArray();
return true;
}
public override long Parse(byte[] data, uint offset, uint ends)
{
try
{
long needed = 2;
var length = (ends - offset);
if (length < needed)
{
//Console.WriteLine("stage 1 " + needed);
return length - needed;
}
uint oOffset = offset;
FIN = ((data[offset] & 0x80) == 0x80);
RSV1 = ((data[offset] & 0x40) == 0x40);
RSV2 = ((data[offset] & 0x20) == 0x20);
RSV3 = ((data[offset] & 0x10) == 0x10);
Opcode = (WSOpcode)(data[offset++] & 0xF);
Mask = ((data[offset] & 0x80) == 0x80);
PayloadLength = (long)(data[offset++] & 0x7F);
if (Mask)
needed += 4;
if (PayloadLength == 126)
{
needed += 2;
if (length < needed)
{
//Console.WriteLine("stage 2 " + needed);
return length - needed;
}
PayloadLength = DC.GetUInt16(data, offset);
offset += 2;
}
else if (PayloadLength == 127)
{
needed += 8;
if (length < needed)
{
//Console.WriteLine("stage 3 " + needed);
return length - needed;
}
PayloadLength = DC.GetInt64(data, offset);
offset += 8;
}
if (Mask)
{
MaskKey = new byte[4];
MaskKey[0] = data[offset++];
MaskKey[1] = data[offset++];
MaskKey[2] = data[offset++];
MaskKey[3] = data[offset++];
//MaskKey = DC.GetUInt32(data, offset);
//offset += 4;
}
needed += PayloadLength;
if (length < needed)
{
//Console.WriteLine("stage 4");
return length - needed;
}
// if ((int)PayloadLength > (ends - offset))
// {
// return -((int)PayloadLength - (ends - offset));
// }
else
{
Message = DC.Clip(data, offset, (uint)PayloadLength);
if (Mask)
{
//var aMask = BitConverter.GetBytes(MaskKey);
for (int i = 0; i < Message.Length; i++)
{
Message[i] = (byte)(Message[i] ^ MaskKey[i % 4]);
}
}
return (offset - oOffset) + (int)PayloadLength;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.WriteLine(offset + "::" + DC.ToHex(data));
throw ex;
}
}
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net;
using System.Collections;
using System.Collections.Generic;
using Esiur.Data;
using Esiur.Misc;
using System.Collections.Concurrent;
using Esiur.Resource;
using Esiur.Engine;
namespace Esiur.Net.Sockets
{
public delegate void ISocketReceiveEvent(NetworkBuffer buffer);
public delegate void ISocketConnectEvent();
public delegate void ISocketCloseEvent();
public interface ISocket: IDestructible
{
SocketState State { get; }
event ISocketReceiveEvent OnReceive;
event ISocketConnectEvent OnConnect;
event ISocketCloseEvent OnClose;
void Send(byte[] message);
void Send(byte[] message, int offset, int size);
void Close();
bool Connect(string hostname, ushort port);
bool Begin();
//ISocket Accept();
AsyncReply<ISocket> Accept();
IPEndPoint RemoteEndPoint { get; }
IPEndPoint LocalEndPoint { get; }
}
}

View File

@ -0,0 +1,310 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using Esiur.Misc;
using Esiur.Engine;
using System.Threading;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Esiur.Resource;
using System.Threading.Tasks;
using Esiur.Data;
namespace Esiur.Net.Sockets
{
public class SSLSocket : ISocket
{
Socket sock;
byte[] receiveBuffer;
NetworkBuffer receiveNetworkBuffer = new NetworkBuffer();
object sendLock = new object();
Queue<byte[]> sendBufferQueue = new Queue<byte[]>();
bool asyncSending;
SocketState state = SocketState.Initial;
public event ISocketReceiveEvent OnReceive;
public event ISocketConnectEvent OnConnect;
public event ISocketCloseEvent OnClose;
public event DestroyedEvent OnDestroy;
SslStream ssl;
X509Certificate2 cert;
bool server;
string hostname;
private void Connected(Task t)
{
if (server)
{
ssl.AuthenticateAsServerAsync(cert).ContinueWith(Authenticated);
}
else
{
ssl.AuthenticateAsClientAsync(hostname).ContinueWith(Authenticated);
}
}
public bool Connect(string hostname, ushort port)
{
try
{
this.hostname = hostname;
server = false;
state = SocketState.Connecting;
sock.ConnectAsync(hostname, port).ContinueWith(Connected);
return true;
}
catch
{
return false;
}
}
private void DataSent(Task task)
{
try
{
if (sendBufferQueue.Count > 0)
{
byte[] data = sendBufferQueue.Dequeue();
lock (sendLock)
ssl.WriteAsync(data, 0, data.Length).ContinueWith(DataSent);
}
else
{
asyncSending = false;
}
}
catch (Exception ex)
{
if (state != SocketState.Closed && !sock.Connected)
{
state = SocketState.Terminated;
Close();
}
asyncSending = false;
Global.Log("SSLSocket", LogType.Error, ex.ToString());
}
}
public IPEndPoint LocalEndPoint
{
get { return (IPEndPoint)sock.LocalEndPoint; }
}
public SSLSocket()
{
sock = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
receiveBuffer = new byte[sock.ReceiveBufferSize];
}
public SSLSocket(IPEndPoint localEndPoint, X509Certificate2 certificate)
{
// create the socket
sock = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
state = SocketState.Listening;
// bind
sock.Bind(localEndPoint);
// start listening
sock.Listen(UInt16.MaxValue);
cert = certificate;
}
public IPEndPoint RemoteEndPoint
{
get { return (IPEndPoint)sock.RemoteEndPoint; }
}
public SocketState State
{
get
{
return state;
}
}
public SSLSocket(Socket Socket, X509Certificate2 certificate, bool authenticateAsServer)
{
cert = certificate;
sock = Socket;
receiveBuffer = new byte[sock.ReceiveBufferSize];
ssl = new SslStream(new NetworkStream(sock));
server = authenticateAsServer;
}
public void Close()
{
if (state != SocketState.Closed && state != SocketState.Terminated)
state = SocketState.Closed;
if (sock.Connected)
{
try
{
sock.Shutdown(SocketShutdown.Both);
}
catch
{
state = SocketState.Terminated;
}
}
sock.Shutdown(SocketShutdown.Both);
OnClose?.Invoke();
}
public void Send(byte[] message)
{
Send(message, 0, message.Length);
}
public void Send(byte[] message, int offset, int size)
{
lock (sendLock)
{
if (asyncSending)
{
sendBufferQueue.Enqueue(message.Clip((uint)offset, (uint)size));
}
else
{
asyncSending = true;
ssl.WriteAsync(message, offset, size).ContinueWith(DataSent);
}
}
}
void Authenticated(Task task)
{
try
{
state = SocketState.Established;
OnConnect?.Invoke();
if (!server)
Begin();
}
catch (Exception ex)
{
state = SocketState.Terminated;
Close();
Global.Log(ex);
}
}
private void DataReceived(Task<int> task)
{
try
{
// SocketError err;
if (state == SocketState.Closed || state == SocketState.Terminated)
return;
if (task.Result <= 0)
{
Close();
return;
}
receiveNetworkBuffer.Write(receiveBuffer, 0, (uint)task.Result);
OnReceive?.Invoke(receiveNetworkBuffer);
if (state == SocketState.Established)
ssl.ReadAsync(receiveBuffer, 0, receiveBuffer.Length).ContinueWith(DataReceived);
}
catch (Exception ex)
{
if (state != SocketState.Closed && !sock.Connected)
{
state = SocketState.Terminated;
Close();
}
Global.Log("SSLSocket", LogType.Error, ex.ToString());
}
}
public bool Begin()
{
if (state == SocketState.Established)
{
ssl.ReadAsync(receiveBuffer, 0, receiveBuffer.Length).ContinueWith(DataReceived);
return true;
}
else
return false;
}
public bool Trigger(ResourceTrigger trigger)
{
return true;
}
public void Destroy()
{
Close();
OnDestroy?.Invoke(this);
}
public AsyncReply<ISocket> Accept()
{
var reply = new AsyncReply<ISocket>();
try
{
sock.AcceptAsync().ContinueWith((x) =>
{
try
{
reply.Trigger(new SSLSocket(x.Result, cert, true));
}
catch
{
reply.Trigger(null);
}
}, null);
}
catch
{
state = SocketState.Terminated;
return null;
}
return reply;
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Net.Sockets
{
public enum SocketState
{
Initial,
Listening,
Connecting,
Established,
Closed,
Terminated
}
}

View File

@ -0,0 +1,291 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using Esiur.Misc;
using Esiur.Engine;
using System.Threading;
using Esiur.Resource;
using System.Threading.Tasks;
using Esiur.Data;
namespace Esiur.Net.Sockets
{
public class TCPSocket : ISocket
{
Socket sock;
byte[] receiveBuffer;
ArraySegment<byte> receiveBufferSegment;
NetworkBuffer receiveNetworkBuffer = new NetworkBuffer();
object sendLock = new object();
Queue<byte[]> sendBufferQueue = new Queue<byte[]>();
bool asyncSending;
SocketState state = SocketState.Initial;
public event ISocketReceiveEvent OnReceive;
public event ISocketConnectEvent OnConnect;
public event ISocketCloseEvent OnClose;
public event DestroyedEvent OnDestroy;
private void Connected(Task t)
{
state = SocketState.Established;
OnConnect?.Invoke();
Begin();
}
public bool Begin()
{
sock.ReceiveAsync(receiveBufferSegment, SocketFlags.None).ContinueWith(DataReceived);
return true;
}
public bool Connect(string hostname, ushort port)
{
try
{
state = SocketState.Connecting;
sock.ConnectAsync(hostname, port).ContinueWith(Connected);
return true;
}
catch
{
return false;
}
}
private void DataReceived(Task<int> task)
{
try
{
// SocketError err;
if (state == SocketState.Closed || state == SocketState.Terminated)
return;
if (task.Result <= 0)
{
Close();
return;
}
receiveNetworkBuffer.Write(receiveBuffer, 0, (uint)task.Result);
OnReceive?.Invoke(receiveNetworkBuffer);
if (state == SocketState.Established)
sock.ReceiveAsync(receiveBufferSegment, SocketFlags.None).ContinueWith(DataReceived);
}
catch (Exception ex)
{
if (state != SocketState.Closed && !sock.Connected)
{
state = SocketState.Terminated;
Close();
}
Global.Log("TCPSocket", LogType.Error, ex.ToString());
}
}
public IPEndPoint LocalEndPoint
{
get { return (IPEndPoint)sock.LocalEndPoint; }
}
public TCPSocket()
{
sock = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
receiveBuffer = new byte[sock.ReceiveBufferSize];
receiveBufferSegment = new ArraySegment<byte>(receiveBuffer);
}
public TCPSocket(string hostname, ushort port)
{
// create the socket
sock = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
receiveBuffer = new byte[sock.ReceiveBufferSize];
receiveBufferSegment = new ArraySegment<byte>(receiveBuffer);
Connect(hostname, port);
}
private void DataSent(Task<int> task)
{
try
{
if (sendBufferQueue.Count > 0)
{
byte[] data = sendBufferQueue.Dequeue();
lock (sendLock)
sock.SendAsync(new ArraySegment<byte>(data), SocketFlags.None).ContinueWith(DataSent);
}
else
{
asyncSending = false;
}
}
catch (Exception ex)
{
if (state != SocketState.Closed && !sock.Connected)
{
state = SocketState.Terminated;
Close();
}
asyncSending = false;
Global.Log("TCPSocket", LogType.Error, ex.ToString());
}
}
public TCPSocket(IPEndPoint localEndPoint)
{
// create the socket
sock = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
receiveBuffer = new byte[sock.ReceiveBufferSize];
state = SocketState.Listening;
// bind
sock.Bind(localEndPoint);
// start listening
sock.Listen(UInt16.MaxValue);
}
public IPEndPoint RemoteEndPoint
{
get { return (IPEndPoint)sock.RemoteEndPoint; }
}
public SocketState State
{
get
{
return state;
}
}
public TCPSocket(Socket socket)
{
sock = socket;
receiveBuffer = new byte[sock.ReceiveBufferSize];
receiveBufferSegment = new ArraySegment<byte>(receiveBuffer);
if (socket.Connected)
state = SocketState.Established;
}
public void Close()
{
if (state != SocketState.Closed && state != SocketState.Terminated)
state = SocketState.Closed;
if (sock.Connected)
{
try
{
sock.Shutdown(SocketShutdown.Both);
}
catch
{
state = SocketState.Terminated;
}
sock.Shutdown(SocketShutdown.Both);// Close();
OnClose?.Invoke();
}
}
public void Send(byte[] message)
{
Send(message, 0, message.Length);
}
public void Send(byte[] message, int offset, int size)
{
lock (sendLock)
{
if (asyncSending)
{
sendBufferQueue.Enqueue(message.Clip((uint)offset, (uint)size));
}
else
{
asyncSending = true;
sock.SendAsync(new ArraySegment<byte>(message, offset, size), SocketFlags.None).ContinueWith(DataSent);
}
}
}
public bool Trigger(ResourceTrigger trigger)
{
return true;
}
public void Destroy()
{
Close();
OnDestroy?.Invoke(this);
}
public AsyncReply<ISocket> Accept()
{
var reply = new AsyncReply<ISocket>();
try
{
sock.AcceptAsync().ContinueWith((x) =>
{
try
{
reply.Trigger(new TCPSocket(x.Result));
}
catch
{
reply.Trigger(null);
}
});
}
catch
{
state = SocketState.Terminated;
return null;
}
return reply;
}
}
}

View File

@ -0,0 +1,220 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using Esiur.Net.Packets;
using Esiur.Misc;
using System.IO;
using Esiur.Engine;
using Esiur.Resource;
namespace Esiur.Net.Sockets
{
public class WSSocket : ISocket
{
WebsocketPacket pkt_receive = new WebsocketPacket();
WebsocketPacket pkt_send = new WebsocketPacket();
ISocket sock;
NetworkBuffer receiveNetworkBuffer = new NetworkBuffer();
object sendLock = new object();
public event ISocketReceiveEvent OnReceive;
public event ISocketConnectEvent OnConnect;
public event ISocketCloseEvent OnClose;
public event DestroyedEvent OnDestroy;
long totalSent, totalReceived;
public IPEndPoint LocalEndPoint
{
get { return (IPEndPoint)sock.LocalEndPoint; }
}
public IPEndPoint RemoteEndPoint
{
get { return sock.RemoteEndPoint; }
}
public SocketState State
{
get
{
return sock.State;
}
}
public WSSocket(ISocket socket)
{
pkt_send.FIN = true;
pkt_send.Mask = false;
pkt_send.Opcode = WebsocketPacket.WSOpcode.BinaryFrame;
sock = socket;
sock.OnClose += Sock_OnClose;
sock.OnConnect += Sock_OnConnect;
sock.OnReceive += Sock_OnReceive;
}
private void Sock_OnReceive(NetworkBuffer buffer)
{
if (sock.State == SocketState.Closed || sock.State == SocketState.Terminated)
return;
if (buffer.Protected)
return;
var msg = buffer.Read();
var wsPacketLength = pkt_receive.Parse(msg, 0, (uint)msg.Length);
if (wsPacketLength < 0)
{
buffer.Protect(msg, 0, (uint)msg.Length + (uint)-wsPacketLength);
return;
}
uint offset = 0;
while (wsPacketLength > 0)
{
if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.ConnectionClose)
{
Close();
return;
}
else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.Ping)
{
var pkt_pong = new WebsocketPacket();
pkt_pong.FIN = true;
pkt_pong.Mask = false;
pkt_pong.Opcode = WebsocketPacket.WSOpcode.Pong;
pkt_pong.Message = pkt_receive.Message;
offset += (uint)wsPacketLength;
Send(pkt_pong);
}
else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.Pong)
{
offset += (uint)wsPacketLength;
}
else if (pkt_receive.Opcode == WebsocketPacket.WSOpcode.BinaryFrame
|| pkt_receive.Opcode == WebsocketPacket.WSOpcode.TextFrame
|| pkt_receive.Opcode == WebsocketPacket.WSOpcode.ContinuationFrame)
{
totalReceived += pkt_receive.Message.Length;
//Console.WriteLine("RX " + pkt_receive.Message.Length + "/" + totalReceived);// + " " + DC.ToHex(message, 0, (uint)size));
receiveNetworkBuffer.Write(pkt_receive.Message);
offset += (uint)wsPacketLength;
}
else
Console.WriteLine("Unknown WS opcode:" + pkt_receive.Opcode);
if (offset == msg.Length)
{
OnReceive?.Invoke(receiveNetworkBuffer);
return;
}
wsPacketLength = pkt_receive.Parse(msg, offset, (uint)msg.Length);
}
if (wsPacketLength < 0)//(offset < msg.Length) && (offset > 0))
{
//receiveNetworkBuffer.HoldFor(msg, offset, (uint)(msg.Length - offset), (uint)msg.Length + (uint)-wsPacketLength);
// save the incomplete packet to the heldBuffer queue
receiveNetworkBuffer.HoldFor(msg, offset, (uint)(msg.Length - offset), (uint)(msg.Length - offset) + (uint)-wsPacketLength);
}
OnReceive?.Invoke(receiveNetworkBuffer);
}
private void Sock_OnConnect()
{
OnConnect?.Invoke();
}
private void Sock_OnClose()
{
OnClose?.Invoke();
}
public void Send(WebsocketPacket packet)
{
lock(sendLock)
if (packet.Compose())
sock.Send(packet.Data);
}
public void Send(byte[] message)
{
lock(sendLock)
{
totalSent += message.Length;
//Console.WriteLine("TX " + message.Length +"/"+totalSent);// + " " + DC.ToHex(message, 0, (uint)size));
pkt_send.Message = message;
if (pkt_send.Compose())
sock.Send(pkt_send.Data);
}
}
public void Send(byte[] message, int offset, int size)
{
lock (sendLock)
{
totalSent += size;
//Console.WriteLine("TX " + size + "/"+totalSent);// + " " + DC.ToHex(message, 0, (uint)size));
pkt_send.Message = new byte[size];
Buffer.BlockCopy(message, offset, pkt_send.Message, 0, size);
if (pkt_send.Compose())
sock.Send(pkt_send.Data);
}
}
public void Close()
{
sock.Close();
}
public bool Connect(string hostname, ushort port)
{
throw new NotImplementedException();
}
public bool Begin()
{
return sock.Begin();
}
public bool Trigger(ResourceTrigger trigger)
{
return true;
}
public void Destroy()
{
Close();
OnDestroy?.Invoke(this);
}
public AsyncReply<ISocket> Accept()
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Net.Sockets;
using System.Net;
using System.Collections;
using Esiur.Misc;
using Esiur.Data;
namespace Esiur.Net.TCP
{
public class TCPConnection: NetworkConnection
{
private KeyList<string, object> variables = new KeyList<string, object>();
public KeyList<string, object> Variables
{
get
{
return variables;
}
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Esiur.Data;
using Esiur.Net.Sockets;
using Esiur.Engine;
using Esiur.Resource;
namespace Esiur.Net.TCP
{
public abstract class TCPFilter: IResource
{
public Instance Instance
{
get;
set;
}
public event DestroyedEvent OnDestroy;
public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
public virtual bool Connected(TCPConnection sender)
{
return false;
}
public virtual bool Disconnected(TCPConnection sender)
{
return false;
}
public abstract bool Execute(byte[] msg, NetworkBuffer data, TCPConnection sender);
public void Destroy()
{
throw new NotImplementedException();
}
}
}

195
Esiur/Net/TCP/TCPServer.cs Normal file
View File

@ -0,0 +1,195 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Net.Sockets;
using Esiur.Misc;
using System.Threading;
using Esiur.Data;
using Esiur.Engine;
using System.Net;
using Esiur.Resource;
namespace Esiur.Net.TCP
{
public class TCPServer : NetworkServer<TCPConnection>, IResource
{
[Storable]
string ip
{
get;
set;
}
[Storable]
ushort port
{
get;
set;
}
[Storable]
uint timeout
{
get;
set;
}
[Storable]
uint clock
{
get;
set;
}
public Instance Instance { get; set; }
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
if (trigger == ResourceTrigger.Initialize)
{
TCPSocket listener;
if (ip != null)
listener =new TCPSocket(new IPEndPoint(IPAddress.Parse(ip), port));
else
listener = new TCPSocket(new IPEndPoint(IPAddress.Any, port));
Start(listener, timeout, clock);
}
else if (trigger == ResourceTrigger.Terminate)
{
Stop();
}
else if (trigger == ResourceTrigger.SystemReload)
{
Trigger(ResourceTrigger.Terminate);
Trigger(ResourceTrigger.Initialize);
}
return new AsyncReply<bool>(true);
}
protected override void DataReceived(TCPConnection sender, NetworkBuffer data)
{
//throw new NotImplementedException();
var msg = data.Read();
foreach (Instance instance in Instance.Children)
{
var f = instance.Resource as TCPFilter;
if (f.Execute(msg, data, sender))
return;
}
}
private void SessionModified(TCPConnection Session, string Key, object NewValue)
{
}
/*
public TCPServer(string IP, int Port, int Timeout, int Clock)
: base(IP, Port, Timeout, Clock)
{
if (Timeout > 0 && Clock > 0)
{
mTimer = new Timer(OnlineThread, null, 0, Clock * 1000);// TimeSpan.FromSeconds(Clock));
mTimeout = Timeout;
}
}
*/
/*
private void OnlineThread(object state)
{
List<TCPConnection> ToBeClosed = null;
//Console.WriteLine("Minute Thread");
if (Connections.Count > 0)
{
Global.Log("TCPServer:OnlineThread", LogType.Debug,
//"Tick:" + DateTime.Now.Subtract(Connections[0].LastAction).TotalSeconds + ":" + mTimeout + ":" +
"Tick | Connections: " + Connections.Count + " Threads:" + System.Diagnostics.Process.GetCurrentProcess().Threads.Count);
}
try
{
foreach (TCPConnection c in Connections)//.Values)
{
if (DateTime.Now.Subtract(c.LastAction).TotalSeconds >= mTimeout)
{
if (ToBeClosed == null)
ToBeClosed = new List<TCPConnection>();
ToBeClosed.Add(c);
}
}
if (ToBeClosed != null)
{
Global.Log("TCPServer:OnlineThread", LogType.Debug, "Inactive Closed:" + ToBeClosed.Count);
foreach (TCPConnection c in ToBeClosed)
c.Close();
ToBeClosed.Clear();
ToBeClosed = null;
}
}
catch (Exception ex)
{
Global.Log("TCPServer:OnlineThread", LogType.Debug, ex.ToString());
}
}
*/
//~TCPServer()
//{
// StopServer();
//}
protected override void ClientConnected(TCPConnection sender)
{
//Console.WriteLine("TCP Client Connected");
// Global.Log("TCPServer",
// LogType.Debug,
// "Connected:" + Connections.Count
// + ":" + sender.RemoteEndPoint.ToString());
foreach (Instance instance in Instance.Children)
{
var f = instance.Resource as TCPFilter;
f.Connected(sender);
}
}
protected override void ClientDisconnected(TCPConnection sender)
{
//Console.WriteLine("TCP Client Disconnected");
// Global.Log("TCPServer", LogType.Debug, "Disconnected:" + Connections.Count);// + ":" + sender.RemoteEndPoint.ToString());
foreach (Instance instance in Instance.Children)
{
try
{
var f = instance.Resource as TCPFilter;
f.Disconnected(sender);
}
catch(Exception ex)
{
Global.Log(ex);
}
}
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Net.TCP
{
public class TCPSession : NetworkSession
{
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Net;
using Esiur.Data;
using Esiur.Engine;
using Esiur.Resource;
namespace Esiur.Net.UDP
{
public abstract class UDPFilter : IResource
{
public Instance Instance
{
get;
set;
}
public event DestroyedEvent OnDestroy;
public abstract AsyncReply<bool> Trigger(ResourceTrigger trigger);
public abstract bool Execute(byte[] data, IPEndPoint sender);
public void Destroy()
{
throw new NotImplementedException();
}
}
}

173
Esiur/Net/UDP/UDPServer.cs Normal file
View File

@ -0,0 +1,173 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System.Collections;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Engine;
using Esiur.Resource;
namespace Esiur.Net.UDP
{
/* public class IIPConnection
{
public EndPoint SenderPoint;
public
}*/
public class UDPServer : IResource
{
Thread Receiver;
UdpClient Udp;
public event DestroyedEvent OnDestroy;
public Instance Instance
{
get;
set;
}
[Storable]
string ip
{
get;
set;
}
[Storable]
ushort port
{
get;
set;
}
public bool Trigger(ResourceTrigger trigger)
{
if (trigger == ResourceTrigger.Initialize)
{
var address = ip == null ? IPAddress.Any : IPAddress.Parse(ip);
Udp = new UdpClient(new IPEndPoint(address, (int)port));
Receiver = new Thread(Receiving);
Receiver.Start();
}
else if (trigger == ResourceTrigger.Terminate)
{
if (Receiver != null)
Receiver.Abort();
}
return true;
}
private void Receiving()
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
byte[] b = Udp.Receive(ref ep);
foreach(var child in Instance.Children)
{
var f = child as UDPFilter;
try
{
if (f.Execute(b, ep))
{
break;
}
}
catch (Exception ex)
{
Global.Log("UDPServer", LogType.Error, ex.ToString());
//Console.WriteLine(ex.ToString());
}
}
}
}
public bool Send(byte[] Data, int Count, IPEndPoint EP)
{
try
{
Udp.Send(Data, Count, EP);
return true;
}
catch
{
return false;
}
}
public bool Send(byte[] Data, IPEndPoint EP)
{
try
{
Udp.Send(Data, Data.Length, EP);
return true;
}
catch
{
return false;
}
}
public bool Send(byte[] Data, int Count, string Host, int Port)
{
try
{
Udp.Send(Data, Count, Host, Port);
return true;
}
catch
{
return false;
}
}
public bool Send(byte[] Data, string Host, int Port)
{
try
{
Udp.Send(Data, Data.Length, Host, Port);
return true;
}
catch
{
return false;
}
}
public bool Send(string Data, IPEndPoint EP)
{
try
{
Udp.Send(Encoding.Default.GetBytes(Data), Data.Length, EP);
return true;
}
catch
{
return false;
}
}
public bool Send(string Data, string Host, int Port)
{
try
{
Udp.Send(Encoding.Default.GetBytes(Data), Data.Length, Host, Port);
return true;
}
catch
{
return false;
}
}
public void Destroy()
{
Udp.Close();
OnDestroy?.Invoke(this);
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esiur.Data;
using Esiur.Engine;
namespace Esiur.Resource
{
public delegate bool QueryFilter<T>(T value);
public interface IResource : IDestructible
{
AsyncReply<bool> Trigger(ResourceTrigger trigger);
Instance Instance
{
get;
set;
}
}
}

17
Esiur/Resource/IStore.cs Normal file
View File

@ -0,0 +1,17 @@
using Esiur.Engine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource
{
public interface IStore:IResource
{
AsyncReply<IResource> Get(string path);
AsyncReply<IResource> Retrieve(uint iid);
bool Put(IResource resource);
string Link(IResource resource);
}
}

427
Esiur/Resource/Instance.cs Normal file
View File

@ -0,0 +1,427 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Esiur.Data;
using System.Runtime.CompilerServices;
using System.Reflection;
using Esiur.Net.IIP;
using Esiur.Misc;
using Esiur.Security.Permissions;
using Esiur.Resource.Template;
namespace Esiur.Resource
{
public class Instance
{
string name;
AutoList<IResource, Instance> children;// = new AutoList<IResource, Instance>();
IResource resource;
IStore store;
AutoList<IResource, Instance> parents;// = new AutoList<IResource>();
bool inherit;
ResourceTemplate template;
AutoList<IPermissionManager, Instance> managers;// = new AutoList<IPermissionManager, Instance>();
public delegate void ResourceModifiedEvent(IResource resource, string propertyName, object newValue, object oldValue);
public delegate void ResourceEventOccurredEvent(IResource resource, string eventName, string[] receivers, object[] args);
public delegate void ResourceDestroyedEvent(IResource resource);
public event ResourceModifiedEvent ResourceModified;
public event ResourceEventOccurredEvent ResourceEventOccured;
public event ResourceDestroyedEvent ResourceDestroyed;
KeyList<string, object> attributes = new KeyList<string, object>();
List<uint> ages = new List<uint>();
private uint age;
uint id;
/// <summary>
/// Instance attributes are custom properties associated with the instance, a place to store information by IStore.
/// </summary>
public KeyList<string, object> Attributes
{
get
{
return attributes;
}
}
/// <summary>
/// Get the age of a given property index.
/// </summary>
/// <param name="index">Zero-based property index.</param>
/// <returns>Age.</returns>
public uint GetAge(byte index)
{
if (index < ages.Count)
return ages[index];
else
return 0;
}
/// <summary>
/// Age of the instance, increments by 1 in every modification.
/// </summary>
public uint Age
{
get { return age; }
internal set { age = value; }
}
/// <summary>
/// Instance Id.
/// </summary>
public uint Id
{
get { return id; }
}
/// <summary>
/// Import properties from bytes array.
/// </summary>
/// <param name="properties"></param>
/// <returns></returns>
public bool Deserialize(object[] properties)
{
foreach (var pt in template.Properties)
{
#if NETSTANDARD1_5
var pi = resource.GetType().GetTypeInfo().GetProperty(pt.Name);
#else
var pi = resource.GetType().GetProperty(pt.Name);
#endif
if (!(properties[pt.Index] is NotModified))
pi.SetValue(resource, properties[pt.Index]);
}
return true;
}
/// <summary>
/// Export all properties with ResourceProperty attributed as bytes array.
/// </summary>
/// <returns></returns>
public object[] Serialize()
{
List<object> props = new List<object>();
foreach (var pt in template.Properties)
{
#if NETSTANDARD1_5
var pi = resource.GetType().GetTypeInfo().GetProperty(pt.Name);
#else
var pi = resource.GetType().GetProperty(pt.Name);
#endif
var rt = pi.GetValue(resource, null);
props.Add(rt);
}
return props.ToArray();
}
/*
public bool Deserialize(byte[] data, uint offset, uint length)
{
var props = Codec.ParseValues(data, offset, length);
Deserialize(props);
return true;
}
*/
/*
public byte[] Serialize(bool includeLength = false, DistributedConnection sender = null)
{
//var bl = new BinaryList();
List<object> props = new List<object>();
foreach (var pt in template.Properties)
{
var pi = resource.GetType().GetProperty(pt.Name);
var rt = pi.GetValue(resource, null);
// this is a cool hack to let the property know the sender
if (rt is Func<DistributedConnection, object>)
rt = (rt as Func<DistributedConnection, object>)(sender);
props.Add(rt);
}
if (includeLength)
{
return Codec.Compose(props.ToArray(), false);
}
else
{
var rt = Codec.Compose(props.ToArray(), false);
return DC.Clip(rt, 4, (uint)(rt.Length - 4));
}
}
public byte[] StorageSerialize()
{
var props = new List<object>();
foreach(var pt in template.Properties)
{
if (!pt.Storable)
continue;
var pi = resource.GetType().GetProperty(pt.Name);
if (!pi.CanWrite)
continue;
var rt = pi.GetValue(resource, null);
props.Add(rt);
}
return Codec.Compose(props.ToArray(), false);
}
*/
/// <summary>
/// If True, the instance can be stored to disk.
/// </summary>
/// <returns></returns>
public bool IsStorable()
{
#if NETSTANDARD1_5
var attrs = resource.GetType().GetTypeInfo().GetCustomAttributes(typeof(Storable), true).ToArray();
#else
var attrs = resource.GetType().GetCustomAttributes(typeof(Storable), true);
#endif
return attrs.Length > 0;
}
/// <summary>
/// Notify listeners that a property was modified.
/// </summary>
/// <param name="propertyName"></param>
/// <param name="newValue"></param>
/// <param name="oldValue"></param>
public void Modified([CallerMemberName] string propertyName = "", object newValue = null, object oldValue = null)
{
if (newValue == null)
{
object val;
if (GetPropertyValue(propertyName, out val))
ResourceModified?.Invoke(resource, propertyName, val, oldValue);
}
else
ResourceModified?.Invoke(resource, propertyName, newValue, oldValue);
}
internal void EmitResourceEvent(string name, string[] receivers, object[] args)
{
ResourceEventOccured?.Invoke(resource, name, receivers, args);
}
/// <summary>
/// Get the value of a given property by name.
/// </summary>
/// <param name="name">Property name</param>
/// <param name="value">Output value</param>
/// <returns>True, if the resource has the property.</returns>
public bool GetPropertyValue(string name, out object value)
{
#if NETSTANDARD1_5
PropertyInfo pi = resource.GetType().GetTypeInfo().GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
#else
PropertyInfo pi = resource.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
#endif
if (pi != null)
{
#if NETSTANDARD1_5
object[] ca = pi.GetCustomAttributes(typeof(ResourceProperty), false).ToArray();
#else
object[] ca = pi.GetCustomAttributes(typeof(ResourceProperty), false);
#endif
if (ca.Length > 0)
{
value = pi.GetValue(resource, null);
//if (value is Func<IManager, object>)
// value = (value as Func<IManager, object>)(sender);
return true;
}
}
value = null;
return false;
}
public bool Inherit
{
get { return inherit; }
}
/// <summary>
/// List of parents.
/// </summary>
public AutoList<IResource, Instance> Parents
{
get { return parents; }
}
/// <summary>
/// Store responsible for creating and keeping the resource.
/// </summary>
public IStore Store
{
get { return store; }
}
/// <summary>
/// List of children.
/// </summary>
public AutoList<IResource, Instance> Children
{
get { return children; }
}
/// <summary>
/// The unique and permanent link to the resource.
/// </summary>
public string Link
{
get
{
if (this.store != null)
return this.store.Link(this.resource);
else
{
var l = new List<string>();
//l.Add(name);
var p = this.resource; // parents.First();
while (true)
{
l.Insert(0, p.Instance.name);
if (p.Instance.parents.Count == 0)
break;
p = p.Instance.parents.First();
}
return String.Join("/", l.ToArray());
}
}
}
/// <summary>
/// Instance name.
/// </summary>
public string Name
{
get { return name; }
}
/// <summary>
/// Resource managed by this instance.
/// </summary>
public IResource Resource
{
get { return resource; }
}
/// <summary>
/// Resource template describes the properties, functions and events of the resource.
/// </summary>
public ResourceTemplate Template
{
get { return template; }
}
/// <summary>
/// Create new instance.
/// </summary>
/// <param name="id">Instance Id.</param>
/// <param name="name">Name of the instance.</param>
/// <param name="resource">Resource to manage.</param>
/// <param name="store">Store responsible for the resource.</param>
public Instance(uint id, string name, IResource resource, IStore store)
{
this.store = store;
this.resource = resource;
this.id = id;
this.name = name;
children = new AutoList<IResource, Instance>(this);
parents = new AutoList<IResource, Instance>(this);
managers = new AutoList<IPermissionManager, Instance>(this);
children.OnAdd += Children_OnAdd;
children.OnRemoved += Children_OnRemoved;
resource.OnDestroy += Resource_OnDestroy;
template = Warehouse.GetTemplate(resource.GetType());
// set ages
for (byte i = 0; i < template.Properties.Length; i++)
ages.Add(0);
// connect events
Type t = resource.GetType();
#if NETSTANDARD1_5
var events = t.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
#else
var events = t.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
#endif
foreach (var evt in events)
{
if (evt.EventHandlerType != typeof(ResourceEventHanlder))
continue;
var ca = (ResourceEvent[])evt.GetCustomAttributes(typeof(ResourceEvent), true);
if (ca.Length == 0)
continue;
ResourceEventHanlder proxyDelegate = (receivers, args) => EmitResourceEvent(evt.Name, receivers, args);
evt.AddEventHandler(resource, proxyDelegate);
}
}
private void Children_OnRemoved(Instance parent, IResource value)
{
value.Instance.parents.Remove(resource);
}
private void Children_OnAdd(Instance parent, IResource value)
{
value.Instance.parents.Add(resource);
}
private void Resource_OnDestroy(object sender)
{
ResourceDestroyed?.Invoke((IResource)sender);
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource
{
[AttributeUsage(AttributeTargets.Event)]
public class ResourceEvent : System.Attribute
{
string expansion;
public string Expansion
{
get
{
return expansion;
}
}
public ResourceEvent(string expansion = null)
{
this.expansion = expansion;
}
}
}

View File

@ -0,0 +1,13 @@
using Esiur.Data;
using Esiur.Engine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource
{
public delegate void ResourceEventHanlder(string[] receivers, params object[] args);
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource
{
[AttributeUsage(AttributeTargets.Method)]
public class ResourceFunction : System.Attribute
{
private string expansion = null;
public string Expansion
{
get
{
return expansion;
}
}
public ResourceFunction(string expansion = null)
{
this.expansion = expansion;
}
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource
{
[AttributeUsage(AttributeTargets.Property)]
public class ResourceProperty : System.Attribute
{
string readExpansion;
string writeExpansion;
public string ReadExpansion
{
get
{
return readExpansion;
}
}
public string WriteExpansion
{
get
{
return writeExpansion;
}
}
public ResourceProperty(string readExpansion = null, string writeExpansion = null)
{
this.readExpansion = readExpansion;
this.writeExpansion = writeExpansion;
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource
{
public enum ResourceTrigger : int
{
Loaded = 0,
Initialize,
Terminate,
Configure,
SystemInitialized,
SystemTerminated,
SystemReload,
}
}

View File

@ -0,0 +1,48 @@
using Esiur.Data;
using Esiur.Engine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource
{
[AttributeUsage(AttributeTargets.All)]
public class Storable : global::System.Attribute
{
public delegate object SerializerFunction(object value);
public delegate object DeserializerFunction(object data);
SerializerFunction serializer;
DeserializerFunction deserializer;
DataType type;
public Storable()
{
type = DataType.Void;
}
public DeserializerFunction Deserializer
{
get { return deserializer; }
}
public SerializerFunction Serializer
{
get { return serializer; }
}
public Storable(DataType type)
{
this.type = type;
}
public Storable(DataType type, SerializerFunction serializer, DeserializerFunction deserializer)
{
this.type = type;
this.serializer = serializer;
this.deserializer = deserializer;
}
}
}

View File

@ -0,0 +1,33 @@
using Esiur.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource.Template
{
public class EventTemplate : MemberTemplate
{
public string Expansion
{
get;
set;
}
public override byte[] Compose()
{
var name = base.Compose();
if (Expansion != null)
{
var exp = DC.ToBytes(Expansion);
return BinaryList.ToBytes((byte)0x50, exp.Length, exp, (byte)name.Length, name);
}
else
return BinaryList.ToBytes((byte)0x40, (byte)name.Length, name);
}
public EventTemplate() { Type = MemberType.Event; }
}
}

View File

@ -0,0 +1,42 @@
using Esiur.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource.Template
{
public class FunctionTemplate : MemberTemplate
{
public string Expansion
{
get;
set;
}
public bool IsVoid
{
get;
set;
}
public override byte[] Compose()
{
var name = base.Compose();
if (Expansion != null)
{
var exp = DC.ToBytes(Expansion);
return BinaryList.ToBytes((byte)(0x10 | (IsVoid ? 0x8 : 0x0)), exp.Length, exp, (byte)name.Length, name);
}
else
return BinaryList.ToBytes((byte)(IsVoid ? 0x8 : 0x0), (byte)name.Length, name);
}
public FunctionTemplate() { Type = MemberType.Function; }
}
}

View File

@ -0,0 +1,29 @@
using Esiur.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource.Template
{
public class MemberTemplate
{
public enum MemberType
{
Function = 0,
Property = 1,
Event = 2,
}
public byte Index { get; set; }
public string Name { get; set; }
public MemberType Type { get; set; }
public virtual byte[] Compose()
{
return DC.ToBytes(Name);
}
}
}

View File

@ -0,0 +1,71 @@
using Esiur.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource.Template
{
public class PropertyTemplate : MemberTemplate
{
public enum PropertyPermission:byte
{
Read = 1,
Write,
ReadWrite
}
//bool ReadOnly;
//IIPTypes::DataType ReturnType;
public PropertyPermission Permission {
get;
set;
}
public string ReadExpansion
{
get;
set;
}
public string WriteExpansion
{
get;
set;
}
public bool Storable
{
get;
set;
}
public override byte[] Compose()
{
var name = base.Compose();
if (WriteExpansion != null && ReadExpansion != null)
{
var rexp = DC.ToBytes(ReadExpansion);
var wexp = DC.ToBytes(WriteExpansion);
return BinaryList.ToBytes((byte)(0x38 | (byte)Permission), wexp.Length, wexp, rexp.Length, rexp, (byte)name.Length, name);
}
else if (WriteExpansion != null)
{
var wexp = DC.ToBytes(WriteExpansion);
return BinaryList.ToBytes((byte)(0x30 | (byte)Permission), wexp.Length, wexp, (byte)name.Length, name);
}
else if (ReadExpansion != null)
{
var rexp = DC.ToBytes(ReadExpansion);
return BinaryList.ToBytes((byte)(0x28 | (byte)Permission), rexp.Length, rexp, (byte)name.Length, name);
}
else
return BinaryList.ToBytes((byte)(0x20 | (byte)Permission), (byte)name.Length, name);
}
public PropertyTemplate() { Type = MemberType.Property; }
}
}

View File

@ -0,0 +1,359 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Esiur.Misc;
using Esiur.Data;
using Esiur.Engine;
using System.Security.Cryptography;
namespace Esiur.Resource.Template
{
public class ResourceTemplate
{
Guid classId;
string className;
List<MemberTemplate> members = new List<MemberTemplate>();
List<FunctionTemplate> functions = new List<FunctionTemplate>();
List<EventTemplate> events = new List<EventTemplate>();
List<PropertyTemplate> properties = new List<PropertyTemplate>();
int version;
//bool isReady;
byte[] content;
public byte[] Content
{
get { return content; }
}
public MemberTemplate GetMemberTemplate(MemberInfo member)
{
if (member is MethodInfo)
return GetFunctionTemplate(member.Name);
else if (member is EventInfo)
return GetEventTemplate(member.Name);
else if (member is PropertyInfo)
return GetPropertyTemplate(member.Name);
else
return null;
}
public EventTemplate GetEventTemplate(string eventName)
{
foreach (var i in events)
if (i.Name == eventName)
return i;
return null;
}
public EventTemplate GetEventTemplate(byte index)
{
foreach (var i in events)
if (i.Index == index)
return i;
return null;
}
public FunctionTemplate GetFunctionTemplate(string functionName)
{
foreach (var i in functions)
if (i.Name == functionName)
return i;
return null;
}
public FunctionTemplate GetFunctionTemplate(byte index)
{
foreach (var i in functions)
if (i.Index == index)
return i;
return null;
}
public PropertyTemplate GetPropertyTemplate(byte index)
{
foreach (var i in properties)
if (i.Index == index)
return i;
return null;
}
public PropertyTemplate GetPropertyTemplate(string propertyName)
{
foreach (var i in properties)
if (i.Name == propertyName)
return i;
return null;
}
public Guid ClassId
{
get { return classId; }
}
public string ClassName
{
get { return className; }
}
public MemberTemplate[] Methods
{
get{return members.ToArray();}
}
public FunctionTemplate[] Functions
{
get { return functions.ToArray(); }
}
public EventTemplate[] Events
{
get { return events.ToArray(); }
}
public PropertyTemplate[] Properties
{
get { return properties.ToArray(); }
}
public ResourceTemplate()
{
}
public ResourceTemplate(Type type)
{
// set guid
var typeName = Encoding.UTF8.GetBytes(type.FullName);
var hash = SHA256.Create().ComputeHash(typeName).Clip(0, 16);
classId = new Guid(hash);
className = type.FullName;
#if NETSTANDARD1_5
PropertyInfo[] propsInfo = type.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
EventInfo[] eventsInfo = type.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
MethodInfo[] methodsInfo = type.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
#else
PropertyInfo[] propsInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
EventInfo[] eventsInfo = type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
MethodInfo[] methodsInfo = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
#endif
//byte currentIndex = 0;
byte i = 0;
foreach (var pi in propsInfo)
{
var ps = (ResourceProperty[])pi.GetCustomAttributes(typeof(ResourceProperty), true);
if (ps.Length > 0)
{
var pt = new PropertyTemplate();
pt.Name = pi.Name;
pt.Index = i++;
pt.ReadExpansion = ps[0].ReadExpansion;
pt.WriteExpansion = ps[0].WriteExpansion;
properties.Add(pt);
}
}
i = 0;
foreach (var ei in eventsInfo)
{
var es = (ResourceEvent[])ei.GetCustomAttributes(typeof(ResourceEvent), true);
if (es.Length > 0)
{
var et = new EventTemplate();
et.Name = ei.Name;
et.Index = i++;
et.Expansion = es[0].Expansion;
events.Add(et);
}
}
i = 0;
foreach (MethodInfo mi in methodsInfo)
{
var fs = (ResourceFunction[])mi.GetCustomAttributes(typeof(ResourceFunction), true);
if (fs.Length > 0)
{
var ft = new FunctionTemplate();
ft.Name = mi.Name;
ft.Index = i++;
ft.IsVoid = mi.ReturnType == typeof(void);
ft.Expansion = fs[0].Expansion;
functions.Add(ft);
}
}
// append signals
for (i = 0; i < events.Count; i++)
members.Add(events[i]);
// append slots
for (i = 0; i < functions.Count; i++)
members.Add(functions[i]);
// append properties
for (i = 0; i < properties.Count; i++)
members.Add(properties[i]);
// bake it binarily
var b = new BinaryList();
b.Append(classId);
b.Append((byte)className.Length, className);
b.Append(version);
b.Append((ushort)members.Count);
foreach (var ft in functions)
b.Append(ft.Compose());
foreach (var pt in properties)
b.Append(pt.Compose());
foreach (var et in events)
b.Append(et.Compose());
content = b.ToArray();
}
public static ResourceTemplate Parse(byte[] data)
{
return Parse(data, 0, (uint)data.Length);
}
public static ResourceTemplate Parse(byte[] data, uint offset, uint contentLength)
{
uint ends = offset + contentLength;
uint oOffset = offset;
// start parsing...
var od = new ResourceTemplate();
od.content = data.Clip(offset, contentLength);
od.classId = data.GetGuid(offset);
offset += 16;
od.className = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
od.version = data.GetInt32(offset);
offset += 4;
ushort methodsCount = data.GetUInt16(offset);
offset += 2;
byte functionIndex = 0;
byte propertyIndex = 0;
byte eventIndex = 0;
for (int i = 0; i < methodsCount; i++)
{
var type = data[offset] >> 5;
if (type == 0) // function
{
var ft = new FunctionTemplate();
ft.Index = functionIndex++;
var expansion = ((data[offset] & 0x10) == 0x10);
ft.IsVoid = ((data[offset++] & 0x08) == 0x08);
ft.Name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
if (expansion) // expansion ?
{
var cs = data.GetUInt32(offset);
offset += 4;
ft.Expansion = data.GetString(offset, cs);
offset += cs;
}
od.functions.Add(ft);
}
else if (type == 1) // property
{
var pt = new PropertyTemplate();
pt.Index = propertyIndex++;
var readExpansion = ((data[offset] & 0x8) == 0x8);
var writeExpansion = ((data[offset] & 0x10) == 0x10);
pt.Permission = (PropertyTemplate.PropertyPermission)((data[offset++] >> 1) & 0x3);
pt.Name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
if (readExpansion) // expansion ?
{
var cs = data.GetUInt32(offset);
offset += 4;
pt.ReadExpansion = data.GetString(offset, cs);
offset += cs;
}
if (writeExpansion) // expansion ?
{
var cs = data.GetUInt32(offset);
offset += 4;
pt.WriteExpansion = data.GetString(offset, cs);
offset += cs;
}
od.properties.Add(pt);
}
else if (type == 2) // Event
{
var et = new EventTemplate();
et.Index = eventIndex++;
var expansion = ((data[offset++] & 0x10) == 0x10);
et.Name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, (int)data[offset]);
offset += (uint)data[offset] + 1;
if (expansion) // expansion ?
{
var cs = data.GetUInt32(offset);
offset += 4;
et.Expansion = data.GetString(offset, cs);
offset += cs;
}
od.events.Add(et);
}
}
// append signals
for (int i = 0; i < od.events.Count; i++)
od.members.Add(od.events[i]);
// append slots
for (int i = 0; i < od.functions.Count; i++)
od.members.Add(od.functions[i]);
// append properties
for (int i = 0; i < od.properties.Count; i++)
od.members.Add(od.properties[i]);
//od.isReady = true;
/*
var oo = owner.Socket.Engine.GetObjectDescription(od.GUID);
if (oo != null)
{
Console.WriteLine("Already there ! description");
return oo;
}
else
{
owner.Socket.Engine.AddObjectDescription(od);
return od;
}
*/
return od;
}
}
}

258
Esiur/Resource/Warehouse.cs Normal file
View File

@ -0,0 +1,258 @@
using Esiur.Data;
using Esiur.Engine;
using Esiur.Resource.Template;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Resource
{
// Centeral Resource Issuer
public static class Warehouse
{
//static byte prefixCounter;
static List<IStore> stores = new List<IStore>();
static Dictionary<uint, IResource> resources = new Dictionary<uint, IResource>();
static uint resourceCounter = 0;
static KeyList<Guid, ResourceTemplate> templates = new KeyList<Guid, ResourceTemplate>();
/// <summary>
/// Get a store by its name.
/// </summary>
/// <param name="name">Store instance name</param>
/// <returns></returns>
public static IStore GetStore(string name)
{
foreach (var s in stores)
if (s.Instance.Name == name)
return s;
return null;
}
/// <summary>
/// Get a resource by instance Id.
/// </summary>
/// <param name="id">Instance Id</param>
/// <returns></returns>
public static AsyncReply<IResource> Get(uint id)
{
if (resources.ContainsKey(id))
return new AsyncReply<IResource>(resources[id]);
else
return null;
}
/// <summary>
/// Open the warehouse.
/// This function issues the initialize trigger to all stores and resources.
/// </summary>
/// <returns>True, if no problem occurred.</returns>
public static AsyncReply<bool> Open()
{
var bag = new AsyncBag<bool>();
foreach (var store in stores)
bag.Add(store.Trigger(ResourceTrigger.Initialize));
foreach (var store in stores)
bag.Add(store.Trigger(ResourceTrigger.SystemInitialized));
bag.Seal();
var rt = new AsyncReply<bool>();
bag.Then((x) =>
{
foreach(var b in x)
if (!b)
{
rt.Trigger(false);
return;
}
rt.Trigger(true);
});
return rt;
}
/// <summary>
/// Close the warehouse.
/// This function issues terminate trigger to all resources and stores.
/// </summary>
/// <returns>True, if no problem occurred.</returns>
public static AsyncReply<bool> Close()
{
var bag = new AsyncBag<bool>();
foreach (var resource in resources.Values)
if (!(resource is IStore))
bag.Add(resource.Trigger(ResourceTrigger.Terminate));
foreach (var store in stores)
bag.Add(store.Trigger(ResourceTrigger.Terminate));
foreach (var resource in resources.Values)
if (!(resource is IStore))
bag.Add(resource.Trigger(ResourceTrigger.SystemTerminated));
foreach (var store in stores)
bag.Add(store.Trigger(ResourceTrigger.SystemTerminated));
bag.Seal();
var rt = new AsyncReply<bool>();
bag.Then((x) =>
{
foreach (var b in x)
if (!b)
{
rt.Trigger(false);
return;
}
rt.Trigger(true);
});
return rt;
}
/// <summary>
/// Get a resource by its path.
/// Resource path is sperated by '/' character, e.g. "system/http".
/// </summary>
/// <param name="path"></param>
/// <returns>Resource instance.</returns>
public static AsyncReply<IResource> Get(string path)
{
var p = path.Split('/');
IResource res;
foreach(IStore d in stores)
if (p[0] == d.Instance.Name)
{
var i = 1;
res = d;
while(p.Length > i)
{
var si = i;
foreach (IResource r in res.Instance.Children)
if (r.Instance.Name == p[i])
{
i++;
res = r;
break;
}
if (si == i)
// not found, ask the store
return d.Get(path.Substring(p[0].Length + 1));
}
return new AsyncReply<IResource>(res);
}
return new AsyncReply<IResource>(null);
}
/// <summary>
/// Put a resource in the warehouse.
/// </summary>
/// <param name="resource">Resource instance.</param>
/// <param name="name">Resource name.</param>
/// <param name="store">IStore that manages the resource. Can be null if the resource is a store.</param>
/// <param name="parent">Parent resource. if not presented the store becomes the parent for the resource.</param>
public static void Put(IResource resource, string name, IStore store = null, IResource parent = null)
{
resource.Instance = new Instance(resourceCounter++, name, resource, store);
if (store == parent)
parent = null;
if (parent == null)
{
if (!(resource is IStore))
store.Instance.Children.Add(resource);
}
else
parent.Instance.Children.Add(resource);
if (resource is IStore)
stores.Add(resource as IStore);
else
store.Put(resource);
resources.Add(resource.Instance.Id, resource);
}
public static T New<T>(string name, IStore store = null, IResource parent = null)
{
var res = Activator.CreateInstance(typeof(T)) as IResource;
Put(res, name, store, parent);
return (T)res;
}
/// <summary>
/// Put a resource template in the templates warehouse.
/// </summary>
/// <param name="template">Resource template.</param>
public static void PutTemplate(ResourceTemplate template)
{
if (templates.ContainsKey(template.ClassId))
templates.Add(template.ClassId, template);
}
/// <summary>
/// Get a template by type from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse.
/// </summary>
/// <param name="type">.Net type.</param>
/// <returns>Resource template.</returns>
public static ResourceTemplate GetTemplate(Type type)
{
// loaded ?
foreach (var t in templates.Values)
if (t.ClassName == type.FullName)
return t;
var template = new ResourceTemplate(type);
templates.Add(template.ClassId, template);
return template;
}
/// <summary>
/// Get a template by class Id from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse.
/// </summary>
/// <param name="classId">Class Id.</param>
/// <returns>Resource template.</returns>
public static AsyncReply<ResourceTemplate> GetTemplate(Guid classId)
{
if (templates.ContainsKey(classId))
return new AsyncReply<ResourceTemplate>(templates[classId]);
return null;
}
/// <summary>
/// Get a template by class name from the templates warehouse. If not in the warehouse, a new ResourceTemplate is created and added to the warehouse.
/// </summary>
/// <param name="className">Class name.</param>
/// <returns>Resource template.</returns>
public static AsyncReply<ResourceTemplate> GetTemplate(string className)
{
foreach (var t in templates.Values)
if (t.ClassName == className)
return new AsyncReply<ResourceTemplate>(t);
return null;
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public class AlienAuthentication : Authentication
{
public AlienAuthentication(Certificate certificate, AuthenticationState state) :
base(certificate, state, AuthenticationType.Alien)
{
}
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public class Authentication
{
Certificate certificate;
AuthenticationState state;
AuthenticationType type;
public Certificate Certificate
{
get { return certificate; }
}
public AuthenticationState State
{
get { return state; }
}
public AuthenticationType Type
{
get { return type; }
}
public Authentication(Certificate certificate, AuthenticationState state, AuthenticationType type)
{
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public enum AuthenticationState : int
{
Denied = 0x1,
Succeeded = 0x2,
Blocked = 0x4,
Rejected = 0x8,
NeedsUpdate = 0x10,
NotFound = 0x20
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public enum AuthenticationType
{
Host,
CoHost,
Client,
Alien
}
}

View File

@ -0,0 +1,163 @@
using Esiur.Data;
using Esiur.Engine;
using Esiur.Misc;
using Esiur.Security.Cryptography;
using Esiur.Security.Integrity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public class CACertificate : Certificate
{
string name;
public string Name
{
get { return name; }
}
public CACertificate(byte[] data, uint offset, uint length, bool privateKeyIncluded = false)
:base(0, DateTime.MinValue, DateTime.MinValue, HashFunctionType.MD5)
{
uint oOffset = offset;
this.id = DC.GetUInt64(data, offset);
offset += 8;
this.issueDate = DC.GetDateTime(data, offset);
offset += 8;
this.expireDate = DC.GetDateTime(data, offset);
offset += 8;
this.hashFunction = (HashFunctionType)(data[offset++] >> 4);
this.name = (Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]));
offset += (uint)data[offset] + 1;
var aea = (AsymetricEncryptionAlgorithmType)(data[offset] >> 5);
if (aea == AsymetricEncryptionAlgorithmType.RSA)
{
var key = new RSAParameters();
uint exponentLength = (uint)data[offset++] & 0x1F;
key.Exponent = DC.Clip(data, offset, exponentLength);
offset += exponentLength;
uint keySize = DC.GetUInt16(data, offset);
offset += 2;
key.Modulus = DC.Clip(data, offset, keySize);
offset += keySize;
// copy cert data
this.publicRawData = new byte[offset - oOffset];
Buffer.BlockCopy(data, (int)oOffset, publicRawData, 0, publicRawData.Length);
if (privateKeyIncluded)
{
uint privateKeyLength = (keySize * 3) + (keySize / 2);
uint halfKeySize = keySize / 2;
privateRawData = DC.Clip(data, offset, privateKeyLength);
key.D = DC.Clip(data, offset, keySize);
offset += keySize;
key.DP = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.DQ = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.InverseQ = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.P = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.Q = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
}
// setup rsa
this.rsa = RSA.Create();// new RSACryptoServiceProvider();
this.rsa.ImportParameters(key);
}
}
public CACertificate(ulong id, string authorityName, DateTime issueDate, DateTime expireDate,
HashFunctionType hashFunction = HashFunctionType.SHA1, uint ip = 0, byte[] ip6 = null)
: base(id, issueDate, expireDate, hashFunction)
{
// assign type
BinaryList cr = new BinaryList();
// make header
cr.Append(id, issueDate, expireDate);
// hash function
cr.Append((byte)((byte)hashFunction << 4));
this.hashFunction = hashFunction;
// CA Name
this.name = authorityName;
cr.Append((byte)(authorityName.Length), Encoding.ASCII.GetBytes(authorityName));
// public key
rsa = RSA.Create();// new RSACryptoServiceProvider(2048);
rsa.KeySize = 2048;
RSAParameters dRSAKey = rsa.ExportParameters(true);
cr.Append((byte)dRSAKey.Exponent.Length, dRSAKey.Exponent, (ushort)dRSAKey.Modulus.Length, dRSAKey.Modulus);
publicRawData = cr.ToArray();
privateRawData = DC.Merge(dRSAKey.D, dRSAKey.DP, dRSAKey.DQ, dRSAKey.InverseQ, dRSAKey.P, dRSAKey.Q);
}
public override bool Save(string filename, bool includePrivate = false)
{
try
{
if (includePrivate)
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.CAPrivate, publicRawData, privateRawData));
else
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.CAPublic, publicRawData));
return true;
}
catch
{
return false;
}
}
public override byte[] Serialize(bool includePrivate = false)
{
if (includePrivate)
return BinaryList.ToBytes(publicRawData, privateRawData);
else
return publicRawData;
}
}
}

View File

@ -0,0 +1,198 @@
using Esiur.Data;
using Esiur.Engine;
using Esiur.Misc;
using Esiur.Security.Cryptography;
using Esiur.Security.Integrity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public abstract class Certificate
{
protected DateTime issueDate, expireDate;
protected RSA rsa;
protected Aes aes;
protected byte[] publicRawData;
protected byte[] privateRawData;
protected ulong id;
protected HashFunctionType hashFunction;
public Certificate(ulong id, DateTime issueDate, DateTime expireDate, HashFunctionType hashFunction)
{
this.id = id;
this.issueDate = issueDate;
this.expireDate = expireDate;
this.hashFunction = hashFunction;
}
public ulong Id
{
get { return id; }
}
public AsymetricEncryptionAlgorithmType AsymetricEncryptionAlgorithm
{
get { return AsymetricEncryptionAlgorithmType.RSA; }
}
public byte[] AsymetricEncrypt(byte[] message)
{
return rsa.Encrypt(message, RSAEncryptionPadding.OaepSHA512);
}
public byte[] AsymetricEncrypt(byte[] message, uint offset, uint length)
{
if (message.Length != length)
return rsa.Encrypt(DC.Clip(message, offset, length), RSAEncryptionPadding.OaepSHA512);
else
return rsa.Encrypt(message, RSAEncryptionPadding.OaepSHA512);
}
public byte[] AsymetricDecrypt(byte[] message)
{
try
{
return rsa.Decrypt(message, RSAEncryptionPadding.OaepSHA512);
}
catch (Exception ex)
{
Global.Log("Certificate", LogType.Error, ex.ToString());
return null;
}
}
public byte[] AsymetricDecrypt(byte[] message, uint offset, uint length)
{
try
{
if (message.Length != length)
return rsa.Decrypt(DC.Clip(message, offset, length), RSAEncryptionPadding.OaepSHA512);
else
return rsa.Decrypt(message, RSAEncryptionPadding.OaepSHA512);
}
catch (Exception ex)
{
Global.Log("Certificate", LogType.Error, ex.ToString());
return null;
}
}
public byte[] SymetricEncrypt(byte[] message, uint offset, uint length)
{
byte[] rt = null;
using (var ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
cs.Write(message, (int)offset, (int)length);
rt = ms.ToArray();
}
return rt;
}
public byte[] SymetricEncrypt(byte[] message)
{
return SymetricEncrypt(message, 0, (uint)message.Length);
}
public byte[] SymetricDecrypt(byte[] message, uint offset, uint length)
{
byte[] rt = null;
using (var ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
cs.Write(message, (int)offset, (int)length);
rt = ms.ToArray();
}
return rt;
}
public byte[] SymetricDecrypt(byte[] message)
{
return SymetricDecrypt(message, 0, (uint)message.Length);
}
public byte[] Sign(byte[] message)
{
return Sign(message, 0, (uint)message.Length);
}
public byte[] Sign(byte[] message, uint offset, uint length)
{
if (hashFunction == HashFunctionType.SHA1)
return rsa.SignData(message, (int)offset, (int)length, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
else if (hashFunction == HashFunctionType.MD5)
return rsa.SignData(message, (int)offset, (int)length, HashAlgorithmName.MD5, RSASignaturePadding.Pkcs1);
else if (hashFunction == HashFunctionType.SHA256)
return rsa.SignData(message, (int)offset, (int)length, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
else if (hashFunction == HashFunctionType.SHA384)
return rsa.SignData(message, (int)offset, (int)length, HashAlgorithmName.SHA384, RSASignaturePadding.Pkcs1);
else if (hashFunction == HashFunctionType.SHA512)
return rsa.SignData(message, (int)offset, (int)length, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
return null;
}
public bool InitializeSymetricCipher(SymetricEncryptionAlgorithmType algorithm, int keyLength, byte[] key, byte[] iv)
{
if (algorithm == SymetricEncryptionAlgorithmType.AES)
{
if (keyLength == 0) // 128 bit
{
aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = key;
aes.IV = iv;
return true;
}
}
return false;
}
public abstract bool Save(string filename, bool includePrivate = false);
public abstract byte[] Serialize(bool includePrivate = false);
public static Certificate Load(string filename)
{
byte[] ar = File.ReadAllBytes(filename);
var t = (CertificateType)ar[0];
switch (t)
{
case CertificateType.CAPublic:
return new CACertificate(ar, 1, (uint)ar.Length - 1);
case CertificateType.CAPrivate:
return new CACertificate(ar, 1, (uint)ar.Length - 1, true);
case CertificateType.DomainPublic:
return new DomainCertificate(ar, 1, (uint)ar.Length - 1);
case CertificateType.DomainPrivate:
return new DomainCertificate(ar, 1, (uint)ar.Length - 1, true);
case CertificateType.UserPublic:
return new UserCertificate(ar, 1, (uint)ar.Length - 1);
case CertificateType.UserPrivate:
return new UserCertificate(ar, 1, (uint)ar.Length - 1, true);
}
return null;
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public enum CertificateType
{
CAPublic = 0,
CAPrivate,
DomainPublic,
DomainPrivate,
UserPublic,
UserPrivate
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public class ClientAuthentication : Authentication
{
public ClientAuthentication(byte[] credentials, UserCertificate certificate, AuthenticationState state)
: base(certificate, state, AuthenticationType.Client)
{
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public class CoHostAuthentication : Authentication
{
public CoHostAuthentication(DomainCertificate certificate, AuthenticationState state)
: base(certificate, state, AuthenticationType.CoHost)
{
}
}
}

View File

@ -0,0 +1,220 @@
using Esiur.Data;
using Esiur.Misc;
using Esiur.Security.Cryptography;
using Esiur.Security.Integrity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public class DomainCertificate : Certificate
{
uint ip;
byte[] ip6;
string domain;
//CACertificate ca;
string caName;
ulong caId;
byte[] signature;
string authorityName;
public string AuthorityName
{
get { return authorityName; }
}
public string Domain
{
get { return domain; }
}
public byte[] Signature
{
get { return signature; }
}
public uint IPAddress
{
get { return ip; }
}
public byte[] IPv6Address
{
get { return ip6; }
}
public DomainCertificate(byte[] data, uint offset, uint length, bool privateKeyIncluded = false)
:base(0, DateTime.MinValue, DateTime.MinValue, HashFunctionType.MD5)
{
var oOffset = offset;
this.id = DC.GetUInt64(data, offset);
offset += 8;
// load IPs
this.ip = DC.GetUInt32(data, offset);
offset += 4;
this.ip6 = DC.Clip(data, offset, 16);
offset += 16;
this.issueDate = DC.GetDateTime(data, offset);
offset += 8;
this.expireDate = DC.GetDateTime(data, offset);
offset += 8;
this.domain = Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
this.authorityName = (Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]));
offset += (uint)data[offset] + 1;
caId = DC.GetUInt64(data, offset);
offset += 8;
var aea = (AsymetricEncryptionAlgorithmType)(data[offset] >> 5);
if (aea == AsymetricEncryptionAlgorithmType.RSA)
{
var key = new RSAParameters();
uint exponentLength = (uint)data[offset++] & 0x1F;
key.Exponent = DC.Clip(data, offset, exponentLength);
offset += exponentLength;
uint keySize = DC.GetUInt16(data, offset);
offset += 2;
key.Modulus = DC.Clip(data, offset, keySize);
offset += keySize;
// copy cert data
publicRawData = new byte[offset - oOffset];
Buffer.BlockCopy(data, (int)oOffset, publicRawData, 0, publicRawData.Length);
if (privateKeyIncluded)
{
uint privateKeyLength = (keySize * 3) + (keySize / 2);
privateRawData = DC.Clip(data, offset, privateKeyLength);
uint halfKeySize = keySize / 2;
key.D = DC.Clip(data, offset, keySize);
offset += keySize;
key.DP = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.DQ = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.InverseQ = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.P = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.Q = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
}
// setup rsa
rsa = RSA.Create();// new RSACryptoServiceProvider();
rsa.ImportParameters(key);
this.signature = DC.Clip(data, offset, length - (offset - oOffset));
}
}
public DomainCertificate(ulong id, string domain, CACertificate authority, DateTime issueDate,
DateTime expireDate, HashFunctionType hashFunction = HashFunctionType.SHA1, uint ip = 0, byte[] ip6 = null)
: base (id, issueDate, expireDate, hashFunction)
{
// assign type
var cr = new BinaryList();
// id
cr.Append(id);
// ip
this.ip = ip;
this.ip6 = ip6;
cr.Append(ip);
if (ip6?.Length == 16)
cr.Append(ip6);
else
cr.Append(new byte[16]);
cr.Append(issueDate, expireDate);
// domain
this.domain = domain;
cr.Append((byte)(domain.Length), Encoding.ASCII.GetBytes(domain));
// CA
this.caName = authority.Name;
cr.Append((byte)(authority.Name.Length), Encoding.ASCII.GetBytes(authority.Name));
this.authorityName = authority.Name;
// CA Index
//co.KeyIndex = authority.KeyIndex;
this.caId = authority.Id;
cr.Append(caId);
// public key
rsa = RSA.Create();// new RSACryptoServiceProvider(2048);
rsa.KeySize = 2048;
RSAParameters dRSAKey = rsa.ExportParameters(true);
cr.Append((byte)dRSAKey.Exponent.Length, dRSAKey.Exponent, (ushort)dRSAKey.Modulus.Length, dRSAKey.Modulus, AsymetricEncryptionAlgorithmType.RSA);
publicRawData = cr.ToArray();
// private key
this.privateRawData = DC.Merge(dRSAKey.D, dRSAKey.DP, dRSAKey.DQ, dRSAKey.InverseQ, dRSAKey.P, dRSAKey.Q);
this.signature = authority.Sign(publicRawData);
}
public override bool Save(string filename, bool includePrivate = false)
{
try
{
if (includePrivate)
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.DomainPrivate, publicRawData, signature, privateRawData));
else
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.DomainPublic, publicRawData, signature));
return true;
}
catch
{
return false;
}
}
public override byte[] Serialize(bool includePrivate = false)
{
if (includePrivate)
return BinaryList.ToBytes(publicRawData, signature, privateRawData);
else
return BinaryList.ToBytes(publicRawData, signature);
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public class HostAuthentication : Authentication
{
public HostAuthentication(DomainCertificate certificate, AuthenticationState state)
: base(certificate, state, AuthenticationType.Host)
{
}
}
}

View File

@ -0,0 +1,23 @@
using Esiur.Data;
using Esiur.Engine;
using Esiur.Net;
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public class Session
{
Authentication Authentication { get; }
Source Source { get; }
string Id { get; }
DateTime Creation { get; }
DateTime Modification { get; }
//KeyList<string, object> Variables { get; }
//IStore Store { get; }
}
}

View File

@ -0,0 +1,30 @@
using Esiur.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public class Source
{
string id;
KeyList<SourceAttributeType, Structure> attributes;
string Id { get { return id; } }
KeyList<SourceAttributeType, Structure> Attributes
{
get { return attributes; }
}
public Source(string id, KeyList<SourceAttributeType, Structure> attributes)
{
this.id = id;
this.attributes = attributes;
}
}
}

View File

@ -0,0 +1,53 @@
using Esiur.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public enum SourceAttributeType
{
Mobility, // Stationary/Mobile
CPU, // Arc, Speed, Cores
IP, // IPv4, IPv6 Address
Route, // Trace Root
Location, // Lon, Lat, Alt, Accuracy
OS, // OS name, version, distro, kernel
Application, // lib version, app version
Network, // Bandwidth, MAC, IP, Route
Display, // Screen WxH
Media, // AudioIn, AudioOut, VideoIn,
Identity, // IMEI, IMSI, Manufacture
}
/*
public class SourceAttribute
{
SourceAttributeType type;
Structure value;
public SourceAttributeType Type
{
get
{
return type;
}
}
public Structure Value
{
get
{
return value;
}
}
public SourceAttribute(SourceAttributeType type, Structure value)
{
this.type = type;
this.value = value;
}
}
*/
}

View File

@ -0,0 +1,230 @@
using Esiur.Data;
using Esiur.Security.Cryptography;
using Esiur.Security.Integrity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Authority
{
public class UserCertificate : Certificate
{
uint ip;
byte[] ip6;
byte[] signature;
string domain;
string username;
ulong domainId;
public ulong DomainId
{
get { return domainId; }
}
public string Username
{
get { return username; }
}
public string Domain
{
get { return domain; }
}
public byte[] Signature
{
get { return signature; }
}
public uint IPAddress
{
get { return ip; }
}
public byte[] IPv6Address
{
get { return ip6; }
}
public UserCertificate(byte[] data, uint offset, uint length, bool privateKeyIncluded = false)
: base(0, DateTime.MinValue, DateTime.MinValue, HashFunctionType.MD5)
{
var oOffset = offset;
this.id = DC.GetUInt64(data, offset);
offset += 8;
// load IPs
this.ip = DC.GetUInt32(data, offset);
offset += 4;
ip6 = DC.Clip(data, offset, 16);
offset += 16;
this.issueDate = DC.GetDateTime(data, offset);
offset += 8;
this.expireDate = DC.GetDateTime(data, offset);
offset += 8;
this.domainId = DC.GetUInt64(data, offset);
offset += 8;
this.domain = Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
this.username = Encoding.ASCII.GetString(data, (int)offset + 1, data[offset]);
offset += (uint)data[offset] + 1;
// Hash Function
this.hashFunction = (HashFunctionType)(data[offset++] >> 4);
// Public Key Encryption Algorithm
var aea = (AsymetricEncryptionAlgorithmType)(data[offset] >> 5);
if (aea == AsymetricEncryptionAlgorithmType.RSA)
{
var key = new RSAParameters();
uint exponentLength = (uint)data[offset++] & 0x1F;
key.Exponent = DC.Clip(data, offset, exponentLength);
offset += exponentLength;
uint keySize = DC.GetUInt16(data, offset);
offset += 2;
key.Modulus = DC.Clip(data, offset, keySize);
offset += keySize;
// copy cert data
this.publicRawData = new byte[offset - oOffset];
Buffer.BlockCopy(data, (int)oOffset, publicRawData, 0, publicRawData.Length);
if (privateKeyIncluded)
{
uint privateKeyLength = (keySize * 3) + (keySize / 2);
uint halfKeySize = keySize / 2;
this.privateRawData = DC.Clip(data, offset, privateKeyLength);
key.D = DC.Clip(data, offset, keySize);
offset += keySize;
key.DP = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.DQ = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.InverseQ = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.P = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
key.Q = DC.Clip(data, offset, halfKeySize);
offset += halfKeySize;
}
// setup rsa
this.rsa = RSA.Create();// new RSACryptoServiceProvider();
this.rsa.ImportParameters(key);
this.signature = DC.Clip(data, offset, length - (offset - oOffset));
}
}
public UserCertificate(ulong id, string username, DomainCertificate domainCertificate, DateTime issueDate,
DateTime expireDate, HashFunctionType hashFunction = HashFunctionType.SHA1, uint ip = 0, byte[] ip6 = null)
: base(id, issueDate, expireDate, hashFunction)
{
// assign type
var cr = new BinaryList();
//id
cr.Append(id);
// ip
this.ip = ip;
this.ip6 = ip6;
cr.Append(ip);
if (ip6?.Length == 16)
cr.Append(ip6);
else
cr.Append(new byte[16]);
// dates
this.issueDate = DateTime.UtcNow;
this.expireDate = expireDate;
cr.Append(issueDate, expireDate);
// domain
this.domainId = domainCertificate.Id;
cr.Append(domainCertificate.Id);
this.domain = domainCertificate.Domain;
cr.Append((byte)domainCertificate.Domain.Length, Encoding.ASCII.GetBytes(domainCertificate.Domain));
// username
this.username = username;
cr.Append((byte)(username.Length), Encoding.ASCII.GetBytes(username));
// hash function (SHA1)
cr.Append((byte)((byte)hashFunction << 4));// (byte)0x10);
// public key
rsa = RSA.Create();// new RSACryptoServiceProvider(2048);
rsa.KeySize = 2048;
// write public certificate file
var key = rsa.ExportParameters(true);
publicRawData = BinaryList.ToBytes((byte)key.Exponent.Length, key.Exponent, (ushort)key.Modulus.Length, key.Modulus);
// sign it
this.signature = domainCertificate.Sign(publicRawData);
// store private info
privateRawData = DC.Merge(key.D, key.DP, key.DQ, key.InverseQ, key.P, key.Q, signature);
}
public override bool Save(string filename, bool includePrivate = false)
{
try
{
if (includePrivate)
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.DomainPrivate, publicRawData, signature, privateRawData));
else
File.WriteAllBytes(filename, BinaryList.ToBytes((byte)CertificateType.DomainPublic, publicRawData, signature));
return true;
}
catch
{
return false;
}
}
public override byte[] Serialize(bool includePrivate = false)
{
if (includePrivate)
return BinaryList.ToBytes(publicRawData, signature, privateRawData);
else
return BinaryList.ToBytes(publicRawData, signature);
}
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Cryptography
{
// Enums
public enum AsymetricEncryptionAlgorithmType
{
RSA = 0,
DSA = 1,
ECDSA = 2
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Cryptography
{
public enum SymetricEncryptionAlgorithmType
{
AES = 0,
Blowfish = 1,
DES = 2
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Integrity
{
public enum HashFunctionType
{
MD5 = 0,
SHA1,
SHA256,
SHA384,
SHA512
}
}

View File

@ -0,0 +1,16 @@
using Esiur.Resource;
using Esiur.Security.Authority;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Authority
{
public interface IDomain : IResource
{
string Name { get; }
DomainCertificate Certificate { get; }
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Esiur.Data;
using Esiur.Net.IIP;
using Esiur.Engine;
using Esiur.Security.Authority;
using Esiur.Resource;
namespace Esiur.Security.Membership
{
public interface IMembership:IResource
{
//IUser[] GetUsers(QueryFilter<string> user);
//bool AddCertificate(Certificate certificate);
//CACertificate[] GetCACertificates(string authority);
//UserCertificate[] GetUserCertificate(string user, string domain);
//DomainCertificate[] GetDomainCertificates(string domain);
bool UserExists(string username);
AsyncReply<byte[]> GetPassword(string username, string domain);
//ClientAuthentication Authenticate(string username, byte[] credentials, int flag);
//HostAuthentication Authenticate(DomainCertificate domainCertificate);
//CoHostAuthentication Authenticate(DomainCertificate hostCertificate, int hostId);
/*
object GetUserInfo(User user, string field);
object[] GetUserInfo(User user, string[] fields);
bool SetUserInfo(User user, string field, object value);
bool SetUserInfo(User user, KeyList<string, object> info);
*/
//bool AddUser(User user, KeyList<string, object> info);
//bool RemoveUser(string username);
}
}

View File

@ -0,0 +1,17 @@
using Esiur.Engine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Membership
{
public interface IUser
{
string Username
{
get;
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Permissions
{
public enum ActionType
{
Attach,
Delete,
Execute,
Get,
Set,
}
}

View File

@ -0,0 +1,18 @@
using Esiur.Engine;
using Esiur.Net;
using Esiur.Resource;
using Esiur.Resource.Template;
using Esiur.Security.Authority;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Esiur.Security.Permissions
{
public interface IPermissionManager
{
bool Applicable(IResource resource, Session session, ActionType action, MemberTemplate member);
}
}

View File

@ -0,0 +1,56 @@
using Esiur.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Esiur.Engine;
namespace Esiur.Stores
{
public class MemoryStore : IStore
{
public Instance Instance { get; set; }
public event DestroyedEvent OnDestroy;
Dictionary<uint, IResource> resources = new Dictionary<uint, IResource>();
public void Destroy()
{
}
public string Link(IResource resource)
{
if (resource.Instance.Store == this)
return this.Instance.Name + "/" + resource.Instance.Id;
return null;
}
public AsyncReply<IResource> Get(string path)
{
return new AsyncReply<IResource>(null);
}
public bool Put(IResource resource)
{
resources.Add(resource.Instance.Id, resource);
return true;
}
public AsyncReply<IResource> Retrieve(uint iid)
{
if (resources.ContainsKey(iid))
return new AsyncReply<IResource>(resources[iid]);
else
return new AsyncReply<IResource>(null);
}
public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{
return new AsyncReply<bool>(true);
}
}
}