mirror of
https://github.com/fergalmoran/DnsServer.git
synced 2025-12-22 09:29:50 +00:00
198 lines
6.4 KiB
C#
198 lines
6.4 KiB
C#
/*
|
|
Technitium DNS Server
|
|
Copyright (C) 2024 Shreyas Zare (shreyas@technitium.com)
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
using DnsServerCore.ApplicationCommon;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
using TechnitiumLibrary;
|
|
using TechnitiumLibrary.Net.Dns;
|
|
using TechnitiumLibrary.Net.Dns.ResourceRecords;
|
|
|
|
namespace ZoneAlias
|
|
{
|
|
public sealed class App : IDnsApplication, IDnsAuthoritativeRequestHandler
|
|
{
|
|
#region variables
|
|
|
|
IDnsServer _dnsServer;
|
|
|
|
bool _enableAliasing;
|
|
Dictionary<string, string> _aliases;
|
|
|
|
#endregion
|
|
|
|
#region IDisposable
|
|
|
|
public void Dispose()
|
|
{
|
|
//do nothing
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region private
|
|
|
|
private static string GetParentZone(string domain)
|
|
{
|
|
int i = domain.IndexOf('.');
|
|
if (i > -1)
|
|
return domain.Substring(i + 1);
|
|
|
|
//dont return root zone
|
|
return null;
|
|
}
|
|
|
|
private bool IsZoneAlias(string domain, out string zone, out string alias)
|
|
{
|
|
domain = domain.ToLowerInvariant();
|
|
|
|
do
|
|
{
|
|
if (_aliases.TryGetValue(domain, out zone))
|
|
{
|
|
//found alias
|
|
alias = domain;
|
|
return true;
|
|
}
|
|
|
|
domain = GetParentZone(domain);
|
|
}
|
|
while (domain is not null);
|
|
|
|
alias = null;
|
|
return false;
|
|
}
|
|
|
|
private static IReadOnlyList<DnsResourceRecord> ConvertRecords(IReadOnlyList<DnsResourceRecord> records, string zone, string alias)
|
|
{
|
|
if (records.Count == 0)
|
|
return records;
|
|
|
|
DnsResourceRecord[] newRecords = new DnsResourceRecord[records.Count];
|
|
int j;
|
|
|
|
for (int i = 0; i < records.Count; i++)
|
|
{
|
|
DnsResourceRecord record = records[i];
|
|
|
|
j = record.Name.LastIndexOf(zone, StringComparison.OrdinalIgnoreCase);
|
|
if (j == 0)
|
|
newRecords[i] = new DnsResourceRecord(alias, record.Type, record.Class, record.TTL, record.RDATA);
|
|
else if (j > 0)
|
|
newRecords[i] = new DnsResourceRecord(string.Concat(record.Name.AsSpan(0, j), alias), record.Type, record.Class, record.TTL, record.RDATA);
|
|
else
|
|
newRecords[i] = record;
|
|
}
|
|
|
|
return newRecords;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region public
|
|
|
|
public Task InitializeAsync(IDnsServer dnsServer, string config)
|
|
{
|
|
_dnsServer = dnsServer;
|
|
|
|
using JsonDocument jsonDocument = JsonDocument.Parse(config);
|
|
JsonElement jsonConfig = jsonDocument.RootElement;
|
|
|
|
_enableAliasing = jsonConfig.GetPropertyValue("enableAliasing", true);
|
|
|
|
if (jsonConfig.TryGetProperty("zoneAliases", out JsonElement jsonZoneAliases))
|
|
{
|
|
Dictionary<string, string> aliases = new Dictionary<string, string>();
|
|
|
|
foreach (JsonProperty jsonZoneAlias in jsonZoneAliases.EnumerateObject())
|
|
{
|
|
string zone = jsonZoneAlias.Name.ToLowerInvariant();
|
|
|
|
foreach (JsonElement jsonAlias in jsonZoneAlias.Value.EnumerateArray())
|
|
aliases.Add(jsonAlias.GetString().ToLowerInvariant(), zone);
|
|
}
|
|
|
|
aliases.TrimExcess();
|
|
|
|
_aliases = aliases;
|
|
}
|
|
else
|
|
{
|
|
_aliases = null;
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public async Task<DnsDatagram> ProcessRequestAsync(DnsDatagram request, IPEndPoint remoteEP, DnsTransportProtocol protocol, bool isRecursionAllowed)
|
|
{
|
|
if (!_enableAliasing || (_aliases is null))
|
|
return null;
|
|
|
|
DnsQuestionRecord question = request.Question[0];
|
|
string qname = question.Name;
|
|
|
|
if (!IsZoneAlias(qname, out string zone, out string alias))
|
|
return null;
|
|
|
|
string newQname;
|
|
int i = qname.LastIndexOf(alias, StringComparison.OrdinalIgnoreCase);
|
|
if (i == 0)
|
|
newQname = zone;
|
|
else if (i > 0)
|
|
newQname = string.Concat(qname.AsSpan(0, i), zone);
|
|
else
|
|
return null;
|
|
|
|
DnsQuestionRecord newQuestion = new DnsQuestionRecord(newQname, question.Type, question.Class);
|
|
|
|
try
|
|
{
|
|
DnsDatagram response = await _dnsServer.DirectQueryAsync(newQuestion);
|
|
|
|
IReadOnlyList<DnsResourceRecord> newAnswer = ConvertRecords(response.Answer, zone, alias);
|
|
IReadOnlyList<DnsResourceRecord> newAuthority = ConvertRecords(response.Authority, zone, alias);
|
|
IReadOnlyList<DnsResourceRecord> newAdditional = ConvertRecords(response.Additional, zone, alias);
|
|
|
|
return new DnsDatagram(request.Identifier, true, request.OPCODE, response.AuthoritativeAnswer, response.Truncation, request.RecursionDesired, isRecursionAllowed, false, false, response.RCODE, request.Question, newAnswer, newAuthority, newAdditional) { Tag = response.Tag };
|
|
}
|
|
catch (TimeoutException)
|
|
{ }
|
|
catch (Exception ex)
|
|
{
|
|
_dnsServer.WriteLog(ex);
|
|
}
|
|
|
|
return new DnsDatagram(request.Identifier, true, request.OPCODE, false, false, request.RecursionDesired, isRecursionAllowed, false, false, DnsResponseCode.ServerFailure, request.Question);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region properties
|
|
|
|
public string Description
|
|
{ get { return "Allows configuring aliases for any zone (internal or external) such that they all return the same set of records."; } }
|
|
|
|
#endregion
|
|
}
|
|
}
|