5using System.Reflection;
7using System.Threading.Tasks;
33 private static object idnMapping =
null;
34 private static MethodInfo getAscii =
null;
35 private static MethodInfo getUnicode =
null;
36 private static bool initialized =
false;
38 private readonly Dictionary<ushort, Rec> outgoingMessages =
new Dictionary<ushort, Rec>();
39 private readonly LinkedList<KeyValuePair<byte[], IPEndPoint>> outputQueue =
new LinkedList<KeyValuePair<byte[], IPEndPoint>>();
42 private bool isWriting =
false;
47 public bool ConstantBuffer;
49 public IPEndPoint Destination;
50 public EventHandlerAsync<DnsMessageEventArgs> Callback;
65 protected virtual void Init()
76 set => this.thread = value;
89 protected async Task
BeginTransmit(ushort ID,
bool ConstantBuffer,
byte[] Message, IPEndPoint Destination,
90 EventHandlerAsync<DnsMessageEventArgs> Callback,
object State)
95 if (!(Callback is
null))
100 ConstantBuffer = ConstantBuffer,
102 Destination = Destination,
107 lock (this.outgoingMessages)
109 this.outgoingMessages[ID] = Rec;
112 this.scheduler.
Add(DateTime.Now.AddSeconds(2),
this.CheckRetry, Rec);
115 lock (this.outputQueue)
119 this.outputQueue.AddLast(
new KeyValuePair<
byte[], IPEndPoint>(Message, Destination));
123 this.isWriting =
true;
128 while (!(Message is
null))
130 this.thread?.Event(
"Tx");
133 await this.
SendAsync(ConstantBuffer, Message, Destination);
138 lock (this.outputQueue)
140 if (this.outputQueue.First is
null)
142 this.isWriting =
false;
147 Message = this.outputQueue.First.Value.Key;
148 Destination = this.outputQueue.First.Value.Value;
149 this.outputQueue.RemoveFirst();
157 this.thread?.Exception(ex);
169 protected abstract Task
SendAsync(
bool ConstantBuffer,
byte[] Message, IPEndPoint Destination);
171 private Task CheckRetry(
object P)
175 lock (this.outgoingMessages)
177 if (!this.outgoingMessages.ContainsKey(Rec.ID))
178 return Task.CompletedTask;
181 return this.
BeginTransmit(Rec.ID, Rec.ConstantBuffer, Rec.Output, Rec.Destination, Rec.Callback, Rec.State);
190 this.thread?.Event(
"Rx");
196 lock (this.outgoingMessages)
198 if (this.outgoingMessages.TryGetValue(Message.
ID, out Rec))
199 this.outgoingMessages.Remove(Message.
ID);
216 lock (this.outgoingMessages)
218 if (this.outgoingMessages.TryGetValue(ID, out Rec))
219 this.outgoingMessages.Remove(ID);
229 (byte)
RCode.ServFail,
244 this.disposed =
true;
247 this.scheduler =
null;
261 IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback,
object State)
263 using MemoryStream Request =
new MemoryStream();
266 WriteUInt16(ID, Request);
268 byte b = (byte)((
int)
OpCode << 3);
272 Request.WriteByte(b);
273 Request.WriteByte((
byte)
RCode.NoError);
275 int c = Questions.Length;
277 throw new ArgumentException(
"No questions included in request.", nameof(Questions));
279 if (c > ushort.MaxValue)
280 throw new ArgumentException(
"Too many questions in request.", nameof(Questions));
282 WriteUInt16((ushort)c, Request);
283 WriteUInt16(0, Request);
284 WriteUInt16(0, Request);
285 WriteUInt16(0, Request);
287 Dictionary<string, ushort> NamePositions =
new Dictionary<string, ushort>();
291 WriteName(Q.
QNAME, Request, NamePositions);
292 WriteUInt16((ushort)Q.
QTYPE, Request);
293 WriteUInt16((ushort)Q.
QCLASS, Request);
296 byte[] Packet = Request.ToArray();
298 return this.
BeginTransmit(ID,
true, Packet, Destination, Callback, State);
311 Question[] Questions, IPEndPoint Destination,
int Timeout)
313 TaskCompletionSource<DnsMessage> Result =
new TaskCompletionSource<DnsMessage>();
314 DateTime TP = DateTime.MinValue;
316 await this.
SendRequest(OpCode, Recursive, Questions, Destination, (Sender, e) =>
318 this.scheduler?.
Remove(TP);
319 ((TaskCompletionSource<DnsMessage>)e.State).TrySetResult(e.Message);
320 return Task.CompletedTask;
323 TP = DateTime.Now.AddMilliseconds(Timeout);
324 TP = this.scheduler.
Add(TP, (P) =>
326 ((TaskCompletionSource<DnsMessage>)P).TrySetException(
327 new TimeoutException(
"No DNS response returned within the given time."));
330 return await Result.Task;
342 public void Query(
string QNAME,
QTYPE QTYPE,
QCLASS QCLASS, IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback,
object State)
356 public void Query(
string QNAME,
QTYPE[] QTYPEs,
QCLASS QCLASS, IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback,
object State)
358 this.
Query(ToQuestions(QNAME, QTYPEs,
QCLASS), Destination, Callback, State);
368 public void Query(
Question[] Questions, IPEndPoint Destination, EventHandlerAsync<DnsMessageEventArgs> Callback,
object State)
420 int i, c = QTYPEs.Length;
423 for (i = 0; i < c; i++)
429 internal static uint ReadUInt32(Stream Data)
431 ushort Result = ReadUInt16(Data);
433 Result |= ReadUInt16(Data);
438 internal static ushort ReadUInt16(Stream Data)
440 ushort Result = (byte)Data.ReadByte();
442 Result |= (byte)Data.ReadByte();
447 internal static string ReadName(Stream Data)
452 StringBuilder sb =
null;
454 bool Continue =
true;
458 int Len = Data.ReadByte();
465 byte[] Bin =
new byte[Len];
466 Data.ReadAll(Bin, 0, Len);
468 s = Encoding.ASCII.GetString(Bin);
470 if (!(getUnicode is
null))
471 s = (string)getUnicode.Invoke(idnMapping,
new object[] { s });
476 ushort Offset = (byte)(Len & 63);
478 Offset |= (byte)(Data.ReadByte());
480 long Bak = Data.Position;
482 Data.Position = Offset;
491 throw new NotSupportedException(
"Unsupported Label Type.");
495 sb =
new StringBuilder();
502 return sb?.ToString() ??
string.Empty;
505 internal static string ReadString(Stream Data)
507 int Len = Data.ReadByte();
511 byte[] Bin =
new byte[Len];
512 Data.ReadAll(Bin, 0, Len);
514 return Encoding.ASCII.GetString(Bin);
517 internal static ResourceRecord[] ReadResourceRecords(Stream Data, ushort Count)
519 List<ResourceRecord> Result =
new List<ResourceRecord>();
530 return Result.ToArray();
533 internal static void WriteName(
string Name, Stream Output,
534 Dictionary<string, ushort> NamePositions)
539 while (!
string.IsNullOrEmpty(Name))
541 if (NamePositions.TryGetValue(Name, out ushort Pos))
543 byte b = (byte)(Pos >> 8);
547 Output.WriteByte((
byte)(Pos & 0xff));
552 NamePositions[Name] = (ushort)Output.Position;
554 int i = Name.IndexOf(
'.');
564 Label = Name.Substring(0, i);
565 Name = Name.Substring(i + 1);
568 if (!(getAscii is
null))
569 Label = (
string)getAscii.Invoke(idnMapping,
new object[] { Label });
571 Output.WriteByte((
byte)Label.Length);
573 byte[] Bin = Encoding.ASCII.GetBytes(Label);
574 Output.Write(Bin, 0, Bin.Length);
581 internal static void WriteUInt16(ushort Value, Stream Output)
583 Output.WriteByte((
byte)(Value >> 8));
584 Output.WriteByte((
byte)Value);
587 private static void Initialize()
590 Type T =
Types.
GetType(
"System.Globalization.IdnMapping");
599 Type[] Parameters =
new Type[] { typeof(
string) };
602 getAscii = T.GetRuntimeMethod(
"GetAscii", Parameters);
603 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.
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.
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)
async Task BeginTransmit(ushort ID, bool ConstantBuffer, byte[] Message, IPEndPoint Destination, EventHandlerAsync< DnsMessageEventArgs > Callback, object State)
Sends a message to a DNS server.
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.
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.
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.
abstract Task SendAsync(bool ConstantBuffer, byte[] Message, IPEndPoint Destination)
Sends a message to a destination.
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, Action< object > 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.