Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
DnsClient.cs
1using System;
3using System.IO;
4using System.Net;
5using System.Reflection;
6using System.Text;
7using System.Threading.Tasks;
8using Waher.Events;
15
17{
21 public abstract class DnsClient : CommunicationLayer, IDisposable
22 {
26 public const int DefaultTimeout = 30000;
27
31 protected bool disposed = false;
32
33 private static object idnMapping = null;
34 private static MethodInfo getAscii = null;
35 private static MethodInfo getUnicode = null;
36 private static bool initialized = false;
37
38 private readonly Dictionary<ushort, Rec> outgoingMessages = new Dictionary<ushort, Rec>();
39 private readonly LinkedList<KeyValuePair<byte[], IPEndPoint>> outputQueue = new LinkedList<KeyValuePair<byte[], IPEndPoint>>();
40 private ProfilerThread thread = null;
41 private Scheduler scheduler;
42 private bool isWriting = false;
43
44 private class Rec
45 {
46 public ushort ID;
47 public bool ConstantBuffer;
48 public byte[] Output;
49 public IPEndPoint Destination;
50 public EventHandlerAsync<DnsMessageEventArgs> Callback;
51 public object State;
52 }
53
57 public DnsClient()
58 : base(false)
59 {
60 }
61
65 protected virtual void Init()
66 {
67 this.scheduler = new Scheduler();
68 }
69
74 {
75 get => this.thread;
76 set => this.thread = value;
77 }
78
89 protected async Task BeginTransmit(ushort ID, bool ConstantBuffer, byte[] Message, IPEndPoint Destination,
90 EventHandlerAsync<DnsMessageEventArgs> Callback, object State)
91 {
92 if (this.disposed)
93 return;
94
95 if (!(Callback is null))
96 {
97 Rec Rec = new Rec()
98 {
99 ID = ID,
100 ConstantBuffer = ConstantBuffer,
101 Output = Message,
102 Destination = Destination,
103 Callback = Callback,
104 State = State
105 };
106
107 lock (this.outgoingMessages)
108 {
109 this.outgoingMessages[ID] = Rec;
110 }
111
112 this.scheduler.Add(DateTime.Now.AddSeconds(2), this.CheckRetry, Rec);
113 }
114
115 lock (this.outputQueue)
116 {
117 if (this.isWriting)
118 {
119 this.outputQueue.AddLast(new KeyValuePair<byte[], IPEndPoint>(Message, Destination));
120 return;
121 }
122 else
123 this.isWriting = true;
124 }
125
126 try
127 {
128 while (!(Message is null))
129 {
130 this.thread?.Event("Tx");
131 this.TransmitBinary(ConstantBuffer, Message);
132
133 await this.SendAsync(ConstantBuffer, Message, Destination);
134
135 if (this.disposed)
136 return;
137
138 lock (this.outputQueue)
139 {
140 if (this.outputQueue.First is null)
141 {
142 this.isWriting = false;
143 Message = null;
144 }
145 else
146 {
147 Message = this.outputQueue.First.Value.Key;
148 Destination = this.outputQueue.First.Value.Value;
149 this.outputQueue.RemoveFirst();
150 }
151 }
152 }
153 }
154 catch (Exception ex)
155 {
156 ex = Log.UnnestException(ex);
157 this.thread?.Exception(ex);
158 this.Exception(ex);
159 }
160 }
161
169 protected abstract Task SendAsync(bool ConstantBuffer, byte[] Message, IPEndPoint Destination);
170
171 private Task CheckRetry(object P)
172 {
173 Rec Rec = (Rec)P;
174
175 lock (this.outgoingMessages)
176 {
177 if (!this.outgoingMessages.ContainsKey(Rec.ID))
178 return Task.CompletedTask;
179 }
180
181 return this.BeginTransmit(Rec.ID, Rec.ConstantBuffer, Rec.Output, Rec.Destination, Rec.Callback, Rec.State);
182 }
183
188 protected virtual async Task ProcessIncomingMessage(DnsMessage Message)
189 {
190 this.thread?.Event("Rx");
191
192 if (Message.Response)
193 {
194 Rec Rec;
195
196 lock (this.outgoingMessages)
197 {
198 if (this.outgoingMessages.TryGetValue(Message.ID, out Rec))
199 this.outgoingMessages.Remove(Message.ID);
200 else
201 return;
202 }
203
204 await Rec.Callback.Raise(this, new DnsMessageEventArgs(Message, Rec.State));
205 }
206 }
207
212 protected virtual async Task ProcessMessageFailure(ushort ID)
213 {
214 Rec Rec;
215
216 lock (this.outgoingMessages)
217 {
218 if (this.outgoingMessages.TryGetValue(ID, out Rec))
219 this.outgoingMessages.Remove(ID);
220 else
221 return;
222 }
223
224 DnsMessage Message = new DnsMessage(new byte[]
225 {
226 (byte)(ID >> 8),
227 (byte)(ID & 255),
228 0x80, // Response
229 (byte)RCode.ServFail,
230 0, 0, // QDCOUNT
231 0, 0, // ANCOUNT
232 0, 0, // NSCOUNT
233 0, 0 // ARCOUNT
234 });
235
236 await Rec.Callback.Raise(this, new DnsMessageEventArgs(Message, Rec.State));
237 }
238
242 public virtual void Dispose()
243 {
244 this.disposed = true;
245
246 this.scheduler?.Dispose();
247 this.scheduler = null;
248 }
249
260 public Task SendRequest(OpCode OpCode, bool Recursive, Question[] Questions,
261 IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback, object State)
262 {
263 using MemoryStream Request = new MemoryStream();
264 ushort ID = DnsResolver.NextID;
265
266 WriteUInt16(ID, Request);
267
268 byte b = (byte)((int)OpCode << 3);
269 if (Recursive)
270 b |= 1;
271
272 Request.WriteByte(b);
273 Request.WriteByte((byte)RCode.NoError);
274
275 int c = Questions.Length;
276 if (c == 0)
277 throw new ArgumentException("No questions included in request.", nameof(Questions));
278
279 if (c > ushort.MaxValue)
280 throw new ArgumentException("Too many questions in request.", nameof(Questions));
281
282 WriteUInt16((ushort)c, Request); // Query Count
283 WriteUInt16(0, Request); // Answer Count
284 WriteUInt16(0, Request); // Authoritative Count
285 WriteUInt16(0, Request); // Additional Count
286
287 Dictionary<string, ushort> NamePositions = new Dictionary<string, ushort>();
288
289 foreach (Question Q in Questions)
290 {
291 WriteName(Q.QNAME, Request, NamePositions);
292 WriteUInt16((ushort)Q.QTYPE, Request);
293 WriteUInt16((ushort)Q.QCLASS, Request);
294 }
295
296 byte[] Packet = Request.ToArray();
297
298 return this.BeginTransmit(ID, true, Packet, Destination, Callback, State);
299 }
300
310 public async Task<DnsMessage> SendRequestAsync(OpCode OpCode, bool Recursive,
311 Question[] Questions, IPEndPoint Destination, int Timeout)
312 {
313 TaskCompletionSource<DnsMessage> Result = new TaskCompletionSource<DnsMessage>();
314 DateTime TP = DateTime.MinValue;
315
316 await this.SendRequest(OpCode, Recursive, Questions, Destination, (Sender, e) =>
317 {
318 this.scheduler?.Remove(TP);
319 ((TaskCompletionSource<DnsMessage>)e.State).TrySetResult(e.Message);
320 return Task.CompletedTask;
321 }, Result);
322
323 TP = DateTime.Now.AddMilliseconds(Timeout);
324 TP = this.scheduler.Add(TP, (P) =>
325 {
326 ((TaskCompletionSource<DnsMessage>)P).TrySetException(
327 new TimeoutException("No DNS response returned within the given time."));
328 }, Result);
329
330 return await Result.Task;
331 }
332
342 public void Query(string QNAME, QTYPE QTYPE, QCLASS QCLASS, IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback, object State)
343 {
344 this.Query(new Question[] { new Question(QNAME, QTYPE, QCLASS) }, Destination, Callback, State);
345 }
346
356 public void Query(string QNAME, QTYPE[] QTYPEs, QCLASS QCLASS, IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback, object State)
357 {
358 this.Query(ToQuestions(QNAME, QTYPEs, QCLASS), Destination, Callback, State);
359 }
360
368 public void Query(Question[] Questions, IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback, object State)
369 {
370 this.SendRequest(OpCode.Query, false, Questions, Destination, Callback, State);
371 }
372
380 public Task<DnsMessage> QueryAsync(string QNAME, QTYPE QTYPE, QCLASS QCLASS, IPEndPoint Destination)
381 {
382 return this.QueryAsync(new Question[] { new Question(QNAME, QTYPE, QCLASS) }, Destination);
383 }
384
392 public Task<DnsMessage> QueryAsync(string QNAME, QTYPE[] QTYPEs, QCLASS QCLASS, IPEndPoint Destination)
393 {
394 return this.QueryAsync(ToQuestions(QNAME, QTYPEs, QCLASS), Destination);
395 }
396
402 public Task<DnsMessage> QueryAsync(Question[] Questions, IPEndPoint Destination)
403 {
404 return this.QueryAsync(Questions, Destination, DefaultTimeout);
405 }
406
413 public Task<DnsMessage> QueryAsync(Question[] Questions, IPEndPoint Destination, int Timeout)
414 {
415 return this.SendRequestAsync(OpCode.Query, false, Questions, Destination, Timeout);
416 }
417
418 private static Question[] ToQuestions(string QNAME, QTYPE[] QTYPEs, QCLASS QCLASS)
419 {
420 int i, c = QTYPEs.Length;
421 Question[] Questions = new Question[c];
422
423 for (i = 0; i < c; i++)
424 Questions[i] = new Question(QNAME, QTYPEs[i], QCLASS);
425
426 return Questions;
427 }
428
429 internal static uint ReadUInt32(Stream Data)
430 {
431 ushort Result = ReadUInt16(Data);
432 Result <<= 16;
433 Result |= ReadUInt16(Data);
434
435 return Result;
436 }
437
438 internal static ushort ReadUInt16(Stream Data)
439 {
440 ushort Result = (byte)Data.ReadByte();
441 Result <<= 8;
442 Result |= (byte)Data.ReadByte();
443
444 return Result;
445 }
446
447 internal static string ReadName(Stream Data)
448 {
449 if (!initialized)
450 Initialize();
451
452 StringBuilder sb = null;
453 string s;
454 bool Continue = true;
455
456 while (Continue)
457 {
458 int Len = Data.ReadByte();
459 if (Len == 0)
460 break;
461
462 switch (Len & 192)
463 {
464 case 0:
465 byte[] Bin = new byte[Len];
466 Data.ReadAll(Bin, 0, Len);
467
468 s = Encoding.ASCII.GetString(Bin);
469
470 if (!(getUnicode is null))
471 s = (string)getUnicode.Invoke(idnMapping, new object[] { s });
472
473 break;
474
475 case 192:
476 ushort Offset = (byte)(Len & 63);
477 Offset <<= 8;
478 Offset |= (byte)(Data.ReadByte());
479
480 long Bak = Data.Position;
481
482 Data.Position = Offset;
483
484 s = ReadName(Data);
485
486 Data.Position = Bak;
487 Continue = false;
488 break;
489
490 default:
491 throw new NotSupportedException("Unsupported Label Type.");
492 }
493
494 if (sb is null)
495 sb = new StringBuilder();
496 else
497 sb.Append('.');
498
499 sb.Append(s);
500 }
501
502 return sb?.ToString() ?? string.Empty;
503 }
504
505 internal static string ReadString(Stream Data)
506 {
507 int Len = Data.ReadByte();
508 if (Len == 0)
509 return string.Empty;
510
511 byte[] Bin = new byte[Len];
512 Data.ReadAll(Bin, 0, Len);
513
514 return Encoding.ASCII.GetString(Bin);
515 }
516
517 internal static ResourceRecord[] ReadResourceRecords(Stream Data, ushort Count)
518 {
519 List<ResourceRecord> Result = new List<ResourceRecord>();
520 ResourceRecord Rec;
521
522 while (Count-- > 0)
523 {
524 Rec = ResourceRecord.Create(Data);
525
526 if (!(Rec is null))
527 Result.Add(Rec);
528 }
529
530 return Result.ToArray();
531 }
532
533 internal static void WriteName(string Name, Stream Output,
534 Dictionary<string, ushort> NamePositions)
535 {
536 if (!initialized)
537 Initialize();
538
539 while (!string.IsNullOrEmpty(Name))
540 {
541 if (NamePositions.TryGetValue(Name, out ushort Pos))
542 {
543 byte b = (byte)(Pos >> 8);
544 b |= 0xc0;
545
546 Output.WriteByte(b);
547 Output.WriteByte((byte)(Pos & 0xff));
548 return;
549 }
550 else
551 {
552 NamePositions[Name] = (ushort)Output.Position;
553
554 int i = Name.IndexOf('.');
555 string Label;
556
557 if (i < 0)
558 {
559 Label = Name;
560 Name = string.Empty;
561 }
562 else
563 {
564 Label = Name.Substring(0, i);
565 Name = Name.Substring(i + 1);
566 }
567
568 if (!(getAscii is null))
569 Label = (string)getAscii.Invoke(idnMapping, new object[] { Label });
570
571 Output.WriteByte((byte)Label.Length);
572
573 byte[] Bin = Encoding.ASCII.GetBytes(Label);
574 Output.Write(Bin, 0, Bin.Length);
575 }
576 }
577
578 Output.WriteByte(0);
579 }
580
581 internal static void WriteUInt16(ushort Value, Stream Output)
582 {
583 Output.WriteByte((byte)(Value >> 8));
584 Output.WriteByte((byte)Value);
585 }
586
587 private static void Initialize()
588 {
589 initialized = true;
590 Type T = Types.GetType("System.Globalization.IdnMapping");
591 if (T is null)
592 {
593 idnMapping = null;
594 getAscii = null;
595 getUnicode = null;
596 }
597 else
598 {
599 Type[] Parameters = new Type[] { typeof(string) };
600
601 idnMapping = Types.Instantiate(T);
602 getAscii = T.GetRuntimeMethod("GetAscii", Parameters);
603 getUnicode = T.GetRuntimeMethod("GetUnicode", Parameters);
604 }
605 }
606
607 }
608}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:828
Simple base class for classes implementing communication protocols.
void Exception(Exception Exception)
Called to inform the viewer of an exception state.
void TransmitBinary(int Count)
Called when binary data has been transmitted.
Abstract base class for DNS clients.
Definition: DnsClient.cs:22
virtual void Init()
Called when DNS client is ready to be initialized.
Definition: DnsClient.cs:65
Task< DnsMessage > QueryAsync(string QNAME, QTYPE QTYPE, QCLASS QCLASS, IPEndPoint Destination)
Execute a DNS query.
Definition: DnsClient.cs:380
const int DefaultTimeout
Default Timeout, in milliseconds (30000 ms)
Definition: DnsClient.cs:26
async Task BeginTransmit(ushort ID, bool ConstantBuffer, byte[] Message, IPEndPoint Destination, EventHandlerAsync< DnsMessageEventArgs > Callback, object State)
Sends a message to a DNS server.
Definition: DnsClient.cs:89
Task SendRequest(OpCode OpCode, bool Recursive, Question[] Questions, IPEndPoint Destination, EventHandlerAsync< DnsMessageEventArgs > Callback, object State)
Sends a DNS Request
Definition: DnsClient.cs:260
void Query(Question[] Questions, IPEndPoint Destination, EventHandlerAsync< DnsMessageEventArgs > Callback, object State)
Execute a DNS query.
Definition: DnsClient.cs:368
async Task< DnsMessage > SendRequestAsync(OpCode OpCode, bool Recursive, Question[] Questions, IPEndPoint Destination, int Timeout)
Sends a DNS Request
Definition: DnsClient.cs:310
void Query(string QNAME, QTYPE QTYPE, QCLASS QCLASS, IPEndPoint Destination, EventHandlerAsync< DnsMessageEventArgs > Callback, object State)
Execute a DNS query.
Definition: DnsClient.cs:342
virtual void Dispose()
IDisposable.Dispose
Definition: DnsClient.cs:242
virtual async Task ProcessIncomingMessage(DnsMessage Message)
Processes an incoming message.
Definition: DnsClient.cs:188
bool disposed
If the object has been disposed
Definition: DnsClient.cs:31
ProfilerThread Thread
Optional thread for profiling.
Definition: DnsClient.cs:74
DnsClient()
Abstract base class for DNS clients.
Definition: DnsClient.cs:57
Task< DnsMessage > QueryAsync(Question[] Questions, IPEndPoint Destination, int Timeout)
Execute a DNS query.
Definition: DnsClient.cs:413
Task< DnsMessage > QueryAsync(string QNAME, QTYPE[] QTYPEs, QCLASS QCLASS, IPEndPoint Destination)
Execute a DNS query.
Definition: DnsClient.cs:392
Task< DnsMessage > QueryAsync(Question[] Questions, IPEndPoint Destination)
Execute a DNS query.
Definition: DnsClient.cs:402
void Query(string QNAME, QTYPE[] QTYPEs, QCLASS QCLASS, IPEndPoint Destination, EventHandlerAsync< DnsMessageEventArgs > Callback, object State)
Execute a DNS query.
Definition: DnsClient.cs:356
virtual async Task ProcessMessageFailure(ushort ID)
Request resulted in a failure.
Definition: DnsClient.cs:212
abstract Task SendAsync(bool ConstantBuffer, byte[] Message, IPEndPoint Destination)
Sends a message to a destination.
bool Response
If a Response (true) or a query (false)
Definition: DnsMessage.cs:85
Contains information about a DNS Question
Definition: Question.cs:9
DNS resolver, as defined in:
Definition: DnsResolver.cs:32
Abstract base class for a resource record.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
static Type GetType(string FullName)
Gets a type, given its full name.
Definition: Types.cs:42
static object Instantiate(Type Type, params object[] Arguments)
Returns an instance of the type Type . If one needs to be created, it is. If the constructor requires...
Definition: Types.cs:1454
Class that keeps track of events and timing for one thread.
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
Definition: Scheduler.cs:14
bool Remove(DateTime When)
Removes an event scheduled for a given point in time.
Definition: Scheduler.cs:186
void Dispose()
IDisposable.Dispose
Definition: Scheduler.cs:34
DateTime Add(DateTime When, Action< object > Callback, object State)
Adds an event.
Definition: Scheduler.cs:54
OpCode
DNS Operation Codes
Definition: OpCode.cs:7
RCode
DNS Response Code
Definition: RCode.cs:7
QTYPE
QTYPE fields appear in the question part of a query.
Definition: QTYPE.cs:7
QCLASS
QCLASS fields appear in the question section of a query.
Definition: QCLASS.cs:7