dev_timestamp
Harald Wolff 2019-08-03 12:50:41 +02:00
parent 6e43e1daf9
commit 16b31d731b
11 changed files with 833 additions and 1 deletions

View File

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.Collections;
namespace ln.types
{
public class Argument
{
public char Short { get; }
public String Long { get; }
public bool NeedsValue { get; }
public bool HasValue => Value != null;
public String Value { get; set; }
public int IntegerValue => int.Parse(Value);
public double DoubleValue => double.Parse(Value);
public bool IsSet { get; set; }
public Argument(char shortName, string longName)
{
Short = shortName;
Long = longName;
NeedsValue = false;
}
public Argument(char shortName, string longName, int defaultValue)
: this(shortName, longName, defaultValue.ToString()) { }
public Argument(char shortName, string longName, double defaultValue)
: this(shortName, longName, defaultValue.ToString()) { }
public Argument(char shortName, string longName, string defaultValue)
{
Short = shortName;
Long = longName;
NeedsValue = true;
Value = defaultValue;
}
}
public class ArgumentContainer
{
List<Argument> arguments;
List<String> additionalArguments;
public ArgumentContainer()
{
arguments = new List<Argument>();
additionalArguments = new List<string>();
}
public ArgumentContainer(IEnumerable<Argument> argumentDefinitions)
: this()
{
arguments.AddRange(argumentDefinitions);
}
public Argument this[string longName]
{
get
{
if (longName == null)
throw new ArgumentNullException(nameof(longName));
foreach (Argument argument in arguments)
{
if (longName.Equals(argument.Long))
return argument;
}
throw new KeyNotFoundException();
}
}
public Argument this[char shortName]
{
get
{
if (shortName == 0)
throw new ArgumentNullException(nameof(shortName));
foreach (Argument argument in arguments)
{
if (shortName.Equals(argument.Short))
return argument;
}
throw new KeyNotFoundException();
}
}
public bool Contains(string longName)
{
foreach (Argument argument in arguments)
{
if (longName.Equals(argument.Long))
return true;
}
return false;
}
public bool Contains(char shortName)
{
if (shortName == (char)0)
return false;
foreach (Argument argument in arguments)
{
if (shortName.Equals(argument.Short))
return true;
}
return false;
}
public ArgumentContainer Add(Argument argument)
{
if (Contains(argument.Short) || Contains(argument.Long))
throw new ArgumentException(nameof(argument),String.Format("0x{0:x} / {1}",(int)argument.Short,argument.Long));
arguments.Add(argument);
return this;
}
public ArgumentContainer Add(char shortName) => Add(new Argument(shortName, null));
public ArgumentContainer Add(string longName) => Add(new Argument((char)0, longName));
public ArgumentContainer Add(char shortName, string longName) => Add(new Argument(shortName, longName));
public ArgumentContainer Add(char shortName, string longName, string defaultValue) => Add(new Argument(shortName, longName, defaultValue));
public ArgumentContainer Add(int shortName, string longName, string defaultValue) => Add(new Argument((char)shortName, longName, defaultValue));
public void Parse(IEnumerable<string> args)
{
Queue<string> q = new Queue<string>(args);
while (q.Count > 0)
{
string currentOption = q.Dequeue();
if (currentOption[0].Equals('-'))
{
if (currentOption[1].Equals('-'))
{
String aname = currentOption.Substring(2);
Argument argument = this[aname];
argument.IsSet = true;
if (argument.NeedsValue)
argument.Value = q.Dequeue();
}
else
{
foreach (char option in currentOption.Substring(1))
{
Argument argument = this[option];
argument.IsSet = true;
if (argument.NeedsValue)
argument.Value = q.Dequeue();
}
}
}
else
{
additionalArguments.Add(currentOption);
}
}
}
}
}

View File

@ -8,7 +8,16 @@ namespace ln.types
{
public static class Extensions
{
public static T[] Combine<T>(this T[] me,IEnumerable<T> other)
public static T[] Concat<T>(this T[] me, T[] other)
{
T[] combined = new T[me.Length + other.Length];
Array.Copy(me, 0, combined, 0, me.Length);
Array.Copy(other, 0, combined, me.Length, combined.Length);
return combined;
}
public static T[] Combine<T>(this T[] me, IEnumerable<T> other)
{
HashSet<T> elements = new HashSet<T>(me);
foreach (T e in other)
@ -104,6 +113,35 @@ namespace ln.types
return BitConverter.ToUInt16(bytes, 0);
}
public static void WriteLong(this Stream stream, long value, bool bigEndian = false)
{
byte[] bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian == bigEndian)
Array.Reverse(bytes);
stream.Write(bytes, 0, bytes.Length);
}
public static long ReadLong(this Stream stream, bool bigEndian = false)
{
byte[] bytes = stream.ReadBytes(8);
if (BitConverter.IsLittleEndian == bigEndian)
Array.Reverse(bytes);
return BitConverter.ToInt64(bytes, 0);
}
public static void WriteULong(this Stream stream, ulong value, bool bigEndian = false)
{
byte[] bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian == bigEndian)
Array.Reverse(bytes);
stream.Write(bytes, 0, bytes.Length);
}
public static ulong ReadULong(this Stream stream, bool bigEndian = false)
{
byte[] bytes = stream.ReadBytes(8);
if (BitConverter.IsLittleEndian == bigEndian)
Array.Reverse(bytes);
return BitConverter.ToUInt64(bytes, 0);
}
@ -135,6 +173,21 @@ namespace ln.types
return BitConverter.ToUInt32(ib, 0);
}
public static byte[] GetBytes(this short v, bool bigEndian = false)
{
byte[] bytes = BitConverter.GetBytes(v);
if (BitConverter.IsLittleEndian == bigEndian)
Array.Reverse(bytes);
return bytes;
}
public static byte[] GetBytes(this ushort v, bool bigEndian = false)
{
byte[] bytes = BitConverter.GetBytes(v);
if (BitConverter.IsLittleEndian == bigEndian)
Array.Reverse(bytes);
return bytes;
}
public static short GetShort(this byte[] bytes, bool bigEndian = false) => GetShort(bytes, 0, bigEndian);
public static short GetShort(this byte[] bytes, int offset, bool bigEndian = false)
{
@ -148,6 +201,23 @@ namespace ln.types
return BitConverter.ToUInt16(ib, 0);
}
public static byte[] GetBytes(this long v, bool bigEndian = false)
{
byte[] bytes = BitConverter.GetBytes(v);
if (BitConverter.IsLittleEndian == bigEndian)
Array.Reverse(bytes);
return bytes;
}
public static byte[] GetBytes(this ulong v, bool bigEndian = false)
{
byte[] bytes = BitConverter.GetBytes(v);
if (BitConverter.IsLittleEndian == bigEndian)
Array.Reverse(bytes);
return bytes;
}
public static T[] Slice<T>(this T[] me, int offset) => Slice(me, offset, me.Length - offset);
public static T[] Slice<T>(this T[] me,int offset,int length)
{

View File

@ -0,0 +1,26 @@
using System;
namespace ln.types.json.rpc
{
public class RPCError
{
public int Code { get; set; }
public string Message { get; set; }
public object Data { get; set; }
public RPCError()
{
}
public RPCError(Exception e)
{
Code = 500;
Message = e.ToString();
}
public RPCError(int code,String message)
{
Code = code;
Message = message;
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
namespace ln.types.json.rpc
{
public class RPCMessage
{
public String Version { get; set; }
public String Method { get; set; }
public object[] Parameters { get; set; }
public object Result { get; set; }
public RPCError Error { get; set; }
public JToken Identifier { get; set; }
public RPCMessage()
{
Version = "2.0";
Parameters = new object[0];
}
public RPCMessage(JObject source)
{
Version = source["jsonrpc"].ToObject<string>();
if (source.ContainsKey("method"))
Method = source["method"].ToObject<string>();
if (source.ContainsKey("params"))
Parameters = source["params"].ToObject<object[]>();
if (source.ContainsKey("result"))
Result = source["result"];
if (source.ContainsKey("error"))
if (source.ContainsKey("id"))
Identifier = source["id"];
else
Identifier = null;
}
}
}

View File

@ -115,6 +115,15 @@
<Compile Include="json\converter\JSONGuidConverter.cs" />
<Compile Include="json\converter\JSONIPv4Converter.cs" />
<Compile Include="json\converter\JSONNetwork4Converter.cs" />
<Compile Include="net\tools\Ping.cs" />
<Compile Include="net\IPv4Header.cs" />
<Compile Include="net\ICMPPacket.cs" />
<Compile Include="json\rpc\RPCMessage.cs" />
<Compile Include="json\rpc\RPCError.cs" />
<Compile Include="ArgumentContainer.cs" />
<Compile Include="rpc\RPCContainer.cs" />
<Compile Include="rpc\RPCCall.cs" />
<Compile Include="rpc\RPCResult.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="sync\" />
@ -132,6 +141,9 @@
<Folder Include="odb\collections\" />
<Folder Include="json\" />
<Folder Include="json\converter\" />
<Folder Include="net\tools\" />
<Folder Include="json\rpc\" />
<Folder Include="rpc\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ln.logging\ln.logging.csproj">

91
net/ICMPPacket.cs 100644
View File

@ -0,0 +1,91 @@
using System;
using System.Security;
namespace ln.types.net
{
public class ICMPHeader
{
public byte Type { get; set; }
public byte Code { get; set; }
public ushort Checksum { get; set; }
public Int32 Rest { get; set; }
public int HeaderLength => 8 + Payload.Length;
public ushort Identifier
{
get => (ushort)(Rest & 0x0000FFFF);
set
{
Rest = (int)(Rest & 0xFFFF0000) | (value & 0x0000FFFF);
}
}
public ushort SequenceNumber
{
get => (ushort)((Rest & 0xFFFF0000) >> 16);
set
{
Rest = (int)(Rest & 0x0000FFFF) | ((value << 16) & 0x0000FFFF);
}
}
public byte[] Payload { get; set; } = new byte[0];
public ICMPHeader()
{
}
public ICMPHeader(byte[] buffer, int offset, int length)
{
if (buffer.Length < 8)
throw new ArgumentOutOfRangeException(nameof(buffer));
if (buffer.Length < (offset + 8))
throw new ArgumentOutOfRangeException(nameof(offset));
if (buffer.Length < (offset + length))
throw new ArgumentOutOfRangeException(nameof(length));
Type = buffer[offset];
Code = buffer[offset + 1];
Checksum = BitConverter.ToUInt16(buffer, offset + 2);
Rest = BitConverter.ToInt32(buffer, offset + 4);
Payload = buffer.Slice(offset + 8);
}
public void Write(byte[] buffer, int offset) => Write(buffer, offset, false);
public void Write(byte[] buffer,int offset,bool performChecksum)
{
if (buffer.Length < HeaderLength)
throw new ArgumentOutOfRangeException(nameof(buffer));
if (buffer.Length < (offset + HeaderLength))
throw new ArgumentOutOfRangeException(nameof(offset));
if (performChecksum)
Checksum = 0;
buffer[offset] = Type;
buffer[offset + 1] = Code;
Array.Copy(BitConverter.GetBytes(Checksum), 0, buffer, offset + 2, 2);
Array.Copy(BitConverter.GetBytes(Rest), 0, buffer, offset + 4, 4);
Array.Copy(Payload, 0, buffer, offset + 8, Payload.Length);
if (performChecksum)
{
Checksum = IPv4Header.PerformChecksum(buffer,offset,HeaderLength);
Array.Copy(BitConverter.GetBytes(Checksum), 0, buffer, offset + 2, 2);
}
}
public byte[] ToBytes(bool performChecksum)
{
byte[] packet = new byte[HeaderLength];
Write(packet, 0, performChecksum);
return packet;
}
public static ICMPHeader Parse(byte[] buffer, int offset, int length)
{
return new ICMPHeader(buffer, offset, length);
}
}
}

123
net/IPv4Header.cs 100644
View File

@ -0,0 +1,123 @@
using System;
namespace ln.types.net
{
public class IPv4Header
{
public byte Version { get; set; }
public byte IHL { get; set; }
public byte TypeOfService { get; set; }
public ushort TotalLength { get; set; }
public ushort Identification { get; set; }
public byte Flags { get; set; }
public ushort FragmentOffset { get; set; }
public byte TimeToLive { get; set; }
public byte Protocol { get; set; }
public ushort HeaderChecksum { get; set; }
public IPv4 SourceAddress { get; set; }
public IPv4 DestinationAddress { get; set; }
public byte[] Options { get; set; }
public IPv4Header()
{
}
public int HeaderLength => IHL << 2;
public void Write(byte[] buffer, int offset) => Write(buffer, offset, false);
public void Write(byte[] buffer,int offset,bool performChecksum)
{
if (buffer.Length < HeaderLength)
throw new ArgumentOutOfRangeException(nameof(buffer));
if (offset + HeaderLength < buffer.Length)
throw new ArgumentOutOfRangeException(nameof(offset));
if ((Options.Length % 4) != 0)
throw new InvalidOperationException("IPv4Header.Options must be length multiple of 4");
if ((Options.Length + 20) != HeaderLength)
throw new InvalidOperationException("IHL must match .Header and .Options length");
if (performChecksum)
HeaderChecksum = 0;
int i = (Version) | (IHL << 4) | (TypeOfService << 8) | (TotalLength << 16);
Array.Copy(BitConverter.GetBytes(i), 0, buffer, offset, 4);
i = (Identification) | (Flags << 16) | (FragmentOffset << 19);
Array.Copy(BitConverter.GetBytes(i), 0, buffer, offset + 4, 4);
i = (TimeToLive) | (Protocol << 8) | (HeaderChecksum << 16);
Array.Copy(BitConverter.GetBytes(i), 0, buffer, offset + 8, 4);
Array.Copy(SourceAddress.IPBytes, 0, buffer, offset + 12, 4);
Array.Copy(SourceAddress.IPBytes, 0, buffer, offset + 16, 4);
Array.Copy(Options, 0, buffer, offset + 20, Options.Length);
if (performChecksum)
{
HeaderChecksum = PerformChecksum(buffer,offset,HeaderLength);
Array.Copy(BitConverter.GetBytes(HeaderChecksum), 0, buffer, offset + 10, 4);
}
}
public byte[] ToBytes() => ToBytes(false);
public byte[] ToBytes(bool performChecksum)
{
byte[] packet = new byte[HeaderLength];
Write(packet, 0, performChecksum);
return packet;
}
public static ushort PerformChecksum(byte[] headerBytes) => PerformChecksum(headerBytes, 0, headerBytes.Length);
public static ushort PerformChecksum(byte[] headerBytes,int offset,int length)
{
uint chksum = 0;
for (int n = 0; n < headerBytes.Length; n += 4)
chksum += BitConverter.ToUInt16(headerBytes, n);
chksum += (chksum & 0xFFFF) + (chksum >> 16);
if ((chksum & 0xFFFF0000)!=0)
chksum += (chksum & 0xFFFF) + (chksum >> 16);
return (ushort)((~chksum) & 0xFFFF);
}
public static IPv4Header Parse(byte[] packet,int offset,int length)
{
if (offset >= packet.Length)
throw new ArgumentOutOfRangeException(nameof(offset));
if ((offset + length > packet.Length)|| (length < 20))
throw new ArgumentOutOfRangeException(nameof(length));
IPv4Header header = new IPv4Header();
uint ui = BitConverter.ToUInt32(packet, offset);
header.Version = (byte)(ui & 0x0F);
header.IHL = (byte)((ui & 0xF0) >> 4);
header.TypeOfService = (byte)(ui >> 8);
header.TotalLength = (ushort)(ui >> 16);
ui = BitConverter.ToUInt32(packet, offset + 4);
header.Identification = (ushort)(ui & 0x0000FFFF);
header.Flags = (byte)((ui & 0x00070000) >> 16);
header.FragmentOffset = (ushort)((ui & 0xFFF80000) >> 19);
ui = BitConverter.ToUInt32(packet, offset + 4);
header.TimeToLive = (byte)(ui & 0x000000FF);
header.Protocol = (byte)((ui & 0x0000FF00) >> 8);
header.HeaderChecksum = (ushort)((ui & 0xFFFF0000)>>16);
header.SourceAddress = new IPv4(packet, 12);
header.DestinationAddress= new IPv4(packet, 16);
header.Options = packet.Slice(20,header.HeaderLength - 20);
return header;
}
}
}

92
net/tools/Ping.cs 100644
View File

@ -0,0 +1,92 @@
using System;
using System.Net.Sockets;
using System.Net;
using System.Net.NetworkInformation;
using ln.logging;
namespace ln.types.net.tools
{
public class Ping
{
public Socket Socket { get; private set; }
public IPEndPoint LocalEndpoint { get; private set; }
public Ping()
{
Socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
LocalEndpoint = new IPEndPoint(IPAddress.Any, 0);
Socket.Bind(LocalEndpoint);
}
public int TimeToLive
{
get => (int)Socket.GetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive);
set => Socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, value);
}
private void Listener()
{
byte[] rxBuffer = new byte[4096];
EndPoint re = null;
IPEndPoint remoteEndpoint;
while (true)
{
Socket.ReceiveFrom(rxBuffer, rxBuffer.Length, SocketFlags.None, ref re);
remoteEndpoint = (IPEndPoint)re;
}
}
private void SendRequest(PingRequest pingRequest)
{
IPv4Header ipHeader = new IPv4Header();
ICMPHeader icmpHeader = new ICMPHeader();
byte[] packet = new byte[ipHeader.HeaderLength + icmpHeader.HeaderLength];
ipHeader.Write(packet, 0, true);
icmpHeader.Write(packet, ipHeader.HeaderLength, true);
Logging.Log(LogLevel.DEBUG, "ICMP-Request: {0}", BitConverter.ToString(packet));
}
}
class PingRequest
{
public PingReply PingReply { get; }
public IPEndPoint RemoteEndpoint => PingReply.RemoteEndpoint;
public int Sequence { get; private set; }
public DateTime TimeStamp { get; set; }
public PingRequest(PingReply pingReply,int sequence)
{
PingReply = pingReply;
Sequence = sequence;
}
public override int GetHashCode() => RemoteEndpoint.GetHashCode() ^ 0;
}
class PingReply
{
public IPEndPoint RemoteEndpoint { get; }
public int TimeOut { get; private set; }
public int PingsToSend { get; }
public int PingsSent { get; private set; }
public int Received { get; private set; }
public PingReply(IPEndPoint remoteEndpoint,int pingsToSend,int timeOut)
{
RemoteEndpoint = remoteEndpoint;
PingsToSend = pingsToSend;
TimeOut = timeOut;
}
}
}

56
rpc/RPCCall.cs 100644
View File

@ -0,0 +1,56 @@
using System;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
namespace ln.types.rpc
{
public class RPCCall
{
public object Identifier { get; set; }
public String ModuleName { get; set; }
public string MethodName { get; set; }
public object[] Parameters { get; set; }
public RPCCall()
{
Identifier = Guid.NewGuid();
}
public RPCCall(JObject json)
{
if (json.ContainsKey("id"))
{
switch (json["id"].Type)
{
case JTokenType.Integer:
Identifier = json["id"].ToObject<int>();
break;
case JTokenType.Float:
Identifier = json["id"].ToObject<float>();
break;
case JTokenType.String:
Identifier = json["id"].ToObject<string>();
break;
default:
throw new NotSupportedException(String.Format("JToken {0} is not supported as RPCCall.Identifier",json["id"]));
}
}
else
{
Identifier = Guid.NewGuid();
}
if (json.ContainsKey("module"))
ModuleName = json["module"].ToObject<string>();
if (json.ContainsKey("method"))
MethodName = json["method"].ToObject<string>();
if (json.ContainsKey("parameters"))
Parameters = json["parameters"].ToObject<object[]>();
else
Parameters = new object[0];
}
}
}

103
rpc/RPCContainer.cs 100644
View File

@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace ln.types.rpc
{
public class PublishAttribute : Attribute
{
}
public class RPCContainer
{
Dictionary<string, RPCModule> modules = new Dictionary<string, RPCModule>();
public RPCContainer()
{
}
public void Add(object moduleInstance) => Add(moduleInstance.GetType().Name, moduleInstance);
public void Add(string moduleName,object moduleInstance)
{
if (modules.ContainsKey(moduleName))
throw new ArgumentOutOfRangeException(nameof(moduleName), "Module with same name already added");
RPCModule rpcModule = new RPCModule(moduleName,moduleInstance);
modules.Add(moduleName, rpcModule);
}
public RPCResult Invoke(RPCCall call)
{
if ((call.ModuleName != null) && !modules.ContainsKey(call.ModuleName))
throw new KeyNotFoundException(call.ModuleName);
RPCModule rpcModule = call.ModuleName != null ? modules[call.ModuleName] : modules[""];
return rpcModule.Invoke(call);
}
public class RPCModule
{
public String Name { get; }
public object ModuleInstance { get; }
public Dictionary<string, MethodInfo> methodInfos = new Dictionary<string, MethodInfo>();
public RPCModule(string moduleName,object instance)
{
Name = moduleName;
ModuleInstance = instance;
Initialize(ModuleInstance.GetType());
}
public RPCModule(string moduleName,Type type)
{
Name = moduleName;
ModuleInstance = null;
Initialize(type);
}
private void Initialize(Type type)
{
foreach (MethodInfo methodInfo in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance))
{
if (methodInfo.IsStatic || !Object.ReferenceEquals(ModuleInstance,null) )
{
if (methodInfo.IsPublic || (methodInfo.GetCustomAttribute<PublishAttribute>() != null))
{
methodInfos.Add(methodInfo.Name, methodInfo);
}
}
}
}
public RPCResult Invoke(RPCCall call)
{
if (!Name.Equals(call.ModuleName) && (call.ModuleName != null))
throw new ArgumentOutOfRangeException(nameof(call.ModuleName), "RPC: Invoke called for wrong module");
try
{
if (!methodInfos.ContainsKey(call.MethodName))
throw new KeyNotFoundException(call.MethodName);
MethodInfo methodInfo = methodInfos[call.MethodName];
ParameterInfo[] paramterInfos = methodInfo.GetParameters();
object[] parameters = new object[paramterInfos.Length];
for (int n=0;n<parameters.Length;n++)
{
parameters[n] = Convert.ChangeType(call.Parameters[n], paramterInfos[n].ParameterType);
}
object result = methodInfo.Invoke(ModuleInstance, parameters);
return new RPCResult(call, result);
} catch (Exception e)
{
return new RPCResult(call, e);
}
}
}
}
}

50
rpc/RPCResult.cs 100644
View File

@ -0,0 +1,50 @@
using System;
using Newtonsoft.Json.Linq;
namespace ln.types.rpc
{
public class RPCResult
{
public object Identifier { get; set; }
public object Result { get; set; }
public String ErrorText { get; set; }
public Exception Exception { get; set; }
public RPCResult(RPCCall call)
{
Identifier = call.Identifier;
}
public RPCResult(RPCCall call,object result)
{
Identifier = call.Identifier;
Result = result;
}
public RPCResult(RPCCall call,Exception e)
{
Identifier = call.Identifier;
Exception = e;
ErrorText = e.ToString();
}
public JObject ToJSON()
{
JObject result = new JObject();
result["id"] = JToken.FromObject(Identifier);
if (ErrorText != null)
{
result["error"] = new JObject();
result["error"]["message"] = JToken.FromObject(ErrorText);
result["error"]["exception"] = JToken.FromObject(Exception);
}
else
{
result["result"] = JToken.FromObject(Result);
}
return result;
}
}
}