5using System.Net.NetworkInformation;
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
193 null, Name,
null,
null,
null,
null,
null,
194 new KeyValuePair<string, object>(
"Name", Name),
195 new KeyValuePair<string, object>(
"TYPE",
TYPE),
196 new KeyValuePair<string, object>(
"CLASS",
CLASS));
250 LinkedList<KeyValuePair<string, IPEndPoint>> Backup =
null;
252 IPEndPoint Destination =
null;
255 if (Enum.TryParse(
TYPE.ToString(), out
TYPE T))
264 if (nestingDepth == 0 && networkChanged)
266 networkChanged =
false;
271 if (udpClient is
null)
286 if (Response is
null)
288 Destination = await NextDestination(Backup);
289 if (Destination is
null)
295 if (!(Response.
Answer is
null))
297 if (!ExpectedType.HasValue)
302 if (RR.
Type == ExpectedType.Value)
305 if (CName is
null && RR.
Type == Enumerations.TYPE.CNAME && RR is
CNAME CNAME)
309 if (ExceptionType.HasValue)
313 if (RR.
Type == ExceptionType.Value)
318 if (!(CName is
null))
333 IPAddress AuthorityAddress =
null;
353 Backup =
new LinkedList<KeyValuePair<string, IPEndPoint>>();
355 if (AuthorityAddress is
null)
356 Backup.AddLast(
new KeyValuePair<string, IPEndPoint>(Authority,
null));
358 Backup.AddLast(
new KeyValuePair<string, IPEndPoint>(
null,
new IPEndPoint(AuthorityAddress,
DefaultDnsPort)));
363 Destination = await NextDestination(Backup);
364 if (Destination is
null)
409 null, Name,
null,
null,
null,
null,
null,
410 new KeyValuePair<string, object>(
"Name", Name),
411 new KeyValuePair<string, object>(
"TYPE",
TYPE),
412 new KeyValuePair<string, object>(
"CLASS",
CLASS));
441 if (nestingDepth == 0 && networkChanged)
443 networkChanged =
false;
448 if (udpClient is
null)
486 if (!(Response is
null) && (Response.Expires <= DateTime.Now || Response.Raw is
null))
504 if (Response is
null)
509 if (Destination is
null)
511 Client = httpsClient;
526 Client.Thread = Thread;
533 }, Destination, Timeout);
535 switch (Message.
RCode)
545 catch (TimeoutException ex)
552 Client.Thread =
null;
555 if (Message is
null || Message.
RCode !=
RCode.NoError)
563 uint Ttl = 60 * 60 * 24 * 30;
570 Answer = CheckTtl(ref Ttl, Message?.Answer),
571 Authority = CheckTtl(ref Ttl, Message?.Authority),
572 Additional = CheckTtl(ref Ttl, Message?.Additional),
574 Expires = DateTime.Now.AddSeconds(Ttl)
594 private static async Task<IPEndPoint> NextDestination(LinkedList<KeyValuePair<string, IPEndPoint>> Backup)
596 IPEndPoint Destination =
null;
598 while (Destination is
null && !(Backup?.First is
null))
600 KeyValuePair<string, IPEndPoint> P = Backup.First.Value;
601 Backup.RemoveFirst();
603 Destination = P.Value;
605 if (Destination is
null)
607 IPAddress[] Addresses;
611 Addresses = await LookupIP4Addresses(P.Key);
618 if (Addresses is
null || Addresses.Length == 0)
622 Addresses = await LookupIP6Addresses(P.Key);
630 if (!(Addresses is
null))
632 foreach (IPAddress Address
in Addresses)
634 IPEndPoint EP =
new IPEndPoint(Address, DefaultDnsPort);
636 if (Destination is
null)
641 Backup =
new LinkedList<KeyValuePair<string, IPEndPoint>>();
643 Backup.AddLast(
new KeyValuePair<string, IPEndPoint>(
null, EP));
655 if (!(Records is
null))
676 return LookupIP4Addresses(DomainName,
false);
686 return LookupIP4Addresses(DomainName,
true);
689 private static async Task<IPAddress[]> LookupIP4Addresses(
string DomainName,
bool Try)
695 Records = await TryResolve(DomainName,
QTYPE.A,
QCLASS.IN);
700 Records = await Resolve(DomainName,
QTYPE.A,
QCLASS.IN);
702 List<IPAddress> Result =
new List<IPAddress>();
710 return Result.ToArray();
722 return LookupIP6Addresses(DomainName,
false);
732 return LookupIP6Addresses(DomainName,
true);
735 private static async Task<IPAddress[]> LookupIP6Addresses(
string DomainName,
bool Try)
741 Records = await TryResolve(DomainName,
QTYPE.AAAA,
QCLASS.IN);
746 Records = await Resolve(DomainName,
QTYPE.AAAA,
QCLASS.IN);
748 List<IPAddress> Result =
new List<IPAddress>();
756 return Result.ToArray();
768 return LookupMailExchange(DomainName,
false);
778 return LookupMailExchange(DomainName,
true);
781 private static async Task<string[]> LookupMailExchange(
string DomainName,
bool Try)
794 List<MX> Records =
new List<MX>();
802 if (Records.Count == 0)
807 Records.Add(
new MX() { Exchange =
A.
Address.ToString() });
811 Records.Sort((r1, r2) => r2.Preference - r1.Preference);
813 int i, c = Records.Count;
814 string[] Result =
new string[c];
816 for (i = 0; i < c; i++)
817 Result[i] = Records[i].Exchange;
830 public static string AddressToName(IPAddress Address,
string IP4DomainName,
string IP6DomainName)
832 byte[] Bin = Address.GetAddressBytes();
837 if (
string.IsNullOrEmpty(IP4DomainName))
838 throw new ArgumentOutOfRangeException(
"IPv4 addresses not supported.");
840 StringBuilder sb =
new StringBuilder();
843 for (i = 3; i >= 0; i--)
845 sb.Append(Bin[i].ToString());
849 sb.Append(IP4DomainName);
851 return sb.ToString();
854 if (
string.IsNullOrEmpty(IP6DomainName))
855 throw new ArgumentOutOfRangeException(
"IPv6 addresses not supported.");
859 sb =
new StringBuilder();
861 for (i = 15; i >= 0; i--)
866 sb.Append((
char)(
'0' + b2));
868 sb.Append((
char)(
'A' + b2 - 10));
874 sb.Append((
char)(
'0' + b2));
876 sb.Append((
char)(
'A' + b2 - 10));
881 sb.Append(IP6DomainName);
883 return sb.ToString();
886 throw new ArgumentOutOfRangeException(
"Unrecognized IP address.", nameof(Address));
899 return LookupDomainName(Address,
false);
911 return LookupDomainName(Address,
true);
914 private static async Task<string[]> LookupDomainName(IPAddress Address,
bool Try)
916 string Name = AddressToName(Address,
"IN-ADDR.ARPA",
"IP6.ARPA");
921 Records = await TryResolve(Name,
QTYPE.PTR,
QCLASS.IN);
926 Records = await Resolve(Name,
QTYPE.PTR,
QCLASS.IN);
928 List<string> Result =
new List<string>();
936 return Result.ToArray();
946 return LookupText(Name,
false);
956 return LookupText(Name,
true);
959 private static async Task<string[]> LookupText(
string Name,
bool Try)
965 Records = await TryResolve(Name,
QTYPE.TXT,
QCLASS.IN);
970 Records = await Resolve(Name,
QTYPE.TXT,
QCLASS.IN);
972 List<string> Result =
new List<string>();
980 return Result.ToArray();
989 public static async Task<string[]>
LookupBlackList(IPAddress Address,
string BlackListDomainName)
991 string Name = AddressToName(Address, BlackListDomainName,
null);
998 List<string> Result =
null;
1001 if (!(Records is
null))
1008 Result =
new List<string>();
1015 if (!(Result is
null))
1016 return Result.ToArray();
1023 Result =
new List<string>();
1029 return Result?.ToArray();
1043 return LookupServiceEndpoint(DomainName, ServiceName, Protocol,
null,
false);
1058 return LookupServiceEndpoint(DomainName, ServiceName, Protocol, Thread,
false);
1071 return LookupServiceEndpoint(DomainName, ServiceName, Protocol,
null,
true);
1085 return LookupServiceEndpoint(DomainName, ServiceName, Protocol, Thread,
true);
1088 private static async Task<SRV> LookupServiceEndpoint(
string DomainName,
string ServiceName,
string Protocol,
ProfilerThread Thread,
bool Try)
1092 string Name =
"_" + ServiceName +
"._" + Protocol.ToLower() +
"." + DomainName;
1097 Records = await TryResolve(Name,
QTYPE.SRV,
QCLASS.IN, Thread);
1098 if (Records is
null)
1102 Records = await Resolve(Name,
QTYPE.SRV,
QCLASS.IN, Thread);
1104 SortedDictionary<ushort, List<SRV>> ServicesByPriority =
new SortedDictionary<ushort, List<SRV>>();
1105 List<SRV> SamePriority;
1111 if (!ServicesByPriority.TryGetValue(
SRV.
Priority, out SamePriority))
1113 SamePriority =
new List<SRV>();
1117 SamePriority.Add(
SRV);
1125 ushort? FirstKey =
null;
1127 SamePriority =
null;
1128 foreach (KeyValuePair<ushort, List<SRV>> P
in ServicesByPriority)
1131 SamePriority = P.Value;
1135 if (!FirstKey.HasValue)
1136 throw new IOException(
"Service Endpoint not found.");
1141 foreach (
SRV SRV in SamePriority)
1144 SRV Selected =
null;
1150 i = rnd.Next(TotWeight);
1153 foreach (
SRV SRV in SamePriority)
1158 SamePriority.Remove(
SRV);
1159 if (SamePriority.Count == 0)
1160 ServicesByPriority.Remove(FirstKey.Value);
1169 foreach (
SRV SRV in SamePriority)
1172 SamePriority.Remove(
SRV);
1173 if (SamePriority.Count == 0)
1174 ServicesByPriority.Remove(FirstKey.Value);
1181 if (Selected is
null)
1182 ServicesByPriority.Remove(FirstKey.Value);
1198 return LookupServiceEndpoints(DomainName, ServiceName, Protocol,
false);
1210 return LookupServiceEndpoints(DomainName, ServiceName, Protocol,
true);
1213 private static async Task<SRV[]> LookupServiceEndpoints(
string DomainName,
string ServiceName,
string Protocol,
bool Try)
1215 string Name =
"_" + ServiceName +
"._" + Protocol.ToLower() +
"." + DomainName;
1216 List<SRV> Result =
new List<SRV>();
1224 return Result.ToArray();
1236 public static int Next(
int MaxValue)
1240 return rnd.Next(MaxValue);
1251 return ReverseDns(Address,
false);
1261 return ReverseDns(Address,
true);
1264 private static async Task<string[]> ReverseDns(IPAddress Address,
bool Try)
1266 StringBuilder sb =
new StringBuilder();
1267 byte[] Bytes = Address.GetAddressBytes();
1268 int i = Bytes.Length;
1270 if (Address.AddressFamily == AddressFamily.InterNetwork)
1274 sb.Append(Bytes[i].ToString());
1278 sb.Append(
"in-addr.arpa");
1280 else if (Address.AddressFamily == AddressFamily.InterNetworkV6)
1282 Address.GetAddressBytes();
1288 sb.Append((b & 15).ToString(
"x1"));
1291 sb.Append((b >> 4).ToString(
"x1"));
1295 sb.Append(
"ip6.arpa");
1298 throw new ArgumentException(
"Unsupported address family.", nameof(Address));
1304 Records = await TryResolve(sb.ToString(),
QTYPE.PTR,
QCLASS.IN);
1305 if (Records is
null)
1309 Records = await Resolve(sb.ToString(),
QTYPE.PTR,
QCLASS.IN);
1311 List<string>
Names =
new List<string>();
1315 if (Rec is
PTR PtrRecord)
1316 Names.Add(PtrRecord.Name2);
1319 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 Task< IPAddress[]> TryLookupIP4Addresses(string DomainName)
Tries to look up the IPv4 addresses related to a given domain name.
static Task< ResourceRecord[]> Resolve(string Name, QTYPE TYPE, QCLASS CLASS)
Resolves a DNS name.
static Task< SRV > TryLookupServiceEndpoint(string DomainName, string ServiceName, string Protocol)
Tries to look up a service endpoint for a domain. If multiple are available, an appropriate one is se...
static Task< string[]> TryLookupMailExchange(string DomainName)
Tries to look up the Mail Exchanges related to a given domain name.
static Task< string[]> ReverseDns(IPAddress Address)
Performs a reverse DNS lookup of an IP address.
static Task< ResourceRecord[]> TryResolve(string Name, QTYPE TYPE, QCLASS CLASS)
Tries to resolve a DNS name.
static Task< string[]> LookupText(string Name)
Looks up text (TXT) records for a name.
static Task< IPAddress[]> TryLookupIP6Addresses(string DomainName)
Tries to look up the IPv6 addresses related to a given domain name.
static async Task< DnsResponse > Query(string Name, QTYPE TYPE, QCLASS CLASS, ProfilerThread Thread)
Queries a DNS name.
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< ResourceRecord[]> TryResolve(string Name, QTYPE TYPE, TYPE? ExceptionType, QCLASS CLASS)
Tries to resolve a DNS name.
static Task< DnsResponse > Query(string Name, QTYPE TYPE, QCLASS CLASS)
Queries a DNS name.
static Task< IPAddress[]> LookupIP6Addresses(string DomainName)
Looks up the IPv6 addresses related to a given domain name.
static Task< ResourceRecord[]> Resolve(string Name, QTYPE TYPE, QCLASS CLASS, ProfilerThread Thread)
Resolves a DNS name.
static Task< string[]> LookupMailExchange(string DomainName)
Looks up the Mail Exchanges related to a given domain name.
static IPAddress[] DnsServerAddresses
Available DNS Server Addresses.
static Task< SRV[]> LookupServiceEndpoints(string DomainName, string ServiceName, string Protocol)
Looks up a available service endpoints for a domain.
static async Task< ResourceRecord[]> TryResolve(string Name, QTYPE TYPE, TYPE? ExceptionType, QCLASS CLASS, ProfilerThread Thread)
Tries to resolve a DNS name.
static Task< SRV > TryLookupServiceEndpoint(string DomainName, string ServiceName, string Protocol, ProfilerThread Thread)
Tries to look up a service endpoint for a domain. If multiple are available, an appropriate one is se...
static Task< DnsResponse > TryQuery(string Name, QTYPE TYPE, QCLASS CLASS)
Tries to query a DNS name.
static Task< IPAddress[]> LookupIP4Addresses(string DomainName)
Looks up the IPv4 addresses related to a given domain name.
static Task< string[]> TryLookupText(string Name)
Tries to look up text (TXT) records for a name.
static Task< ResourceRecord[]> Resolve(string Name, QTYPE TYPE, TYPE? ExceptionType, QCLASS CLASS)
Resolves a DNS name.
static Task< string[]> LookupDomainName(IPAddress Address)
Looks up the domain name pointing to a specific IP address.
static Task< DnsResponse > TryQuery(string Name, QTYPE TYPE, QCLASS CLASS, ProfilerThread Thread)
Tries to query a DNS 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 Task< string[]> TryLookupDomainName(IPAddress Address)
Tries to look up the domain name pointing to a specific IP address.
static Uri DnsOverHttpsUri
URI used in DNS over HTTPS (DoH) requests. Setting the property to null disables DNS over HTTPS (DoH)...
static Task< ResourceRecord[]> TryResolve(string Name, QTYPE TYPE, QCLASS CLASS, ProfilerThread Thread)
Tries to resolve a DNS name.
static Task< string[]> TryReverseDns(IPAddress Address)
Tries to perform a reverse DNS lookup of an IP address.
static async Task< ResourceRecord[]> Resolve(string Name, QTYPE TYPE, TYPE? ExceptionType, QCLASS CLASS, ProfilerThread Thread)
Resolves a DNS name.
static Task< SRV[]> TryLookupServiceEndpoints(string DomainName, string ServiceName, string Protocol)
Tries to look up available service endpoints for a domain.
static 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< string[]> LookupBlackList(IPAddress Address, string BlackListDomainName)
Looks up an IP Address in a DNS Block List.
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 bool HasProvider
If a database provider is registered.
static async Task Delete(object Object)
Deletes an object in the database.
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
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.
class Names(Vector NamesVector)
Contains a collection of distinguished names.
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.