2using System.Collections.Generic;
4using System.Net.Sockets;
5using System.Security.Cryptography;
10 internal class ClusterUdpClient : IDisposable
12 private readonly ClusterEndpoint endpoint;
13 private readonly LinkedList<byte[]> outputQueue =
new LinkedList<byte[]>();
14 private readonly IPAddress localAddress;
15 private readonly
byte[] ivTx =
new byte[16];
16 private readonly
byte[] ivRx =
new byte[16];
17 private HMACSHA1 hmac;
18 private UdpClient client;
19 private bool isWriting =
false;
20 private bool disposed =
false;
22 internal ClusterUdpClient(ClusterEndpoint Endpoint, UdpClient Client, IPAddress LocalAddress)
24 this.endpoint = Endpoint;
26 this.localAddress = LocalAddress;
28 if (LocalAddress is
null)
32 byte[] A = LocalAddress.GetAddressBytes();
33 Array.Copy(A, 0, this.ivTx, 8, 4);
35 this.hmac =
new HMACSHA1(A);
39 internal IPAddress Address => this.localAddress;
40 internal IPEndPoint EndPoint => this.client.Client.LocalEndPoint as IPEndPoint;
42 internal bool IsEndpoint(
string AddressString,
int Port)
44 if (!(this.client.Client.LocalEndPoint is IPEndPoint EndPoint))
47 return (EndPoint.Address.ToString() == AddressString && EndPoint.Port == Port);
54 this.client?.Dispose();
61 internal async
void BeginReceive()
65 while (!this.disposed)
67 UdpReceiveResult Data = await this.client.ReceiveAsync();
71 await this.endpoint.Information(Data.Buffer.Length.ToString() +
" bytes received. (" + DateTime.Now.TimeOfDay.ToString() +
")");
75 byte[] Datagram = Data.Buffer;
76 int i, c = Datagram.Length - 12;
78 if (c <= 0 || (c & 15) != 0)
81 long Ticks = BitConverter.ToInt64(Datagram, 0);
82 DateTime TP =
new DateTime(Ticks, DateTimeKind.Utc);
83 if ((DateTime.UtcNow - TP).TotalSeconds >= 10)
86 Array.Copy(Datagram, 0, this.ivRx, 0, 8);
87 Array.Copy(Datagram, 8, this.ivRx, 12, 4);
89 byte[] A = Data.RemoteEndPoint.Address.GetAddressBytes();
90 Array.Copy(A, 0, this.ivRx, 8, 4);
92 int FragmentNr = this.ivRx[13];
93 bool LastFragment = (FragmentNr & 0x80) != 0;
96 FragmentNr |= this.ivRx[12];
98 int Padding = this.ivRx[14] >> 4;
100 using (ICryptoTransform Decryptor = this.endpoint.aes.CreateDecryptor(this.endpoint.key,
this.ivRx))
102 byte[] Decrypted = Decryptor.TransformFinalBlock(Datagram, 12, Datagram.Length - 12);
104 using (HMACSHA1 HMAC =
new HMACSHA1(A))
106 c = Decrypted.Length - 20 - Padding;
108 byte[] MAC = HMAC.ComputeHash(Decrypted, 20, c);
110 for (i = 0; i < 20; i++)
112 if (MAC[i] != Decrypted[i])
119 byte[] Received =
new byte[c];
120 Array.Copy(Decrypted, 20, Received, 0, c);
122 if (LastFragment && FragmentNr == 0)
123 this.endpoint.DataReceived(Received, Data.RemoteEndPoint);
126 string Key = Data.RemoteEndPoint.ToString() +
" " + Ticks.ToString();
128 if (!this.endpoint.currentStatus.TryGetValue(Key, out
object Obj) ||
129 !(Obj is Fragments Fragments))
131 Fragments =
new Fragments()
133 Source = Data.RemoteEndPoint,
137 this.endpoint.currentStatus[Key] = Fragments;
140 Fragments.Parts[FragmentNr] = Received;
144 Fragments.Done =
true;
145 Fragments.NrParts = FragmentNr + 1;
148 if (Fragments.Done &&
149 Fragments.NrParts == Fragments.Parts.Count)
151 this.endpoint.currentStatus.Remove(Key);
152 this.endpoint.DataReceived(Fragments.ToByteArray(), Fragments.Source);
160 await this.endpoint.Exception(ex);
165 catch (ObjectDisposedException)
171 await this.endpoint.Exception(ex);
175 internal async
void BeginTransmit(
byte[] Message, IPEndPoint Destination)
182 lock (this.outputQueue)
186 this.outputQueue.AddLast(Message);
190 this.isWriting =
true;
193 while (!(Message is
null))
195 int Len = Message.Length;
196 int NrFragments = (Len + 32767) >> 15;
200 if (NrFragments == 0)
203 if (NrFragments >= 32768)
204 throw new ArgumentOutOfRangeException(
"Message too big.", nameof(Message));
206 byte[] TP = BitConverter.GetBytes(DateTime.UtcNow.Ticks);
207 Array.Copy(TP, 0, this.ivTx, 0, 8);
209 for (FragmentNr = 0; FragmentNr < NrFragments; FragmentNr++, Pos += 32768)
211 int FragmentSize = Math.Min(32768, Len - (FragmentNr << 15));
212 int Padding = (-(20 + FragmentSize)) & 15;
213 byte[] Datagram =
new byte[32 + FragmentSize + Padding];
215 this.ivTx[12] = (byte)FragmentNr;
216 this.ivTx[13] = (byte)(FragmentNr >> 8);
218 if (FragmentNr == NrFragments - 1)
219 this.ivTx[13] |= 0x80;
221 this.ivTx[14] = (byte)((this.ivTx[14] & 0x0f) | (Padding << 4));
223 Array.Copy(this.ivTx, 0, Datagram, 0, 8);
224 Array.Copy(this.ivTx, 12, Datagram, 8, 4);
226 byte[] MAC = this.hmac.ComputeHash(Message, Pos, FragmentSize);
228 Array.Copy(MAC, 0, Datagram, 12, 20);
229 Array.Copy(Message, Pos, Datagram, 32, FragmentSize);
231 using (ICryptoTransform Encryptor = this.endpoint.aes.CreateEncryptor(this.endpoint.key,
this.ivTx))
233 byte[] Encrypted = Encryptor.TransformFinalBlock(Datagram, 12, Datagram.Length - 12);
234 Array.Copy(Encrypted, 0, Datagram, 12, Encrypted.Length);
237 if (++this.ivTx[15] == 0)
240 await this.client.SendAsync(Datagram, Datagram.Length, Destination);
246 lock (this.outputQueue)
248 if (this.outputQueue.First is
null)
250 this.isWriting =
false;
255 Message = this.outputQueue.First.Value;
256 this.outputQueue.RemoveFirst();
263 await this.endpoint.Exception(ex);
265 lock (this.outputQueue)
267 this.outputQueue.Clear();
268 this.isWriting =
false;
273 if (this.endpoint.shuttingDown)
274 this.endpoint.Dispose2();
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.