2using System.Collections.Generic;
5using System.Reflection;
7using System.Threading.Tasks;
32 private static object idnMapping =
null;
33 private static MethodInfo getAscii =
null;
34 private static MethodInfo getUnicode =
null;
35 private static bool initialized =
false;
37 private readonly Dictionary<ushort, Rec> outgoingMessages =
new Dictionary<ushort, Rec>();
38 private readonly LinkedList<KeyValuePair<byte[], IPEndPoint>> outputQueue =
new LinkedList<KeyValuePair<byte[], IPEndPoint>>();
41 private bool isWriting =
false;
47 public IPEndPoint Destination;
48 public EventHandlerAsync<DnsMessageEventArgs> Callback;
63 protected virtual void Init()
74 set => this.thread = value;
85 protected async Task
BeginTransmit(ushort ID,
byte[] Message, IPEndPoint Destination,
86 EventHandlerAsync<DnsMessageEventArgs> Callback,
object State)
91 if (!(Callback is
null))
97 Destination = Destination,
102 lock (this.outgoingMessages)
104 this.outgoingMessages[ID] = Rec;
107 this.scheduler.
Add(DateTime.Now.AddSeconds(2),
this.CheckRetry, Rec);
110 lock (this.outputQueue)
114 this.outputQueue.AddLast(
new KeyValuePair<
byte[], IPEndPoint>(Message, Destination));
118 this.isWriting =
true;
123 while (!(Message is
null))
125 this.thread?.Event(
"Tx");
128 await this.
SendAsync(Message, Destination);
133 lock (this.outputQueue)
135 if (this.outputQueue.First is
null)
137 this.isWriting =
false;
142 Message = this.outputQueue.First.Value.Key;
143 Destination = this.outputQueue.First.Value.Value;
144 this.outputQueue.RemoveFirst();
152 this.thread?.Exception(ex);
162 protected abstract Task
SendAsync(
byte[] Message, IPEndPoint Destination);
164 private Task CheckRetry(
object P)
168 lock (this.outgoingMessages)
170 if (!this.outgoingMessages.ContainsKey(Rec.ID))
171 return Task.CompletedTask;
174 return this.
BeginTransmit(Rec.ID, Rec.Output, Rec.Destination, Rec.Callback, Rec.State);
183 this.thread?.Event(
"Rx");
189 lock (this.outgoingMessages)
191 if (this.outgoingMessages.TryGetValue(Message.
ID, out Rec))
192 this.outgoingMessages.Remove(Message.
ID);
209 lock (this.outgoingMessages)
211 if (this.outgoingMessages.TryGetValue(ID, out Rec))
212 this.outgoingMessages.Remove(ID);
222 (byte)
RCode.ServFail,
237 this.disposed =
true;
240 this.scheduler =
null;
254 IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback,
object State)
256 using (MemoryStream Request =
new MemoryStream())
260 WriteUInt16(ID, Request);
262 byte b = (byte)((
int)
OpCode << 3);
266 Request.WriteByte(b);
267 Request.WriteByte((
byte)
RCode.NoError);
269 int c = Questions.Length;
271 throw new ArgumentException(
"No questions included in request.", nameof(Questions));
273 if (c > ushort.MaxValue)
274 throw new ArgumentException(
"Too many questions in request.", nameof(Questions));
276 WriteUInt16((ushort)c, Request);
277 WriteUInt16(0, Request);
278 WriteUInt16(0, Request);
279 WriteUInt16(0, Request);
281 Dictionary<string, ushort> NamePositions =
new Dictionary<string, ushort>();
285 WriteName(Q.
QNAME, Request, NamePositions);
286 WriteUInt16((ushort)Q.
QTYPE, Request);
287 WriteUInt16((ushort)Q.
QCLASS, Request);
290 byte[] Packet = Request.ToArray();
292 return this.
BeginTransmit(ID, Packet, Destination, Callback, State);
306 Question[] Questions, IPEndPoint Destination,
int Timeout)
308 TaskCompletionSource<DnsMessage> Result =
new TaskCompletionSource<DnsMessage>();
309 DateTime TP = DateTime.MinValue;
311 await this.
SendRequest(OpCode, Recursive, Questions, Destination, (Sender, e) =>
313 this.scheduler?.
Remove(TP);
314 ((TaskCompletionSource<DnsMessage>)e.State).TrySetResult(e.Message);
315 return Task.CompletedTask;
318 TP = DateTime.Now.AddMilliseconds(Timeout);
319 TP = this.scheduler.
Add(TP, (P) =>
321 ((TaskCompletionSource<DnsMessage>)P).TrySetException(
322 new TimeoutException(
"No DNS response returned within the given time."));
325 return await Result.Task;
337 public void Query(
string QNAME,
QTYPE QTYPE,
QCLASS QCLASS, IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback,
object State)
351 public void Query(
string QNAME,
QTYPE[] QTYPEs,
QCLASS QCLASS, IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback,
object State)
353 this.
Query(ToQuestions(QNAME, QTYPEs,
QCLASS), Destination, Callback, State);
363 public void Query(
Question[] Questions, IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback,
object State)
415 int i, c = QTYPEs.Length;
418 for (i = 0; i < c; i++)
424 internal static uint ReadUInt32(Stream Data)
426 ushort Result = ReadUInt16(Data);
428 Result |= ReadUInt16(Data);
433 internal static ushort ReadUInt16(Stream Data)
435 ushort Result = (byte)Data.ReadByte();
437 Result |= (byte)Data.ReadByte();
442 internal static string ReadName(Stream Data)
447 StringBuilder sb =
null;
449 bool Continue =
true;
453 int Len = Data.ReadByte();
460 byte[] Bin =
new byte[Len];
461 Data.ReadAll(Bin, 0, Len);
463 s = Encoding.ASCII.GetString(Bin);
465 if (!(getUnicode is
null))
466 s = (string)getUnicode.Invoke(idnMapping,
new object[] { s });
471 ushort Offset = (byte)(Len & 63);
473 Offset |= (byte)(Data.ReadByte());
475 long Bak = Data.Position;
477 Data.Position = Offset;
486 throw new NotSupportedException(
"Unsupported Label Type.");
490 sb =
new StringBuilder();
497 return sb?.ToString() ??
string.Empty;
500 internal static string ReadString(Stream Data)
502 int Len = Data.ReadByte();
506 byte[] Bin =
new byte[Len];
507 Data.ReadAll(Bin, 0, Len);
509 return Encoding.ASCII.GetString(Bin);
512 internal static ResourceRecord[] ReadResourceRecords(Stream Data, ushort Count)
514 List<ResourceRecord> Result =
new List<ResourceRecord>();
526 return Result.ToArray();
529 internal static void WriteName(
string Name, Stream Output,
530 Dictionary<string, ushort> NamePositions)
535 while (!
string.IsNullOrEmpty(Name))
537 if (NamePositions.TryGetValue(Name, out ushort Pos))
539 byte b = (byte)(Pos >> 8);
543 Output.WriteByte((
byte)(Pos & 0xff));
548 NamePositions[Name] = (ushort)Output.Position;
550 int i = Name.IndexOf(
'.');
560 Label = Name.Substring(0, i);
561 Name = Name.Substring(i + 1);
564 if (!(getAscii is
null))
565 Label = (
string)getAscii.Invoke(idnMapping,
new object[] { Label });
567 Output.WriteByte((
byte)Label.Length);
569 byte[] Bin = Encoding.ASCII.GetBytes(Label);
570 Output.Write(Bin, 0, Bin.Length);
577 internal static void WriteUInt16(ushort Value, Stream Output)
579 Output.WriteByte((
byte)(Value >> 8));
580 Output.WriteByte((
byte)Value);
583 private static void Initialize()
586 Type T =
Types.
GetType(
"System.Globalization.IdnMapping");
595 Type[] Parameters =
new Type[] { typeof(
string) };
598 getAscii = T.GetRuntimeMethod(
"GetAscii", Parameters);
599 getUnicode = T.GetRuntimeMethod(
"GetUnicode", Parameters);
Static class managing the application event log. Applications and services log events on this static ...
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Simple base class for classes implementing communication protocols.
Task TransmitBinary(byte[] Data)
Called when binary data has been transmitted.
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
Abstract base class for DNS clients.
virtual void Init()
Called when DNS client is ready to be initialized.
Task< DnsMessage > QueryAsync(string QNAME, QTYPE QTYPE, QCLASS QCLASS, IPEndPoint Destination)
Execute a DNS query.
const int DefaultTimeout
Default Timeout, in milliseconds (30000 ms)
Task SendRequest(OpCode OpCode, bool Recursive, Question[] Questions, IPEndPoint Destination, EventHandlerAsync< DnsMessageEventArgs > Callback, object State)
Sends a DNS Request
void Query(Question[] Questions, IPEndPoint Destination, EventHandlerAsync< DnsMessageEventArgs > Callback, object State)
Execute a DNS query.
async Task< DnsMessage > SendRequestAsync(OpCode OpCode, bool Recursive, Question[] Questions, IPEndPoint Destination, int Timeout)
Sends a DNS Request
void Query(string QNAME, QTYPE QTYPE, QCLASS QCLASS, IPEndPoint Destination, EventHandlerAsync< DnsMessageEventArgs > Callback, object State)
Execute a DNS query.
virtual void Dispose()
IDisposable.Dispose
virtual async Task ProcessIncomingMessage(DnsMessage Message)
Processes an incoming message.
bool disposed
If the object has been disposed
ProfilerThread Thread
Optional thread for profiling.
async Task BeginTransmit(ushort ID, byte[] Message, IPEndPoint Destination, EventHandlerAsync< DnsMessageEventArgs > Callback, object State)
Sends a message to a DNS server.
DnsClient()
Abstract base class for DNS clients.
Task< DnsMessage > QueryAsync(Question[] Questions, IPEndPoint Destination, int Timeout)
Execute a DNS query.
Task< DnsMessage > QueryAsync(string QNAME, QTYPE[] QTYPEs, QCLASS QCLASS, IPEndPoint Destination)
Execute a DNS query.
abstract Task SendAsync(byte[] Message, IPEndPoint Destination)
Sends a message to a destination.
Task< DnsMessage > QueryAsync(Question[] Questions, IPEndPoint Destination)
Execute a DNS query.
void Query(string QNAME, QTYPE[] QTYPEs, QCLASS QCLASS, IPEndPoint Destination, EventHandlerAsync< DnsMessageEventArgs > Callback, object State)
Execute a DNS query.
virtual async Task ProcessMessageFailure(ushort ID)
Request resulted in a failure.
DNS Messge event arguments.
bool Response
If a Response (true) or a query (false)
ushort ID
Message identifier
Contains information about a DNS Question
DNS resolver, as defined in:
Abstract base class for a resource record.
Static class that dynamically manages types and interfaces available in the runtime environment.
static Type GetType(string FullName)
Gets a type, given its full name.
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...
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 ...
bool Remove(DateTime When)
Removes an event scheduled for a given point in time.
void Dispose()
IDisposable.Dispose
DateTime Add(DateTime When, ScheduledEventCallback Callback, object State)
Adds an event.
OpCode
DNS Operation Codes
QTYPE
QTYPE fields appear in the question part of a query.
QCLASS
QCLASS fields appear in the question section of a query.