2using System.Collections.Generic;
5using System.Net.NetworkInformation;
6using System.Net.Sockets;
8using System.Text.RegularExpressions;
9using System.Threading.Tasks;
38 private static readonly Regex arpanetHostName =
new Regex(
@"^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?([.][a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", RegexOptions.Compiled | RegexOptions.Singleline);
39 private static readonly
object synchObject =
new object();
40 private static readonly Random rnd =
new Random();
41 private static ushort nextId = 0;
44 private static bool networkChanged =
false;
45 private static int nestingDepth = 0;
51 NetworkChange.NetworkAddressChanged += (Sender, e) => networkChanged =
true;
64 get => httpsClient?.
Uri;
72 else if (httpsClient is
null)
75 httpsClient.Uri = value;
86 List<IPAddress> Addresses =
new List<IPAddress>();
88 NetworkInterface[] Interfaces = NetworkInterface.GetAllNetworkInterfaces();
89 foreach (NetworkInterface Interface
in Interfaces)
91 if (Interface.OperationalStatus == OperationalStatus.Up)
93 foreach (IPAddress Address
in Interface.GetIPProperties().DnsAddresses)
95 if (!Addresses.Contains(Address))
96 Addresses.Add(Address);
101 return Addresses.ToArray();
112 if (HostName.Length > 255)
117 lock (arpanetHostName)
119 M = arpanetHostName.Match(HostName);
122 return (M.Success && M.Index == 0 && M.Length == HostName.Length);
128 internal static ushort NextID
192 LinkedList<KeyValuePair<string, IPEndPoint>> Backup =
null;
194 IPEndPoint Destination =
null;
197 if (Enum.TryParse(
TYPE.ToString(), out
TYPE T))
206 if (nestingDepth == 0 && networkChanged)
208 networkChanged =
false;
213 if (udpClient is
null)
228 if (Response is
null)
230 Destination = await NextDestination(Backup);
231 if (Destination is
null)
232 throw new IOException(
"Unable to resolve DNS query: " + Name +
", " +
TYPE.ToString() +
", " +
CLASS.ToString());
237 if (!(Response.
Answer is
null))
239 if (!ExpectedType.HasValue)
244 if (RR.
Type == ExpectedType.Value)
247 if (CName is
null && RR.
Type == Enumerations.TYPE.CNAME && RR is
CNAME CNAME)
251 if (ExceptionType.HasValue)
255 if (RR.
Type == ExceptionType.Value)
260 if (!(CName is
null))
275 IPAddress AuthorityAddress =
null;
295 Backup =
new LinkedList<KeyValuePair<string, IPEndPoint>>();
297 if (AuthorityAddress is
null)
298 Backup.AddLast(
new KeyValuePair<string, IPEndPoint>(Authority,
null));
300 Backup.AddLast(
new KeyValuePair<string, IPEndPoint>(
null,
new IPEndPoint(AuthorityAddress,
DefaultDnsPort)));
305 Destination = await NextDestination(Backup);
306 if (Destination is
null)
307 throw new IOException(
"Unable to resolve DNS query: " + Name +
", " +
TYPE.ToString() +
", " +
CLASS.ToString());
347 if (nestingDepth == 0 && networkChanged)
349 networkChanged =
false;
354 if (udpClient is
null)
392 if (!(Response is
null) && (Response.Expires <= DateTime.Now || Response.Raw is
null))
410 if (Response is
null)
415 if (Destination is
null)
417 Client = httpsClient;
432 Client.Thread = Thread;
439 }, Destination, Timeout);
441 switch (Message.
RCode)
451 catch (TimeoutException ex)
458 Client.Thread =
null;
461 if (Message is
null || Message.
RCode !=
RCode.NoError)
469 uint Ttl = 60 * 60 * 24 * 30;
476 Answer = CheckTtl(ref Ttl, Message?.Answer),
477 Authority = CheckTtl(ref Ttl, Message?.Authority),
478 Additional = CheckTtl(ref Ttl, Message?.Additional),
480 Expires = DateTime.Now.AddSeconds(Ttl)
500 private static async Task<IPEndPoint> NextDestination(LinkedList<KeyValuePair<string, IPEndPoint>> Backup)
502 IPEndPoint Destination =
null;
504 while (Destination is
null && !(Backup?.First is
null))
506 KeyValuePair<string, IPEndPoint> P = Backup.First.Value;
507 Backup.RemoveFirst();
509 Destination = P.Value;
511 if (Destination is
null)
513 IPAddress[] Addresses;
517 Addresses = await LookupIP4Addresses(P.Key);
524 if (Addresses is
null || Addresses.Length == 0)
528 Addresses = await LookupIP6Addresses(P.Key);
536 if (!(Addresses is
null))
538 foreach (IPAddress Address
in Addresses)
540 IPEndPoint EP =
new IPEndPoint(Address, DefaultDnsPort);
542 if (Destination is
null)
547 Backup =
new LinkedList<KeyValuePair<string, IPEndPoint>>();
549 Backup.AddLast(
new KeyValuePair<string, IPEndPoint>(
null, EP));
561 if (!(Records is
null))
582 List<IPAddress> Result =
new List<IPAddress>();
590 return Result.ToArray();
602 List<IPAddress> Result =
new List<IPAddress>();
610 return Result.ToArray();
622 List<MX> Records =
new List<MX>();
631 if (Records.Count == 0)
636 Records.Add(
new MX() { Exchange =
A.
Address.ToString() });
640 Records.Sort((r1, r2) => r2.Preference - r1.Preference);
642 int i, c = Records.Count;
643 string[] Result =
new string[c];
645 for (i = 0; i < c; i++)
646 Result[i] = Records[i].Exchange;
659 public static string AddressToName(IPAddress Address,
string IP4DomainName,
string IP6DomainName)
661 byte[] Bin = Address.GetAddressBytes();
666 if (
string.IsNullOrEmpty(IP4DomainName))
667 throw new ArgumentOutOfRangeException(
"IPv4 addresses not supported.");
669 StringBuilder sb =
new StringBuilder();
672 for (i = 3; i >= 0; i--)
674 sb.Append(Bin[i].ToString());
678 sb.Append(IP4DomainName);
680 return sb.ToString();
683 if (
string.IsNullOrEmpty(IP6DomainName))
684 throw new ArgumentOutOfRangeException(
"IPv6 addresses not supported.");
688 sb =
new StringBuilder();
690 for (i = 15; i >= 0; i--)
695 sb.Append((
char)(
'0' + b2));
697 sb.Append((
char)(
'A' + b2 - 10));
703 sb.Append((
char)(
'0' + b2));
705 sb.Append((
char)(
'A' + b2 - 10));
710 sb.Append(IP6DomainName);
712 return sb.ToString();
715 throw new ArgumentOutOfRangeException(
"Unrecognized IP address.", nameof(Address));
728 string Name = AddressToName(Address,
"IN-ADDR.ARPA",
"IP6.ARPA");
729 List<string> Result =
new List<string>();
737 return Result.ToArray();
747 List<string> Result =
new List<string>();
755 return Result.ToArray();
765 public static async Task<string[]>
LookupBlackList(IPAddress Address,
string BlackListDomainName)
767 string Name = AddressToName(Address, BlackListDomainName,
null);
774 catch (ArgumentException)
779 List<string> Result =
null;
788 Result =
new List<string>();
794 if (!(Result is
null))
795 return Result.ToArray();
807 Result =
new List<string>();
813 return Result?.ToArray();
827 return LookupServiceEndpoint(DomainName, ServiceName, Protocol,
null);
844 string Name =
"_" + ServiceName +
"._" + Protocol.ToLower() +
"." + DomainName;
845 SortedDictionary<ushort, List<SRV>> ServicesByPriority =
new SortedDictionary<ushort, List<SRV>>();
846 List<SRV> SamePriority;
852 if (!ServicesByPriority.TryGetValue(
SRV.
Priority, out SamePriority))
854 SamePriority =
new List<SRV>();
858 SamePriority.Add(
SRV);
866 ushort? FirstKey =
null;
869 foreach (KeyValuePair<ushort, List<SRV>> P
in ServicesByPriority)
872 SamePriority = P.Value;
876 if (!FirstKey.HasValue)
877 throw new IOException(
"Service Endpoint not found.");
882 foreach (
SRV SRV in SamePriority)
891 i = rnd.Next(TotWeight);
894 foreach (
SRV SRV in SamePriority)
899 SamePriority.Remove(
SRV);
900 if (SamePriority.Count == 0)
901 ServicesByPriority.Remove(FirstKey.Value);
910 foreach (
SRV SRV in SamePriority)
913 SamePriority.Remove(
SRV);
914 if (SamePriority.Count == 0)
915 ServicesByPriority.Remove(FirstKey.Value);
922 if (Selected is
null)
923 ServicesByPriority.Remove(FirstKey.Value);
939 string Name =
"_" + ServiceName +
"._" + Protocol.ToLower() +
"." + DomainName;
940 List<SRV> Result =
new List<SRV>();
948 return Result.ToArray();
960 public static int Next(
int MaxValue)
964 return rnd.Next(MaxValue);
973 public static async Task<string[]>
ReverseDns(IPAddress Address)
975 StringBuilder sb =
new StringBuilder();
976 byte[] Bytes = Address.GetAddressBytes();
977 int i = Bytes.Length;
979 if (Address.AddressFamily == AddressFamily.InterNetwork)
983 sb.Append(Bytes[i].ToString());
987 sb.Append(
"in-addr.arpa");
989 else if (Address.AddressFamily == AddressFamily.InterNetworkV6)
991 Address.GetAddressBytes();
997 sb.Append((b & 15).ToString(
"x1"));
1000 sb.Append((b >> 4).ToString(
"x1"));
1004 sb.Append(
"ip6.arpa");
1007 throw new ArgumentException(
"Unsupported address family.", nameof(Address));
1010 List<string> Names =
new List<string>();
1014 if (Rec is
PTR PtrRecord)
1015 Names.Add(PtrRecord.Name2);
1018 return Names.ToArray();
Generic exception, with meta-data for logging.
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.
Abstract base class for DNS clients.
async Task< DnsMessage > SendRequestAsync(OpCode OpCode, bool Recursive, Question[] Questions, IPEndPoint Destination, int Timeout)
Sends a DNS Request
virtual void Dispose()
IDisposable.Dispose
Implements a DNS over HTTPS (DoH)-based client.
Uri Uri
DNS over HTTPS URI.
byte[] Binary
Binary response.
ResourceRecord[] Authority
Authority resource records
ResourceRecord[] Answer
Answer resource records
ResourceRecord[] Additional
Additional resource records
Implements a DNS UDP-based client.
override void Dispose()
IDisposable.Dispose
Contains information about a DNS Question
DNS resolver, as defined in:
const int DefaultDnsPort
53
static bool IsValidArpanetHostName(string HostName)
Checks if a host name is a valid ARPHANET host name.
static async Task< string[]> LookupText(string Name)
Looks up text (TXT) records for a name.
static async Task< string[]> LookupDomainName(IPAddress Address)
Looks up the domain name pointing to a specific IP address.
static Task< ResourceRecord[]> Resolve(string Name, QTYPE TYPE, QCLASS CLASS)
Resolves a DNS name.
static Task< DnsResponse > Query(string Name, QTYPE TYPE, QCLASS CLASS, ProfilerThread Thread)
Resolves a DNS name.
static async Task< string[]> LookupMailExchange(string DomainName)
Looks up the Mail Exchanges related to a given domain name.
static async Task< string[]> ReverseDns(IPAddress Address)
Performs a reverse DNS lookup of an IP address.
static string AddressToName(IPAddress Address, string IP4DomainName, string IP6DomainName)
Converts an IP Address to a domain name for reverse IP lookup, or DNSBL lookup.
static Task< DnsResponse > Query(string Name, QTYPE TYPE, QCLASS CLASS)
Resolves a DNS name.
static Task< ResourceRecord[]> Resolve(string Name, QTYPE TYPE, QCLASS CLASS, ProfilerThread Thread)
Resolves a DNS name.
static IPAddress[] DnsServerAddresses
Available DNS Server Addresses.
static Task< ResourceRecord[]> Resolve(string Name, QTYPE TYPE, TYPE? ExceptionType, QCLASS CLASS)
Resolves a DNS name.
static async Task< IPAddress[]> LookupIP6Addresses(string DomainName)
Looks up the IPv6 addresses related to a given domain name.
static async Task< IPAddress[]> LookupIP4Addresses(string DomainName)
Looks up the IPv4 addresses related to a given domain name.
static Task< SRV > LookupServiceEndpoint(string DomainName, string ServiceName, string Protocol)
Looks up a service endpoint for a domain. If multiple are available, an appropriate one is selected a...
static Uri DnsOverHttpsUri
URI used in DNS over HTTPS (DoH) requests. Setting the property to null disables DNS over HTTPS (DoH)...
static async Task< SRV > LookupServiceEndpoint(string DomainName, string ServiceName, string Protocol, ProfilerThread Thread)
Looks up a service endpoint for a domain. If multiple are available, an appropriate one is selected a...
static async Task< ResourceRecord[]> Resolve(string Name, QTYPE TYPE, TYPE? ExceptionType, QCLASS CLASS, ProfilerThread Thread)
Resolves a DNS name.
static async Task< string[]> LookupBlackList(IPAddress Address, string BlackListDomainName)
Looks up an IP Address in a DNS Block List.
static async Task< SRV[]> LookupServiceEndpoints(string DomainName, string ServiceName, string Protocol)
Looks up a available service endpoints for a domain.
static int Next(int MaxValue)
Returns a non-negative random integer that is less than the specified maximum.
IPAddress Address
IP address
string Name2
Name being referred to.
Abstract base class for a resource record.
TYPE Type
Resource Record Type
string TargetHost
Target Host
string[] Text
Descriptive text.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
static async Task DeleteLazy(object Object)
Deletes an object in the database, if unlocked. If locked, object will be deleted at next opportunity...
static bool HasProvider
If a database provider is registered.
static async Task InsertLazy(object Object)
Inserts an object into the database, if unlocked. If locked, object will be inserted at next opportun...
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
static async Task Clear(string CollectionName)
Clears a collection of all objects.
This filter selects objects that conform to all child-filters provided.
This filter selects objects that have a named field equal to a given value.
Class that keeps track of events and timing for one thread.
void Exception(System.Exception Exception)
Exception occurred
void NewState(string State)
Thread changes state.
OpCode
DNS Operation Codes
CLASS
TYPE fields are used in resource records.
QTYPE
QTYPE fields appear in the question part of a query.
QCLASS
QCLASS fields appear in the question section of a query.
TYPE
TYPE fields are used in resource records.