4853 lines
259 KiB
C#
4853 lines
259 KiB
C#
/*
|
|
Technitium DNS Server
|
|
Copyright (C) 2025 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.Auth;
|
|
using DnsServerCore.Dns;
|
|
using DnsServerCore.Dns.Dnssec;
|
|
using DnsServerCore.Dns.ResourceRecords;
|
|
using DnsServerCore.Dns.ZoneManagers;
|
|
using DnsServerCore.Dns.Zones;
|
|
using Microsoft.AspNetCore.Http;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
using TechnitiumLibrary;
|
|
using TechnitiumLibrary.Net;
|
|
using TechnitiumLibrary.Net.Dns;
|
|
using TechnitiumLibrary.Net.Dns.ResourceRecords;
|
|
|
|
namespace DnsServerCore
|
|
{
|
|
public partial class DnsWebService
|
|
{
|
|
class WebServiceZonesApi
|
|
{
|
|
#region variables
|
|
|
|
static readonly char[] _commaSeparator = new char[] { ',' };
|
|
static readonly char[] _pipeSeparator = new char[] { '|' };
|
|
static readonly char[] _commaSpaceSeparator = new char[] { ',', ' ' };
|
|
static readonly char[] _newLineSeparator = new char[] { '\r', '\n' };
|
|
|
|
readonly DnsWebService _dnsWebService;
|
|
|
|
uint _defaultRecordTtl = 3600;
|
|
|
|
#endregion
|
|
|
|
#region constructor
|
|
|
|
public WebServiceZonesApi(DnsWebService dnsWebService)
|
|
{
|
|
_dnsWebService = dnsWebService;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region static
|
|
|
|
public static void WriteRecordsAsJson(List<DnsResourceRecord> records, Utf8JsonWriter jsonWriter, bool authoritativeZoneRecords, AuthZoneInfo zoneInfo = null)
|
|
{
|
|
if (records is null)
|
|
{
|
|
jsonWriter.WritePropertyName("records");
|
|
jsonWriter.WriteStartArray();
|
|
jsonWriter.WriteEndArray();
|
|
|
|
return;
|
|
}
|
|
|
|
records.Sort();
|
|
|
|
Dictionary<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> groupedByDomainRecords = DnsResourceRecord.GroupRecords(records);
|
|
|
|
jsonWriter.WritePropertyName("records");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (KeyValuePair<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> groupedByTypeRecords in groupedByDomainRecords)
|
|
{
|
|
foreach (KeyValuePair<DnsResourceRecordType, List<DnsResourceRecord>> groupedRecords in groupedByTypeRecords.Value)
|
|
{
|
|
foreach (DnsResourceRecord record in groupedRecords.Value)
|
|
WriteRecordAsJson(record, jsonWriter, authoritativeZoneRecords, zoneInfo);
|
|
}
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region private
|
|
|
|
private static void WriteRecordAsJson(DnsResourceRecord record, Utf8JsonWriter jsonWriter, bool authoritativeZoneRecords, AuthZoneInfo zoneInfo = null)
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("name", record.Name);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(record.Name, out string idn))
|
|
jsonWriter.WriteString("nameIdn", idn);
|
|
|
|
jsonWriter.WriteString("type", record.Type.ToString());
|
|
|
|
if (authoritativeZoneRecords)
|
|
{
|
|
GenericRecordInfo authRecordInfo = record.GetAuthGenericRecordInfo();
|
|
|
|
jsonWriter.WriteNumber("ttl", record.TTL);
|
|
jsonWriter.WriteString("ttlString", ZoneFile.GetTtlString(record.TTL));
|
|
jsonWriter.WriteBoolean("disabled", authRecordInfo.Disabled);
|
|
|
|
string comments = authRecordInfo.Comments;
|
|
if (!string.IsNullOrEmpty(comments))
|
|
jsonWriter.WriteString("comments", comments);
|
|
}
|
|
else
|
|
{
|
|
if (record.IsStale)
|
|
jsonWriter.WriteString("ttl", "0 (0s)");
|
|
else
|
|
jsonWriter.WriteString("ttl", record.TTL + " (" + ZoneFile.GetTtlString(record.TTL) + ")");
|
|
}
|
|
|
|
jsonWriter.WritePropertyName("rData");
|
|
jsonWriter.WriteStartObject();
|
|
|
|
switch (record.Type)
|
|
{
|
|
case DnsResourceRecordType.A:
|
|
{
|
|
if (record.RDATA is DnsARecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("ipAddress", rdata.Address.ToString());
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.NS:
|
|
{
|
|
if (record.RDATA is DnsNSRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("nameServer", rdata.NameServer.Length == 0 ? "." : rdata.NameServer);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(rdata.NameServer, out string nameServerIdn))
|
|
jsonWriter.WriteString("nameServerIdn", nameServerIdn);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.CNAME:
|
|
{
|
|
if (record.RDATA is DnsCNAMERecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("cname", rdata.Domain.Length == 0 ? "." : rdata.Domain);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(rdata.Domain, out string cnameIdn))
|
|
jsonWriter.WriteString("cnameIdn", cnameIdn);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SOA:
|
|
{
|
|
if (record.RDATA is DnsSOARecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("primaryNameServer", rdata.PrimaryNameServer);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(rdata.PrimaryNameServer, out string primaryNameServerIdn))
|
|
jsonWriter.WriteString("primaryNameServerIdn", primaryNameServerIdn);
|
|
|
|
jsonWriter.WriteString("responsiblePerson", rdata.ResponsiblePerson);
|
|
jsonWriter.WriteNumber("serial", rdata.Serial);
|
|
|
|
if (authoritativeZoneRecords)
|
|
{
|
|
jsonWriter.WriteNumber("refresh", rdata.Refresh);
|
|
jsonWriter.WriteNumber("retry", rdata.Retry);
|
|
jsonWriter.WriteNumber("expire", rdata.Expire);
|
|
jsonWriter.WriteNumber("minimum", rdata.Minimum);
|
|
|
|
jsonWriter.WriteString("refreshString", ZoneFile.GetTtlString(rdata.Refresh));
|
|
jsonWriter.WriteString("retryString", ZoneFile.GetTtlString(rdata.Retry));
|
|
jsonWriter.WriteString("expireString", ZoneFile.GetTtlString(rdata.Expire));
|
|
jsonWriter.WriteString("minimumString", ZoneFile.GetTtlString(rdata.Minimum));
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("refresh", rdata.Refresh + " (" + ZoneFile.GetTtlString(rdata.Refresh) + ")");
|
|
jsonWriter.WriteString("retry", rdata.Retry + " (" + ZoneFile.GetTtlString(rdata.Retry) + ")");
|
|
jsonWriter.WriteString("expire", rdata.Expire + " (" + ZoneFile.GetTtlString(rdata.Expire) + ")");
|
|
jsonWriter.WriteString("minimum", rdata.Minimum + " (" + ZoneFile.GetTtlString(rdata.Minimum) + ")");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
|
|
if (authoritativeZoneRecords && (zoneInfo is not null))
|
|
{
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Forwarder:
|
|
case AuthZoneType.Catalog:
|
|
jsonWriter.WriteBoolean("useSerialDateScheme", record.GetAuthSOARecordInfo().UseSoaSerialDateScheme);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.PTR:
|
|
{
|
|
if (record.RDATA is DnsPTRRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("ptrName", rdata.Domain.Length == 0 ? "." : rdata.Domain);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(rdata.Domain, out string ptrNameIdn))
|
|
jsonWriter.WriteString("ptrNameIdn", ptrNameIdn);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.MX:
|
|
{
|
|
if (record.RDATA is DnsMXRecordData rdata)
|
|
{
|
|
jsonWriter.WriteNumber("preference", rdata.Preference);
|
|
jsonWriter.WriteString("exchange", rdata.Exchange.Length == 0 ? "." : rdata.Exchange);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(rdata.Exchange, out string exchangeIdn))
|
|
jsonWriter.WriteString("exchangeIdn", exchangeIdn);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.TXT:
|
|
{
|
|
if (record.RDATA is DnsTXTRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("text", rdata.GetText());
|
|
jsonWriter.WriteBoolean("splitText", rdata.CharacterStrings.Count > 1);
|
|
|
|
jsonWriter.WriteStartArray("characterStrings");
|
|
|
|
foreach (string characterString in rdata.CharacterStrings)
|
|
jsonWriter.WriteStringValue(characterString);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.RP:
|
|
{
|
|
if (record.RDATA is DnsRPRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("mailbox", rdata.Mailbox);
|
|
jsonWriter.WriteString("txtDomain", rdata.TxtDomain);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(rdata.Mailbox, out string txtDomainIdn))
|
|
jsonWriter.WriteString("txtDomainIdn", txtDomainIdn);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.AAAA:
|
|
{
|
|
if (record.RDATA is DnsAAAARecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("ipAddress", rdata.Address.ToString());
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SRV:
|
|
{
|
|
if (record.RDATA is DnsSRVRecordData rdata)
|
|
{
|
|
jsonWriter.WriteNumber("priority", rdata.Priority);
|
|
jsonWriter.WriteNumber("weight", rdata.Weight);
|
|
jsonWriter.WriteNumber("port", rdata.Port);
|
|
jsonWriter.WriteString("target", rdata.Target.Length == 0 ? "." : rdata.Target);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(rdata.Target, out string targetIdn))
|
|
jsonWriter.WriteString("targetIdn", targetIdn);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.NAPTR:
|
|
{
|
|
if (record.RDATA is DnsNAPTRRecordData rdata)
|
|
{
|
|
jsonWriter.WriteNumber("order", rdata.Order);
|
|
jsonWriter.WriteNumber("preference", rdata.Preference);
|
|
jsonWriter.WriteString("flags", rdata.Flags);
|
|
jsonWriter.WriteString("services", rdata.Services);
|
|
jsonWriter.WriteString("regexp", rdata.Regexp);
|
|
jsonWriter.WriteString("replacement", rdata.Replacement.Length == 0 ? "." : rdata.Replacement);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(rdata.Replacement, out string replacementIdn))
|
|
jsonWriter.WriteString("replacementIdn", replacementIdn);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.DNAME:
|
|
{
|
|
if (record.RDATA is DnsDNAMERecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("dname", rdata.Domain.Length == 0 ? "." : rdata.Domain);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(rdata.Domain, out string dnameIdn))
|
|
jsonWriter.WriteString("dnameIdn", dnameIdn);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.APL:
|
|
{
|
|
if (record.RDATA is DnsAPLRecordData rdata)
|
|
{
|
|
jsonWriter.WriteStartArray("addressPrefixes");
|
|
|
|
foreach (DnsAPLRecordData.APItem apItem in rdata.APItems)
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("addressFamily", apItem.AddressFamily.ToString());
|
|
jsonWriter.WriteNumber("prefix", apItem.Prefix);
|
|
jsonWriter.WriteBoolean("negation", apItem.Negation);
|
|
jsonWriter.WriteString("afdPart", apItem.NetworkAddress.Address.ToString());
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.DS:
|
|
{
|
|
if (record.RDATA is DnsDSRecordData rdata)
|
|
{
|
|
jsonWriter.WriteNumber("keyTag", rdata.KeyTag);
|
|
jsonWriter.WriteString("algorithm", rdata.Algorithm.ToString());
|
|
jsonWriter.WriteNumber("algorithmNumber", (byte)rdata.Algorithm);
|
|
jsonWriter.WriteString("digestType", rdata.DigestType.ToString());
|
|
jsonWriter.WriteNumber("digestTypeNumber", (byte)rdata.DigestType);
|
|
jsonWriter.WriteString("digest", Convert.ToHexString(rdata.Digest));
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SSHFP:
|
|
{
|
|
if (record.RDATA is DnsSSHFPRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("algorithm", rdata.Algorithm.ToString());
|
|
jsonWriter.WriteString("fingerprintType", rdata.FingerprintType.ToString());
|
|
jsonWriter.WriteString("fingerprint", Convert.ToHexString(rdata.Fingerprint));
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.RRSIG:
|
|
{
|
|
if (record.RDATA is DnsRRSIGRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("typeCovered", rdata.TypeCovered.ToString());
|
|
jsonWriter.WriteString("algorithm", rdata.Algorithm.ToString());
|
|
jsonWriter.WriteNumber("algorithmNumber", (byte)rdata.Algorithm);
|
|
jsonWriter.WriteNumber("labels", rdata.Labels);
|
|
jsonWriter.WriteNumber("originalTtl", rdata.OriginalTtl);
|
|
jsonWriter.WriteString("signatureExpiration", DateTime.UnixEpoch.AddSeconds(rdata.SignatureExpiration));
|
|
jsonWriter.WriteString("signatureInception", DateTime.UnixEpoch.AddSeconds(rdata.SignatureInception));
|
|
jsonWriter.WriteNumber("keyTag", rdata.KeyTag);
|
|
jsonWriter.WriteString("signersName", rdata.SignersName.Length == 0 ? "." : rdata.SignersName);
|
|
jsonWriter.WriteString("signature", Convert.ToBase64String(rdata.Signature));
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.NSEC:
|
|
{
|
|
if (record.RDATA is DnsNSECRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("nextDomainName", rdata.NextDomainName);
|
|
|
|
jsonWriter.WritePropertyName("types");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DnsResourceRecordType type in rdata.Types)
|
|
jsonWriter.WriteStringValue(type.ToString());
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.DNSKEY:
|
|
{
|
|
if (record.RDATA is DnsDNSKEYRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("flags", rdata.Flags.ToString());
|
|
jsonWriter.WriteNumber("protocol", rdata.Protocol);
|
|
jsonWriter.WriteString("algorithm", rdata.Algorithm.ToString());
|
|
jsonWriter.WriteNumber("algorithmNumber", (byte)rdata.Algorithm);
|
|
jsonWriter.WriteString("publicKey", rdata.PublicKey.ToString());
|
|
jsonWriter.WriteNumber("computedKeyTag", rdata.ComputedKeyTag);
|
|
|
|
if (authoritativeZoneRecords)
|
|
{
|
|
if ((zoneInfo is not null) && (zoneInfo.Type == AuthZoneType.Primary))
|
|
{
|
|
IReadOnlyCollection<DnssecPrivateKey> dnssecPrivateKeys = zoneInfo.DnssecPrivateKeys;
|
|
if (dnssecPrivateKeys is not null)
|
|
{
|
|
foreach (DnssecPrivateKey dnssecPrivateKey in dnssecPrivateKeys)
|
|
{
|
|
if (dnssecPrivateKey.KeyTag == rdata.ComputedKeyTag)
|
|
{
|
|
jsonWriter.WriteString("dnsKeyState", dnssecPrivateKey.State.ToString());
|
|
|
|
if (dnssecPrivateKey.State == DnssecPrivateKeyState.Published)
|
|
{
|
|
switch (dnssecPrivateKey.KeyType)
|
|
{
|
|
case DnssecPrivateKeyType.KeySigningKey:
|
|
jsonWriter.WriteString("dnsKeyStateReadyBy", dnssecPrivateKey.StateTransitionByWithDelays);
|
|
break;
|
|
|
|
case DnssecPrivateKeyType.ZoneSigningKey:
|
|
jsonWriter.WriteString("dnsKeyStateActiveBy", dnssecPrivateKey.StateTransitionByWithDelays);
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rdata.Flags.HasFlag(DnsDnsKeyFlag.SecureEntryPoint))
|
|
{
|
|
jsonWriter.WritePropertyName("computedDigests");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("digestType", "SHA256");
|
|
jsonWriter.WriteString("digest", Convert.ToHexString(rdata.CreateDS(record.Name, DnssecDigestType.SHA256).Digest));
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("digestType", "SHA384");
|
|
jsonWriter.WriteString("digest", Convert.ToHexString(rdata.CreateDS(record.Name, DnssecDigestType.SHA384).Digest));
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.NSEC3:
|
|
{
|
|
if (record.RDATA is DnsNSEC3RecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("hashAlgorithm", rdata.HashAlgorithm.ToString());
|
|
jsonWriter.WriteString("flags", rdata.Flags.ToString());
|
|
jsonWriter.WriteNumber("iterations", rdata.Iterations);
|
|
jsonWriter.WriteString("salt", Convert.ToHexString(rdata.Salt));
|
|
jsonWriter.WriteString("nextHashedOwnerName", rdata.NextHashedOwnerName);
|
|
|
|
jsonWriter.WritePropertyName("types");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DnsResourceRecordType type in rdata.Types)
|
|
jsonWriter.WriteStringValue(type.ToString());
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.NSEC3PARAM:
|
|
{
|
|
if (record.RDATA is DnsNSEC3PARAMRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("hashAlgorithm", rdata.HashAlgorithm.ToString());
|
|
jsonWriter.WriteString("flags", rdata.Flags.ToString());
|
|
jsonWriter.WriteNumber("iterations", rdata.Iterations);
|
|
jsonWriter.WriteString("salt", Convert.ToHexString(rdata.Salt));
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.TLSA:
|
|
{
|
|
if (record.RDATA is DnsTLSARecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("certificateUsage", rdata.CertificateUsage.ToString().Replace('_', '-'));
|
|
jsonWriter.WriteString("selector", rdata.Selector.ToString());
|
|
jsonWriter.WriteString("matchingType", rdata.MatchingType.ToString().Replace('_', '-'));
|
|
jsonWriter.WriteString("certificateAssociationData", Convert.ToHexString(rdata.CertificateAssociationData));
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.ZONEMD:
|
|
{
|
|
if (record.RDATA is DnsZONEMDRecordData rdata)
|
|
{
|
|
jsonWriter.WriteNumber("serial", rdata.Serial);
|
|
jsonWriter.WriteString("scheme", rdata.Scheme.ToString());
|
|
jsonWriter.WriteString("hashAlgorithm", rdata.HashAlgorithm.ToString());
|
|
jsonWriter.WriteString("digest", Convert.ToHexString(rdata.Digest));
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SVCB:
|
|
case DnsResourceRecordType.HTTPS:
|
|
{
|
|
if (record.RDATA is DnsSVCBRecordData rdata)
|
|
{
|
|
jsonWriter.WriteNumber("svcPriority", rdata.SvcPriority);
|
|
jsonWriter.WriteString("svcTargetName", rdata.TargetName);
|
|
|
|
jsonWriter.WritePropertyName("svcParams");
|
|
jsonWriter.WriteStartObject();
|
|
|
|
foreach (KeyValuePair<DnsSvcParamKey, DnsSvcParamValue> svcParam in rdata.SvcParams)
|
|
jsonWriter.WriteString(svcParam.Key.ToString().ToLowerInvariant().Replace('_', '-'), svcParam.Value.ToString());
|
|
|
|
jsonWriter.WriteEndObject();
|
|
|
|
if (authoritativeZoneRecords)
|
|
{
|
|
SVCBRecordInfo rrInfo = record.GetAuthSVCBRecordInfo();
|
|
|
|
jsonWriter.WriteBoolean("autoIpv4Hint", rrInfo.AutoIpv4Hint);
|
|
jsonWriter.WriteBoolean("autoIpv6Hint", rrInfo.AutoIpv6Hint);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.URI:
|
|
{
|
|
if (record.RDATA is DnsURIRecordData rdata)
|
|
{
|
|
jsonWriter.WriteNumber("priority", rdata.Priority);
|
|
jsonWriter.WriteNumber("weight", rdata.Weight);
|
|
jsonWriter.WriteString("uri", rdata.Uri.AbsoluteUri);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.CAA:
|
|
{
|
|
if (record.RDATA is DnsCAARecordData rdata)
|
|
{
|
|
jsonWriter.WriteNumber("flags", rdata.Flags);
|
|
jsonWriter.WriteString("tag", rdata.Tag);
|
|
jsonWriter.WriteString("value", rdata.Value);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.ANAME:
|
|
{
|
|
if (record.RDATA is DnsANAMERecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("aname", rdata.Domain.Length == 0 ? "." : rdata.Domain);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(rdata.Domain, out string anameIdn))
|
|
jsonWriter.WriteString("anameIdn", anameIdn);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.FWD:
|
|
{
|
|
if (record.RDATA is DnsForwarderRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("protocol", rdata.Protocol.ToString());
|
|
jsonWriter.WriteString("forwarder", rdata.Forwarder);
|
|
jsonWriter.WriteNumber("priority", rdata.Priority);
|
|
jsonWriter.WriteBoolean("dnssecValidation", rdata.DnssecValidation);
|
|
jsonWriter.WriteString("proxyType", rdata.ProxyType.ToString());
|
|
|
|
switch (rdata.ProxyType)
|
|
{
|
|
case DnsForwarderRecordProxyType.Http:
|
|
case DnsForwarderRecordProxyType.Socks5:
|
|
jsonWriter.WriteString("proxyAddress", rdata.ProxyAddress);
|
|
jsonWriter.WriteNumber("proxyPort", rdata.ProxyPort);
|
|
jsonWriter.WriteString("proxyUsername", rdata.ProxyUsername);
|
|
jsonWriter.WriteString("proxyPassword", rdata.ProxyPassword);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.APP:
|
|
{
|
|
if (record.RDATA is DnsApplicationRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("appName", rdata.AppName);
|
|
jsonWriter.WriteString("classPath", rdata.ClassPath);
|
|
jsonWriter.WriteString("data", rdata.Data);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.ALIAS:
|
|
{
|
|
if (record.RDATA is DnsALIASRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("type", rdata.Type.ToString());
|
|
jsonWriter.WriteString("alias", rdata.Domain.Length == 0 ? "." : rdata.Domain);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(rdata.Domain, out string aliasIdn))
|
|
jsonWriter.WriteString("aliasIdn", aliasIdn);
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
if (record.RDATA is DnsUnknownRecordData rdata)
|
|
{
|
|
jsonWriter.WriteString("value", BitConverter.ToString(rdata.DATA).Replace('-', ':'));
|
|
}
|
|
else
|
|
{
|
|
jsonWriter.WriteString("dataType", record.RDATA.GetType().Name);
|
|
jsonWriter.WriteString("data", record.RDATA.ToString());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
jsonWriter.WriteEndObject();
|
|
|
|
jsonWriter.WriteString("dnssecStatus", record.DnssecStatus.ToString());
|
|
|
|
if (authoritativeZoneRecords)
|
|
{
|
|
GenericRecordInfo authRecordInfo = record.GetAuthGenericRecordInfo();
|
|
|
|
if (authRecordInfo is NSRecordInfo nsRecordInfo)
|
|
{
|
|
IReadOnlyList<DnsResourceRecord> glueRecords = nsRecordInfo.GlueRecords;
|
|
if (glueRecords is not null)
|
|
{
|
|
jsonWriter.WritePropertyName("glueRecords");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DnsResourceRecord glueRecord in glueRecords)
|
|
jsonWriter.WriteStringValue(glueRecord.RDATA.ToString());
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
}
|
|
|
|
jsonWriter.WriteString("lastUsedOn", authRecordInfo.LastUsedOn);
|
|
jsonWriter.WriteString("lastModified", authRecordInfo.LastModified);
|
|
jsonWriter.WriteNumber("expiryTtl", authRecordInfo.ExpiryTtl);
|
|
jsonWriter.WriteString("expiryTtlString", ZoneFile.GetTtlString(authRecordInfo.ExpiryTtl));
|
|
}
|
|
else
|
|
{
|
|
CacheRecordInfo cacheRecordInfo = record.GetCacheRecordInfo();
|
|
|
|
IReadOnlyList<DnsResourceRecord> glueRecords = cacheRecordInfo.GlueRecords;
|
|
if (glueRecords is not null)
|
|
{
|
|
jsonWriter.WritePropertyName("glueRecords");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DnsResourceRecord glueRecord in glueRecords)
|
|
jsonWriter.WriteStringValue(glueRecord.RDATA.ToString());
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
IReadOnlyList<DnsResourceRecord> rrsigRecords = cacheRecordInfo.RRSIGRecords;
|
|
IReadOnlyList<DnsResourceRecord> nsecRecords = cacheRecordInfo.NSECRecords;
|
|
|
|
if ((rrsigRecords is not null) || (nsecRecords is not null))
|
|
{
|
|
jsonWriter.WritePropertyName("dnssecRecords");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
if (rrsigRecords is not null)
|
|
{
|
|
foreach (DnsResourceRecord rrsigRecord in rrsigRecords)
|
|
jsonWriter.WriteStringValue(rrsigRecord.ToString());
|
|
}
|
|
|
|
if (nsecRecords is not null)
|
|
{
|
|
foreach (DnsResourceRecord nsecRecord in nsecRecords)
|
|
jsonWriter.WriteStringValue(nsecRecord.ToString());
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
NetworkAddress eDnsClientSubnet = cacheRecordInfo.EDnsClientSubnet;
|
|
if (eDnsClientSubnet is not null)
|
|
jsonWriter.WriteString("eDnsClientSubnet", eDnsClientSubnet.ToString());
|
|
|
|
if (record.RDATA is DnsNSRecordData nsRData)
|
|
{
|
|
NameServerMetadata metadata = nsRData.Metadata;
|
|
|
|
jsonWriter.WriteStartObject("nameServerMetadata");
|
|
|
|
jsonWriter.WriteNumber("totalQueries", metadata.TotalQueries);
|
|
jsonWriter.WriteString("answerRate", Math.Round(metadata.GetAnswerRate(), 2) + "%");
|
|
jsonWriter.WriteString("smoothedRoundTripTime", Math.Round(metadata.SRTT, 2) + " ms");
|
|
jsonWriter.WriteString("smoothedPenaltyRoundTripTime", Math.Round(metadata.SPRTT, 2) + " ms");
|
|
jsonWriter.WriteString("netRoundTripTime", Math.Round(metadata.GetNetRTT(), 2) + " ms");
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
DnsDatagramMetadata responseMetadata = cacheRecordInfo.ResponseMetadata;
|
|
if (responseMetadata is not null)
|
|
{
|
|
jsonWriter.WritePropertyName("responseMetadata");
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("nameServer", responseMetadata.NameServer?.ToString());
|
|
jsonWriter.WriteString("protocol", (responseMetadata.NameServer is null ? DnsTransportProtocol.Udp : responseMetadata.NameServer.Protocol).ToString());
|
|
jsonWriter.WriteString("datagramSize", responseMetadata.DatagramSize + " bytes");
|
|
jsonWriter.WriteString("roundTripTime", Math.Round(responseMetadata.RoundTripTime, 2) + " ms");
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
jsonWriter.WriteString("lastUsedOn", cacheRecordInfo.LastUsedOn);
|
|
}
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
private static void WriteZoneInfoAsJson(AuthZoneInfo zoneInfo, Utf8JsonWriter jsonWriter)
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("name", zoneInfo.Name);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(zoneInfo.Name, out string nameIdn))
|
|
jsonWriter.WriteString("nameIdn", nameIdn);
|
|
|
|
jsonWriter.WriteString("type", zoneInfo.Type.ToString());
|
|
jsonWriter.WriteString("lastModified", zoneInfo.LastModified);
|
|
jsonWriter.WriteBoolean("disabled", zoneInfo.Disabled);
|
|
jsonWriter.WriteNumber("soaSerial", zoneInfo.ApexZone.GetZoneSoaSerial());
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
jsonWriter.WriteBoolean("internal", zoneInfo.Internal);
|
|
break;
|
|
}
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.Stub:
|
|
case AuthZoneType.Forwarder:
|
|
case AuthZoneType.SecondaryForwarder:
|
|
jsonWriter.WriteString("catalog", zoneInfo.CatalogZoneName);
|
|
break;
|
|
}
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
jsonWriter.WriteString("dnssecStatus", zoneInfo.ApexZone.DnssecStatus.ToString());
|
|
break;
|
|
}
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Secondary:
|
|
jsonWriter.WriteBoolean("validationFailed", zoneInfo.ValidationFailed);
|
|
break;
|
|
}
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.Stub:
|
|
case AuthZoneType.SecondaryForwarder:
|
|
case AuthZoneType.SecondaryCatalog:
|
|
jsonWriter.WriteString("expiry", zoneInfo.Expiry);
|
|
jsonWriter.WriteBoolean("isExpired", zoneInfo.IsExpired);
|
|
jsonWriter.WriteBoolean("syncFailed", zoneInfo.SyncFailed);
|
|
break;
|
|
}
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.Forwarder:
|
|
case AuthZoneType.Catalog:
|
|
if (!zoneInfo.Internal)
|
|
{
|
|
string[] notifyFailed = zoneInfo.NotifyFailed;
|
|
|
|
jsonWriter.WriteBoolean("notifyFailed", notifyFailed.Length > 0);
|
|
|
|
jsonWriter.WritePropertyName("notifyFailedFor");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (string server in notifyFailed)
|
|
jsonWriter.WriteStringValue(server);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
break;
|
|
}
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
private static string[] DecodeCharacterStrings(string text)
|
|
{
|
|
string[] characterStrings = text.Split(_newLineSeparator, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
for (int i = 0; i < characterStrings.Length; i++)
|
|
characterStrings[i] = Unescape(characterStrings[i]);
|
|
|
|
return characterStrings;
|
|
}
|
|
|
|
private static string Unescape(string text)
|
|
{
|
|
StringBuilder sb = new StringBuilder(text.Length);
|
|
|
|
for (int i = 0, j; i < text.Length; i++)
|
|
{
|
|
char c = text[i];
|
|
if (c == '\\')
|
|
{
|
|
j = i + 1;
|
|
|
|
if (j == text.Length)
|
|
{
|
|
sb.Append(c);
|
|
break;
|
|
}
|
|
|
|
char next = text[j];
|
|
switch (next)
|
|
{
|
|
case 'n':
|
|
sb.Append('\n');
|
|
break;
|
|
|
|
case 'r':
|
|
sb.Append('\r');
|
|
break;
|
|
|
|
case 't':
|
|
sb.Append('\t');
|
|
break;
|
|
|
|
case '\\':
|
|
sb.Append('\\');
|
|
break;
|
|
|
|
default:
|
|
sb.Append(c).Append(next);
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
sb.Append(c);
|
|
}
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static string GetSvcbTargetName(DnsResourceRecord svcbRecord)
|
|
{
|
|
DnsSVCBRecordData rData = svcbRecord.RDATA as DnsSVCBRecordData;
|
|
|
|
if (rData.TargetName.Length > 0)
|
|
return rData.TargetName;
|
|
|
|
if (rData.SvcPriority == 0) //alias mode
|
|
return null;
|
|
|
|
//service mode
|
|
return svcbRecord.Name;
|
|
}
|
|
|
|
private void ResolveSvcbAutoHints(string zoneName, DnsResourceRecord svcbRecord, bool resolveIpv4Hint, bool resolveIpv6Hint, Dictionary<DnsSvcParamKey, DnsSvcParamValue> svcParams, IReadOnlyCollection<DnsResourceRecord> importRecords = null)
|
|
{
|
|
string targetName = GetSvcbTargetName(svcbRecord);
|
|
if (targetName is not null)
|
|
ResolveSvcbAutoHints(zoneName, targetName, resolveIpv4Hint, resolveIpv6Hint, svcParams, importRecords);
|
|
}
|
|
|
|
private void ResolveSvcbAutoHints(string zoneName, string targetName, bool resolveIpv4Hint, bool resolveIpv6Hint, Dictionary<DnsSvcParamKey, DnsSvcParamValue> svcParams, IReadOnlyCollection<DnsResourceRecord> importRecords = null)
|
|
{
|
|
if (resolveIpv4Hint)
|
|
{
|
|
List<IPAddress> ipv4Hint = new List<IPAddress>();
|
|
|
|
IReadOnlyList<DnsResourceRecord> records = _dnsWebService._dnsServer.AuthZoneManager.GetRecords(zoneName, targetName, DnsResourceRecordType.A);
|
|
|
|
foreach (DnsResourceRecord record in records)
|
|
{
|
|
if (record.GetAuthGenericRecordInfo().Disabled)
|
|
continue;
|
|
|
|
ipv4Hint.Add((record.RDATA as DnsARecordData).Address);
|
|
}
|
|
|
|
if (importRecords is not null)
|
|
{
|
|
foreach (DnsResourceRecord record in importRecords)
|
|
{
|
|
if (record.Type != DnsResourceRecordType.A)
|
|
continue;
|
|
|
|
if (record.Name.Equals(targetName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
IPAddress address = (record.RDATA as DnsARecordData).Address;
|
|
|
|
if (!ipv4Hint.Contains(address))
|
|
ipv4Hint.Add(address);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ipv4Hint.Count > 0)
|
|
svcParams[DnsSvcParamKey.IPv4Hint] = new DnsSvcIPv4HintParamValue(ipv4Hint);
|
|
else
|
|
svcParams.Remove(DnsSvcParamKey.IPv4Hint);
|
|
}
|
|
|
|
if (resolveIpv6Hint)
|
|
{
|
|
List<IPAddress> ipv6Hint = new List<IPAddress>();
|
|
|
|
IReadOnlyList<DnsResourceRecord> records = _dnsWebService._dnsServer.AuthZoneManager.GetRecords(zoneName, targetName, DnsResourceRecordType.AAAA);
|
|
|
|
foreach (DnsResourceRecord record in records)
|
|
{
|
|
if (record.GetAuthGenericRecordInfo().Disabled)
|
|
continue;
|
|
|
|
ipv6Hint.Add((record.RDATA as DnsAAAARecordData).Address);
|
|
}
|
|
|
|
if (importRecords is not null)
|
|
{
|
|
foreach (DnsResourceRecord record in importRecords)
|
|
{
|
|
if (record.Type != DnsResourceRecordType.AAAA)
|
|
continue;
|
|
|
|
if (record.Name.Equals(targetName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
IPAddress address = (record.RDATA as DnsAAAARecordData).Address;
|
|
|
|
if (!ipv6Hint.Contains(address))
|
|
ipv6Hint.Add(address);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ipv6Hint.Count > 0)
|
|
svcParams[DnsSvcParamKey.IPv6Hint] = new DnsSvcIPv6HintParamValue(ipv6Hint);
|
|
else
|
|
svcParams.Remove(DnsSvcParamKey.IPv6Hint);
|
|
}
|
|
}
|
|
|
|
private void UpdateSvcbAutoHints(string zoneName, string targetName, bool resolveIpv4Hint, bool resolveIpv6Hint)
|
|
{
|
|
List<DnsResourceRecord> allSvcbRecords = new List<DnsResourceRecord>();
|
|
_dnsWebService._dnsServer.AuthZoneManager.ListAllZoneRecords(zoneName, [DnsResourceRecordType.SVCB, DnsResourceRecordType.HTTPS], allSvcbRecords);
|
|
|
|
foreach (DnsResourceRecord record in allSvcbRecords)
|
|
{
|
|
SVCBRecordInfo info = record.GetAuthSVCBRecordInfo();
|
|
if ((info.AutoIpv4Hint && resolveIpv4Hint) || (info.AutoIpv6Hint && resolveIpv6Hint))
|
|
{
|
|
string scvbTargetName = GetSvcbTargetName(record);
|
|
if (targetName.Equals(scvbTargetName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
DnsSVCBRecordData oldRData = record.RDATA as DnsSVCBRecordData;
|
|
|
|
Dictionary<DnsSvcParamKey, DnsSvcParamValue> newSvcParams = new Dictionary<DnsSvcParamKey, DnsSvcParamValue>(oldRData.SvcParams);
|
|
ResolveSvcbAutoHints(zoneName, targetName, resolveIpv4Hint, resolveIpv6Hint, newSvcParams);
|
|
|
|
DnsSVCBRecordData newRData = new DnsSVCBRecordData(oldRData.SvcPriority, oldRData.TargetName, newSvcParams);
|
|
DnsResourceRecord newRecord = new DnsResourceRecord(record.Name, record.Type, record.Class, record.TTL, newRData) { Tag = record.Tag };
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.UpdateRecord(zoneName, record, newRecord);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task<List<DnsResourceRecord>> ReadRecordsToImportFromAsync(string zoneName, TextReader zoneFile)
|
|
{
|
|
List<DnsResourceRecord> records = await ZoneFile.ReadZoneFileFromAsync(zoneFile, zoneName, _dnsWebService._zonesApi.DefaultRecordTtl);
|
|
List<DnsResourceRecord> newRecords = new List<DnsResourceRecord>(records.Count);
|
|
|
|
foreach (DnsResourceRecord record in records)
|
|
{
|
|
if (record.Class != DnsClass.IN)
|
|
throw new DnsWebServiceException("Cannot import records: only IN class is supported by the DNS server.");
|
|
|
|
if (!AuthZoneManager.DomainBelongsToZone(zoneName, record.Name))
|
|
{
|
|
switch (record.Type)
|
|
{
|
|
case DnsResourceRecordType.A:
|
|
case DnsResourceRecordType.AAAA:
|
|
continue; //glue records
|
|
|
|
default:
|
|
throw new DnsServerException("Cannot import records: the domain name '" + record.Name + "' does not belong to the zone '" + zoneName + "'.");
|
|
}
|
|
}
|
|
|
|
switch (record.Type)
|
|
{
|
|
case DnsResourceRecordType.DNSKEY:
|
|
case DnsResourceRecordType.RRSIG:
|
|
case DnsResourceRecordType.NSEC:
|
|
case DnsResourceRecordType.NSEC3:
|
|
case DnsResourceRecordType.NSEC3PARAM:
|
|
continue; //skip DNSSEC records
|
|
|
|
case DnsResourceRecordType.NS:
|
|
{
|
|
if (record.Tag is string comments)
|
|
{
|
|
NSRecordInfo rrInfo = new NSRecordInfo();
|
|
rrInfo.Comments = comments;
|
|
|
|
record.Tag = rrInfo;
|
|
}
|
|
|
|
record.SyncGlueRecords(records);
|
|
|
|
newRecords.Add(record);
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SOA:
|
|
{
|
|
if (record.Tag is string comments)
|
|
{
|
|
SOARecordInfo rrInfo = new SOARecordInfo();
|
|
rrInfo.Comments = comments;
|
|
|
|
record.Tag = rrInfo;
|
|
}
|
|
|
|
newRecords.Add(record);
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SVCB:
|
|
case DnsResourceRecordType.HTTPS:
|
|
{
|
|
if (record.Tag is string comments)
|
|
{
|
|
SVCBRecordInfo rrInfo = new SVCBRecordInfo();
|
|
rrInfo.Comments = comments;
|
|
|
|
record.Tag = rrInfo;
|
|
}
|
|
|
|
if (record.RDATA is DnsSVCBRecordData rdata && (rdata.AutoIpv4Hint || rdata.AutoIpv6Hint))
|
|
{
|
|
if (rdata.AutoIpv4Hint)
|
|
record.GetAuthSVCBRecordInfo().AutoIpv4Hint = true;
|
|
|
|
if (rdata.AutoIpv6Hint)
|
|
record.GetAuthSVCBRecordInfo().AutoIpv6Hint = true;
|
|
|
|
Dictionary<DnsSvcParamKey, DnsSvcParamValue> svcParams = new Dictionary<DnsSvcParamKey, DnsSvcParamValue>(rdata.SvcParams);
|
|
DnsResourceRecord newRecord = new DnsResourceRecord(record.Name, record.Type, record.Class, record.TTL, new DnsSVCBRecordData(rdata.SvcPriority, rdata.TargetName, svcParams)) { Tag = record.Tag };
|
|
|
|
ResolveSvcbAutoHints(zoneName, record, rdata.AutoIpv4Hint, rdata.AutoIpv6Hint, svcParams, records);
|
|
|
|
newRecords.Add(newRecord);
|
|
break;
|
|
}
|
|
|
|
newRecords.Add(record);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
if (record.Tag is string comments)
|
|
{
|
|
GenericRecordInfo rrInfo = new GenericRecordInfo();
|
|
rrInfo.Comments = comments;
|
|
|
|
record.Tag = rrInfo;
|
|
}
|
|
|
|
newRecords.Add(record);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return newRecords;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region public
|
|
|
|
public void ListZones(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
|
|
|
|
IReadOnlyList<AuthZoneInfo> zoneInfoList = _dnsWebService._dnsServer.AuthZoneManager.GetZones(delegate (AuthZoneInfo zoneInfo)
|
|
{
|
|
return _dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.View);
|
|
});
|
|
|
|
if (request.TryGetQueryOrForm("pageNumber", int.Parse, out int pageNumber))
|
|
{
|
|
int zonesPerPage = request.GetQueryOrForm("zonesPerPage", int.Parse, 10);
|
|
int totalPages;
|
|
int totalZones = zoneInfoList.Count;
|
|
|
|
if (totalZones > 0)
|
|
{
|
|
if (pageNumber == 0)
|
|
pageNumber = 1;
|
|
|
|
totalPages = (totalZones / zonesPerPage) + (totalZones % zonesPerPage > 0 ? 1 : 0);
|
|
|
|
if ((pageNumber > totalPages) || (pageNumber < 0))
|
|
pageNumber = totalPages;
|
|
|
|
int start = (pageNumber - 1) * zonesPerPage;
|
|
int end = Math.Min(start + zonesPerPage, totalZones);
|
|
|
|
List<AuthZoneInfo> zoneInfoPageList = new List<AuthZoneInfo>(end - start);
|
|
|
|
for (int i = start; i < end; i++)
|
|
zoneInfoPageList.Add(zoneInfoList[i]);
|
|
|
|
zoneInfoList = zoneInfoPageList;
|
|
}
|
|
else
|
|
{
|
|
pageNumber = 0;
|
|
totalPages = 0;
|
|
}
|
|
|
|
jsonWriter.WriteNumber("pageNumber", pageNumber);
|
|
jsonWriter.WriteNumber("totalPages", totalPages);
|
|
jsonWriter.WriteNumber("totalZones", totalZones);
|
|
}
|
|
|
|
jsonWriter.WritePropertyName("zones");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (AuthZoneInfo zoneInfo in zoneInfoList)
|
|
WriteZoneInfoAsJson(zoneInfo, jsonWriter);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
public void ListCatalogZones(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
|
|
|
|
IReadOnlyList<AuthZoneInfo> catalogZoneInfoList = _dnsWebService._dnsServer.AuthZoneManager.GetCatalogZones(delegate (AuthZoneInfo catalogZoneInfo)
|
|
{
|
|
return !catalogZoneInfo.Disabled && _dnsWebService._authManager.IsPermitted(PermissionSection.Zones, catalogZoneInfo.Name, session.User, PermissionFlag.Modify);
|
|
});
|
|
|
|
jsonWriter.WritePropertyName("catalogZoneNames");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (AuthZoneInfo catalogZoneInfo in catalogZoneInfoList)
|
|
jsonWriter.WriteStringValue(catalogZoneInfo.Name);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
public async Task CreateZoneAsync(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrFormAlt("zone", "domain");
|
|
if (zoneName.Contains('*'))
|
|
throw new DnsWebServiceException("Domain name for a zone cannot contain wildcard character.");
|
|
|
|
if (IPAddress.TryParse(zoneName, out IPAddress ipAddress))
|
|
{
|
|
zoneName = ipAddress.GetReverseDomain().ToLowerInvariant();
|
|
}
|
|
else if (zoneName.Contains('/'))
|
|
{
|
|
string[] parts = zoneName.Split('/');
|
|
if ((parts.Length == 2) && IPAddress.TryParse(parts[0], out ipAddress) && int.TryParse(parts[1], out int subnetMaskWidth))
|
|
zoneName = Zone.GetReverseZone(ipAddress, subnetMaskWidth);
|
|
}
|
|
else if (zoneName.EndsWith('.'))
|
|
{
|
|
zoneName = zoneName.Substring(0, zoneName.Length - 1);
|
|
}
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
AuthZoneType type = request.GetQueryOrFormEnum("type", AuthZoneType.Primary);
|
|
|
|
//read records to import, if any
|
|
List<DnsResourceRecord> importRecords = null;
|
|
|
|
switch (type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Forwarder:
|
|
if (request.HasFormContentType && (request.Form.Files.Count > 0))
|
|
{
|
|
using (TextReader zoneFile = new StreamReader(request.Form.Files[0].OpenReadStream()))
|
|
{
|
|
importRecords = await ReadRecordsToImportFromAsync(zoneName, zoneFile);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//create zone
|
|
AuthZoneInfo zoneInfo;
|
|
|
|
switch (type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
{
|
|
bool useSoaSerialDateScheme = request.GetQueryOrForm("useSoaSerialDateScheme", bool.Parse, _dnsWebService._dnsServer.AuthZoneManager.UseSoaSerialDateScheme);
|
|
string catalogZoneName = request.GetQueryOrForm("catalog", null);
|
|
|
|
AuthZoneInfo catalogZoneInfo = null;
|
|
|
|
if (catalogZoneName is not null)
|
|
{
|
|
catalogZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(catalogZoneName);
|
|
if (catalogZoneInfo is null)
|
|
throw new DnsWebServiceException("No such Catalog zone was found: " + catalogZoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, catalogZoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied to use Catalog zone: " + catalogZoneInfo.Name);
|
|
}
|
|
|
|
zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.CreatePrimaryZone(zoneName, useSoaSerialDateScheme);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("Zone already exists: " + zoneName);
|
|
|
|
//set permissions
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SaveConfigFile();
|
|
|
|
//add membership for catalog zone
|
|
if (catalogZoneInfo is not null)
|
|
_dnsWebService._dnsServer.AuthZoneManager.AddCatalogMemberZone(catalogZoneInfo.Name, zoneInfo);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Authoritative Primary zone was created: " + zoneInfo.DisplayName);
|
|
}
|
|
break;
|
|
|
|
case AuthZoneType.Secondary:
|
|
{
|
|
string primaryNameServerAddresses = request.GetQueryOrForm("primaryNameServerAddresses", null);
|
|
DnsTransportProtocol primaryZoneTransferProtocol = request.GetQueryOrFormEnum("zoneTransferProtocol", DnsTransportProtocol.Tcp);
|
|
string primaryZoneTransferTsigKeyName = request.GetQueryOrForm("tsigKeyName", null);
|
|
bool validateZone = request.GetQueryOrForm("validateZone", bool.Parse, false);
|
|
|
|
if (primaryZoneTransferProtocol == DnsTransportProtocol.Quic)
|
|
DnsWebService.ValidateQuicSupport();
|
|
|
|
zoneInfo = await _dnsWebService._dnsServer.AuthZoneManager.CreateSecondaryZoneAsync(zoneName, primaryNameServerAddresses, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName, validateZone);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("Zone already exists: " + zoneName);
|
|
|
|
//set permissions
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SaveConfigFile();
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Authoritative Secondary zone was created: " + zoneInfo.DisplayName);
|
|
}
|
|
break;
|
|
|
|
case AuthZoneType.Stub:
|
|
{
|
|
string primaryNameServerAddresses = request.GetQueryOrForm("primaryNameServerAddresses", null);
|
|
string catalogZoneName = request.GetQueryOrForm("catalog", null);
|
|
|
|
AuthZoneInfo catalogZoneInfo = null;
|
|
|
|
if (catalogZoneName is not null)
|
|
{
|
|
catalogZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(catalogZoneName);
|
|
if (catalogZoneInfo is null)
|
|
throw new DnsWebServiceException("No such Catalog zone was found: " + catalogZoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, catalogZoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied to use Catalog zone: " + catalogZoneInfo.Name);
|
|
}
|
|
|
|
zoneInfo = await _dnsWebService._dnsServer.AuthZoneManager.CreateStubZoneAsync(zoneName, primaryNameServerAddresses);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("Zone already exists: " + zoneName);
|
|
|
|
//set permissions
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SaveConfigFile();
|
|
|
|
//add membership for catalog zone
|
|
if (catalogZoneInfo is not null)
|
|
_dnsWebService._dnsServer.AuthZoneManager.AddCatalogMemberZone(catalogZoneInfo.Name, zoneInfo);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Stub zone was created: " + zoneInfo.DisplayName);
|
|
}
|
|
break;
|
|
|
|
case AuthZoneType.Forwarder:
|
|
{
|
|
bool initializeForwarder = request.GetQueryOrForm("initializeForwarder", bool.Parse, true);
|
|
string catalogZoneName = request.GetQueryOrForm("catalog", null);
|
|
|
|
AuthZoneInfo catalogZoneInfo = null;
|
|
|
|
if (catalogZoneName is not null)
|
|
{
|
|
catalogZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(catalogZoneName);
|
|
if (catalogZoneInfo is null)
|
|
throw new DnsWebServiceException("No such Catalog zone was found: " + catalogZoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, catalogZoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied to use Catalog zone: " + catalogZoneInfo.Name);
|
|
}
|
|
|
|
if (initializeForwarder)
|
|
{
|
|
DnsTransportProtocol forwarderProtocol = request.GetQueryOrFormEnum("protocol", DnsTransportProtocol.Udp);
|
|
string forwarder = request.GetQueryOrForm("forwarder");
|
|
bool dnssecValidation = request.GetQueryOrForm("dnssecValidation", bool.Parse, false);
|
|
DnsForwarderRecordProxyType proxyType = request.GetQueryOrFormEnum("proxyType", DnsForwarderRecordProxyType.DefaultProxy);
|
|
|
|
string proxyAddress = null;
|
|
ushort proxyPort = 0;
|
|
string proxyUsername = null;
|
|
string proxyPassword = null;
|
|
|
|
switch (proxyType)
|
|
{
|
|
case DnsForwarderRecordProxyType.Http:
|
|
case DnsForwarderRecordProxyType.Socks5:
|
|
proxyAddress = request.GetQueryOrForm("proxyAddress");
|
|
proxyPort = request.GetQueryOrForm("proxyPort", ushort.Parse);
|
|
proxyUsername = request.QueryOrForm("proxyUsername");
|
|
proxyPassword = request.QueryOrForm("proxyPassword");
|
|
break;
|
|
}
|
|
|
|
if (forwarderProtocol == DnsTransportProtocol.Quic)
|
|
DnsWebService.ValidateQuicSupport();
|
|
|
|
zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.CreateForwarderZone(zoneName, forwarderProtocol, forwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword, null);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("Zone already exists: " + zoneName);
|
|
}
|
|
else
|
|
{
|
|
zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.CreateForwarderZone(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("Zone already exists: " + zoneName);
|
|
}
|
|
|
|
//set permissions
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SaveConfigFile();
|
|
|
|
//add membership for catalog zone
|
|
if (catalogZoneInfo is not null)
|
|
_dnsWebService._dnsServer.AuthZoneManager.AddCatalogMemberZone(catalogZoneInfo.Name, zoneInfo);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Forwarder zone was created: " + zoneInfo.DisplayName);
|
|
}
|
|
break;
|
|
|
|
case AuthZoneType.SecondaryForwarder:
|
|
{
|
|
string primaryNameServerAddresses = request.GetQueryOrForm("primaryNameServerAddresses");
|
|
DnsTransportProtocol primaryZoneTransferProtocol = request.GetQueryOrFormEnum("zoneTransferProtocol", DnsTransportProtocol.Tcp);
|
|
string primaryZoneTransferTsigKeyName = request.GetQueryOrForm("tsigKeyName", null);
|
|
|
|
if (primaryZoneTransferProtocol == DnsTransportProtocol.Quic)
|
|
DnsWebService.ValidateQuicSupport();
|
|
|
|
zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.CreateSecondaryForwarderZone(zoneName, primaryNameServerAddresses, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("Zone already exists: " + zoneName);
|
|
|
|
//set permissions
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SaveConfigFile();
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Secondary Forwarder zone was created: " + zoneInfo.DisplayName);
|
|
}
|
|
break;
|
|
|
|
case AuthZoneType.Catalog:
|
|
{
|
|
zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.CreateCatalogZone(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("Zone already exists: " + zoneName);
|
|
|
|
//set permissions
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SaveConfigFile();
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Catalog zone was created: " + zoneInfo.DisplayName);
|
|
}
|
|
break;
|
|
|
|
case AuthZoneType.SecondaryCatalog:
|
|
{
|
|
string primaryNameServerAddresses = request.GetQueryOrForm("primaryNameServerAddresses");
|
|
DnsTransportProtocol primaryZoneTransferProtocol = request.GetQueryOrFormEnum("zoneTransferProtocol", DnsTransportProtocol.Tcp);
|
|
string primaryZoneTransferTsigKeyName = request.GetQueryOrForm("tsigKeyName", null);
|
|
|
|
if (primaryZoneTransferProtocol == DnsTransportProtocol.Quic)
|
|
DnsWebService.ValidateQuicSupport();
|
|
|
|
zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.CreateSecondaryCatalogZone(zoneName, primaryNameServerAddresses, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("Zone already exists: " + zoneName);
|
|
|
|
//set permissions
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SaveConfigFile();
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Secondary Catalog zone was created: " + zoneInfo.DisplayName);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException("Zone type not supported.");
|
|
}
|
|
|
|
//delete cache for this zone to allow rebuilding cache data as needed by stub or forwarder zones
|
|
_dnsWebService._dnsServer.CacheZoneManager.DeleteZone(zoneInfo.Name);
|
|
|
|
//import records, if any
|
|
if (importRecords is not null)
|
|
{
|
|
//delete existing NS/FWD record
|
|
switch (type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
_dnsWebService._dnsServer.AuthZoneManager.DeleteRecords(zoneInfo.Name, zoneInfo.Name, DnsResourceRecordType.NS);
|
|
break;
|
|
|
|
case AuthZoneType.Forwarder:
|
|
_dnsWebService._dnsServer.AuthZoneManager.DeleteRecords(zoneInfo.Name, zoneInfo.Name, DnsResourceRecordType.FWD);
|
|
break;
|
|
}
|
|
|
|
//import records
|
|
_dnsWebService._dnsServer.AuthZoneManager.ImportRecords(zoneInfo.Name, importRecords, false, false);
|
|
}
|
|
|
|
Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
|
|
jsonWriter.WriteString("domain", string.IsNullOrEmpty(zoneInfo.Name) ? "." : zoneInfo.Name);
|
|
}
|
|
|
|
public async Task ImportZoneAsync(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + zoneName);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string importType = request.GetQueryOrForm("importType", "Text");
|
|
bool overwrite = request.GetQueryOrForm("overwrite", bool.Parse, true);
|
|
bool overwriteSoaSerial = request.GetQueryOrForm("overwriteSoaSerial", bool.Parse, false);
|
|
|
|
TextReader textReader;
|
|
|
|
switch (importType.ToUpperInvariant())
|
|
{
|
|
case "FILE":
|
|
if (!request.HasFormContentType || (request.Form.Files.Count == 0))
|
|
throw new DnsWebServiceException("The zone file to import is missing.");
|
|
|
|
textReader = new StreamReader(request.Form.Files[0].OpenReadStream());
|
|
break;
|
|
|
|
case "TEXT":
|
|
switch (request.ContentType?.ToLowerInvariant())
|
|
{
|
|
case "application/x-www-form-urlencoded":
|
|
string zoneRecords = request.GetQueryOrForm("records");
|
|
textReader = new StringReader(zoneRecords);
|
|
break;
|
|
|
|
case "text/plain":
|
|
textReader = new StreamReader(request.Body);
|
|
break;
|
|
|
|
default:
|
|
throw new DnsWebServiceException("Content type is not supported: " + request.ContentType);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new DnsWebServiceException("Import type is not supported: " + importType);
|
|
}
|
|
|
|
List<DnsResourceRecord> records;
|
|
|
|
using (TextReader zoneFile = textReader)
|
|
{
|
|
records = await ReadRecordsToImportFromAsync(zoneInfo.Name, zoneFile);
|
|
}
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.ImportRecords(zoneInfo.Name, records, overwrite, overwriteSoaSerial);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Total " + records.Count + " record(s) were imported successfully into " + zoneInfo.TypeName + " zone: " + zoneInfo.DisplayName);
|
|
}
|
|
|
|
public async Task ExportZoneAsync(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + zoneName);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
List<DnsResourceRecord> records = new List<DnsResourceRecord>();
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.ListAllZoneRecords(zoneInfo.Name, records);
|
|
|
|
foreach (DnsResourceRecord record in records)
|
|
{
|
|
switch (record.Type)
|
|
{
|
|
case DnsResourceRecordType.SVCB:
|
|
case DnsResourceRecordType.HTTPS:
|
|
SVCBRecordInfo info = record.GetAuthSVCBRecordInfo();
|
|
|
|
if (info.AutoIpv4Hint)
|
|
(record.RDATA as DnsSVCBRecordData).AutoIpv4Hint = true;
|
|
|
|
if (info.AutoIpv6Hint)
|
|
(record.RDATA as DnsSVCBRecordData).AutoIpv6Hint = true;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
HttpResponse response = context.Response;
|
|
|
|
response.ContentType = "text/plain";
|
|
response.Headers.ContentDisposition = "attachment;filename=" + (zoneInfo.Name.Length == 0 ? "root.zone" : zoneInfo.Name + ".zone");
|
|
|
|
await using (StreamWriter sW = new StreamWriter(response.Body))
|
|
{
|
|
await ZoneFile.WriteZoneFileToAsync(sW, zoneInfo.Name, records, delegate (DnsResourceRecord record)
|
|
{
|
|
if (record.Tag is null)
|
|
return null;
|
|
|
|
return record.GetAuthGenericRecordInfo().Comments;
|
|
});
|
|
}
|
|
}
|
|
|
|
public void CloneZone(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
string sourceZoneName = request.GetQueryOrForm("sourceZone").TrimEnd('.');
|
|
if (DnsClient.IsDomainNameUnicode(sourceZoneName))
|
|
sourceZoneName = DnsClient.ConvertDomainNameToAscii(sourceZoneName);
|
|
|
|
AuthZoneInfo sourceZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(sourceZoneName);
|
|
if (sourceZoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + sourceZoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, sourceZoneInfo.Name, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.CloneZone(zoneName, sourceZoneInfo.Name);
|
|
|
|
//clone user/group permissions from source zone
|
|
Permission sourceZonePermissions = _dnsWebService._authManager.GetPermission(PermissionSection.Zones, sourceZoneInfo.Name);
|
|
|
|
foreach (KeyValuePair<User, PermissionFlag> userPermission in sourceZonePermissions.UserPermissions)
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, userPermission.Key, userPermission.Value);
|
|
|
|
foreach (KeyValuePair<Group, PermissionFlag> groupPermissions in sourceZonePermissions.GroupPermissions)
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, groupPermissions.Key, groupPermissions.Value);
|
|
|
|
//set default permissions
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SaveConfigFile();
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] " + sourceZoneInfo.TypeName + " zone '" + sourceZoneInfo.DisplayName + "' was cloned as '" + zoneInfo.DisplayName + "' sucessfully.");
|
|
}
|
|
|
|
public void ConvertZone(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
AuthZoneType type = request.GetQueryOrFormEnum<AuthZoneType>("type");
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + zoneName);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.ConvertZoneType(zoneInfo.Name, type);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] " + zoneInfo.TypeName + " zone '" + zoneInfo.DisplayName + "' was converted to " + AuthZoneInfo.GetZoneTypeName(type) + " zone sucessfully.");
|
|
}
|
|
|
|
public void SignPrimaryZone(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string algorithm = request.GetQueryOrForm("algorithm");
|
|
string pemKskPrivateKey = request.GetQueryOrForm("pemKskPrivateKey", null);
|
|
string pemZskPrivateKey = request.GetQueryOrForm("pemZskPrivateKey", null);
|
|
uint dnsKeyTtl = request.GetQueryOrForm("dnsKeyTtl", ZoneFile.ParseTtl, 3600u);
|
|
ushort zskRolloverDays = request.GetQueryOrForm("zskRolloverDays", ushort.Parse, Convert.ToUInt16(pemZskPrivateKey is null ? 30 : 0));
|
|
|
|
bool useNSEC3 = false;
|
|
string strNxProof = request.QueryOrForm("nxProof");
|
|
if (!string.IsNullOrEmpty(strNxProof))
|
|
{
|
|
switch (strNxProof.ToUpper())
|
|
{
|
|
case "NSEC":
|
|
useNSEC3 = false;
|
|
break;
|
|
|
|
case "NSEC3":
|
|
useNSEC3 = true;
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException("Non-existence proof type is not supported: " + strNxProof);
|
|
}
|
|
}
|
|
|
|
ushort iterations = 0;
|
|
byte saltLength = 0;
|
|
|
|
if (useNSEC3)
|
|
{
|
|
iterations = request.GetQueryOrForm<ushort>("iterations", ushort.Parse, 0);
|
|
saltLength = request.GetQueryOrForm<byte>("saltLength", byte.Parse, 0);
|
|
}
|
|
|
|
DnssecPrivateKey kskPrivateKey;
|
|
DnssecPrivateKey zskPrivateKey;
|
|
|
|
switch (algorithm.ToUpper())
|
|
{
|
|
case "RSA":
|
|
{
|
|
string hashAlgorithm = request.GetQueryOrForm("hashAlgorithm");
|
|
|
|
DnssecAlgorithm dnssecAlgorithm;
|
|
|
|
switch (hashAlgorithm.ToUpper())
|
|
{
|
|
case "MD5":
|
|
dnssecAlgorithm = DnssecAlgorithm.RSAMD5;
|
|
break;
|
|
|
|
case "SHA1":
|
|
dnssecAlgorithm = DnssecAlgorithm.RSASHA1;
|
|
break;
|
|
|
|
case "SHA256":
|
|
dnssecAlgorithm = DnssecAlgorithm.RSASHA256;
|
|
break;
|
|
|
|
case "SHA512":
|
|
dnssecAlgorithm = DnssecAlgorithm.RSASHA512;
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException("Hash algorithm is not supported: " + hashAlgorithm);
|
|
}
|
|
|
|
if (pemKskPrivateKey is null)
|
|
kskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.KeySigningKey, request.GetQueryOrForm("kskKeySize", int.Parse));
|
|
else
|
|
kskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.KeySigningKey, pemKskPrivateKey);
|
|
|
|
if (pemZskPrivateKey is null)
|
|
zskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.ZoneSigningKey, request.GetQueryOrForm("zskKeySize", int.Parse));
|
|
else
|
|
zskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.ZoneSigningKey, pemZskPrivateKey);
|
|
}
|
|
break;
|
|
|
|
case "ECDSA":
|
|
{
|
|
string curve = request.GetQueryOrForm("curve");
|
|
|
|
DnssecAlgorithm dnssecAlgorithm;
|
|
|
|
switch (curve.ToUpper())
|
|
{
|
|
case "P256":
|
|
dnssecAlgorithm = DnssecAlgorithm.ECDSAP256SHA256;
|
|
break;
|
|
|
|
case "P384":
|
|
dnssecAlgorithm = DnssecAlgorithm.ECDSAP384SHA384;
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException("ECDSA curve is not supported: " + curve);
|
|
}
|
|
|
|
if (pemKskPrivateKey is null)
|
|
kskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.KeySigningKey);
|
|
else
|
|
kskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.KeySigningKey, pemKskPrivateKey);
|
|
|
|
if (pemZskPrivateKey is null)
|
|
zskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.ZoneSigningKey);
|
|
else
|
|
zskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.ZoneSigningKey, pemZskPrivateKey);
|
|
}
|
|
break;
|
|
|
|
case "EDDSA":
|
|
{
|
|
string curve = request.GetQueryOrForm("curve");
|
|
|
|
DnssecAlgorithm dnssecAlgorithm;
|
|
|
|
switch (curve.ToUpper())
|
|
{
|
|
case "ED25519":
|
|
dnssecAlgorithm = DnssecAlgorithm.ED25519;
|
|
break;
|
|
|
|
case "ED448":
|
|
dnssecAlgorithm = DnssecAlgorithm.ED448;
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException("EdDSA curve is not supported: " + curve);
|
|
}
|
|
|
|
if (pemKskPrivateKey is null)
|
|
kskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.KeySigningKey);
|
|
else
|
|
kskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.KeySigningKey, pemKskPrivateKey);
|
|
|
|
if (pemZskPrivateKey is null)
|
|
zskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.ZoneSigningKey);
|
|
else
|
|
zskPrivateKey = DnssecPrivateKey.Create(dnssecAlgorithm, DnssecPrivateKeyType.ZoneSigningKey, pemZskPrivateKey);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException("Algorithm is not supported: " + algorithm);
|
|
}
|
|
|
|
zskPrivateKey.RolloverDays = zskRolloverDays;
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.SignPrimaryZone(zoneName, kskPrivateKey, zskPrivateKey, dnsKeyTtl, useNSEC3, iterations, saltLength);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Primary zone was signed successfully: " + zoneName);
|
|
}
|
|
|
|
public void UnsignPrimaryZone(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string zoneName = context.Request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.UnsignPrimaryZone(zoneName);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Primary zone was unsigned successfully: " + zoneName);
|
|
}
|
|
|
|
public void GetPrimaryZoneDsInfo(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string zoneName = context.Request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + zoneName);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
if (zoneInfo.Type != AuthZoneType.Primary)
|
|
throw new DnsWebServiceException("The zone must be a primary zone.");
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
if (zoneInfo.ApexZone.DnssecStatus == AuthZoneDnssecStatus.Unsigned)
|
|
throw new DnsWebServiceException("The zone must be signed with DNSSEC.");
|
|
|
|
IReadOnlyList<DnsResourceRecord> dnsKeyRecords = zoneInfo.ApexZone.GetRecords(DnsResourceRecordType.DNSKEY);
|
|
|
|
Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
|
|
|
|
jsonWriter.WriteString("name", zoneInfo.Name);
|
|
jsonWriter.WriteString("type", zoneInfo.Type.ToString());
|
|
jsonWriter.WriteBoolean("internal", zoneInfo.Internal);
|
|
jsonWriter.WriteBoolean("disabled", zoneInfo.Disabled);
|
|
jsonWriter.WriteString("dnssecStatus", zoneInfo.ApexZone.DnssecStatus.ToString());
|
|
|
|
jsonWriter.WritePropertyName("dsRecords");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DnsResourceRecord record in dnsKeyRecords)
|
|
{
|
|
if (record.RDATA is DnsDNSKEYRecordData rdata && rdata.Flags.HasFlag(DnsDnsKeyFlag.SecureEntryPoint))
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteNumber("keyTag", rdata.ComputedKeyTag);
|
|
|
|
IReadOnlyCollection<DnssecPrivateKey> dnssecPrivateKeys = zoneInfo.DnssecPrivateKeys;
|
|
if (dnssecPrivateKeys is not null)
|
|
{
|
|
foreach (DnssecPrivateKey dnssecPrivateKey in dnssecPrivateKeys)
|
|
{
|
|
if ((dnssecPrivateKey.KeyType == DnssecPrivateKeyType.KeySigningKey) && (dnssecPrivateKey.KeyTag == rdata.ComputedKeyTag))
|
|
{
|
|
jsonWriter.WriteString("dnsKeyState", dnssecPrivateKey.State.ToString());
|
|
|
|
if (dnssecPrivateKey.State == DnssecPrivateKeyState.Published)
|
|
jsonWriter.WriteString("dnsKeyStateReadyBy", dnssecPrivateKey.StateTransitionByWithDelays);
|
|
|
|
jsonWriter.WriteBoolean("isRetiring", dnssecPrivateKey.IsRetiring);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
jsonWriter.WriteString("algorithm", rdata.Algorithm.ToString());
|
|
jsonWriter.WriteNumber("algorithmNumber", (byte)rdata.Algorithm);
|
|
jsonWriter.WriteString("publicKey", rdata.PublicKey.ToString());
|
|
|
|
jsonWriter.WritePropertyName("digests");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("digestType", "SHA256");
|
|
jsonWriter.WriteString("digestTypeNumber", "2");
|
|
jsonWriter.WriteString("digest", Convert.ToHexString(rdata.CreateDS(record.Name, DnssecDigestType.SHA256).Digest));
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("digestType", "SHA384");
|
|
jsonWriter.WriteString("digestTypeNumber", "4");
|
|
jsonWriter.WriteString("digest", Convert.ToHexString(rdata.CreateDS(record.Name, DnssecDigestType.SHA384).Digest));
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
public void GetPrimaryZoneDnssecProperties(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string zoneName = context.Request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + zoneName);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
if (zoneInfo.Type != AuthZoneType.Primary)
|
|
throw new DnsWebServiceException("The zone must be a primary zone.");
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
|
|
|
|
jsonWriter.WriteString("name", zoneInfo.Name);
|
|
jsonWriter.WriteString("type", zoneInfo.Type.ToString());
|
|
jsonWriter.WriteBoolean("internal", zoneInfo.Internal);
|
|
jsonWriter.WriteBoolean("disabled", zoneInfo.Disabled);
|
|
jsonWriter.WriteString("dnssecStatus", zoneInfo.ApexZone.DnssecStatus.ToString());
|
|
|
|
if (zoneInfo.ApexZone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3)
|
|
{
|
|
IReadOnlyList<DnsResourceRecord> nsec3ParamRecords = zoneInfo.ApexZone.GetRecords(DnsResourceRecordType.NSEC3PARAM);
|
|
DnsNSEC3PARAMRecordData nsec3Param = nsec3ParamRecords[0].RDATA as DnsNSEC3PARAMRecordData;
|
|
|
|
jsonWriter.WriteNumber("nsec3Iterations", nsec3Param.Iterations);
|
|
jsonWriter.WriteNumber("nsec3SaltLength", nsec3Param.Salt.Length);
|
|
}
|
|
|
|
jsonWriter.WriteNumber("dnsKeyTtl", zoneInfo.DnsKeyTtl);
|
|
|
|
jsonWriter.WritePropertyName("dnssecPrivateKeys");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
IReadOnlyCollection<DnssecPrivateKey> dnssecPrivateKeys = zoneInfo.DnssecPrivateKeys;
|
|
if (dnssecPrivateKeys is not null)
|
|
{
|
|
List<DnssecPrivateKey> sortedDnssecPrivateKey = new List<DnssecPrivateKey>(dnssecPrivateKeys);
|
|
|
|
sortedDnssecPrivateKey.Sort(delegate (DnssecPrivateKey key1, DnssecPrivateKey key2)
|
|
{
|
|
int value = key1.KeyType.CompareTo(key2.KeyType);
|
|
if (value == 0)
|
|
value = key1.StateChangedOn.CompareTo(key2.StateChangedOn);
|
|
|
|
return value;
|
|
});
|
|
|
|
foreach (DnssecPrivateKey dnssecPrivateKey in sortedDnssecPrivateKey)
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteNumber("keyTag", dnssecPrivateKey.KeyTag);
|
|
jsonWriter.WriteString("keyType", dnssecPrivateKey.KeyType.ToString());
|
|
|
|
switch (dnssecPrivateKey.Algorithm)
|
|
{
|
|
case DnssecAlgorithm.RSAMD5:
|
|
case DnssecAlgorithm.RSASHA1:
|
|
case DnssecAlgorithm.RSASHA1_NSEC3_SHA1:
|
|
case DnssecAlgorithm.RSASHA256:
|
|
case DnssecAlgorithm.RSASHA512:
|
|
jsonWriter.WriteString("algorithm", dnssecPrivateKey.Algorithm.ToString() + " (" + (dnssecPrivateKey as DnssecRsaPrivateKey).KeySize + " bits)");
|
|
break;
|
|
|
|
default:
|
|
jsonWriter.WriteString("algorithm", dnssecPrivateKey.Algorithm.ToString());
|
|
break;
|
|
}
|
|
|
|
jsonWriter.WriteNumber("algorithmNumber", (byte)dnssecPrivateKey.Algorithm);
|
|
|
|
jsonWriter.WriteString("state", dnssecPrivateKey.State.ToString());
|
|
jsonWriter.WriteString("stateChangedOn", dnssecPrivateKey.StateChangedOn);
|
|
|
|
if (dnssecPrivateKey.State == DnssecPrivateKeyState.Published)
|
|
{
|
|
switch (dnssecPrivateKey.KeyType)
|
|
{
|
|
case DnssecPrivateKeyType.KeySigningKey:
|
|
jsonWriter.WriteString("stateReadyBy", dnssecPrivateKey.StateTransitionByWithDelays);
|
|
break;
|
|
|
|
case DnssecPrivateKeyType.ZoneSigningKey:
|
|
jsonWriter.WriteString("stateActiveBy", dnssecPrivateKey.StateTransitionByWithDelays);
|
|
break;
|
|
}
|
|
}
|
|
|
|
jsonWriter.WriteBoolean("isRetiring", dnssecPrivateKey.IsRetiring);
|
|
jsonWriter.WriteNumber("rolloverDays", dnssecPrivateKey.RolloverDays);
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
public void ConvertPrimaryZoneToNSEC(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string zoneName = context.Request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.ConvertPrimaryZoneToNSEC(zoneName);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Primary zone was converted to NSEC successfully: " + zoneName);
|
|
}
|
|
|
|
public void ConvertPrimaryZoneToNSEC3(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
ushort iterations = request.GetQueryOrForm<ushort>("iterations", ushort.Parse, 0);
|
|
byte saltLength = request.GetQueryOrForm<byte>("saltLength", byte.Parse, 0);
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.ConvertPrimaryZoneToNSEC3(zoneName, iterations, saltLength);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Primary zone was converted to NSEC3 successfully: " + zoneName);
|
|
}
|
|
|
|
public void UpdatePrimaryZoneNSEC3Parameters(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
ushort iterations = request.GetQueryOrForm<ushort>("iterations", ushort.Parse, 0);
|
|
byte saltLength = request.GetQueryOrForm<byte>("saltLength", byte.Parse, 0);
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.UpdatePrimaryZoneNSEC3Parameters(zoneName, iterations, saltLength);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Primary zone NSEC3 parameters were updated successfully: " + zoneName);
|
|
}
|
|
|
|
public void UpdatePrimaryZoneDnssecDnsKeyTtl(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
uint dnsKeyTtl = request.GetQueryOrForm("ttl", ZoneFile.ParseTtl);
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.UpdatePrimaryZoneDnsKeyTtl(zoneName, dnsKeyTtl);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Primary zone DNSKEY TTL was updated successfully: " + zoneName);
|
|
}
|
|
|
|
public void AddPrimaryZoneDnssecPrivateKey(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
DnssecPrivateKeyType keyType = request.GetQueryOrFormEnum<DnssecPrivateKeyType>("keyType");
|
|
ushort rolloverDays = request.GetQueryOrForm("rolloverDays", ushort.Parse, (ushort)(keyType == DnssecPrivateKeyType.ZoneSigningKey ? 30 : 0));
|
|
string algorithm = request.GetQueryOrForm("algorithm");
|
|
string pemPrivateKey = request.GetQueryOrForm("pemPrivateKey", null);
|
|
|
|
switch (algorithm.ToUpper())
|
|
{
|
|
case "RSA":
|
|
{
|
|
string hashAlgorithm = request.GetQueryOrForm("hashAlgorithm");
|
|
|
|
DnssecAlgorithm dnssecAlgorithm;
|
|
|
|
switch (hashAlgorithm.ToUpper())
|
|
{
|
|
case "MD5":
|
|
dnssecAlgorithm = DnssecAlgorithm.RSAMD5;
|
|
break;
|
|
|
|
case "SHA1":
|
|
dnssecAlgorithm = DnssecAlgorithm.RSASHA1;
|
|
break;
|
|
|
|
case "SHA256":
|
|
dnssecAlgorithm = DnssecAlgorithm.RSASHA256;
|
|
break;
|
|
|
|
case "SHA512":
|
|
dnssecAlgorithm = DnssecAlgorithm.RSASHA512;
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException("Hash algorithm is not supported: " + hashAlgorithm);
|
|
}
|
|
|
|
if (pemPrivateKey is null)
|
|
{
|
|
int keySize = request.GetQueryOrForm("keySize", int.Parse);
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.GenerateAndAddPrimaryZoneDnssecPrivateKey(zoneName, keyType, dnssecAlgorithm, rolloverDays, keySize);
|
|
}
|
|
else
|
|
{
|
|
DnssecPrivateKey privateKey = DnssecPrivateKey.Create(dnssecAlgorithm, keyType, pemPrivateKey);
|
|
privateKey.RolloverDays = rolloverDays;
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.AddPrimaryZoneDnssecPrivateKey(zoneName, privateKey);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case "ECDSA":
|
|
{
|
|
string curve = request.GetQueryOrForm("curve");
|
|
|
|
DnssecAlgorithm dnssecAlgorithm;
|
|
|
|
switch (curve.ToUpper())
|
|
{
|
|
case "P256":
|
|
dnssecAlgorithm = DnssecAlgorithm.ECDSAP256SHA256;
|
|
break;
|
|
|
|
case "P384":
|
|
dnssecAlgorithm = DnssecAlgorithm.ECDSAP384SHA384;
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException("ECDSA curve is not supported: " + curve);
|
|
}
|
|
|
|
if (pemPrivateKey is null)
|
|
{
|
|
_dnsWebService._dnsServer.AuthZoneManager.GenerateAndAddPrimaryZoneDnssecPrivateKey(zoneName, keyType, dnssecAlgorithm, rolloverDays);
|
|
}
|
|
else
|
|
{
|
|
DnssecPrivateKey privateKey = DnssecPrivateKey.Create(dnssecAlgorithm, keyType, pemPrivateKey);
|
|
privateKey.RolloverDays = rolloverDays;
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.AddPrimaryZoneDnssecPrivateKey(zoneName, privateKey);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case "EDDSA":
|
|
{
|
|
string curve = request.GetQueryOrForm("curve");
|
|
|
|
DnssecAlgorithm dnssecAlgorithm;
|
|
|
|
switch (curve.ToUpper())
|
|
{
|
|
case "ED25519":
|
|
dnssecAlgorithm = DnssecAlgorithm.ED25519;
|
|
break;
|
|
|
|
case "ED448":
|
|
dnssecAlgorithm = DnssecAlgorithm.ED448;
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException("EdDSA curve is not supported: " + curve);
|
|
}
|
|
|
|
if (pemPrivateKey is null)
|
|
{
|
|
_dnsWebService._dnsServer.AuthZoneManager.GenerateAndAddPrimaryZoneDnssecPrivateKey(zoneName, keyType, dnssecAlgorithm, rolloverDays);
|
|
}
|
|
else
|
|
{
|
|
DnssecPrivateKey privateKey = DnssecPrivateKey.Create(dnssecAlgorithm, keyType, pemPrivateKey);
|
|
privateKey.RolloverDays = rolloverDays;
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.AddPrimaryZoneDnssecPrivateKey(zoneName, privateKey);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException("Algorithm is not supported: " + algorithm);
|
|
}
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] DNSSEC private key was generated and added to the primary zone successfully: " + zoneName);
|
|
}
|
|
|
|
public void UpdatePrimaryZoneDnssecPrivateKey(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
ushort keyTag = request.GetQueryOrForm("keyTag", ushort.Parse);
|
|
ushort rolloverDays = request.GetQueryOrForm("rolloverDays", ushort.Parse);
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.UpdatePrimaryZoneDnssecPrivateKey(zoneName, keyTag, rolloverDays);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Primary zone DNSSEC private key config was updated successfully: " + zoneName);
|
|
}
|
|
|
|
public void DeletePrimaryZoneDnssecPrivateKey(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
ushort keyTag = request.GetQueryOrForm("keyTag", ushort.Parse);
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.DeletePrimaryZoneDnssecPrivateKey(zoneName, keyTag);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] DNSSEC private key was deleted from primary zone successfully: " + zoneName);
|
|
}
|
|
|
|
public void PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string zoneName = context.Request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(zoneName);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] All DNSSEC private keys from the primary zone were published successfully: " + zoneName);
|
|
}
|
|
|
|
public void RolloverPrimaryZoneDnsKey(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
ushort keyTag = request.GetQueryOrForm("keyTag", ushort.Parse);
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.RolloverPrimaryZoneDnsKey(zoneName, keyTag);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] The DNSKEY (" + keyTag + ") from the primary zone was rolled over successfully: " + zoneName);
|
|
}
|
|
|
|
public async Task RetirePrimaryZoneDnsKeyAsync(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrForm("zone").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneName, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
ushort keyTag = request.GetQueryOrForm("keyTag", ushort.Parse);
|
|
|
|
await _dnsWebService._dnsServer.AuthZoneManager.RetirePrimaryZoneDnsKeyAsync(zoneName, keyTag);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] The DNSKEY (" + keyTag + ") from the primary zone was retired successfully: " + zoneName);
|
|
}
|
|
|
|
public void DeleteZone(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string zoneName = context.Request.GetQueryOrFormAlt("zone", "domain").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + zoneName);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteZone(zoneInfo, true))
|
|
throw new DnsWebServiceException("Failed to delete the zone: " + zoneInfo.DisplayName);
|
|
|
|
_dnsWebService._authManager.RemoveAllPermissions(PermissionSection.Zones, zoneInfo.Name);
|
|
_dnsWebService._authManager.SaveConfigFile();
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] " + zoneInfo.TypeName + " zone was deleted: " + zoneInfo.DisplayName);
|
|
}
|
|
|
|
public void EnableZone(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string zoneName = context.Request.GetQueryOrFormAlt("zone", "domain").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + zoneName);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
zoneInfo.Disabled = false;
|
|
_dnsWebService._dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] " + zoneInfo.TypeName + " zone was enabled: " + zoneInfo.DisplayName);
|
|
|
|
//delete cache for this zone to allow rebuilding cache data as needed by stub or forwarder zones
|
|
_dnsWebService._dnsServer.CacheZoneManager.DeleteZone(zoneInfo.Name);
|
|
}
|
|
|
|
public void DisableZone(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string zoneName = context.Request.GetQueryOrFormAlt("zone", "domain").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + zoneName);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
zoneInfo.Disabled = true;
|
|
_dnsWebService._dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name);
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] " + zoneInfo.TypeName + " zone was disabled: " + zoneInfo.DisplayName);
|
|
}
|
|
|
|
public void GetZoneOptions(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrFormAlt("zone", "domain").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
bool includeAvailableCatalogZoneNames = request.GetQueryOrForm("includeAvailableCatalogZoneNames", bool.Parse, false);
|
|
bool includeAvailableTsigKeyNames = request.GetQueryOrForm("includeAvailableTsigKeyNames", bool.Parse, false);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + zoneName);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
|
|
|
|
jsonWriter.WriteString("name", zoneInfo.Name);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(zoneInfo.Name, out string nameIdn))
|
|
jsonWriter.WriteString("nameIdn", nameIdn);
|
|
|
|
jsonWriter.WriteString("type", zoneInfo.Type.ToString());
|
|
|
|
if (zoneInfo.Type == AuthZoneType.Primary)
|
|
jsonWriter.WriteBoolean("internal", zoneInfo.Internal);
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
jsonWriter.WriteString("dnssecStatus", zoneInfo.ApexZone.DnssecStatus.ToString());
|
|
break;
|
|
}
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.Forwarder:
|
|
case AuthZoneType.Catalog:
|
|
if (!zoneInfo.Internal)
|
|
{
|
|
string[] notifyFailed = zoneInfo.NotifyFailed;
|
|
|
|
jsonWriter.WriteBoolean("notifyFailed", notifyFailed.Length > 0);
|
|
|
|
jsonWriter.WritePropertyName("notifyFailedFor");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (string server in notifyFailed)
|
|
jsonWriter.WriteStringValue(server);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
break;
|
|
}
|
|
|
|
jsonWriter.WriteBoolean("disabled", zoneInfo.Disabled);
|
|
|
|
//catalog zone
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Forwarder:
|
|
jsonWriter.WriteString("catalog", zoneInfo.CatalogZoneName);
|
|
|
|
if (zoneInfo.CatalogZoneName is not null)
|
|
{
|
|
jsonWriter.WriteBoolean("overrideCatalogQueryAccess", zoneInfo.OverrideCatalogQueryAccess);
|
|
jsonWriter.WriteBoolean("overrideCatalogZoneTransfer", zoneInfo.OverrideCatalogZoneTransfer);
|
|
jsonWriter.WriteBoolean("overrideCatalogNotify", zoneInfo.OverrideCatalogNotify);
|
|
}
|
|
|
|
break;
|
|
|
|
case AuthZoneType.Stub:
|
|
jsonWriter.WriteString("catalog", zoneInfo.CatalogZoneName);
|
|
|
|
if (zoneInfo.CatalogZoneName is not null)
|
|
{
|
|
jsonWriter.WriteBoolean("isSecondaryCatalogMember", zoneInfo.ApexZone.SecondaryCatalogZone is not null);
|
|
jsonWriter.WriteBoolean("overrideCatalogQueryAccess", zoneInfo.OverrideCatalogQueryAccess);
|
|
}
|
|
break;
|
|
|
|
case AuthZoneType.Secondary:
|
|
jsonWriter.WriteString("catalog", zoneInfo.CatalogZoneName);
|
|
|
|
if (zoneInfo.CatalogZoneName is not null)
|
|
{
|
|
jsonWriter.WriteBoolean("overrideCatalogQueryAccess", zoneInfo.OverrideCatalogQueryAccess);
|
|
jsonWriter.WriteBoolean("overrideCatalogZoneTransfer", zoneInfo.OverrideCatalogZoneTransfer);
|
|
jsonWriter.WriteBoolean("overrideCatalogPrimaryNameServers", zoneInfo.OverrideCatalogPrimaryNameServers);
|
|
}
|
|
break;
|
|
|
|
case AuthZoneType.SecondaryForwarder:
|
|
jsonWriter.WriteString("catalog", zoneInfo.CatalogZoneName);
|
|
|
|
if (zoneInfo.CatalogZoneName is not null)
|
|
jsonWriter.WriteBoolean("overrideCatalogQueryAccess", zoneInfo.OverrideCatalogQueryAccess);
|
|
|
|
break;
|
|
}
|
|
|
|
//primary server
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.SecondaryForwarder:
|
|
case AuthZoneType.SecondaryCatalog:
|
|
case AuthZoneType.Stub:
|
|
jsonWriter.WriteStartArray("primaryNameServerAddresses");
|
|
|
|
IReadOnlyList<NameServerAddress> primaryNameServerAddresses = zoneInfo.PrimaryNameServerAddresses;
|
|
if (primaryNameServerAddresses is not null)
|
|
{
|
|
foreach (NameServerAddress primaryNameServerAddress in primaryNameServerAddresses)
|
|
jsonWriter.WriteStringValue(primaryNameServerAddress.OriginalAddress);
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
break;
|
|
}
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.SecondaryForwarder:
|
|
case AuthZoneType.SecondaryCatalog:
|
|
if (zoneInfo.PrimaryZoneTransferProtocol == DnsTransportProtocol.Udp)
|
|
jsonWriter.WriteString("primaryZoneTransferProtocol", "Tcp");
|
|
else
|
|
jsonWriter.WriteString("primaryZoneTransferProtocol", zoneInfo.PrimaryZoneTransferProtocol.ToString());
|
|
|
|
jsonWriter.WriteString("primaryZoneTransferTsigKeyName", zoneInfo.PrimaryZoneTransferTsigKeyName);
|
|
break;
|
|
}
|
|
|
|
if (zoneInfo.Type == AuthZoneType.Secondary)
|
|
jsonWriter.WriteBoolean("validateZone", zoneInfo.ValidateZone);
|
|
|
|
//query access
|
|
{
|
|
jsonWriter.WriteString("queryAccess", zoneInfo.QueryAccess.ToString());
|
|
jsonWriter.WriteStartArray("queryAccessNetworkACL");
|
|
|
|
if (zoneInfo.QueryAccessNetworkACL is not null)
|
|
{
|
|
foreach (NetworkAccessControl nac in zoneInfo.QueryAccessNetworkACL)
|
|
jsonWriter.WriteStringValue(nac.ToString());
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
//zone transfer
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.Forwarder:
|
|
case AuthZoneType.Catalog:
|
|
case AuthZoneType.SecondaryCatalog:
|
|
jsonWriter.WriteString("zoneTransfer", zoneInfo.ZoneTransfer.ToString());
|
|
|
|
jsonWriter.WritePropertyName("zoneTransferNetworkACL");
|
|
{
|
|
jsonWriter.WriteStartArray();
|
|
|
|
if (zoneInfo.ZoneTransferNetworkACL is not null)
|
|
{
|
|
foreach (NetworkAccessControl nac in zoneInfo.ZoneTransferNetworkACL)
|
|
jsonWriter.WriteStringValue(nac.ToString());
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
jsonWriter.WritePropertyName("zoneTransferTsigKeyNames");
|
|
{
|
|
jsonWriter.WriteStartArray();
|
|
|
|
if (zoneInfo.ZoneTransferTsigKeyNames is not null)
|
|
{
|
|
foreach (KeyValuePair<string, object> tsigKeyName in zoneInfo.ZoneTransferTsigKeyNames)
|
|
jsonWriter.WriteStringValue(tsigKeyName.Key);
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//notify
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.Forwarder:
|
|
case AuthZoneType.Catalog:
|
|
jsonWriter.WriteString("notify", zoneInfo.Notify.ToString());
|
|
|
|
jsonWriter.WritePropertyName("notifyNameServers");
|
|
{
|
|
jsonWriter.WriteStartArray();
|
|
|
|
if (zoneInfo.NotifyNameServers is not null)
|
|
{
|
|
foreach (IPAddress nameServer in zoneInfo.NotifyNameServers)
|
|
jsonWriter.WriteStringValue(nameServer.ToString());
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
if (zoneInfo.Type == AuthZoneType.Catalog)
|
|
{
|
|
jsonWriter.WriteStartArray("notifySecondaryCatalogsNameServers");
|
|
|
|
if (zoneInfo.NotifySecondaryCatalogNameServers is not null)
|
|
{
|
|
foreach (IPAddress nameServer in zoneInfo.NotifySecondaryCatalogNameServers)
|
|
jsonWriter.WriteStringValue(nameServer.ToString());
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
break;
|
|
}
|
|
|
|
//update
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.SecondaryForwarder:
|
|
case AuthZoneType.Forwarder:
|
|
jsonWriter.WriteString("update", zoneInfo.Update.ToString());
|
|
|
|
jsonWriter.WritePropertyName("updateNetworkACL");
|
|
{
|
|
jsonWriter.WriteStartArray();
|
|
|
|
if (zoneInfo.UpdateNetworkACL is not null)
|
|
{
|
|
foreach (NetworkAccessControl nac in zoneInfo.UpdateNetworkACL)
|
|
jsonWriter.WriteStringValue(nac.ToString());
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Forwarder:
|
|
jsonWriter.WritePropertyName("updateSecurityPolicies");
|
|
{
|
|
jsonWriter.WriteStartArray();
|
|
|
|
if (zoneInfo.UpdateSecurityPolicies is not null)
|
|
{
|
|
foreach (KeyValuePair<string, IReadOnlyDictionary<string, IReadOnlyList<DnsResourceRecordType>>> updateSecurityPolicy in zoneInfo.UpdateSecurityPolicies)
|
|
{
|
|
foreach (KeyValuePair<string, IReadOnlyList<DnsResourceRecordType>> policy in updateSecurityPolicy.Value)
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("tsigKeyName", updateSecurityPolicy.Key);
|
|
jsonWriter.WriteString("domain", policy.Key);
|
|
|
|
jsonWriter.WritePropertyName("allowedTypes");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DnsResourceRecordType allowedType in policy.Value)
|
|
jsonWriter.WriteStringValue(allowedType.ToString());
|
|
|
|
jsonWriter.WriteEndArray();
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
}
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (includeAvailableCatalogZoneNames)
|
|
{
|
|
IReadOnlyList<AuthZoneInfo> catalogZoneInfoList = _dnsWebService._dnsServer.AuthZoneManager.GetCatalogZones(delegate (AuthZoneInfo catalogZoneInfo)
|
|
{
|
|
return !catalogZoneInfo.Disabled && _dnsWebService._authManager.IsPermitted(PermissionSection.Zones, catalogZoneInfo.Name, session.User, PermissionFlag.Modify);
|
|
});
|
|
|
|
jsonWriter.WritePropertyName("availableCatalogZoneNames");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (AuthZoneInfo catalogZoneInfo in catalogZoneInfoList)
|
|
jsonWriter.WriteStringValue(catalogZoneInfo.Name);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
if (includeAvailableTsigKeyNames)
|
|
{
|
|
jsonWriter.WritePropertyName("availableTsigKeyNames");
|
|
{
|
|
jsonWriter.WriteStartArray();
|
|
|
|
if (_dnsWebService._dnsServer.TsigKeys is not null)
|
|
{
|
|
foreach (KeyValuePair<string, TsigKey> tsigKey in _dnsWebService._dnsServer.TsigKeys)
|
|
jsonWriter.WriteStringValue(tsigKey.Key);
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetZoneOptions(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
string zoneName = request.GetQueryOrFormAlt("zone", "domain").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + zoneName);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
if (request.TryGetQueryOrForm("disabled", bool.Parse, out bool disabled))
|
|
zoneInfo.Disabled = disabled;
|
|
|
|
//catalog zone override options
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Forwarder:
|
|
{
|
|
if (request.TryGetQueryOrForm("overrideCatalogQueryAccess", bool.Parse, out bool overrideCatalogQueryAccess))
|
|
zoneInfo.OverrideCatalogQueryAccess = overrideCatalogQueryAccess;
|
|
|
|
if (request.TryGetQueryOrForm("overrideCatalogZoneTransfer", bool.Parse, out bool overrideCatalogZoneTransfer))
|
|
zoneInfo.OverrideCatalogZoneTransfer = overrideCatalogZoneTransfer;
|
|
|
|
if (request.TryGetQueryOrForm("overrideCatalogNotify", bool.Parse, out bool overrideCatalogNotify))
|
|
zoneInfo.OverrideCatalogNotify = overrideCatalogNotify;
|
|
}
|
|
break;
|
|
|
|
case AuthZoneType.Stub:
|
|
{
|
|
if (zoneInfo.ApexZone.SecondaryCatalogZone is not null)
|
|
break; //cannot set option for Stub zone that is a member of Secondary Catalog Zone
|
|
|
|
if (request.TryGetQueryOrForm("overrideCatalogQueryAccess", bool.Parse, out bool overrideCatalogQueryAccess))
|
|
zoneInfo.OverrideCatalogQueryAccess = overrideCatalogQueryAccess;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//primary server
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.SecondaryForwarder:
|
|
case AuthZoneType.SecondaryCatalog:
|
|
{
|
|
if (zoneInfo.ApexZone.SecondaryCatalogZone is not null)
|
|
break; //cannot set option for zone that is a member of Secondary Catalog Zone
|
|
|
|
if (request.TryGetQueryOrFormEnum("primaryZoneTransferProtocol", out DnsTransportProtocol primaryZoneTransferProtocol))
|
|
{
|
|
if (primaryZoneTransferProtocol == DnsTransportProtocol.Quic)
|
|
DnsWebService.ValidateQuicSupport();
|
|
|
|
zoneInfo.PrimaryZoneTransferProtocol = primaryZoneTransferProtocol;
|
|
}
|
|
|
|
string primaryNameServerAddresses = request.QueryOrForm("primaryNameServerAddresses");
|
|
if (primaryNameServerAddresses is not null)
|
|
{
|
|
if (primaryNameServerAddresses.Length == 0)
|
|
{
|
|
zoneInfo.PrimaryNameServerAddresses = null;
|
|
}
|
|
else
|
|
{
|
|
zoneInfo.PrimaryNameServerAddresses = primaryNameServerAddresses.Split(delegate (string address)
|
|
{
|
|
NameServerAddress nameServer = NameServerAddress.Parse(address);
|
|
|
|
if (nameServer.Protocol != primaryZoneTransferProtocol)
|
|
nameServer = nameServer.ChangeProtocol(primaryZoneTransferProtocol);
|
|
|
|
return nameServer;
|
|
}, ',');
|
|
}
|
|
}
|
|
|
|
string primaryZoneTransferTsigKeyName = request.QueryOrForm("primaryZoneTransferTsigKeyName");
|
|
if (primaryZoneTransferTsigKeyName is not null)
|
|
{
|
|
if (primaryZoneTransferTsigKeyName.Length == 0)
|
|
zoneInfo.PrimaryZoneTransferTsigKeyName = null;
|
|
else
|
|
zoneInfo.PrimaryZoneTransferTsigKeyName = primaryZoneTransferTsigKeyName;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AuthZoneType.Stub:
|
|
{
|
|
if (zoneInfo.ApexZone.SecondaryCatalogZone is not null)
|
|
break; //cannot set option for Stub zone that is a member of Secondary Catalog Zone
|
|
|
|
string primaryNameServerAddresses = request.QueryOrForm("primaryNameServerAddresses");
|
|
if (primaryNameServerAddresses is not null)
|
|
{
|
|
if (primaryNameServerAddresses.Length == 0)
|
|
{
|
|
zoneInfo.PrimaryNameServerAddresses = null;
|
|
}
|
|
else
|
|
{
|
|
zoneInfo.PrimaryNameServerAddresses = primaryNameServerAddresses.Split(delegate (string address)
|
|
{
|
|
NameServerAddress nameServer = NameServerAddress.Parse(address);
|
|
|
|
if (nameServer.Protocol != DnsTransportProtocol.Udp)
|
|
nameServer = nameServer.ChangeProtocol(DnsTransportProtocol.Udp);
|
|
|
|
return nameServer;
|
|
}, ',');
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (zoneInfo.Type == AuthZoneType.Secondary)
|
|
{
|
|
if (zoneInfo.ApexZone.SecondaryCatalogZone is not null)
|
|
{
|
|
//cannot set option for zone that is a member of Secondary Catalog Zone
|
|
}
|
|
else if (request.TryGetQueryOrForm("validateZone", bool.Parse, out bool validateZone))
|
|
{
|
|
zoneInfo.ValidateZone = validateZone;
|
|
}
|
|
}
|
|
|
|
//query access
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.Stub:
|
|
case AuthZoneType.Forwarder:
|
|
case AuthZoneType.SecondaryForwarder:
|
|
case AuthZoneType.Catalog:
|
|
if (zoneInfo.ApexZone.SecondaryCatalogZone is not null)
|
|
break; //cannot set option for zone that is a member of Secondary Catalog Zone
|
|
|
|
string queryAccessNetworkACL = request.QueryOrForm("queryAccessNetworkACL");
|
|
if (queryAccessNetworkACL is not null)
|
|
{
|
|
if ((queryAccessNetworkACL.Length == 0) || queryAccessNetworkACL.Equals("false", StringComparison.OrdinalIgnoreCase))
|
|
zoneInfo.QueryAccessNetworkACL = null;
|
|
else
|
|
zoneInfo.QueryAccessNetworkACL = queryAccessNetworkACL.Split(NetworkAccessControl.Parse, ',');
|
|
}
|
|
|
|
if (request.TryGetQueryOrFormEnum("queryAccess", out AuthZoneQueryAccess queryAccess))
|
|
zoneInfo.QueryAccess = queryAccess;
|
|
|
|
break;
|
|
}
|
|
|
|
//zone transfer
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.Forwarder:
|
|
case AuthZoneType.Catalog:
|
|
if (zoneInfo.ApexZone.SecondaryCatalogZone is not null)
|
|
break; //cannot set option for zone that is a member of Secondary Catalog Zone
|
|
|
|
string strZoneTransferNetworkACL = request.QueryOrForm("zoneTransferNetworkACL");
|
|
if (strZoneTransferNetworkACL is not null)
|
|
{
|
|
if ((strZoneTransferNetworkACL.Length == 0) || strZoneTransferNetworkACL.Equals("false", StringComparison.OrdinalIgnoreCase))
|
|
zoneInfo.ZoneTransferNetworkACL = null;
|
|
else
|
|
zoneInfo.ZoneTransferNetworkACL = strZoneTransferNetworkACL.Split(NetworkAccessControl.Parse, ',');
|
|
}
|
|
|
|
if (request.TryGetQueryOrFormEnum("zoneTransfer", out AuthZoneTransfer zoneTransfer))
|
|
zoneInfo.ZoneTransfer = zoneTransfer;
|
|
|
|
string strZoneTransferTsigKeyNames = request.QueryOrForm("zoneTransferTsigKeyNames");
|
|
if (strZoneTransferTsigKeyNames is not null)
|
|
{
|
|
if ((strZoneTransferTsigKeyNames.Length == 0) || strZoneTransferTsigKeyNames.Equals("false", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
zoneInfo.ZoneTransferTsigKeyNames = null;
|
|
}
|
|
else
|
|
{
|
|
string[] strZoneTransferTsigKeyNamesParts = strZoneTransferTsigKeyNames.Split(_commaSeparator, StringSplitOptions.RemoveEmptyEntries);
|
|
Dictionary<string, object> zoneTransferTsigKeyNames = new Dictionary<string, object>(strZoneTransferTsigKeyNamesParts.Length);
|
|
|
|
for (int i = 0; i < strZoneTransferTsigKeyNamesParts.Length; i++)
|
|
zoneTransferTsigKeyNames.Add(strZoneTransferTsigKeyNamesParts[i].TrimEnd('.').ToLowerInvariant(), null);
|
|
|
|
zoneInfo.ZoneTransferTsigKeyNames = zoneTransferTsigKeyNames;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//notify
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.Forwarder:
|
|
case AuthZoneType.Catalog:
|
|
if (request.TryGetQueryOrFormEnum("notify", out AuthZoneNotify notify))
|
|
zoneInfo.Notify = notify;
|
|
|
|
string strNotifyNameServers = request.QueryOrForm("notifyNameServers");
|
|
if (strNotifyNameServers is not null)
|
|
{
|
|
if ((strNotifyNameServers.Length == 0) || strNotifyNameServers.Equals("false", StringComparison.OrdinalIgnoreCase))
|
|
zoneInfo.NotifyNameServers = null;
|
|
else
|
|
zoneInfo.NotifyNameServers = strNotifyNameServers.Split(IPAddress.Parse, ',');
|
|
}
|
|
|
|
if (zoneInfo.Type == AuthZoneType.Catalog)
|
|
{
|
|
string strNotifySecondaryCatalogNameServers = request.QueryOrForm("notifySecondaryCatalogsNameServers");
|
|
if (strNotifySecondaryCatalogNameServers is not null)
|
|
{
|
|
if ((strNotifySecondaryCatalogNameServers.Length == 0) || strNotifySecondaryCatalogNameServers.Equals("false", StringComparison.OrdinalIgnoreCase))
|
|
zoneInfo.NotifySecondaryCatalogNameServers = null;
|
|
else
|
|
zoneInfo.NotifySecondaryCatalogNameServers = strNotifySecondaryCatalogNameServers.Split(IPAddress.Parse, ',');
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//update
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.SecondaryForwarder:
|
|
case AuthZoneType.Forwarder:
|
|
if (request.TryGetQueryOrFormEnum("update", out AuthZoneUpdate update))
|
|
zoneInfo.Update = update;
|
|
|
|
string strUpdateNetworkACL = request.QueryOrForm("updateNetworkACL");
|
|
if (strUpdateNetworkACL is not null)
|
|
{
|
|
if ((strUpdateNetworkACL.Length == 0) || strUpdateNetworkACL.Equals("false", StringComparison.OrdinalIgnoreCase))
|
|
zoneInfo.UpdateNetworkACL = null;
|
|
else
|
|
zoneInfo.UpdateNetworkACL = strUpdateNetworkACL.Split(NetworkAccessControl.Parse, ',');
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Forwarder:
|
|
string strUpdateSecurityPolicies = request.QueryOrForm("updateSecurityPolicies");
|
|
if (strUpdateSecurityPolicies is not null)
|
|
{
|
|
if ((strUpdateSecurityPolicies.Length == 0) || strUpdateSecurityPolicies.Equals("false", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
zoneInfo.UpdateSecurityPolicies = null;
|
|
}
|
|
else
|
|
{
|
|
string[] strUpdateSecurityPoliciesParts = strUpdateSecurityPolicies.Split(_pipeSeparator, StringSplitOptions.RemoveEmptyEntries);
|
|
Dictionary<string, IReadOnlyDictionary<string, IReadOnlyList<DnsResourceRecordType>>> updateSecurityPolicies = new Dictionary<string, IReadOnlyDictionary<string, IReadOnlyList<DnsResourceRecordType>>>(strUpdateSecurityPoliciesParts.Length);
|
|
|
|
for (int i = 0; i < strUpdateSecurityPoliciesParts.Length; i += 3)
|
|
{
|
|
string tsigKeyName = strUpdateSecurityPoliciesParts[i].TrimEnd('.').ToLowerInvariant();
|
|
string domain = strUpdateSecurityPoliciesParts[i + 1].TrimEnd('.').ToLowerInvariant();
|
|
string strTypes = strUpdateSecurityPoliciesParts[i + 2];
|
|
|
|
if (!domain.Equals(zoneInfo.Name, StringComparison.OrdinalIgnoreCase) && !domain.EndsWith("." + zoneInfo.Name, StringComparison.OrdinalIgnoreCase))
|
|
throw new DnsWebServiceException("Cannot set Dynamic Updates security policies: the domain '" + domain + "' must be part of the current zone.");
|
|
|
|
if (!updateSecurityPolicies.TryGetValue(tsigKeyName, out IReadOnlyDictionary<string, IReadOnlyList<DnsResourceRecordType>> policyMap))
|
|
{
|
|
policyMap = new Dictionary<string, IReadOnlyList<DnsResourceRecordType>>();
|
|
updateSecurityPolicies.Add(tsigKeyName, policyMap);
|
|
}
|
|
|
|
if (!policyMap.TryGetValue(domain, out IReadOnlyList<DnsResourceRecordType> types))
|
|
{
|
|
types = new List<DnsResourceRecordType>();
|
|
(policyMap as Dictionary<string, IReadOnlyList<DnsResourceRecordType>>).Add(domain, types);
|
|
}
|
|
|
|
foreach (string strType in strTypes.Split(_commaSpaceSeparator, StringSplitOptions.RemoveEmptyEntries))
|
|
(types as List<DnsResourceRecordType>).Add(Enum.Parse<DnsResourceRecordType>(strType, true));
|
|
}
|
|
|
|
zoneInfo.UpdateSecurityPolicies = updateSecurityPolicies;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
//catalog zone; done last to allow using updated properties when there is change of ownership
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Stub:
|
|
case AuthZoneType.Forwarder:
|
|
if (zoneInfo.ApexZone.SecondaryCatalogZone is not null)
|
|
break; //cannot set option for Stub zone that is a member of Secondary Catalog Zone
|
|
|
|
string catalogZoneName = request.QueryOrForm("catalog");
|
|
if (catalogZoneName is not null)
|
|
{
|
|
string oldCatalogZoneName = zoneInfo.CatalogZoneName;
|
|
|
|
if (catalogZoneName.Length == 0)
|
|
{
|
|
if (!string.IsNullOrEmpty(oldCatalogZoneName))
|
|
_dnsWebService._dnsServer.AuthZoneManager.RemoveCatalogMemberZone(zoneInfo);
|
|
}
|
|
else
|
|
{
|
|
if (string.IsNullOrEmpty(oldCatalogZoneName))
|
|
{
|
|
//check catalog permissions
|
|
AuthZoneInfo catalogZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(catalogZoneName);
|
|
if (catalogZoneInfo is null)
|
|
throw new DnsWebServiceException("No such Catalog zone was found: " + catalogZoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, catalogZoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied to use Catalog zone: " + catalogZoneInfo.Name);
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.AddCatalogMemberZone(catalogZoneName, zoneInfo);
|
|
}
|
|
else if (!catalogZoneName.Equals(oldCatalogZoneName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
//check catalog permissions
|
|
AuthZoneInfo catalogZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(catalogZoneName);
|
|
if (catalogZoneInfo is null)
|
|
throw new DnsWebServiceException("No such Catalog zone was found: " + catalogZoneName);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, catalogZoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied to use Catalog zone: " + catalogZoneInfo.Name);
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.ChangeCatalogMemberZoneOwnership(zoneInfo, catalogZoneName);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (zoneInfo.ApexZone.CatalogZone is not null)
|
|
_dnsWebService._dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.ApexZone.CatalogZoneName);
|
|
|
|
break;
|
|
}
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] " + zoneInfo.TypeName + " zone options were updated successfully: " + zoneInfo.DisplayName);
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name);
|
|
}
|
|
|
|
public void ResyncZone(HttpContext context)
|
|
{
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string zoneName = context.Request.GetQueryOrFormAlt("zone", "domain").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + zoneName);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Secondary:
|
|
case AuthZoneType.SecondaryForwarder:
|
|
case AuthZoneType.SecondaryCatalog:
|
|
case AuthZoneType.Stub:
|
|
zoneInfo.TriggerResync();
|
|
break;
|
|
|
|
default:
|
|
throw new DnsWebServiceException("Only Secondary, Secondary Forwarder, Secondary Catalog, and Stub zones support resync.");
|
|
}
|
|
}
|
|
|
|
public void AddRecord(HttpContext context)
|
|
{
|
|
HttpRequest request = context.Request;
|
|
|
|
string domain = request.GetQueryOrForm("domain").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(domain))
|
|
domain = DnsClient.ConvertDomainNameToAscii(domain);
|
|
|
|
string zoneName = request.QueryOrForm("zone");
|
|
if (zoneName is not null)
|
|
{
|
|
zoneName = zoneName.TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
}
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.FindAuthZoneInfo(string.IsNullOrEmpty(zoneName) ? domain : zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + domain);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
DnsResourceRecordType type = request.GetQueryOrFormEnum<DnsResourceRecordType>("type");
|
|
uint ttl = request.GetQueryOrForm("ttl", ZoneFile.ParseTtl, _defaultRecordTtl);
|
|
bool overwrite = request.GetQueryOrForm("overwrite", bool.Parse, false);
|
|
string comments = request.QueryOrForm("comments");
|
|
uint expiryTtl = request.GetQueryOrForm("expiryTtl", ZoneFile.ParseTtl, 0u);
|
|
|
|
DnsResourceRecord newRecord;
|
|
|
|
switch (type)
|
|
{
|
|
case DnsResourceRecordType.A:
|
|
case DnsResourceRecordType.AAAA:
|
|
{
|
|
string strIPAddress = request.GetQueryOrFormAlt("ipAddress", "value");
|
|
IPAddress ipAddress;
|
|
|
|
if (strIPAddress.Equals("request-ip-address"))
|
|
ipAddress = context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader).Address;
|
|
else
|
|
ipAddress = IPAddress.Parse(strIPAddress);
|
|
|
|
bool ptr = request.GetQueryOrForm("ptr", bool.Parse, false);
|
|
if (ptr)
|
|
{
|
|
string ptrDomain = Zone.GetReverseZone(ipAddress, type == DnsResourceRecordType.A ? 32 : 128);
|
|
|
|
AuthZoneInfo reverseZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.FindAuthZoneInfo(ptrDomain);
|
|
if (reverseZoneInfo is null)
|
|
{
|
|
bool createPtrZone = request.GetQueryOrForm("createPtrZone", bool.Parse, false);
|
|
if (!createPtrZone)
|
|
throw new DnsWebServiceException("No reverse zone available to add PTR record.");
|
|
|
|
string ptrZone = Zone.GetReverseZone(ipAddress, type == DnsResourceRecordType.A ? 24 : 64);
|
|
|
|
reverseZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.CreatePrimaryZone(ptrZone);
|
|
if (reverseZoneInfo == null)
|
|
throw new DnsWebServiceException("Failed to create reverse zone to add PTR record: " + ptrZone);
|
|
|
|
//set permissions
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, reverseZoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SaveConfigFile();
|
|
}
|
|
|
|
if (reverseZoneInfo.Internal)
|
|
throw new DnsWebServiceException("Reverse zone '" + reverseZoneInfo.DisplayName + "' is an internal zone.");
|
|
|
|
if ((reverseZoneInfo.Type != AuthZoneType.Primary) && (reverseZoneInfo.Type != AuthZoneType.Forwarder))
|
|
throw new DnsWebServiceException("Reverse zone '" + reverseZoneInfo.DisplayName + "' is not a primary or forwarder zone.");
|
|
|
|
DnsResourceRecord ptrRecord = new DnsResourceRecord(ptrDomain, DnsResourceRecordType.PTR, DnsClass.IN, ttl, new DnsPTRRecordData(domain));
|
|
ptrRecord.GetAuthGenericRecordInfo().LastModified = DateTime.UtcNow;
|
|
ptrRecord.GetAuthGenericRecordInfo().ExpiryTtl = expiryTtl;
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.SetRecord(reverseZoneInfo.Name, ptrRecord);
|
|
_dnsWebService._dnsServer.AuthZoneManager.SaveZoneFile(reverseZoneInfo.Name);
|
|
}
|
|
|
|
if (type == DnsResourceRecordType.A)
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsARecordData(ipAddress));
|
|
else
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsAAAARecordData(ipAddress));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.NS:
|
|
{
|
|
string nameServer = request.GetQueryOrFormAlt("nameServer", "value").TrimEnd('.');
|
|
string glueAddresses = request.GetQueryOrForm("glue", null);
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsNSRecordData(nameServer));
|
|
|
|
if (!string.IsNullOrEmpty(glueAddresses))
|
|
{
|
|
if (zoneInfo.Name.Equals(domain, StringComparison.OrdinalIgnoreCase) && (nameServer.Equals(domain, StringComparison.OrdinalIgnoreCase) || nameServer.EndsWith("." + domain, StringComparison.OrdinalIgnoreCase)))
|
|
throw new DnsWebServiceException("The zone's own NS records cannot have glue addresses. Please add separate A/AAAA records in the zone instead.");
|
|
|
|
newRecord.SetGlueRecords(glueAddresses);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.CNAME:
|
|
{
|
|
if (!overwrite)
|
|
{
|
|
IReadOnlyList<DnsResourceRecord> existingRecords = _dnsWebService._dnsServer.AuthZoneManager.GetRecords(zoneInfo.Name, domain, type);
|
|
if (existingRecords.Count > 0)
|
|
throw new DnsWebServiceException("Record already exists. Use overwrite option if you wish to overwrite existing record.");
|
|
}
|
|
|
|
string cname = request.GetQueryOrFormAlt("cname", "value").TrimEnd('.');
|
|
|
|
if (cname.Equals(domain, StringComparison.OrdinalIgnoreCase))
|
|
throw new DnsWebServiceException("CNAME domain name cannot be same as that of the record name.");
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsCNAMERecordData(cname));
|
|
|
|
overwrite = true; //force SetRecord
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.PTR:
|
|
{
|
|
string ptrName = request.GetQueryOrFormAlt("ptrName", "value").TrimEnd('.');
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsPTRRecordData(ptrName));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.MX:
|
|
{
|
|
ushort preference = request.GetQueryOrForm("preference", ushort.Parse);
|
|
string exchange = request.GetQueryOrFormAlt("exchange", "value").TrimEnd('.');
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsMXRecordData(preference, exchange));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.TXT:
|
|
{
|
|
string text = request.GetQueryOrFormAlt("text", "value");
|
|
bool splitText = request.GetQueryOrForm("splitText", bool.Parse, false);
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, splitText ? new DnsTXTRecordData(DecodeCharacterStrings(text)) : new DnsTXTRecordData(text));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.RP:
|
|
{
|
|
string mailbox = request.GetQueryOrForm("mailbox", "").TrimEnd('.');
|
|
string txtDomain = request.GetQueryOrForm("txtDomain", "").TrimEnd('.');
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsRPRecordData(mailbox, txtDomain));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SRV:
|
|
{
|
|
ushort priority = request.GetQueryOrForm("priority", ushort.Parse);
|
|
ushort weight = request.GetQueryOrForm("weight", ushort.Parse);
|
|
ushort port = request.GetQueryOrForm("port", ushort.Parse);
|
|
string target = request.GetQueryOrFormAlt("target", "value").TrimEnd('.');
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsSRVRecordData(priority, weight, port, target));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.NAPTR:
|
|
{
|
|
ushort order = request.GetQueryOrForm("naptrOrder", ushort.Parse);
|
|
ushort preference = request.GetQueryOrForm("naptrPreference", ushort.Parse);
|
|
string flags = request.GetQueryOrForm("naptrFlags", "");
|
|
string services = request.GetQueryOrForm("naptrServices", "");
|
|
string regexp = request.GetQueryOrForm("naptrRegexp", "");
|
|
string replacement = request.GetQueryOrForm("naptrReplacement", "").TrimEnd('.');
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsNAPTRRecordData(order, preference, flags, services, regexp, replacement));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.DNAME:
|
|
{
|
|
if (!overwrite)
|
|
{
|
|
IReadOnlyList<DnsResourceRecord> existingRecords = _dnsWebService._dnsServer.AuthZoneManager.GetRecords(zoneInfo.Name, domain, type);
|
|
if (existingRecords.Count > 0)
|
|
throw new DnsWebServiceException("Record already exists. Use overwrite option if you wish to overwrite existing record.");
|
|
}
|
|
|
|
string dname = request.GetQueryOrFormAlt("dname", "value").TrimEnd('.');
|
|
|
|
if (dname.EndsWith("." + domain, StringComparison.OrdinalIgnoreCase))
|
|
throw new DnsWebServiceException("DNAME domain name cannot be a sub domain of the record name.");
|
|
|
|
if (dname.Equals(domain, StringComparison.OrdinalIgnoreCase))
|
|
throw new DnsWebServiceException("DNAME domain name cannot be same as that of the record name.");
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsDNAMERecordData(dname));
|
|
|
|
overwrite = true; //force SetRecord
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.DS:
|
|
{
|
|
ushort keyTag = request.GetQueryOrForm("keyTag", ushort.Parse);
|
|
DnssecAlgorithm algorithm = Enum.Parse<DnssecAlgorithm>(request.GetQueryOrForm("algorithm").Replace('-', '_'), true);
|
|
DnssecDigestType digestType = Enum.Parse<DnssecDigestType>(request.GetQueryOrForm("digestType").Replace('-', '_'), true);
|
|
byte[] digest = request.GetQueryOrFormAlt("digest", "value", Convert.FromHexString);
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsDSRecordData(keyTag, algorithm, digestType, digest));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SSHFP:
|
|
{
|
|
DnsSSHFPAlgorithm sshfpAlgorithm = request.GetQueryOrFormEnum<DnsSSHFPAlgorithm>("sshfpAlgorithm");
|
|
DnsSSHFPFingerprintType sshfpFingerprintType = request.GetQueryOrFormEnum<DnsSSHFPFingerprintType>("sshfpFingerprintType");
|
|
byte[] sshfpFingerprint = request.GetQueryOrForm("sshfpFingerprint", Convert.FromHexString);
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsSSHFPRecordData(sshfpAlgorithm, sshfpFingerprintType, sshfpFingerprint));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.TLSA:
|
|
{
|
|
DnsTLSACertificateUsage tlsaCertificateUsage = Enum.Parse<DnsTLSACertificateUsage>(request.GetQueryOrForm("tlsaCertificateUsage").Replace('-', '_'), true);
|
|
DnsTLSASelector tlsaSelector = request.GetQueryOrFormEnum<DnsTLSASelector>("tlsaSelector");
|
|
DnsTLSAMatchingType tlsaMatchingType = Enum.Parse<DnsTLSAMatchingType>(request.GetQueryOrForm("tlsaMatchingType").Replace('-', '_'), true);
|
|
string tlsaCertificateAssociationData = request.GetQueryOrForm("tlsaCertificateAssociationData");
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsTLSARecordData(tlsaCertificateUsage, tlsaSelector, tlsaMatchingType, tlsaCertificateAssociationData));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SVCB:
|
|
case DnsResourceRecordType.HTTPS:
|
|
{
|
|
ushort svcPriority = request.GetQueryOrForm("svcPriority", ushort.Parse);
|
|
string targetName = request.GetQueryOrForm("svcTargetName").TrimEnd('.');
|
|
string strSvcParams = request.GetQueryOrForm("svcParams");
|
|
bool autoIpv4Hint = request.GetQueryOrForm("autoIpv4Hint", bool.Parse, false);
|
|
bool autoIpv6Hint = request.GetQueryOrForm("autoIpv6Hint", bool.Parse, false);
|
|
|
|
Dictionary<DnsSvcParamKey, DnsSvcParamValue> svcParams;
|
|
|
|
if (strSvcParams.Equals("false", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
svcParams = new Dictionary<DnsSvcParamKey, DnsSvcParamValue>(0);
|
|
}
|
|
else
|
|
{
|
|
string[] strSvcParamsParts = strSvcParams.Split('|');
|
|
svcParams = new Dictionary<DnsSvcParamKey, DnsSvcParamValue>(strSvcParamsParts.Length / 2);
|
|
|
|
for (int i = 0; i < strSvcParamsParts.Length; i += 2)
|
|
{
|
|
DnsSvcParamKey svcParamKey = Enum.Parse<DnsSvcParamKey>(strSvcParamsParts[i].Replace('-', '_'), true);
|
|
DnsSvcParamValue svcParamValue = DnsSvcParamValue.Parse(svcParamKey, strSvcParamsParts[i + 1]);
|
|
|
|
svcParams.Add(svcParamKey, svcParamValue);
|
|
}
|
|
}
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsSVCBRecordData(svcPriority, targetName, svcParams));
|
|
|
|
if (autoIpv4Hint)
|
|
newRecord.GetAuthSVCBRecordInfo().AutoIpv4Hint = true;
|
|
|
|
if (autoIpv6Hint)
|
|
newRecord.GetAuthSVCBRecordInfo().AutoIpv6Hint = true;
|
|
|
|
if (autoIpv4Hint || autoIpv6Hint)
|
|
ResolveSvcbAutoHints(zoneInfo.Name, newRecord, autoIpv4Hint, autoIpv6Hint, svcParams);
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.URI:
|
|
{
|
|
ushort priority = request.GetQueryOrForm("uriPriority", ushort.Parse);
|
|
ushort weight = request.GetQueryOrForm("uriWeight", ushort.Parse);
|
|
Uri uri = request.GetQueryOrForm("uri", delegate (string value) { return new Uri(value); });
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsURIRecordData(priority, weight, uri));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.CAA:
|
|
{
|
|
byte flags = request.GetQueryOrForm("flags", byte.Parse);
|
|
string tag = request.GetQueryOrForm("tag");
|
|
string value = request.GetQueryOrForm("value");
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsCAARecordData(flags, tag, value));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.ANAME:
|
|
{
|
|
string aname = request.GetQueryOrFormAlt("aname", "value").TrimEnd('.');
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsANAMERecordData(aname));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.FWD:
|
|
{
|
|
DnsTransportProtocol protocol = request.GetQueryOrFormEnum("protocol", DnsTransportProtocol.Udp);
|
|
string forwarder = request.GetQueryOrFormAlt("forwarder", "value");
|
|
bool dnssecValidation = request.GetQueryOrForm("dnssecValidation", bool.Parse, false);
|
|
|
|
DnsForwarderRecordProxyType proxyType = DnsForwarderRecordProxyType.DefaultProxy;
|
|
string proxyAddress = null;
|
|
ushort proxyPort = 0;
|
|
string proxyUsername = null;
|
|
string proxyPassword = null;
|
|
|
|
if (!forwarder.Equals("this-server"))
|
|
{
|
|
proxyType = request.GetQueryOrFormEnum("proxyType", DnsForwarderRecordProxyType.DefaultProxy);
|
|
switch (proxyType)
|
|
{
|
|
case DnsForwarderRecordProxyType.Http:
|
|
case DnsForwarderRecordProxyType.Socks5:
|
|
proxyAddress = request.GetQueryOrForm("proxyAddress");
|
|
proxyPort = request.GetQueryOrForm("proxyPort", ushort.Parse);
|
|
proxyUsername = request.QueryOrForm("proxyUsername");
|
|
proxyPassword = request.QueryOrForm("proxyPassword");
|
|
break;
|
|
}
|
|
}
|
|
|
|
byte priority = request.GetQueryOrForm("forwarderPriority", byte.Parse, byte.MinValue);
|
|
|
|
if (protocol == DnsTransportProtocol.Quic)
|
|
DnsWebService.ValidateQuicSupport();
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsForwarderRecordData(protocol, forwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword, priority));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.APP:
|
|
{
|
|
if (!overwrite)
|
|
{
|
|
IReadOnlyList<DnsResourceRecord> existingRecords = _dnsWebService._dnsServer.AuthZoneManager.GetRecords(zoneInfo.Name, domain, type);
|
|
if (existingRecords.Count > 0)
|
|
throw new DnsWebServiceException("Record already exists. Use overwrite option if you wish to overwrite existing record.");
|
|
}
|
|
|
|
string appName = request.GetQueryOrFormAlt("appName", "value");
|
|
string classPath = request.GetQueryOrForm("classPath");
|
|
string recordData = request.GetQueryOrForm("recordData", "");
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsApplicationRecordData(appName, classPath, recordData));
|
|
|
|
overwrite = true; //force SetRecord
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
string strRData = request.GetQueryOrForm("rdata");
|
|
|
|
byte[] rdata;
|
|
|
|
if (strRData.Contains(':'))
|
|
rdata = strRData.ParseColonHexString();
|
|
else
|
|
rdata = Convert.FromHexString(strRData);
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, DnsResourceRecord.ReadRecordDataFrom(type, rdata));
|
|
}
|
|
break;
|
|
}
|
|
|
|
//update record info
|
|
GenericRecordInfo recordInfo = newRecord.GetAuthGenericRecordInfo();
|
|
|
|
recordInfo.LastModified = DateTime.UtcNow;
|
|
recordInfo.ExpiryTtl = expiryTtl;
|
|
|
|
if (!string.IsNullOrEmpty(comments))
|
|
recordInfo.Comments = comments;
|
|
|
|
//add record
|
|
if (overwrite)
|
|
{
|
|
_dnsWebService._dnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord);
|
|
}
|
|
else
|
|
{
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord))
|
|
throw new DnsWebServiceException("Cannot add record: record already exists.");
|
|
}
|
|
|
|
//additional processing
|
|
if ((type == DnsResourceRecordType.A) || (type == DnsResourceRecordType.AAAA))
|
|
{
|
|
bool updateSvcbHints = request.GetQueryOrForm("updateSvcbHints", bool.Parse, false);
|
|
if (updateSvcbHints)
|
|
UpdateSvcbAutoHints(zoneInfo.Name, domain, type == DnsResourceRecordType.A, type == DnsResourceRecordType.AAAA);
|
|
}
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] New record was added to " + zoneInfo.TypeName + " zone '" + zoneInfo.DisplayName + "' successfully {record: " + newRecord.ToString() + "}");
|
|
|
|
//save zone
|
|
_dnsWebService._dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name);
|
|
|
|
Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
|
|
|
|
jsonWriter.WritePropertyName("zone");
|
|
WriteZoneInfoAsJson(zoneInfo, jsonWriter);
|
|
|
|
jsonWriter.WritePropertyName("addedRecord");
|
|
WriteRecordAsJson(newRecord, jsonWriter, true, null);
|
|
}
|
|
|
|
public void GetRecords(HttpContext context)
|
|
{
|
|
HttpRequest request = context.Request;
|
|
|
|
string domain = request.GetQueryOrForm("domain").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(domain))
|
|
domain = DnsClient.ConvertDomainNameToAscii(domain);
|
|
|
|
string zoneName = request.QueryOrForm("zone");
|
|
if (zoneName is not null)
|
|
{
|
|
zoneName = zoneName.TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
}
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.FindAuthZoneInfo(string.IsNullOrEmpty(zoneName) ? domain : zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + domain);
|
|
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
bool listZone = request.GetQueryOrForm("listZone", bool.Parse, false);
|
|
|
|
List<DnsResourceRecord> records = new List<DnsResourceRecord>();
|
|
|
|
if (listZone)
|
|
_dnsWebService._dnsServer.AuthZoneManager.ListAllZoneRecords(zoneInfo.Name, records);
|
|
else
|
|
_dnsWebService._dnsServer.AuthZoneManager.ListAllRecords(zoneInfo.Name, domain, records);
|
|
|
|
Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
|
|
|
|
jsonWriter.WritePropertyName("zone");
|
|
WriteZoneInfoAsJson(zoneInfo, jsonWriter);
|
|
|
|
WriteRecordsAsJson(records, jsonWriter, true, zoneInfo);
|
|
}
|
|
|
|
public void DeleteRecord(HttpContext context)
|
|
{
|
|
HttpRequest request = context.Request;
|
|
|
|
string domain = request.GetQueryOrForm("domain").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(domain))
|
|
domain = DnsClient.ConvertDomainNameToAscii(domain);
|
|
|
|
string zoneName = request.QueryOrForm("zone");
|
|
if (zoneName is not null)
|
|
{
|
|
zoneName = zoneName.TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
}
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.FindAuthZoneInfo(string.IsNullOrEmpty(zoneName) ? domain : zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + domain);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Delete))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
DnsResourceRecordType type = request.GetQueryOrFormEnum<DnsResourceRecordType>("type");
|
|
switch (type)
|
|
{
|
|
case DnsResourceRecordType.A:
|
|
case DnsResourceRecordType.AAAA:
|
|
{
|
|
IPAddress ipAddress = IPAddress.Parse(request.GetQueryOrFormAlt("ipAddress", "value"));
|
|
|
|
if (type == DnsResourceRecordType.A)
|
|
{
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsARecordData(ipAddress)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
else
|
|
{
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsAAAARecordData(ipAddress)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
|
|
string ptrDomain = Zone.GetReverseZone(ipAddress, type == DnsResourceRecordType.A ? 32 : 128);
|
|
AuthZoneInfo reverseZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.FindAuthZoneInfo(ptrDomain);
|
|
if ((reverseZoneInfo is not null) && !reverseZoneInfo.Internal && ((reverseZoneInfo.Type == AuthZoneType.Primary) || (reverseZoneInfo.Type == AuthZoneType.Forwarder)))
|
|
{
|
|
IReadOnlyList<DnsResourceRecord> ptrRecords = _dnsWebService._dnsServer.AuthZoneManager.GetRecords(reverseZoneInfo.Name, ptrDomain, DnsResourceRecordType.PTR);
|
|
if (ptrRecords.Count > 0)
|
|
{
|
|
foreach (DnsResourceRecord ptrRecord in ptrRecords)
|
|
{
|
|
if ((ptrRecord.RDATA as DnsPTRRecordData).Domain.Equals(domain, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
//delete PTR record and save reverse zone
|
|
_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(reverseZoneInfo.Name, ptrDomain, DnsResourceRecordType.PTR, ptrRecord.RDATA);
|
|
_dnsWebService._dnsServer.AuthZoneManager.SaveZoneFile(reverseZoneInfo.Name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool updateSvcbHints = request.GetQueryOrForm("updateSvcbHints", bool.Parse, false);
|
|
if (updateSvcbHints)
|
|
UpdateSvcbAutoHints(zoneInfo.Name, domain, type == DnsResourceRecordType.A, type == DnsResourceRecordType.AAAA);
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.NS:
|
|
{
|
|
string nameServer = request.GetQueryOrFormAlt("nameServer", "value").TrimEnd('.');
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsNSRecordData(nameServer, false)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.CNAME:
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecords(zoneInfo.Name, domain, type))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
|
|
break;
|
|
|
|
case DnsResourceRecordType.PTR:
|
|
{
|
|
string ptrName = request.GetQueryOrFormAlt("ptrName", "value").TrimEnd('.');
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsPTRRecordData(ptrName)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.MX:
|
|
{
|
|
ushort preference = request.GetQueryOrForm("preference", ushort.Parse);
|
|
string exchange = request.GetQueryOrFormAlt("exchange", "value").TrimEnd('.');
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsMXRecordData(preference, exchange)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.TXT:
|
|
{
|
|
string text = request.GetQueryOrFormAlt("text", "value");
|
|
bool splitText = request.GetQueryOrForm("splitText", bool.Parse, false);
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, splitText ? new DnsTXTRecordData(DecodeCharacterStrings(text)) : new DnsTXTRecordData(text)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.RP:
|
|
{
|
|
string mailbox = request.GetQueryOrForm("mailbox", "").TrimEnd('.');
|
|
string txtDomain = request.GetQueryOrForm("txtDomain", "").TrimEnd('.');
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsRPRecordData(mailbox, txtDomain)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SRV:
|
|
{
|
|
ushort priority = request.GetQueryOrForm("priority", ushort.Parse);
|
|
ushort weight = request.GetQueryOrForm("weight", ushort.Parse);
|
|
ushort port = request.GetQueryOrForm("port", ushort.Parse);
|
|
string target = request.GetQueryOrFormAlt("target", "value").TrimEnd('.');
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsSRVRecordData(priority, weight, port, target)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.NAPTR:
|
|
{
|
|
ushort order = request.GetQueryOrForm("naptrOrder", ushort.Parse);
|
|
ushort preference = request.GetQueryOrForm("naptrPreference", ushort.Parse);
|
|
string flags = request.GetQueryOrForm("naptrFlags", "");
|
|
string services = request.GetQueryOrForm("naptrServices", "");
|
|
string regexp = request.GetQueryOrForm("naptrRegexp", "");
|
|
string replacement = request.GetQueryOrForm("naptrReplacement", "").TrimEnd('.');
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsNAPTRRecordData(order, preference, flags, services, regexp, replacement)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.DNAME:
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecords(zoneInfo.Name, domain, type))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
|
|
break;
|
|
|
|
case DnsResourceRecordType.DS:
|
|
{
|
|
ushort keyTag = request.GetQueryOrForm("keyTag", ushort.Parse);
|
|
DnssecAlgorithm algorithm = Enum.Parse<DnssecAlgorithm>(request.GetQueryOrForm("algorithm").Replace('-', '_'), true);
|
|
DnssecDigestType digestType = Enum.Parse<DnssecDigestType>(request.GetQueryOrForm("digestType").Replace('-', '_'), true);
|
|
byte[] digest = Convert.FromHexString(request.GetQueryOrFormAlt("digest", "value"));
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsDSRecordData(keyTag, algorithm, digestType, digest)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SSHFP:
|
|
{
|
|
DnsSSHFPAlgorithm sshfpAlgorithm = request.GetQueryOrFormEnum<DnsSSHFPAlgorithm>("sshfpAlgorithm");
|
|
DnsSSHFPFingerprintType sshfpFingerprintType = request.GetQueryOrFormEnum<DnsSSHFPFingerprintType>("sshfpFingerprintType");
|
|
byte[] sshfpFingerprint = request.GetQueryOrForm("sshfpFingerprint", Convert.FromHexString);
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsSSHFPRecordData(sshfpAlgorithm, sshfpFingerprintType, sshfpFingerprint)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.TLSA:
|
|
{
|
|
DnsTLSACertificateUsage tlsaCertificateUsage = Enum.Parse<DnsTLSACertificateUsage>(request.GetQueryOrForm("tlsaCertificateUsage").Replace('-', '_'), true);
|
|
DnsTLSASelector tlsaSelector = request.GetQueryOrFormEnum<DnsTLSASelector>("tlsaSelector");
|
|
DnsTLSAMatchingType tlsaMatchingType = Enum.Parse<DnsTLSAMatchingType>(request.GetQueryOrForm("tlsaMatchingType").Replace('-', '_'), true);
|
|
string tlsaCertificateAssociationData = request.GetQueryOrForm("tlsaCertificateAssociationData");
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsTLSARecordData(tlsaCertificateUsage, tlsaSelector, tlsaMatchingType, tlsaCertificateAssociationData)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SVCB:
|
|
case DnsResourceRecordType.HTTPS:
|
|
{
|
|
ushort svcPriority = request.GetQueryOrForm("svcPriority", ushort.Parse);
|
|
string targetName = request.GetQueryOrForm("svcTargetName").TrimEnd('.');
|
|
string strSvcParams = request.GetQueryOrForm("svcParams");
|
|
|
|
Dictionary<DnsSvcParamKey, DnsSvcParamValue> svcParams;
|
|
|
|
if (strSvcParams.Equals("false", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
svcParams = new Dictionary<DnsSvcParamKey, DnsSvcParamValue>(0);
|
|
}
|
|
else
|
|
{
|
|
string[] strSvcParamsParts = strSvcParams.Split('|');
|
|
svcParams = new Dictionary<DnsSvcParamKey, DnsSvcParamValue>(strSvcParamsParts.Length / 2);
|
|
|
|
for (int i = 0; i < strSvcParamsParts.Length; i += 2)
|
|
{
|
|
DnsSvcParamKey svcParamKey = Enum.Parse<DnsSvcParamKey>(strSvcParamsParts[i].Replace('-', '_'), true);
|
|
DnsSvcParamValue svcParamValue = DnsSvcParamValue.Parse(svcParamKey, strSvcParamsParts[i + 1]);
|
|
|
|
svcParams.Add(svcParamKey, svcParamValue);
|
|
}
|
|
}
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsSVCBRecordData(svcPriority, targetName, svcParams)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.URI:
|
|
{
|
|
ushort priority = request.GetQueryOrForm("uriPriority", ushort.Parse);
|
|
ushort weight = request.GetQueryOrForm("uriWeight", ushort.Parse);
|
|
Uri uri = request.GetQueryOrForm("uri", delegate (string value) { return new Uri(value); });
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsURIRecordData(priority, weight, uri)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.CAA:
|
|
{
|
|
byte flags = request.GetQueryOrForm("flags", byte.Parse);
|
|
string tag = request.GetQueryOrForm("tag");
|
|
string value = request.GetQueryOrForm("value");
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsCAARecordData(flags, tag, value)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.ANAME:
|
|
{
|
|
string aname = request.GetQueryOrFormAlt("aname", "value").TrimEnd('.');
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsANAMERecordData(aname)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.FWD:
|
|
{
|
|
DnsTransportProtocol protocol = request.GetQueryOrFormEnum("protocol", DnsTransportProtocol.Udp);
|
|
string forwarder = request.GetQueryOrFormAlt("forwarder", "value");
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, DnsForwarderRecordData.CreatePartialRecordData(protocol, forwarder)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.APP:
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecords(zoneInfo.Name, domain, type))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
|
|
break;
|
|
|
|
default:
|
|
{
|
|
string strRData = request.GetQueryOrForm("rdata", string.Empty);
|
|
|
|
byte[] rdata;
|
|
|
|
if (strRData.Contains(':'))
|
|
rdata = strRData.ParseColonHexString();
|
|
else
|
|
rdata = Convert.FromHexString(strRData);
|
|
|
|
if (!_dnsWebService._dnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsUnknownRecordData(rdata)))
|
|
throw new DnsWebServiceException("Cannot delete record: no such record exists.");
|
|
}
|
|
break;
|
|
}
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Record was deleted from " + zoneInfo.TypeName + " zone '" + zoneInfo.DisplayName + "' successfully {domain: " + domain + "; type: " + type + ";}");
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name);
|
|
}
|
|
|
|
public void UpdateRecord(HttpContext context)
|
|
{
|
|
HttpRequest request = context.Request;
|
|
|
|
string domain = request.GetQueryOrForm("domain").TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(domain))
|
|
domain = DnsClient.ConvertDomainNameToAscii(domain);
|
|
|
|
string zoneName = request.QueryOrForm("zone");
|
|
if (zoneName is not null)
|
|
{
|
|
zoneName = zoneName.TrimEnd('.');
|
|
|
|
if (DnsClient.IsDomainNameUnicode(zoneName))
|
|
zoneName = DnsClient.ConvertDomainNameToAscii(zoneName);
|
|
}
|
|
|
|
AuthZoneInfo zoneInfo = _dnsWebService._dnsServer.AuthZoneManager.FindAuthZoneInfo(string.IsNullOrEmpty(zoneName) ? domain : zoneName);
|
|
if (zoneInfo is null)
|
|
throw new DnsWebServiceException("No such zone was found: " + domain);
|
|
|
|
if (zoneInfo.Internal)
|
|
throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone.");
|
|
|
|
UserSession session = context.GetCurrentSession();
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Modify))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
string newDomain = request.GetQueryOrForm("newDomain", domain).TrimEnd('.');
|
|
uint ttl = request.GetQueryOrForm("ttl", ZoneFile.ParseTtl, _defaultRecordTtl);
|
|
bool disable = request.GetQueryOrForm("disable", bool.Parse, false);
|
|
string comments = request.QueryOrForm("comments");
|
|
uint expiryTtl = request.GetQueryOrForm("expiryTtl", ZoneFile.ParseTtl, 0u);
|
|
DnsResourceRecordType type = request.GetQueryOrFormEnum<DnsResourceRecordType>("type");
|
|
|
|
DnsResourceRecord oldRecord = null;
|
|
DnsResourceRecord newRecord;
|
|
|
|
switch (type)
|
|
{
|
|
case DnsResourceRecordType.A:
|
|
case DnsResourceRecordType.AAAA:
|
|
{
|
|
IPAddress ipAddress = IPAddress.Parse(request.GetQueryOrFormAlt("ipAddress", "value"));
|
|
IPAddress newIpAddress = IPAddress.Parse(request.GetQueryOrFormAlt("newIpAddress", "newValue", ipAddress.ToString()));
|
|
|
|
bool ptr = request.GetQueryOrForm("ptr", bool.Parse, false);
|
|
if (ptr)
|
|
{
|
|
string newPtrDomain = Zone.GetReverseZone(newIpAddress, type == DnsResourceRecordType.A ? 32 : 128);
|
|
|
|
AuthZoneInfo newReverseZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.FindAuthZoneInfo(newPtrDomain);
|
|
if (newReverseZoneInfo is null)
|
|
{
|
|
bool createPtrZone = request.GetQueryOrForm("createPtrZone", bool.Parse, false);
|
|
if (!createPtrZone)
|
|
throw new DnsWebServiceException("No reverse zone available to add PTR record.");
|
|
|
|
string ptrZone = Zone.GetReverseZone(newIpAddress, type == DnsResourceRecordType.A ? 24 : 64);
|
|
|
|
newReverseZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.CreatePrimaryZone(ptrZone);
|
|
if (newReverseZoneInfo is null)
|
|
throw new DnsWebServiceException("Failed to create reverse zone to add PTR record: " + ptrZone);
|
|
|
|
//set permissions
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, newReverseZoneInfo.Name, session.User, PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, newReverseZoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SetPermission(PermissionSection.Zones, newReverseZoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete);
|
|
_dnsWebService._authManager.SaveConfigFile();
|
|
}
|
|
|
|
if (newReverseZoneInfo.Internal)
|
|
throw new DnsWebServiceException("Reverse zone '" + newReverseZoneInfo.DisplayName + "' is an internal zone.");
|
|
|
|
if ((newReverseZoneInfo.Type != AuthZoneType.Primary) && (newReverseZoneInfo.Type != AuthZoneType.Forwarder))
|
|
throw new DnsWebServiceException("Reverse zone '" + newReverseZoneInfo.DisplayName + "' is not a primary or forwarder zone.");
|
|
|
|
string oldPtrDomain = Zone.GetReverseZone(ipAddress, type == DnsResourceRecordType.A ? 32 : 128);
|
|
|
|
AuthZoneInfo oldReverseZoneInfo = _dnsWebService._dnsServer.AuthZoneManager.FindAuthZoneInfo(oldPtrDomain);
|
|
if ((oldReverseZoneInfo is not null) && !oldReverseZoneInfo.Internal && ((oldReverseZoneInfo.Type == AuthZoneType.Primary) || (oldReverseZoneInfo.Type == AuthZoneType.Forwarder)))
|
|
{
|
|
//delete old PTR record if any and save old reverse zone
|
|
_dnsWebService._dnsServer.AuthZoneManager.DeleteRecords(oldReverseZoneInfo.Name, oldPtrDomain, DnsResourceRecordType.PTR);
|
|
_dnsWebService._dnsServer.AuthZoneManager.SaveZoneFile(oldReverseZoneInfo.Name);
|
|
}
|
|
|
|
//add new PTR record and save reverse zone
|
|
DnsResourceRecord ptrRecord = new DnsResourceRecord(newPtrDomain, DnsResourceRecordType.PTR, DnsClass.IN, ttl, new DnsPTRRecordData(domain));
|
|
ptrRecord.GetAuthGenericRecordInfo().LastModified = DateTime.UtcNow;
|
|
ptrRecord.GetAuthGenericRecordInfo().ExpiryTtl = expiryTtl;
|
|
|
|
_dnsWebService._dnsServer.AuthZoneManager.SetRecord(newReverseZoneInfo.Name, ptrRecord);
|
|
_dnsWebService._dnsServer.AuthZoneManager.SaveZoneFile(newReverseZoneInfo.Name);
|
|
}
|
|
|
|
if (type == DnsResourceRecordType.A)
|
|
{
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsARecordData(ipAddress));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsARecordData(newIpAddress));
|
|
}
|
|
else
|
|
{
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsAAAARecordData(ipAddress));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsAAAARecordData(newIpAddress));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.NS:
|
|
{
|
|
string nameServer = request.GetQueryOrFormAlt("nameServer", "value").TrimEnd('.');
|
|
string newNameServer = request.GetQueryOrFormAlt("newNameServer", "newValue", nameServer).TrimEnd('.');
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsNSRecordData(nameServer));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsNSRecordData(newNameServer));
|
|
|
|
if (request.TryGetQueryOrForm("glue", out string glueAddresses))
|
|
{
|
|
if (zoneInfo.Name.Equals(newDomain, StringComparison.OrdinalIgnoreCase) && (newNameServer.Equals(newDomain, StringComparison.OrdinalIgnoreCase) || newNameServer.EndsWith("." + newDomain, StringComparison.OrdinalIgnoreCase)))
|
|
throw new DnsWebServiceException("The zone's own NS records cannot have glue addresses. Please add separate A/AAAA records in the zone instead.");
|
|
|
|
newRecord.SetGlueRecords(glueAddresses);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.CNAME:
|
|
{
|
|
string cname = request.GetQueryOrFormAlt("cname", "value").TrimEnd('.');
|
|
|
|
if (cname.Equals(newDomain, StringComparison.OrdinalIgnoreCase))
|
|
throw new DnsWebServiceException("CNAME domain name cannot be same as that of the record name.");
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsCNAMERecordData(cname));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsCNAMERecordData(cname));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SOA:
|
|
{
|
|
string primaryNameServer = request.GetQueryOrForm("primaryNameServer").TrimEnd('.');
|
|
string responsiblePerson = request.GetQueryOrForm("responsiblePerson").TrimEnd('.');
|
|
uint serial = request.GetQueryOrForm("serial", uint.Parse);
|
|
uint refresh = request.GetQueryOrForm("refresh", ZoneFile.ParseTtl);
|
|
uint retry = request.GetQueryOrForm("retry", ZoneFile.ParseTtl);
|
|
uint expire = request.GetQueryOrForm("expire", ZoneFile.ParseTtl);
|
|
uint minimum = request.GetQueryOrForm("minimum", ZoneFile.ParseTtl);
|
|
|
|
newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsSOARecordData(primaryNameServer, responsiblePerson, serial, refresh, retry, expire, minimum));
|
|
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Forwarder:
|
|
case AuthZoneType.Catalog:
|
|
{
|
|
if (request.TryGetQueryOrForm("useSerialDateScheme", bool.Parse, out bool useSerialDateScheme))
|
|
newRecord.GetAuthSOARecordInfo().UseSoaSerialDateScheme = useSerialDateScheme;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.PTR:
|
|
{
|
|
string ptrName = request.GetQueryOrFormAlt("ptrName", "value").TrimEnd('.');
|
|
string newPtrName = request.GetQueryOrFormAlt("newPtrName", "newValue", ptrName).TrimEnd('.');
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsPTRRecordData(ptrName));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsPTRRecordData(newPtrName));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.MX:
|
|
{
|
|
ushort preference = request.GetQueryOrForm("preference", ushort.Parse);
|
|
ushort newPreference = request.GetQueryOrForm("newPreference", ushort.Parse, preference);
|
|
|
|
string exchange = request.GetQueryOrFormAlt("exchange", "value").TrimEnd('.');
|
|
string newExchange = request.GetQueryOrFormAlt("newExchange", "newValue", exchange).TrimEnd('.');
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsMXRecordData(preference, exchange));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsMXRecordData(newPreference, newExchange));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.TXT:
|
|
{
|
|
string text = request.GetQueryOrFormAlt("text", "value");
|
|
string newText = request.GetQueryOrFormAlt("newText", "newValue", text);
|
|
|
|
bool splitText = request.GetQueryOrForm("splitText", bool.Parse, false);
|
|
bool newSplitText = request.GetQueryOrForm("newSplitText", bool.Parse, splitText);
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, splitText ? new DnsTXTRecordData(DecodeCharacterStrings(text)) : new DnsTXTRecordData(text));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, newSplitText ? new DnsTXTRecordData(DecodeCharacterStrings(newText)) : new DnsTXTRecordData(newText));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.RP:
|
|
{
|
|
string mailbox = request.GetQueryOrForm("mailbox", "").TrimEnd('.');
|
|
string newMailbox = request.GetQueryOrForm("newMailbox", mailbox).TrimEnd('.');
|
|
|
|
string txtDomain = request.GetQueryOrForm("txtDomain", "").TrimEnd('.');
|
|
string newTxtDomain = request.GetQueryOrForm("newTxtDomain", txtDomain).TrimEnd('.');
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsRPRecordData(mailbox, txtDomain));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsRPRecordData(newMailbox, newTxtDomain));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SRV:
|
|
{
|
|
ushort priority = request.GetQueryOrForm("priority", ushort.Parse);
|
|
ushort newPriority = request.GetQueryOrForm("newPriority", ushort.Parse, priority);
|
|
|
|
ushort weight = request.GetQueryOrForm("weight", ushort.Parse);
|
|
ushort newWeight = request.GetQueryOrForm("newWeight", ushort.Parse, weight);
|
|
|
|
ushort port = request.GetQueryOrForm("port", ushort.Parse);
|
|
ushort newPort = request.GetQueryOrForm("newPort", ushort.Parse, port);
|
|
|
|
string target = request.GetQueryOrFormAlt("target", "value").TrimEnd('.');
|
|
string newTarget = request.GetQueryOrFormAlt("newTarget", "newValue", target).TrimEnd('.');
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsSRVRecordData(priority, weight, port, target));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsSRVRecordData(newPriority, newWeight, newPort, newTarget));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.NAPTR:
|
|
{
|
|
ushort order = request.GetQueryOrForm("naptrOrder", ushort.Parse);
|
|
ushort newOrder = request.GetQueryOrForm("naptrNewOrder", ushort.Parse, order);
|
|
|
|
ushort preference = request.GetQueryOrForm("naptrPreference", ushort.Parse);
|
|
ushort newPreference = request.GetQueryOrForm("naptrNewPreference", ushort.Parse, preference);
|
|
|
|
string flags = request.GetQueryOrForm("naptrFlags", "");
|
|
string newFlags = request.GetQueryOrForm("naptrNewFlags", flags);
|
|
|
|
string services = request.GetQueryOrForm("naptrServices", "");
|
|
string newServices = request.GetQueryOrForm("naptrNewServices", services);
|
|
|
|
string regexp = request.GetQueryOrForm("naptrRegexp", "");
|
|
string newRegexp = request.GetQueryOrForm("naptrNewRegexp", regexp);
|
|
|
|
string replacement = request.GetQueryOrForm("naptrReplacement", "").TrimEnd('.');
|
|
string newReplacement = request.GetQueryOrForm("naptrNewReplacement", replacement).TrimEnd('.');
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsNAPTRRecordData(order, preference, flags, services, regexp, replacement));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsNAPTRRecordData(newOrder, newPreference, newFlags, newServices, newRegexp, newReplacement));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.DNAME:
|
|
{
|
|
string dname = request.GetQueryOrFormAlt("dname", "value").TrimEnd('.');
|
|
|
|
if (dname.EndsWith("." + newDomain, StringComparison.OrdinalIgnoreCase))
|
|
throw new DnsWebServiceException("DNAME domain name cannot be a sub domain of the record name.");
|
|
|
|
if (dname.Equals(newDomain, StringComparison.OrdinalIgnoreCase))
|
|
throw new DnsWebServiceException("DNAME domain name cannot be same as that of the record name.");
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsDNAMERecordData(dname));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsDNAMERecordData(dname));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.DS:
|
|
{
|
|
ushort keyTag = request.GetQueryOrForm("keyTag", ushort.Parse);
|
|
ushort newKeyTag = request.GetQueryOrForm("newKeyTag", ushort.Parse, keyTag);
|
|
|
|
DnssecAlgorithm algorithm = Enum.Parse<DnssecAlgorithm>(request.GetQueryOrForm("algorithm").Replace('-', '_'), true);
|
|
DnssecAlgorithm newAlgorithm = Enum.Parse<DnssecAlgorithm>(request.GetQueryOrForm("newAlgorithm", algorithm.ToString()).Replace('-', '_'), true);
|
|
|
|
DnssecDigestType digestType = Enum.Parse<DnssecDigestType>(request.GetQueryOrForm("digestType").Replace('-', '_'), true);
|
|
DnssecDigestType newDigestType = Enum.Parse<DnssecDigestType>(request.GetQueryOrForm("newDigestType", digestType.ToString()).Replace('-', '_'), true);
|
|
|
|
byte[] digest = request.GetQueryOrFormAlt("digest", "value", Convert.FromHexString);
|
|
byte[] newDigest = request.GetQueryOrFormAlt("newDigest", "newValue", Convert.FromHexString, digest);
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsDSRecordData(keyTag, algorithm, digestType, digest));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsDSRecordData(newKeyTag, newAlgorithm, newDigestType, newDigest));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SSHFP:
|
|
{
|
|
DnsSSHFPAlgorithm sshfpAlgorithm = request.GetQueryOrFormEnum<DnsSSHFPAlgorithm>("sshfpAlgorithm");
|
|
DnsSSHFPAlgorithm newSshfpAlgorithm = request.GetQueryOrFormEnum("newSshfpAlgorithm", sshfpAlgorithm);
|
|
|
|
DnsSSHFPFingerprintType sshfpFingerprintType = request.GetQueryOrFormEnum<DnsSSHFPFingerprintType>("sshfpFingerprintType");
|
|
DnsSSHFPFingerprintType newSshfpFingerprintType = request.GetQueryOrFormEnum("newSshfpFingerprintType", sshfpFingerprintType);
|
|
|
|
byte[] sshfpFingerprint = request.GetQueryOrForm("sshfpFingerprint", Convert.FromHexString);
|
|
byte[] newSshfpFingerprint = request.GetQueryOrForm("newSshfpFingerprint", Convert.FromHexString, sshfpFingerprint);
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsSSHFPRecordData(sshfpAlgorithm, sshfpFingerprintType, sshfpFingerprint));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsSSHFPRecordData(newSshfpAlgorithm, newSshfpFingerprintType, newSshfpFingerprint));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.TLSA:
|
|
{
|
|
DnsTLSACertificateUsage tlsaCertificateUsage = Enum.Parse<DnsTLSACertificateUsage>(request.GetQueryOrForm("tlsaCertificateUsage").Replace('-', '_'), true);
|
|
DnsTLSACertificateUsage newTlsaCertificateUsage = Enum.Parse<DnsTLSACertificateUsage>(request.GetQueryOrForm("newTlsaCertificateUsage", tlsaCertificateUsage.ToString()).Replace('-', '_'), true);
|
|
|
|
DnsTLSASelector tlsaSelector = request.GetQueryOrFormEnum<DnsTLSASelector>("tlsaSelector");
|
|
DnsTLSASelector newTlsaSelector = request.GetQueryOrFormEnum("newTlsaSelector", tlsaSelector);
|
|
|
|
DnsTLSAMatchingType tlsaMatchingType = Enum.Parse<DnsTLSAMatchingType>(request.GetQueryOrForm("tlsaMatchingType").Replace('-', '_'), true);
|
|
DnsTLSAMatchingType newTlsaMatchingType = Enum.Parse<DnsTLSAMatchingType>(request.GetQueryOrForm("newTlsaMatchingType", tlsaMatchingType.ToString()).Replace('-', '_'), true);
|
|
|
|
string tlsaCertificateAssociationData = request.GetQueryOrForm("tlsaCertificateAssociationData");
|
|
string newTlsaCertificateAssociationData = request.GetQueryOrForm("newTlsaCertificateAssociationData", tlsaCertificateAssociationData);
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsTLSARecordData(tlsaCertificateUsage, tlsaSelector, tlsaMatchingType, tlsaCertificateAssociationData));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsTLSARecordData(newTlsaCertificateUsage, newTlsaSelector, newTlsaMatchingType, newTlsaCertificateAssociationData));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.SVCB:
|
|
case DnsResourceRecordType.HTTPS:
|
|
{
|
|
ushort svcPriority = request.GetQueryOrForm("svcPriority", ushort.Parse);
|
|
ushort newSvcPriority = request.GetQueryOrForm("newSvcPriority", ushort.Parse, svcPriority);
|
|
|
|
string targetName = request.GetQueryOrForm("svcTargetName").TrimEnd('.');
|
|
string newTargetName = request.GetQueryOrForm("newSvcTargetName", targetName).TrimEnd('.');
|
|
|
|
string strSvcParams = request.GetQueryOrForm("svcParams");
|
|
string strNewSvcParams = request.GetQueryOrForm("newSvcParams", strSvcParams);
|
|
|
|
bool autoIpv4Hint = request.GetQueryOrForm("autoIpv4Hint", bool.Parse, false);
|
|
bool autoIpv6Hint = request.GetQueryOrForm("autoIpv6Hint", bool.Parse, false);
|
|
|
|
Dictionary<DnsSvcParamKey, DnsSvcParamValue> svcParams;
|
|
|
|
if (strSvcParams.Equals("false", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
svcParams = new Dictionary<DnsSvcParamKey, DnsSvcParamValue>(0);
|
|
}
|
|
else
|
|
{
|
|
string[] strSvcParamsParts = strSvcParams.Split('|');
|
|
svcParams = new Dictionary<DnsSvcParamKey, DnsSvcParamValue>(strSvcParamsParts.Length / 2);
|
|
|
|
for (int i = 0; i < strSvcParamsParts.Length; i += 2)
|
|
{
|
|
DnsSvcParamKey svcParamKey = Enum.Parse<DnsSvcParamKey>(strSvcParamsParts[i].Replace('-', '_'), true);
|
|
DnsSvcParamValue svcParamValue = DnsSvcParamValue.Parse(svcParamKey, strSvcParamsParts[i + 1]);
|
|
|
|
svcParams.Add(svcParamKey, svcParamValue);
|
|
}
|
|
}
|
|
|
|
Dictionary<DnsSvcParamKey, DnsSvcParamValue> newSvcParams;
|
|
|
|
if (strNewSvcParams.Equals("false", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
newSvcParams = new Dictionary<DnsSvcParamKey, DnsSvcParamValue>(0);
|
|
}
|
|
else
|
|
{
|
|
string[] strSvcParamsParts = strNewSvcParams.Split('|');
|
|
newSvcParams = new Dictionary<DnsSvcParamKey, DnsSvcParamValue>(strSvcParamsParts.Length / 2);
|
|
|
|
for (int i = 0; i < strSvcParamsParts.Length; i += 2)
|
|
{
|
|
DnsSvcParamKey svcParamKey = Enum.Parse<DnsSvcParamKey>(strSvcParamsParts[i].Replace('-', '_'), true);
|
|
DnsSvcParamValue svcParamValue = DnsSvcParamValue.Parse(svcParamKey, strSvcParamsParts[i + 1]);
|
|
|
|
newSvcParams.Add(svcParamKey, svcParamValue);
|
|
}
|
|
}
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsSVCBRecordData(svcPriority, targetName, svcParams));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsSVCBRecordData(newSvcPriority, newTargetName, newSvcParams));
|
|
|
|
if (autoIpv4Hint)
|
|
newRecord.GetAuthSVCBRecordInfo().AutoIpv4Hint = true;
|
|
|
|
if (autoIpv6Hint)
|
|
newRecord.GetAuthSVCBRecordInfo().AutoIpv6Hint = true;
|
|
|
|
if (autoIpv4Hint || autoIpv6Hint)
|
|
ResolveSvcbAutoHints(zoneInfo.Name, newRecord, autoIpv4Hint, autoIpv6Hint, newSvcParams);
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.URI:
|
|
{
|
|
ushort priority = request.GetQueryOrForm("uriPriority", ushort.Parse);
|
|
ushort newPriority = request.GetQueryOrForm("newUriPriority", ushort.Parse, priority);
|
|
|
|
ushort weight = request.GetQueryOrForm("uriWeight", ushort.Parse);
|
|
ushort newWeight = request.GetQueryOrForm("newUriWeight", ushort.Parse, weight);
|
|
|
|
Uri uri = request.GetQueryOrForm("uri", delegate (string value) { return new Uri(value); });
|
|
Uri newUri = request.GetQueryOrForm("newUri", delegate (string value) { return new Uri(value); }, uri);
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsURIRecordData(priority, weight, uri));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsURIRecordData(newPriority, newWeight, newUri));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.CAA:
|
|
{
|
|
byte flags = request.GetQueryOrForm("flags", byte.Parse);
|
|
byte newFlags = request.GetQueryOrForm("newFlags", byte.Parse, flags);
|
|
|
|
string tag = request.GetQueryOrForm("tag");
|
|
string newTag = request.GetQueryOrForm("newTag", tag);
|
|
|
|
string value = request.GetQueryOrForm("value");
|
|
string newValue = request.GetQueryOrForm("newValue", value);
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsCAARecordData(flags, tag, value));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsCAARecordData(newFlags, newTag, newValue));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.ANAME:
|
|
{
|
|
string aname = request.GetQueryOrFormAlt("aname", "value").TrimEnd('.');
|
|
string newAName = request.GetQueryOrFormAlt("newAName", "newValue", aname).TrimEnd('.');
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsANAMERecordData(aname));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsANAMERecordData(newAName));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.FWD:
|
|
{
|
|
DnsTransportProtocol protocol = request.GetQueryOrFormEnum("protocol", DnsTransportProtocol.Udp);
|
|
DnsTransportProtocol newProtocol = request.GetQueryOrFormEnum("newProtocol", protocol);
|
|
|
|
string forwarder = request.GetQueryOrFormAlt("forwarder", "value");
|
|
string newForwarder = request.GetQueryOrFormAlt("newForwarder", "newValue", forwarder);
|
|
|
|
bool dnssecValidation = request.GetQueryOrForm("dnssecValidation", bool.Parse, false);
|
|
|
|
DnsForwarderRecordProxyType proxyType = DnsForwarderRecordProxyType.DefaultProxy;
|
|
string proxyAddress = null;
|
|
ushort proxyPort = 0;
|
|
string proxyUsername = null;
|
|
string proxyPassword = null;
|
|
|
|
if (!newForwarder.Equals("this-server"))
|
|
{
|
|
proxyType = request.GetQueryOrFormEnum("proxyType", DnsForwarderRecordProxyType.DefaultProxy);
|
|
switch (proxyType)
|
|
{
|
|
case DnsForwarderRecordProxyType.Http:
|
|
case DnsForwarderRecordProxyType.Socks5:
|
|
proxyAddress = request.GetQueryOrForm("proxyAddress");
|
|
proxyPort = request.GetQueryOrForm("proxyPort", ushort.Parse);
|
|
proxyUsername = request.QueryOrForm("proxyUsername");
|
|
proxyPassword = request.QueryOrForm("proxyPassword");
|
|
break;
|
|
}
|
|
}
|
|
|
|
byte priority = request.GetQueryOrForm("forwarderPriority", byte.Parse, byte.MinValue);
|
|
|
|
if (newProtocol == DnsTransportProtocol.Quic)
|
|
DnsWebService.ValidateQuicSupport();
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, DnsForwarderRecordData.CreatePartialRecordData(protocol, forwarder));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, 0, new DnsForwarderRecordData(newProtocol, newForwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword, priority));
|
|
}
|
|
break;
|
|
|
|
case DnsResourceRecordType.APP:
|
|
{
|
|
string appName = request.GetQueryOrFormAlt("appName", "value");
|
|
string classPath = request.GetQueryOrForm("classPath");
|
|
string recordData = request.GetQueryOrForm("recordData", "");
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsApplicationRecordData(appName, classPath, recordData));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsApplicationRecordData(appName, classPath, recordData));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
string strRData = request.GetQueryOrForm("rdata");
|
|
string strNewRData = request.GetQueryOrForm("newRData", strRData);
|
|
|
|
byte[] rdata;
|
|
|
|
if (strRData.Contains(':'))
|
|
rdata = strRData.ParseColonHexString();
|
|
else
|
|
rdata = Convert.FromHexString(strRData);
|
|
|
|
byte[] newRData;
|
|
|
|
if (strNewRData.Contains(':'))
|
|
newRData = strNewRData.ParseColonHexString();
|
|
else
|
|
newRData = Convert.FromHexString(strNewRData);
|
|
|
|
oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsUnknownRecordData(rdata));
|
|
newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsUnknownRecordData(newRData));
|
|
}
|
|
break;
|
|
}
|
|
|
|
//update record info
|
|
GenericRecordInfo recordInfo = newRecord.GetAuthGenericRecordInfo();
|
|
|
|
recordInfo.LastModified = DateTime.UtcNow;
|
|
recordInfo.ExpiryTtl = expiryTtl;
|
|
recordInfo.Disabled = disable;
|
|
recordInfo.Comments = comments;
|
|
|
|
//update record
|
|
if (type == DnsResourceRecordType.SOA)
|
|
{
|
|
//special SOA case
|
|
switch (zoneInfo.Type)
|
|
{
|
|
case AuthZoneType.Primary:
|
|
case AuthZoneType.Forwarder:
|
|
case AuthZoneType.Catalog:
|
|
_dnsWebService._dnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord);
|
|
break;
|
|
}
|
|
|
|
//get updated record to return json
|
|
newRecord = zoneInfo.ApexZone.GetRecords(DnsResourceRecordType.SOA)[0];
|
|
}
|
|
else
|
|
{
|
|
_dnsWebService._dnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord);
|
|
}
|
|
|
|
//additional processing
|
|
if ((type == DnsResourceRecordType.A) || (type == DnsResourceRecordType.AAAA))
|
|
{
|
|
bool updateSvcbHints = request.GetQueryOrForm("updateSvcbHints", bool.Parse, false);
|
|
if (updateSvcbHints)
|
|
UpdateSvcbAutoHints(zoneInfo.Name, newDomain, type == DnsResourceRecordType.A, type == DnsResourceRecordType.AAAA);
|
|
}
|
|
|
|
_dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Record was updated for " + zoneInfo.TypeName + " zone '" + zoneInfo.DisplayName + "' successfully {" + (oldRecord is null ? "" : "oldRecord: " + oldRecord.ToString() + "; ") + "newRecord: " + newRecord.ToString() + "}");
|
|
|
|
//save zone
|
|
_dnsWebService._dnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name);
|
|
|
|
Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
|
|
|
|
jsonWriter.WritePropertyName("zone");
|
|
WriteZoneInfoAsJson(zoneInfo, jsonWriter);
|
|
|
|
jsonWriter.WritePropertyName("updatedRecord");
|
|
WriteRecordAsJson(newRecord, jsonWriter, true, zoneInfo);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region properties
|
|
|
|
public uint DefaultRecordTtl
|
|
{
|
|
get { return _defaultRecordTtl; }
|
|
set { _defaultRecordTtl = value; }
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
}
|