-
Notifications
You must be signed in to change notification settings - Fork 804
Expand file tree
/
Copy pathDNSLookup.cs
More file actions
293 lines (238 loc) · 11.7 KB
/
DNSLookup.cs
File metadata and controls
293 lines (238 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
using DnsClient;
using DnsClient.Protocol;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
namespace NETworkManager.Models.Network;
public sealed class DNSLookup
{
#region Constructor
public DNSLookup(DNSLookupSettings settings, IEnumerable<ServerConnectionInfo> dnsServers = null)
{
_settings = settings;
_servers = GetDnsServer(dnsServers);
// Get the dns suffix from windows or use custom dns suffix from settings if enabled
if (_settings.AddDNSSuffix)
{
_suffix = _settings.UseCustomDNSSuffix
? _settings.CustomDNSSuffix
: IPGlobalProperties.GetIPGlobalProperties().DomainName;
_addSuffix = !string.IsNullOrEmpty(_suffix);
}
}
#endregion
#region Variables
/// <summary>
/// DNS lookup settings to use for the DNS lookup.
/// </summary>
private readonly DNSLookupSettings _settings;
/// <summary>
/// List of Windows DNS servers or custom DNS servers from the settings to use for the DNS lookup.
/// </summary>
private readonly IEnumerable<IPEndPoint> _servers;
/// <summary>
/// Indicates whether the DNS suffix should be appended to the hostname.
/// </summary>
private readonly bool _addSuffix;
/// <summary>
/// DNS suffix to append to hostname.
/// </summary>
private readonly string _suffix;
#endregion
#region Events
public event EventHandler<DNSLookupRecordReceivedArgs> RecordReceived;
private void OnRecordReceived(DNSLookupRecordReceivedArgs e)
{
RecordReceived?.Invoke(this, e);
}
public event EventHandler<DNSLookupErrorArgs> LookupError;
private void OnLookupError(DNSLookupErrorArgs e)
{
LookupError?.Invoke(this, e);
}
public event EventHandler LookupComplete;
private void OnLookupComplete()
{
LookupComplete?.Invoke(this, EventArgs.Empty);
}
#endregion
#region Methods
/// <summary>
/// Get the DNS servers from Windows or get custom DNS servers from <see cref="DNSLookupSettings" />.
/// </summary>
/// <returns>List of DNS servers as <see cref="IPEndPoint" />.</returns>
private IEnumerable<IPEndPoint> GetDnsServer(IEnumerable<ServerConnectionInfo> dnsServers = null)
{
List<IPEndPoint> servers = [];
// Use windows dns servers
servers.AddRange(dnsServers == null
? NameServer.ResolveNameServers(true, false).Select(dnsServer =>
new IPEndPoint(IPAddress.Parse(dnsServer.Address), dnsServer.Port))
: dnsServers.Select(dnsServer => new IPEndPoint(IPAddress.Parse(dnsServer.Server), dnsServer.Port)));
return servers;
}
/// <summary>
/// Append DNS suffix to hostname if not set.
/// </summary>
/// <param name="hosts">List of hosts</param>
/// <returns>List of host with DNS suffix</returns>
private IEnumerable<string> GetHostWithSuffix(IEnumerable<string> hosts)
{
return hosts.Select(host => host.Contains('.') ? host : $"{host}.{_suffix}").ToList();
}
/// <summary>
/// Resolve hostname, fqdn or ip address.
/// </summary>
/// <param name="hosts">List of hostnames, FQDNs or ip addresses.</param>
public void ResolveAsync(IEnumerable<string> hosts)
{
Task.Run(() =>
{
// Append dns suffix to hostname, if option is set, otherwise just copy the list
var queries = _addSuffix && _settings.QueryType != QueryType.PTR ? GetHostWithSuffix(hosts) : hosts;
// For each dns server
Parallel.ForEach(_servers, dnsServer =>
{
// Init each dns server once
LookupClientOptions lookupClientOptions = new(dnsServer)
{
UseTcpOnly = _settings.UseTCPOnly,
UseCache = _settings.UseCache,
Recursion = _settings.Recursion,
Timeout = _settings.Timeout,
Retries = _settings.Retries
};
LookupClient lookupClient = new(lookupClientOptions);
// Get the dns server hostname for some additional information
var dnsServerHostName = string.Empty;
try
{
var result = lookupClient.QueryReverse(dnsServer.Address);
if (!result.HasError)
{
var record = result.Answers.PtrRecords().FirstOrDefault();
if (record != null)
dnsServerHostName = record.PtrDomainName;
}
}
catch
{
// ignored
}
// For each host
Parallel.ForEach(queries, query =>
{
try
{
// Resolve A, AAAA, CNAME, PTR, etc.
var dnsResponse = _settings.QueryType == QueryType.PTR
? lookupClient.QueryReverse(IPAddress.Parse(query))
: lookupClient.Query(query, _settings.QueryType, _settings.QueryClass);
// Pass the error we got from the lookup client (dns server).
if (dnsResponse.HasError)
{
OnLookupError(new DNSLookupErrorArgs(query, $"{dnsServer.Address}",
$"{dnsServer.Address}:{dnsServer.Port}", dnsResponse.ErrorMessage));
return; // continue
}
if (dnsResponse.Answers.Count == 0)
{
var digAdditionalCommand = _settings.QueryType == QueryType.PTR ? " -x " : " ";
OnLookupError(new DNSLookupErrorArgs(query, $"{dnsServer.Address}",
$"{dnsServer.Address}:{dnsServer.Port}",
$"No DNS resource records received for query \"{query}\" (Query type: \"{_settings.QueryType}\") and the DNS server did not return an error. Try to check your DNS server with: dig @{dnsServer.Address}{digAdditionalCommand}{query}"));
return; // continue
}
// Process the results...
ProcessDnsAnswers(dnsResponse.Answers, dnsResponse.NameServer, dnsServerHostName);
}
catch (Exception ex)
{
OnLookupError(new DNSLookupErrorArgs(query, $"{dnsServer.Address}",
$"{dnsServer.Address}:{dnsServer.Port}", ex.Message));
}
});
});
OnLookupComplete();
});
}
/// <summary>
/// Process the DNS answers and raise the <see cref="RecordReceived" /> event.
/// </summary>
/// <param name="answers">List of DNS resource records.</param>
/// <param name="nameServer">DNS name server that answered the query.</param>
/// <param name="nameServerHostname">DNS name server hostname.</param>
private void ProcessDnsAnswers(IEnumerable<DnsResourceRecord> answers, NameServer nameServer,
string nameServerHostname = null)
{
if (answers is not DnsResourceRecord[] dnsResourceRecords)
return;
Debug.WriteLine(dnsResourceRecords);
// A
foreach (var record in dnsResourceRecords.OfType<ARecord>())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
record.DomainName, record.TimeToLive, $"{record.RecordClass}", $"{record.RecordType}",
$"{record.Address}", $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// AAAA
foreach (var record in dnsResourceRecords.OfType<AaaaRecord>())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
record.DomainName, record.TimeToLive, $"{record.RecordClass}", $"{record.RecordType}",
$"{record.Address}", $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// CNAME
foreach (var record in dnsResourceRecords.OfType<CNameRecord>())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
record.DomainName, record.TimeToLive, $"{record.RecordClass}", $"{record.RecordType}",
record.CanonicalName, $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// DNSKEY
foreach (var record in dnsResourceRecords.OfType<DnsKeyRecord>())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
record.DomainName, record.TimeToLive, $"{record.RecordClass}", $"{record.RecordType}",
$"{record.Flags} {record.Protocol} {record.Algorithm} {Convert.ToBase64String(record.PublicKey.ToArray())}", $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// MX
foreach (var record in dnsResourceRecords.OfType<MxRecord>())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
record.DomainName, record.TimeToLive, $"{record.RecordClass}", $"{record.RecordType}",
record.Exchange, $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// NS
foreach (var record in dnsResourceRecords.OfType<NsRecord>())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
record.DomainName, record.TimeToLive, $"{record.RecordClass}", $"{record.RecordType}",
record.NSDName, $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// PTR
foreach (var record in dnsResourceRecords.OfType<PtrRecord>())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
record.DomainName, record.TimeToLive, $"{record.RecordClass}", $"{record.RecordType}",
record.PtrDomainName, $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// SOA
foreach (var record in dnsResourceRecords.OfType<SoaRecord>())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
record.DomainName, record.TimeToLive, $"{record.RecordClass}", $"{record.RecordType}",
record.MName + ", " + record.RName, $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// SRV
foreach (var record in dnsResourceRecords.OfType<SrvRecord>())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
record.DomainName, record.TimeToLive, $"{record.RecordClass}", $"{record.RecordType}",
$"{record.Priority} {record.Weight} {record.Port} {record.Target}", $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// TXT
foreach (var record in dnsResourceRecords.OfType<TxtRecord>())
OnRecordReceived(new DNSLookupRecordReceivedArgs(
new DNSLookupRecordInfo(
record.DomainName, record.TimeToLive, $"{record.RecordClass}", $"{record.RecordType}",
string.Join(", ", record.Text), $"{nameServer.Address}", nameServerHostname, nameServer.Port)));
// ToDo: implement more
}
#endregion
}