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 Buffer.BlockCopy(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 IPEndPoint From = Data.RemoteEndPoint;
73 if (this.endpoint.IsEcho(From))
75 if (this.endpoint.HasSniffers)
76 this.endpoint.Information(
"Echo received from " + From.ToString());
83 byte[] Datagram = Data.Buffer;
84 int i, c = Datagram.Length - 12;
86 if (this.endpoint.HasSniffers)
87 this.endpoint.Information(Datagram.Length.ToString() +
" bytes received from " + From.ToString());
89 if (c <= 0 || (c & 15) != 0)
91 this.endpoint.Error(
"Invalid packet length.");
95 long Ticks = BitConverter.ToInt64(Datagram, 0);
96 DateTime TP =
new DateTime(Ticks, DateTimeKind.Utc);
97 if (Math.Abs((DateTime.UtcNow - TP).TotalSeconds) >= 10)
99 this.endpoint.Error(
"Clock not synchronized. Ignoring packet.");
103 Buffer.BlockCopy(Datagram, 0, this.ivRx, 0, 8);
104 Buffer.BlockCopy(Datagram, 8, this.ivRx, 12, 4);
106 byte[] A = Data.RemoteEndPoint.Address.GetAddressBytes();
107 Buffer.BlockCopy(A, 0, this.ivRx, 8, 4);
109 int FragmentNr = this.ivRx[13];
110 bool LastFragment = (FragmentNr & 0x80) != 0;
113 FragmentNr |= this.ivRx[12];
115 int Padding = this.ivRx[14] >> 4;
117 using ICryptoTransform Decryptor = this.endpoint.aes.CreateDecryptor(this.endpoint.key,
this.ivRx);
118 byte[] Decrypted = Decryptor.TransformFinalBlock(Datagram, 12, Datagram.Length - 12);
120 using HMACSHA1 HMAC =
new HMACSHA1(A);
121 c = Decrypted.Length - 20 - Padding;
123 byte[] MAC = HMAC.ComputeHash(Decrypted, 20, c);
125 for (i = 0; i < 20; i++)
127 if (MAC[i] != Decrypted[i])
133 this.endpoint.Error(
"MAC invalid.");
137 byte[] Received =
new byte[c];
138 Buffer.BlockCopy(Decrypted, 20, Received, 0, c);
140 if (LastFragment && FragmentNr == 0)
141 this.endpoint.DataReceived(
true, Received, Data.RemoteEndPoint);
144 string Key = Data.RemoteEndPoint.ToString() +
" " + Ticks.ToString();
146 if (!this.endpoint.currentStatus.TryGetValue(Key, out
object Obj) ||
147 !(Obj is Fragments Fragments))
149 Fragments =
new Fragments()
151 Source = Data.RemoteEndPoint,
155 this.endpoint.currentStatus[Key] = Fragments;
158 Fragments.Parts[FragmentNr] = Received;
162 Fragments.Done =
true;
163 Fragments.NrParts = FragmentNr + 1;
166 if (Fragments.Done &&
167 Fragments.NrParts == Fragments.Parts.Count)
169 this.endpoint.currentStatus.Remove(Key);
170 this.endpoint.DataReceived(
true, Fragments.ToByteArray(), Fragments.Source);
176 this.endpoint.Exception(ex);
181 catch (ObjectDisposedException)
185 catch (SocketException)
191 this.endpoint.Exception(ex);
195 internal async
void BeginTransmit(
bool ConstantBuffer,
byte[] Message, IPEndPoint Destination)
202 lock (this.outputQueue)
208 Message = (
byte[])Message.Clone();
209 ConstantBuffer =
true;
212 this.outputQueue.AddLast(Message);
216 this.isWriting =
true;
219 while (!(Message is
null))
221 int Len = Message.Length;
222 int NrFragments = (Len + 32767) >> 15;
226 if (NrFragments == 0)
229 if (NrFragments >= 32768)
230 throw new ArgumentOutOfRangeException(
"Message too big.", nameof(Message));
232 byte[] TP = BitConverter.GetBytes(DateTime.UtcNow.Ticks);
233 Buffer.BlockCopy(TP, 0, this.ivTx, 0, 8);
235 for (FragmentNr = 0; FragmentNr < NrFragments; FragmentNr++, Pos += 32768)
237 int FragmentSize = Math.Min(32768, Len - (FragmentNr << 15));
238 int Padding = (-(20 + FragmentSize)) & 15;
239 byte[] Datagram =
new byte[32 + FragmentSize + Padding];
241 this.ivTx[12] = (byte)FragmentNr;
242 this.ivTx[13] = (byte)(FragmentNr >> 8);
244 if (FragmentNr == NrFragments - 1)
245 this.ivTx[13] |= 0x80;
247 this.ivTx[14] = (byte)((this.ivTx[14] & 0x0f) | (Padding << 4));
249 Buffer.BlockCopy(this.ivTx, 0, Datagram, 0, 8);
250 Buffer.BlockCopy(this.ivTx, 12, Datagram, 8, 4);
252 byte[] MAC = this.hmac.ComputeHash(Message, Pos, FragmentSize);
254 Buffer.BlockCopy(MAC, 0, Datagram, 12, 20);
255 Buffer.BlockCopy(Message, Pos, Datagram, 32, FragmentSize);
257 using (ICryptoTransform Encryptor = this.endpoint.aes.CreateEncryptor(this.endpoint.key,
this.ivTx))
259 byte[] Encrypted = Encryptor.TransformFinalBlock(Datagram, 12, Datagram.Length - 12);
260 Buffer.BlockCopy(Encrypted, 0, Datagram, 12, Encrypted.Length);
263 if (++this.ivTx[15] == 0)
266 if (this.endpoint.HasSniffers)
268 this.endpoint.Information(
"Transmitting " + Datagram.Length.ToString() +
" bytes to " +
269 Destination +
" from " + this.EndPoint);
272 await this.client.SendAsync(Datagram, Datagram.Length, Destination);
278 lock (this.outputQueue)
280 if (this.outputQueue.First is
null)
282 this.isWriting =
false;
287 Message = this.outputQueue.First.Value;
288 this.outputQueue.RemoveFirst();
295 this.endpoint.Exception(ex.Message +
" (" +
this.EndPoint +
")");
297 lock (this.outputQueue)
299 this.outputQueue.Clear();
300 this.isWriting =
false;
305 if (this.endpoint.shuttingDown)
306 await 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.