mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-07-31 01:40:42 +00:00
RateControl
This commit is contained in:
@@ -89,8 +89,8 @@ public class AutoList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
|
||||
/// <returns>Array</returns>
|
||||
public T[] ToArray()
|
||||
{
|
||||
// list.OrderBy()
|
||||
return list.ToArray();
|
||||
lock (syncRoot)
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -242,7 +242,11 @@ public class AutoList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get { return list.Count; }
|
||||
get
|
||||
{
|
||||
lock (syncRoot)
|
||||
return list.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSynchronized => (list as ICollection).IsSynchronized;
|
||||
@@ -256,7 +260,8 @@ public class AutoList<T, ST> : IEnumerable<T>, ICollection, ICollection<T>
|
||||
/// <param name="value">Item to check if exists</param>
|
||||
public bool Contains(T value)
|
||||
{
|
||||
return list.Contains(value);
|
||||
lock (syncRoot)
|
||||
return list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -23,6 +23,18 @@ public static class DataDeserializer
|
||||
{
|
||||
internal static readonly AsyncLocal<ulong[]> TypeDefRequestSequence = new();
|
||||
|
||||
private static void EnsureTypedArrayBudget(ParsedTdu tdu, Warehouse warehouse, int elementSize)
|
||||
=> ParserGuard.EnsureAllocation(
|
||||
warehouse,
|
||||
ParserGuard.MultiplySaturated(tdu.PayloadLength, (ulong)elementSize),
|
||||
"typed array");
|
||||
|
||||
private static T[] GuardDecodedArray<T>(Warehouse warehouse, T[] values, int elementSize)
|
||||
{
|
||||
ParserGuard.EnsureCollectionCount(warehouse, values.Length, elementSize);
|
||||
return values;
|
||||
}
|
||||
|
||||
public static async AsyncReply<object> TypeDefInfoParserAsync(
|
||||
ParsedTdu tdu, EpConnection connection, uint[] requestSequence)
|
||||
{
|
||||
@@ -324,6 +336,10 @@ public static class DataDeserializer
|
||||
|
||||
public static object ResourceLinkParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence)
|
||||
{
|
||||
ParserGuard.EnsureAllocation(
|
||||
connection?.ParsingWarehouse,
|
||||
ParserGuard.MultiplySaturated(tdu.PayloadLength, 2),
|
||||
"resource link");
|
||||
var link = tdu.Data.GetString(tdu.PayloadOffset, (uint)tdu.PayloadLength);
|
||||
if (connection == null)
|
||||
{
|
||||
@@ -337,6 +353,10 @@ public static class DataDeserializer
|
||||
|
||||
public static object ResourceLinkParser(ParsedTdu tdu, Warehouse warehouse)
|
||||
{
|
||||
ParserGuard.EnsureAllocation(
|
||||
warehouse,
|
||||
ParserGuard.MultiplySaturated(tdu.PayloadLength, 2),
|
||||
"resource link");
|
||||
var link = tdu.Data.GetString(tdu.PayloadOffset, (uint)tdu.PayloadLength);
|
||||
return new ResourceLink(link);
|
||||
}
|
||||
@@ -432,27 +452,41 @@ public static class DataDeserializer
|
||||
|
||||
public static unsafe object RawDataParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence)
|
||||
{
|
||||
ParserGuard.EnsureAllocation(connection?.ParsingWarehouse, tdu.PayloadLength, "raw data");
|
||||
return tdu.Data.Clip(tdu.PayloadOffset, (uint)tdu.PayloadLength);
|
||||
}
|
||||
|
||||
public static unsafe object RawDataParser(ParsedTdu tdu, Warehouse warehouse)
|
||||
{
|
||||
ParserGuard.EnsureAllocation(warehouse, tdu.PayloadLength, "raw data");
|
||||
return tdu.Data.Clip(tdu.PayloadOffset, (uint)tdu.PayloadLength);
|
||||
}
|
||||
|
||||
|
||||
public static unsafe object StringParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence)
|
||||
{
|
||||
ParserGuard.EnsureAllocation(
|
||||
connection?.ParsingWarehouse,
|
||||
ParserGuard.MultiplySaturated(tdu.PayloadLength, 2),
|
||||
"string");
|
||||
return tdu.Data.GetString(tdu.PayloadOffset, (uint)tdu.PayloadLength);
|
||||
}
|
||||
|
||||
public static unsafe object StringParser(ParsedTdu tdu, Warehouse warehouse)
|
||||
{
|
||||
ParserGuard.EnsureAllocation(
|
||||
warehouse,
|
||||
ParserGuard.MultiplySaturated(tdu.PayloadLength, 2),
|
||||
"string");
|
||||
return tdu.Data.GetString(tdu.PayloadOffset, (uint)tdu.PayloadLength);
|
||||
}
|
||||
|
||||
public static async AsyncReply<IRecord> RecordParserAsync(ParsedTdu tdu, TypeDef recordTypeDef, EpConnection connection, uint[] requestSequence)
|
||||
{
|
||||
ParserGuard.EnsureCollectionCount(
|
||||
connection?.ParsingWarehouse,
|
||||
recordTypeDef.Properties.Length,
|
||||
IntPtr.Size);
|
||||
//if (tdu.Metadata.TypeDefId == null)
|
||||
// throw new Exception("TypeDefId metadata is required for record parsing.");
|
||||
|
||||
@@ -618,6 +652,8 @@ public static class DataDeserializer
|
||||
"TypeDef not found for record.");
|
||||
}
|
||||
|
||||
ParserGuard.EnsureCollectionCount(warehouse, recordTypeDef.Properties.Length, IntPtr.Size);
|
||||
|
||||
var list = new List<object>();
|
||||
|
||||
ParsedTdu current;
|
||||
@@ -789,6 +825,7 @@ public static class DataDeserializer
|
||||
public static async AsyncReply<object> RecordListParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence)
|
||||
{
|
||||
var rt = new AsyncBag<IRecord>();
|
||||
var count = 0;
|
||||
|
||||
var length = tdu.PayloadLength;
|
||||
var offset = tdu.PayloadOffset;
|
||||
@@ -798,6 +835,7 @@ public static class DataDeserializer
|
||||
//var (cs, reply)
|
||||
var pr = await Codec.ParseAsync(tdu.Data, offset, connection, requestSequence);
|
||||
|
||||
ParserGuard.EnsureCollectionCount(connection?.ParsingWarehouse, ++count, IntPtr.Size);
|
||||
rt.Add(pr.Value);
|
||||
|
||||
if (pr.Size > 0)
|
||||
@@ -827,6 +865,7 @@ public static class DataDeserializer
|
||||
{
|
||||
var (cs, reply) = Codec.ParseSync(tdu.Data, offset, warehouse);
|
||||
|
||||
ParserGuard.EnsureCollectionCount(warehouse, rt.Count + 1, IntPtr.Size);
|
||||
rt.Add(reply as IRecord);
|
||||
|
||||
if (cs > 0)
|
||||
@@ -845,6 +884,7 @@ public static class DataDeserializer
|
||||
public static async AsyncReply<object> ResourceListParserAsync(ParsedTdu tdu, EpConnection connection, uint[] requestSequence)
|
||||
{
|
||||
var rt = new AsyncBag<IResource>();
|
||||
var count = 0;
|
||||
|
||||
var length = tdu.PayloadLength;
|
||||
var offset = tdu.PayloadOffset;
|
||||
@@ -854,6 +894,7 @@ public static class DataDeserializer
|
||||
//var (cs, reply)
|
||||
var pr = await Codec.ParseAsync(tdu.Data, offset, connection, requestSequence);
|
||||
|
||||
ParserGuard.EnsureCollectionCount(connection?.ParsingWarehouse, ++count, IntPtr.Size);
|
||||
rt.Add(pr.Value);// reply);
|
||||
|
||||
if (pr.Size > 0)
|
||||
@@ -884,6 +925,7 @@ public static class DataDeserializer
|
||||
{
|
||||
var (cs, reply) = Codec.ParseSync(tdu.Data, offset, warehouse);
|
||||
|
||||
ParserGuard.EnsureCollectionCount(warehouse, rt.Count + 1, IntPtr.Size);
|
||||
rt.Add(reply as IResource);
|
||||
|
||||
if (cs > 0)
|
||||
@@ -931,6 +973,7 @@ public static class DataDeserializer
|
||||
|
||||
|
||||
var rt = new AsyncBag<object>();
|
||||
var count = 0;
|
||||
|
||||
//var list = new List<object>();
|
||||
|
||||
@@ -962,6 +1005,7 @@ public static class DataDeserializer
|
||||
//var (cs, reply)
|
||||
var value = Codec.ParseAsync(current, connection, requestSequence);
|
||||
|
||||
ParserGuard.EnsureCollectionCount(connection?.ParsingWarehouse, ++count, IntPtr.Size);
|
||||
rt.Add(value);
|
||||
|
||||
if (current.TotalLength > 0)
|
||||
@@ -1012,6 +1056,7 @@ public static class DataDeserializer
|
||||
|
||||
var reply = Codec.ParseSync(current, warehouse);
|
||||
|
||||
ParserGuard.EnsureCollectionCount(warehouse, list.Count + 1, IntPtr.Size);
|
||||
list.Add(reply);
|
||||
|
||||
if (current.TotalLength > 0)
|
||||
@@ -1040,6 +1085,7 @@ public static class DataDeserializer
|
||||
{
|
||||
var (cs, reply) = Codec.ParseSync(data, offset, warehouse);
|
||||
|
||||
ParserGuard.EnsureCollectionCount(warehouse, rt.Count + 1, IntPtr.Size);
|
||||
rt.Add(reply);
|
||||
|
||||
if (cs > 0)
|
||||
@@ -1151,26 +1197,37 @@ public static class DataDeserializer
|
||||
|
||||
public static Array TypedArrayParser(ParsedTdu tdu, Tru elementTru, Warehouse warehouse)
|
||||
{
|
||||
var primitiveElementSize = elementTru.Identifier switch
|
||||
{
|
||||
TruIdentifier.Int16 or TruIdentifier.UInt16 => 2,
|
||||
TruIdentifier.Int32 or TruIdentifier.UInt32 => 4,
|
||||
TruIdentifier.Int64 or TruIdentifier.UInt64 => 8,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
if (primitiveElementSize > 0)
|
||||
EnsureTypedArrayBudget(tdu, warehouse, primitiveElementSize);
|
||||
|
||||
switch (elementTru.Identifier)
|
||||
{
|
||||
case TruIdentifier.Int32:
|
||||
return GroupInt32Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength));
|
||||
return GuardDecodedArray(warehouse, GroupInt32Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 4);
|
||||
case TruIdentifier.Int64:
|
||||
return GroupInt64Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength));
|
||||
return GuardDecodedArray(warehouse, GroupInt64Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 8);
|
||||
case TruIdentifier.Int16:
|
||||
return GroupInt16Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength));
|
||||
return GuardDecodedArray(warehouse, GroupInt16Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 2);
|
||||
case TruIdentifier.UInt32:
|
||||
return GroupUInt32Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength));
|
||||
return GuardDecodedArray(warehouse, GroupUInt32Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 4);
|
||||
case TruIdentifier.UInt64:
|
||||
return GroupUInt64Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength));
|
||||
return GuardDecodedArray(warehouse, GroupUInt64Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 8);
|
||||
case TruIdentifier.UInt16:
|
||||
return GroupUInt16Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength));
|
||||
return GuardDecodedArray(warehouse, GroupUInt16Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 2);
|
||||
//case TruIdentifier.Enum:
|
||||
|
||||
// var enumType = tru.GetRuntimeType(warehouse);
|
||||
@@ -1224,6 +1281,7 @@ public static class DataDeserializer
|
||||
|
||||
var value = Codec.ParseSync(current, warehouse);
|
||||
|
||||
ParserGuard.EnsureCollectionCount(warehouse, list.Count + 1, IntPtr.Size);
|
||||
list.Add(value);
|
||||
|
||||
if (current.TotalLength > 0)
|
||||
@@ -1249,26 +1307,38 @@ public static class DataDeserializer
|
||||
// Non-async/await version of TypedArrayParserAsync using continuations
|
||||
public static AsyncReply TypedArrayParserAsync2(ParsedTdu tdu, Tru elementTru, EpConnection connection, uint[] requestSequence)
|
||||
{
|
||||
var warehouse = connection?.ParsingWarehouse;
|
||||
var primitiveElementSize = elementTru.Identifier switch
|
||||
{
|
||||
TruIdentifier.Int16 or TruIdentifier.UInt16 => 2,
|
||||
TruIdentifier.Int32 or TruIdentifier.UInt32 => 4,
|
||||
TruIdentifier.Int64 or TruIdentifier.UInt64 => 8,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
if (primitiveElementSize > 0)
|
||||
EnsureTypedArrayBudget(tdu, warehouse, primitiveElementSize);
|
||||
|
||||
switch (elementTru.Identifier)
|
||||
{
|
||||
case TruIdentifier.Int32:
|
||||
return new AsyncReply(GroupInt32Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)));
|
||||
return new AsyncReply(GuardDecodedArray(warehouse, GroupInt32Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 4));
|
||||
case TruIdentifier.Int64:
|
||||
return new AsyncReply(GroupInt64Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)));
|
||||
return new AsyncReply(GuardDecodedArray(warehouse, GroupInt64Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 8));
|
||||
case TruIdentifier.Int16:
|
||||
return new AsyncReply(GroupInt16Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)));
|
||||
return new AsyncReply(GuardDecodedArray(warehouse, GroupInt16Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 2));
|
||||
case TruIdentifier.UInt32:
|
||||
return new AsyncReply(GroupUInt32Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)));
|
||||
return new AsyncReply(GuardDecodedArray(warehouse, GroupUInt32Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 4));
|
||||
case TruIdentifier.UInt64:
|
||||
return new AsyncReply(GroupUInt64Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)));
|
||||
return new AsyncReply(GuardDecodedArray(warehouse, GroupUInt64Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 8));
|
||||
case TruIdentifier.UInt16:
|
||||
return new AsyncReply(GroupUInt16Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)));
|
||||
return new AsyncReply(GuardDecodedArray(warehouse, GroupUInt16Codec.Decode(tdu.Data.AsSpan(
|
||||
(int)tdu.PayloadOffset, (int)tdu.PayloadLength)), 2));
|
||||
|
||||
default:
|
||||
var rt = new AsyncReply();
|
||||
@@ -1285,6 +1355,7 @@ public static class DataDeserializer
|
||||
|
||||
// Recursive processor using continuations
|
||||
Action<uint, uint, ParsedTdu?> processNext = null;
|
||||
var itemCount = 0;
|
||||
|
||||
processNext = (curOffset, curLength, previous) =>
|
||||
{
|
||||
@@ -1333,6 +1404,19 @@ public static class DataDeserializer
|
||||
|
||||
var reply = Codec.ParseAsync(current, connection, requestSequence);
|
||||
|
||||
try
|
||||
{
|
||||
ParserGuard.EnsureCollectionCount(
|
||||
warehouse,
|
||||
++itemCount,
|
||||
IntPtr.Size);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
rt.TriggerError(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
list.Add(reply);
|
||||
|
||||
if (current.TotalLength > 0)
|
||||
|
||||
@@ -62,14 +62,16 @@ public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>>
|
||||
|
||||
public T Take(KT key)
|
||||
{
|
||||
if (dic.ContainsKey(key))
|
||||
lock (syncRoot)
|
||||
{
|
||||
var v = dic[key];
|
||||
Remove(key);
|
||||
return v;
|
||||
}
|
||||
else
|
||||
if (dic.TryGetValue(key, out var value))
|
||||
{
|
||||
Remove(key);
|
||||
return value;
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
public void Sort(Func<KeyValuePair<KT, T>, object> keySelector)
|
||||
@@ -79,9 +81,12 @@ public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>>
|
||||
|
||||
public T[] ToArray()
|
||||
{
|
||||
var a = new T[Count];
|
||||
dic.Values.CopyTo(a, 0);
|
||||
return a;
|
||||
lock (syncRoot)
|
||||
{
|
||||
var a = new T[dic.Count];
|
||||
dic.Values.CopyTo(a, 0);
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(KT key, T value)
|
||||
@@ -132,10 +137,8 @@ public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>>
|
||||
{
|
||||
get
|
||||
{
|
||||
if (dic.ContainsKey(key))
|
||||
return dic[key];
|
||||
else
|
||||
return default(T);
|
||||
lock (syncRoot)
|
||||
return dic.TryGetValue(key, out var value) ? value : default(T);
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -157,13 +160,15 @@ public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>>
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (removableList)
|
||||
foreach (IDestructible v in dic.Values)
|
||||
if (v != null)
|
||||
v.OnDestroy -= ItemDestroyed;
|
||||
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (removableList)
|
||||
foreach (IDestructible v in dic.Values)
|
||||
if (v != null)
|
||||
v.OnDestroy -= ItemDestroyed;
|
||||
|
||||
dic.Clear();
|
||||
}
|
||||
|
||||
if (OnCleared != null)
|
||||
OnCleared(this);
|
||||
@@ -184,17 +189,18 @@ public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>>
|
||||
|
||||
public void Remove(KT key)
|
||||
{
|
||||
if (!dic.ContainsKey(key))
|
||||
return;
|
||||
|
||||
var value = dic[key];
|
||||
|
||||
if (removableList)
|
||||
if (value != null)
|
||||
((IDestructible)value).OnDestroy -= ItemDestroyed;
|
||||
|
||||
T value;
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (!dic.TryGetValue(key, out value))
|
||||
return;
|
||||
|
||||
if (removableList)
|
||||
if (value != null)
|
||||
((IDestructible)value).OnDestroy -= ItemDestroyed;
|
||||
|
||||
dic.Remove(key);
|
||||
}
|
||||
|
||||
if (OnRemoved != null)
|
||||
OnRemoved(key, value, this);
|
||||
@@ -208,19 +214,26 @@ public class KeyList<KT, T> : IEnumerable<KeyValuePair<KT, T>>
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return dic.Count; }
|
||||
get
|
||||
{
|
||||
lock (syncRoot)
|
||||
return dic.Count;
|
||||
}
|
||||
}
|
||||
public bool Contains(KT Key)
|
||||
{
|
||||
return dic.ContainsKey(Key);
|
||||
lock (syncRoot)
|
||||
return dic.ContainsKey(Key);
|
||||
}
|
||||
public bool ContainsKey(KT Key)
|
||||
{
|
||||
return dic.ContainsKey(Key);
|
||||
lock (syncRoot)
|
||||
return dic.ContainsKey(Key);
|
||||
}
|
||||
public bool ContainsValue(T Value)
|
||||
{
|
||||
return dic.ContainsValue(Value);
|
||||
lock (syncRoot)
|
||||
return dic.ContainsValue(Value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@ namespace Esiur.Data
|
||||
|
||||
public static async AsyncReply<ParsedTdu> ParseAsync(byte[] data, uint offset, uint ends, EpConnection connection)
|
||||
{
|
||||
// @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it.
|
||||
|
||||
var h = data[offset++];
|
||||
|
||||
var cls = (TduClass)(h >> 6);
|
||||
@@ -91,6 +89,8 @@ namespace Esiur.Data
|
||||
for (uint i = 0; i < cll; i++)
|
||||
cl = cl << 8 | data[offset++];
|
||||
|
||||
ParserGuard.EnsurePacketSize(ParserGuard.GetWarehouse(connection), cl);
|
||||
|
||||
if (ends - offset < cl)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
@@ -102,6 +102,10 @@ namespace Esiur.Data
|
||||
//offset += data[offset] + (uint)1;
|
||||
|
||||
var metaDataTru = await Tru.ParseAsync(data, offset, connection, null);
|
||||
|
||||
if (metaDataTru.Size > cl)
|
||||
throw new ParserLimitException("Typed TDU metadata exceeds its declared payload length.");
|
||||
|
||||
offset += metaDataTru.Size;
|
||||
|
||||
return new ParsedTdu()
|
||||
@@ -133,6 +137,8 @@ namespace Esiur.Data
|
||||
for (uint i = 0; i < cll; i++)
|
||||
cl = cl << 8 | data[offset++];
|
||||
|
||||
ParserGuard.EnsurePacketSize(ParserGuard.GetWarehouse(connection), cl);
|
||||
|
||||
if (ends - offset < cl)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
@@ -158,8 +164,6 @@ namespace Esiur.Data
|
||||
|
||||
public static object Parse(byte[] data, uint offset, uint ends, EpConnection connection)
|
||||
{
|
||||
// @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it.
|
||||
|
||||
var h = data[offset++];
|
||||
|
||||
var cls = (TduClass)(h >> 6);
|
||||
@@ -222,6 +226,8 @@ namespace Esiur.Data
|
||||
for (uint i = 0; i < cll; i++)
|
||||
cl = cl << 8 | data[offset++];
|
||||
|
||||
ParserGuard.EnsurePacketSize(ParserGuard.GetWarehouse(connection), cl);
|
||||
|
||||
if (ends - offset < cl)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
@@ -235,6 +241,13 @@ namespace Esiur.Data
|
||||
|
||||
Tru.ParseAsync(data, offset, connection, null).Then(metaDataTru =>
|
||||
{
|
||||
if (metaDataTru.Size > cl)
|
||||
{
|
||||
rt.TriggerError(new ParserLimitException(
|
||||
"Typed TDU metadata exceeds its declared payload length."));
|
||||
return;
|
||||
}
|
||||
|
||||
offset += metaDataTru.Size;
|
||||
|
||||
rt.Trigger(new ParsedTdu()
|
||||
@@ -269,6 +282,8 @@ namespace Esiur.Data
|
||||
for (uint i = 0; i < cll; i++)
|
||||
cl = cl << 8 | data[offset++];
|
||||
|
||||
ParserGuard.EnsurePacketSize(ParserGuard.GetWarehouse(connection), cl);
|
||||
|
||||
if (ends - offset < cl)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
@@ -347,8 +362,6 @@ namespace Esiur.Data
|
||||
|
||||
public static ParsedTdu ParseSync(byte[] data, uint offset, uint ends, Warehouse warehouse)
|
||||
{
|
||||
// @TODO: add protection against memory allocation attacks by checking the length of the data before parsing it.
|
||||
|
||||
var h = data[offset++];
|
||||
|
||||
var cls = (TduClass)(h >> 6);
|
||||
@@ -411,6 +424,8 @@ namespace Esiur.Data
|
||||
for (uint i = 0; i < cll; i++)
|
||||
cl = cl << 8 | data[offset++];
|
||||
|
||||
ParserGuard.EnsurePacketSize(warehouse, cl);
|
||||
|
||||
if (ends - offset < cl)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
@@ -422,6 +437,10 @@ namespace Esiur.Data
|
||||
//offset += data[offset] + (uint)1;
|
||||
|
||||
var metaDataTru = Tru.Parse(data, offset, warehouse);
|
||||
|
||||
if (metaDataTru.Size > cl)
|
||||
throw new ParserLimitException("Typed TDU metadata exceeds its declared payload length.");
|
||||
|
||||
offset += metaDataTru.Size;
|
||||
|
||||
return new ParsedTdu()
|
||||
@@ -453,6 +472,8 @@ namespace Esiur.Data
|
||||
for (uint i = 0; i < cll; i++)
|
||||
cl = cl << 8 | data[offset++];
|
||||
|
||||
ParserGuard.EnsurePacketSize(warehouse, cl);
|
||||
|
||||
if (ends - offset < cl)
|
||||
return new ParsedTdu()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using Esiur.Protocol;
|
||||
using Esiur.Resource;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Esiur.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when untrusted input exceeds a configured parser budget.
|
||||
/// </summary>
|
||||
public sealed class ParserLimitException : Exception
|
||||
{
|
||||
public ParserLimitException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal static class ParserGuard
|
||||
{
|
||||
internal static Warehouse? GetWarehouse(EpConnection? connection)
|
||||
=> connection?.ParsingWarehouse;
|
||||
|
||||
internal static void EnsurePacketSize(Warehouse? warehouse, ulong size)
|
||||
{
|
||||
var limit = warehouse?.Configuration.Parser.MaximumPacketSize ?? 0;
|
||||
if (limit > 0 && size > limit)
|
||||
throw new ParserLimitException(
|
||||
$"Declared packet payload of {size} bytes exceeds the {limit}-byte limit.");
|
||||
}
|
||||
|
||||
internal static void EnsureAllocation(Warehouse? warehouse, ulong size, string kind)
|
||||
{
|
||||
var limit = warehouse?.Configuration.Parser.MaximumAllocationSize ?? 0;
|
||||
if (limit > 0 && size > limit)
|
||||
throw new ParserLimitException(
|
||||
$"Decoded {kind} allocation of {size} bytes exceeds the {limit}-byte limit.");
|
||||
}
|
||||
|
||||
internal static void EnsureCollectionCount(
|
||||
Warehouse? warehouse,
|
||||
int count,
|
||||
int estimatedBytesPerItem = 0)
|
||||
{
|
||||
var configuration = warehouse?.Configuration.Parser;
|
||||
if (configuration == null)
|
||||
return;
|
||||
|
||||
if (configuration.MaximumCollectionItems > 0 && count > configuration.MaximumCollectionItems)
|
||||
throw new ParserLimitException(
|
||||
$"Decoded collection count of {count} exceeds the {configuration.MaximumCollectionItems}-item limit.");
|
||||
|
||||
if (estimatedBytesPerItem > 0)
|
||||
EnsureAllocation(
|
||||
warehouse,
|
||||
(ulong)count * (ulong)estimatedBytesPerItem,
|
||||
"collection");
|
||||
}
|
||||
|
||||
internal static ulong MultiplySaturated(ulong value, ulong multiplier)
|
||||
=> value > ulong.MaxValue / multiplier ? ulong.MaxValue : value * multiplier;
|
||||
}
|
||||
@@ -21,7 +21,11 @@ namespace Esiur.Data
|
||||
|
||||
|
||||
|
||||
public static PlainTdu Parse(byte[] data, uint offset, uint ends)
|
||||
public static PlainTdu Parse(
|
||||
byte[] data,
|
||||
uint offset,
|
||||
uint ends,
|
||||
ulong maximumPayloadLength = ulong.MaxValue)
|
||||
{
|
||||
var oOffset = offset;
|
||||
|
||||
@@ -91,6 +95,10 @@ namespace Esiur.Data
|
||||
for (uint i = 0; i < cll; i++)
|
||||
cl = cl << 8 | data[offset++];
|
||||
|
||||
if (cl > maximumPayloadLength)
|
||||
throw new ParserLimitException(
|
||||
$"Declared packet payload of {cl} bytes exceeds the {maximumPayloadLength}-byte limit.");
|
||||
|
||||
if (ends - offset < cl)
|
||||
return new PlainTdu()
|
||||
{
|
||||
@@ -128,6 +136,10 @@ namespace Esiur.Data
|
||||
for (uint i = 0; i < cll; i++)
|
||||
cl = cl << 8 | data[offset++];
|
||||
|
||||
if (cl > maximumPayloadLength)
|
||||
throw new ParserLimitException(
|
||||
$"Declared packet payload of {cl} bytes exceeds the {maximumPayloadLength}-byte limit.");
|
||||
|
||||
if (ends - offset < cl)
|
||||
return new PlainTdu()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user