Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ClusterUdpClient.cs
1using System;
3using System.Net;
6using Waher.Events;
7
9{
10 internal class ClusterUdpClient : IDisposable
11 {
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;
21
22 internal ClusterUdpClient(ClusterEndpoint Endpoint, UdpClient Client, IPAddress LocalAddress)
23 {
24 this.endpoint = Endpoint;
25 this.client = Client;
26 this.localAddress = LocalAddress;
27
28 if (LocalAddress is null)
29 this.hmac = null;
30 else
31 {
32 byte[] A = LocalAddress.GetAddressBytes();
33 Buffer.BlockCopy(A, 0, this.ivTx, 8, 4);
34
35 this.hmac = new HMACSHA1(A);
36 }
37 }
38
39 internal IPAddress Address => this.localAddress;
40 internal IPEndPoint EndPoint => this.client.Client.LocalEndPoint as IPEndPoint;
41
42 internal bool IsEndpoint(string AddressString, int Port)
43 {
44 if (!(this.client.Client.LocalEndPoint is IPEndPoint EndPoint))
45 return false;
46
47 return EndPoint.Address.ToString() == AddressString && EndPoint.Port == Port;
48 }
49
50 public void Dispose()
51 {
52 this.disposed = true;
53
54 this.client?.Dispose();
55 this.client = null;
56
57 this.hmac?.Dispose();
58 this.hmac = null;
59 }
60
61 internal async void BeginReceive() // Starts parallel task
62 {
63 try
64 {
65 while (!this.disposed)
66 {
67 UdpReceiveResult Data = await this.client.ReceiveAsync();
68 if (this.disposed)
69 return;
70
71 IPEndPoint From = Data.RemoteEndPoint;
72
73 if (this.endpoint.IsEcho(From))
74 {
75 if (this.endpoint.HasSniffers)
76 this.endpoint.Information("Echo received from " + From.ToString());
77
78 continue;
79 }
80
81 try
82 {
83 byte[] Datagram = Data.Buffer;
84 int i, c = Datagram.Length - 12;
85
86 if (this.endpoint.HasSniffers)
87 this.endpoint.Information(Datagram.Length.ToString() + " bytes received from " + From.ToString());
88
89 if (c <= 0 || (c & 15) != 0)
90 {
91 this.endpoint.Error("Invalid packet length.");
92 continue;
93 }
94
95 long Ticks = BitConverter.ToInt64(Datagram, 0);
96 DateTime TP = new DateTime(Ticks, DateTimeKind.Utc);
97 if (Math.Abs((DateTime.UtcNow - TP).TotalSeconds) >= 10) // Margin for unsynchronized clocks.
98 {
99 this.endpoint.Error("Clock not synchronized. Ignoring packet.");
100 continue;
101 }
102
103 Buffer.BlockCopy(Datagram, 0, this.ivRx, 0, 8);
104 Buffer.BlockCopy(Datagram, 8, this.ivRx, 12, 4);
105
106 byte[] A = Data.RemoteEndPoint.Address.GetAddressBytes();
107 Buffer.BlockCopy(A, 0, this.ivRx, 8, 4);
108
109 int FragmentNr = this.ivRx[13];
110 bool LastFragment = (FragmentNr & 0x80) != 0;
111 FragmentNr &= 0x7f;
112 FragmentNr <<= 8;
113 FragmentNr |= this.ivRx[12];
114
115 int Padding = this.ivRx[14] >> 4;
116
117 using ICryptoTransform Decryptor = this.endpoint.aes.CreateDecryptor(this.endpoint.key, this.ivRx);
118 byte[] Decrypted = Decryptor.TransformFinalBlock(Datagram, 12, Datagram.Length - 12);
119
120 using HMACSHA1 HMAC = new HMACSHA1(A);
121 c = Decrypted.Length - 20 - Padding;
122
123 byte[] MAC = HMAC.ComputeHash(Decrypted, 20, c);
124
125 for (i = 0; i < 20; i++)
126 {
127 if (MAC[i] != Decrypted[i])
128 break;
129 }
130
131 if (i < 20)
132 {
133 this.endpoint.Error("MAC invalid.");
134 continue;
135 }
136
137 byte[] Received = new byte[c];
138 Buffer.BlockCopy(Decrypted, 20, Received, 0, c);
139
140 if (LastFragment && FragmentNr == 0)
141 this.endpoint.DataReceived(true, Received, Data.RemoteEndPoint);
142 else
143 {
144 string Key = Data.RemoteEndPoint.ToString() + " " + Ticks.ToString();
145
146 if (!this.endpoint.currentStatus.TryGetValue(Key, out object Obj) ||
147 !(Obj is Fragments Fragments))
148 {
149 Fragments = new Fragments()
150 {
151 Source = Data.RemoteEndPoint,
152 Timestamp = Ticks
153 };
154
155 this.endpoint.currentStatus[Key] = Fragments;
156 }
157
158 Fragments.Parts[FragmentNr] = Received;
159
160 if (LastFragment)
161 {
162 Fragments.Done = true;
163 Fragments.NrParts = FragmentNr + 1;
164 }
165
166 if (Fragments.Done &&
167 Fragments.NrParts == Fragments.Parts.Count)
168 {
169 this.endpoint.currentStatus.Remove(Key);
170 this.endpoint.DataReceived(true, Fragments.ToByteArray(), Fragments.Source);
171 }
172 }
173 }
174 catch (Exception ex)
175 {
176 this.endpoint.Exception(ex);
177 Log.Exception(ex);
178 }
179 }
180 }
181 catch (ObjectDisposedException)
182 {
183 // Closed.
184 }
185 catch (SocketException)
186 {
187 // Closed.
188 }
189 catch (Exception ex)
190 {
191 this.endpoint.Exception(ex);
192 }
193 }
194
195 internal async void BeginTransmit(bool ConstantBuffer, byte[] Message, IPEndPoint Destination) // Starts parallel task
196 {
197 try
198 {
199 if (this.disposed)
200 return;
201
202 lock (this.outputQueue)
203 {
204 if (this.isWriting)
205 {
206 if (!ConstantBuffer)
207 {
208 Message = (byte[])Message.Clone();
209 ConstantBuffer = true;
210 }
211
212 this.outputQueue.AddLast(Message);
213 return;
214 }
215 else
216 this.isWriting = true;
217 }
218
219 while (!(Message is null))
220 {
221 int Len = Message.Length;
222 int NrFragments = (Len + 32767) >> 15;
223 int FragmentNr;
224 int Pos = 0;
225
226 if (NrFragments == 0)
227 return;
228
229 if (NrFragments >= 32768)
230 throw new ArgumentOutOfRangeException("Message too big.", nameof(Message));
231
232 byte[] TP = BitConverter.GetBytes(DateTime.UtcNow.Ticks);
233 Buffer.BlockCopy(TP, 0, this.ivTx, 0, 8);
234
235 for (FragmentNr = 0; FragmentNr < NrFragments; FragmentNr++, Pos += 32768)
236 {
237 int FragmentSize = Math.Min(32768, Len - (FragmentNr << 15));
238 int Padding = (-(20 + FragmentSize)) & 15;
239 byte[] Datagram = new byte[32 + FragmentSize + Padding];
240
241 this.ivTx[12] = (byte)FragmentNr;
242 this.ivTx[13] = (byte)(FragmentNr >> 8);
243
244 if (FragmentNr == NrFragments - 1)
245 this.ivTx[13] |= 0x80;
246
247 this.ivTx[14] = (byte)((this.ivTx[14] & 0x0f) | (Padding << 4));
248
249 Buffer.BlockCopy(this.ivTx, 0, Datagram, 0, 8);
250 Buffer.BlockCopy(this.ivTx, 12, Datagram, 8, 4);
251
252 byte[] MAC = this.hmac.ComputeHash(Message, Pos, FragmentSize);
253
254 Buffer.BlockCopy(MAC, 0, Datagram, 12, 20);
255 Buffer.BlockCopy(Message, Pos, Datagram, 32, FragmentSize);
256
257 using (ICryptoTransform Encryptor = this.endpoint.aes.CreateEncryptor(this.endpoint.key, this.ivTx))
258 {
259 byte[] Encrypted = Encryptor.TransformFinalBlock(Datagram, 12, Datagram.Length - 12);
260 Buffer.BlockCopy(Encrypted, 0, Datagram, 12, Encrypted.Length);
261 }
262
263 if (++this.ivTx[15] == 0)
264 ++this.ivTx[14];
265
266 if (this.endpoint.HasSniffers)
267 {
268 this.endpoint.Information("Transmitting " + Datagram.Length.ToString() + " bytes to " +
269 Destination + " from " + this.EndPoint);
270 }
271
272 await this.client.SendAsync(Datagram, Datagram.Length, Destination);
273
274 if (this.disposed)
275 return;
276 }
277
278 lock (this.outputQueue)
279 {
280 if (this.outputQueue.First is null)
281 {
282 this.isWriting = false;
283 Message = null;
284 }
285 else
286 {
287 Message = this.outputQueue.First.Value;
288 this.outputQueue.RemoveFirst();
289 }
290 }
291 }
292 }
293 catch (Exception ex)
294 {
295 this.endpoint.Exception(ex.Message + " (" + this.EndPoint + ")");
296
297 lock (this.outputQueue)
298 {
299 this.outputQueue.Clear();
300 this.isWriting = false;
301 }
302 }
303 finally
304 {
305 if (this.endpoint.shuttingDown)
306 await this.endpoint.Dispose2();
307 }
308 }
309 }
310}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
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.
Definition: Log.cs:1657