MailSharp / MailSharp.DNS / Resolver.Query.cs
Code · 71 lines · 2275 bytes
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
71using MailSharp.DNS.Records;
using System.Net;

namespace MailSharp.DNS;

public partial class Resolver
{
	public async Task<Response> QueryAsync(IPEndPoint server, string name, DnsQType type, DnsQClass @class, CancellationToken ct = default)
	{
		if (!name.EndsWith('.'))
			name += '.';

		if (recordCache.IsEmpty)
			await BootstrapRootAsync(ct);

		var question = new QuestionRecord(name, type, @class);
		if (SearchInRecordCache(question) is Response cached)
			return cached;

		var request = new Request();
		request.AddQuestion(question);
		return await GetResponseAsync(server, request);
	}

	private async Task BootstrapRootAsync(CancellationToken ct)
	{
		if (!File.Exists("root\\named_root.txt"))
			return;
		List<RR> authorities = [];
		await foreach (var authority in RootHintsParser.ParseAsync("root\\named_root.txt", ct))
			authorities.Add(authority);
		AddToCache(new Response
		{
			Questions = [new QuestionRecord(".", DnsQType.NS, DnsQClass.IN)],
			Authorities = authorities
		});
	}

	public async Task<IPHostEntry> GetHostEntryAsync(IPEndPoint server, string hostNameOrAddress, CancellationToken ct = default)
		=> IPAddress.TryParse(hostNameOrAddress, out var ip)
			? await GetHostEntryAsync(server, ip, ct)
			: await ResolveForwardAsync(server, hostNameOrAddress, ct);

	public async Task<IPHostEntry> GetHostEntryAsync(IPEndPoint server, IPAddress ip, CancellationToken ct = default)
	{
		var response = await QueryAsync(server, GetArpaFromIp(ip), DnsQType.PTR, DnsQClass.IN, ct);
		return response.RecordsPTR.Length > 0
			? await ResolveForwardAsync(server, response.RecordsPTR[0].PtrName, ct)
			: new IPHostEntry();
	}

	private async Task<IPHostEntry> ResolveForwardAsync(IPEndPoint server, string hostName, CancellationToken ct)
	{
		var response = await QueryAsync(server, hostName, DnsQType.A, DnsQClass.IN, ct);
		var entry = new IPHostEntry { HostName = hostName };
		var addresses = new List<IPAddress>();
		var aliases = new List<string>();

		foreach (var rr in response.Answers)
		{
			if (rr.Type == DnsType.A && rr.Record is RecordA a)
				addresses.Add(a.Address);
			else if (rr.Type == DnsType.CNAME)
				aliases.Add(rr.Name);
		}

		entry.AddressList = [.. addresses];
		entry.Aliases = [.. aliases];
		return entry;
	}
}