/* Technitium DNS Server Copyright (C) 2024 Shreyas Zare (shreyas@technitium.com) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ using DnsServerCore.Auth; using DnsServerCore.Dns.Dnssec; using DnsServerCore.Dns.ResourceRecords; 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 { 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 records, Utf8JsonWriter jsonWriter, bool authoritativeZoneRecords, AuthZoneInfo zoneInfo = null) { if (records is null) { jsonWriter.WritePropertyName("records"); jsonWriter.WriteStartArray(); jsonWriter.WriteEndArray(); return; } records.Sort(); Dictionary>> groupedByDomainRecords = DnsResourceRecord.GroupRecords(records); jsonWriter.WritePropertyName("records"); jsonWriter.WriteStartArray(); foreach (KeyValuePair>> groupedByTypeRecords in groupedByDomainRecords) { foreach (KeyValuePair> 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.WriteBoolean("disabled", authRecordInfo.Disabled); string comments = authRecordInfo.Comments; if (!string.IsNullOrEmpty(comments)) jsonWriter.WriteString("comments", comments); } else { if (record.IsStale) jsonWriter.WriteString("ttl", "0 (0 sec)"); else jsonWriter.WriteString("ttl", record.TTL + " (" + WebUtilities.GetFormattedTime((int)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); if (!authoritativeZoneRecords) { if (rdata.IsParentSideTtlSet) jsonWriter.WriteString("parentSideTtl", rdata.ParentSideTtl + " (" + WebUtilities.GetFormattedTime((int)rdata.ParentSideTtl) + ")"); } } 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); jsonWriter.WriteNumber("refresh", rdata.Refresh); jsonWriter.WriteNumber("retry", rdata.Retry); jsonWriter.WriteNumber("expire", rdata.Expire); jsonWriter.WriteNumber("minimum", 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.WriteString("digestType", rdata.DigestType.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.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("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.WriteString("publicKey", rdata.PublicKey.ToString()); jsonWriter.WriteNumber("computedKeyTag", rdata.ComputedKeyTag); if (authoritativeZoneRecords) { if ((zoneInfo is not null) && (zoneInfo.Type == AuthZoneType.Primary)) { IReadOnlyCollection 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.KeyType == DnssecPrivateKeyType.KeySigningKey) && (dnssecPrivateKey.State == DnssecPrivateKeyState.Published)) jsonWriter.WriteString("dnsKeyStateReadyBy", (zoneInfo.ApexZone as PrimaryZone).GetKskDnsKeyStateReadyBy(dnssecPrivateKey)); 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 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 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); } else { CacheRecordInfo cacheRecordInfo = record.GetCacheRecordInfo(); IReadOnlyList 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 rrsigRecords = cacheRecordInfo.RRSIGRecords; IReadOnlyList 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 svcParams, IReadOnlyCollection 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 svcParams, IReadOnlyCollection importRecords = null) { if (resolveIpv4Hint) { List ipv4Hint = new List(); IReadOnlyList 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 ipv6Hint = new List(); IReadOnlyList 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 allSvcbRecords = new List(); _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 newSvcParams = new Dictionary(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); } } } } #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 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 zoneInfoPageList = new List(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 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); 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: { 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(); 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.CreateForwarderZone(zoneName, forwarderProtocol, forwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword, null); 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); 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."); bool overwrite = request.GetQueryOrForm("overwrite", bool.Parse, true); bool overwriteSoaSerial = request.GetQueryOrForm("overwriteSoaSerial", bool.Parse, false); TextReader textReader; 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); } using TextReader zoneReader = textReader; IReadOnlyCollection records = await ZoneFile.ReadZoneFileFromAsync(zoneReader, zoneInfo.Name, _dnsWebService._zonesApi.DefaultRecordTtl); List newRecords = new List(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."); switch (record.Type) { case DnsResourceRecordType.DNSKEY: case DnsResourceRecordType.RRSIG: case DnsResourceRecordType.NSEC: case DnsResourceRecordType.NSEC3: case DnsResourceRecordType.NSEC3PARAM: continue; //skip DNSSEC records 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 svcParams = new Dictionary(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(zoneInfo.Name, 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; } } _dnsWebService.DnsServer.AuthZoneManager.ImportRecords(zoneInfo.Name, newRecords, overwrite, overwriteSoaSerial); _dnsWebService._log.Write(context.GetRemoteEndPoint(_dnsWebService._webServiceRealIpHeader), "[" + session.User.Username + "] Total " + newRecords.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 records = new List(); _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 userPermission in sourceZonePermissions.UserPermissions) _dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, userPermission.Key, userPermission.Value); foreach (KeyValuePair 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("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"); uint dnsKeyTtl = request.GetQueryOrForm("dnsKeyTtl", uint.Parse, 24 * 60 * 60); ushort zskRolloverDays = request.GetQueryOrForm("zskRolloverDays", ushort.Parse, 30); 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("iterations", ushort.Parse, 0); saltLength = request.GetQueryOrForm("saltLength", byte.Parse, 0); } switch (algorithm.ToUpper()) { case "RSA": string hashAlgorithm = request.GetQueryOrForm("hashAlgorithm"); int kskKeySize = request.GetQueryOrForm("kskKeySize", int.Parse); int zskKeySize = request.GetQueryOrForm("zskKeySize", int.Parse); if (useNSEC3) _dnsWebService.DnsServer.AuthZoneManager.SignPrimaryZoneWithRsaNSEC3(zoneName, hashAlgorithm, kskKeySize, zskKeySize, iterations, saltLength, dnsKeyTtl, zskRolloverDays); else _dnsWebService.DnsServer.AuthZoneManager.SignPrimaryZoneWithRsaNSEC(zoneName, hashAlgorithm, kskKeySize, zskKeySize, dnsKeyTtl, zskRolloverDays); break; case "ECDSA": string curve = request.GetQueryOrForm("curve"); if (useNSEC3) _dnsWebService.DnsServer.AuthZoneManager.SignPrimaryZoneWithEcdsaNSEC3(zoneName, curve, iterations, saltLength, dnsKeyTtl, zskRolloverDays); else _dnsWebService.DnsServer.AuthZoneManager.SignPrimaryZoneWithEcdsaNSEC(zoneName, curve, dnsKeyTtl, zskRolloverDays); break; default: throw new NotSupportedException("Algorithm is not supported: " + algorithm); } _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 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 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", (zoneInfo.ApexZone as PrimaryZone).GetKskDnsKeyStateReadyBy(dnssecPrivateKey)); break; } } } jsonWriter.WriteString("algorithm", rdata.Algorithm.ToString()); jsonWriter.WriteString("publicKey", rdata.PublicKey.ToString()); jsonWriter.WritePropertyName("digests"); 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(); 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 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 dnssecPrivateKeys = zoneInfo.DnssecPrivateKeys; if (dnssecPrivateKeys is not null) { List sortedDnssecPrivateKey = new List(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.WriteString("state", dnssecPrivateKey.State.ToString()); jsonWriter.WriteString("stateChangedOn", dnssecPrivateKey.StateChangedOn); if ((dnssecPrivateKey.KeyType == DnssecPrivateKeyType.KeySigningKey) && (dnssecPrivateKey.State == DnssecPrivateKeyState.Published)) jsonWriter.WriteString("stateReadyBy", (zoneInfo.ApexZone as PrimaryZone).GetKskDnsKeyStateReadyBy(dnssecPrivateKey)); 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("iterations", ushort.Parse, 0); byte saltLength = request.GetQueryOrForm("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("iterations", ushort.Parse, 0); byte saltLength = request.GetQueryOrForm("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", uint.Parse); _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 GenerateAndAddPrimaryZoneDnssecPrivateKey(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("keyType"); ushort rolloverDays = request.GetQueryOrForm("rolloverDays", ushort.Parse, (ushort)(keyType == DnssecPrivateKeyType.ZoneSigningKey ? 30 : 0)); string algorithm = request.GetQueryOrForm("algorithm"); switch (algorithm.ToUpper()) { case "RSA": string hashAlgorithm = request.GetQueryOrForm("hashAlgorithm"); int keySize = request.GetQueryOrForm("keySize", int.Parse); _dnsWebService.DnsServer.AuthZoneManager.GenerateAndAddPrimaryZoneDnssecRsaPrivateKey(zoneName, keyType, hashAlgorithm, keySize, rolloverDays); break; case "ECDSA": string curve = request.GetQueryOrForm("curve"); _dnsWebService.DnsServer.AuthZoneManager.GenerateAndAddPrimaryZoneDnssecEcdsaPrivateKey(zoneName, keyType, curve, rolloverDays); 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 void RetirePrimaryZoneDnsKey(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.RetirePrimaryZoneDnsKey(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 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 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>> updateSecurityPolicy in zoneInfo.UpdateSecurityPolicies) { foreach (KeyValuePair> 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 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 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 zoneTransferTsigKeyNames = new Dictionary(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>> updateSecurityPolicies = new Dictionary>>(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> policyMap)) { policyMap = new Dictionary>(); updateSecurityPolicies.Add(tsigKeyName, policyMap); } if (!policyMap.TryGetValue(domain, out IReadOnlyList types)) { types = new List(); (policyMap as Dictionary>).Add(domain, types); } foreach (string strType in strTypes.Split(_commaSpaceSeparator, StringSplitOptions.RemoveEmptyEntries)) (types as List).Add(Enum.Parse(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("type"); uint ttl = request.GetQueryOrForm("ttl", uint.Parse, _defaultRecordTtl); bool overwrite = request.GetQueryOrForm("overwrite", bool.Parse, false); string comments = request.QueryOrForm("comments"); uint expiryTtl = request.GetQueryOrForm("expiryTtl", uint.Parse, 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)) newRecord.SetGlueRecords(glueAddresses); } break; case DnsResourceRecordType.CNAME: { if (!overwrite) { IReadOnlyList 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 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(request.GetQueryOrForm("algorithm").Replace('-', '_'), true); DnssecDigestType digestType = Enum.Parse(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("sshfpAlgorithm"); DnsSSHFPFingerprintType sshfpFingerprintType = request.GetQueryOrFormEnum("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(request.GetQueryOrForm("tlsaCertificateUsage").Replace('-', '_'), true); DnsTLSASelector tlsaSelector = request.GetQueryOrFormEnum("tlsaSelector"); DnsTLSAMatchingType tlsaMatchingType = Enum.Parse(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 svcParams; if (strSvcParams.Equals("false", StringComparison.OrdinalIgnoreCase)) { svcParams = new Dictionary(0); } else { string[] strSvcParamsParts = strSvcParams.Split('|'); svcParams = new Dictionary(strSvcParamsParts.Length / 2); for (int i = 0; i < strSvcParamsParts.Length; i += 2) { DnsSvcParamKey svcParamKey = Enum.Parse(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 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 _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); //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 records = new List(); 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("type"); switch (type) { case DnsResourceRecordType.A: case DnsResourceRecordType.AAAA: { IPAddress ipAddress = IPAddress.Parse(request.GetQueryOrFormAlt("ipAddress", "value")); if (type == DnsResourceRecordType.A) _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsARecordData(ipAddress)); else _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsAAAARecordData(ipAddress)); 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 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('.'); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsNSRecordData(nameServer, false)); } break; case DnsResourceRecordType.CNAME: _dnsWebService.DnsServer.AuthZoneManager.DeleteRecords(zoneInfo.Name, domain, type); break; case DnsResourceRecordType.PTR: { string ptrName = request.GetQueryOrFormAlt("ptrName", "value").TrimEnd('.'); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsPTRRecordData(ptrName)); } break; case DnsResourceRecordType.MX: { ushort preference = request.GetQueryOrForm("preference", ushort.Parse); string exchange = request.GetQueryOrFormAlt("exchange", "value").TrimEnd('.'); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsMXRecordData(preference, exchange)); } break; case DnsResourceRecordType.TXT: { string text = request.GetQueryOrFormAlt("text", "value"); bool splitText = request.GetQueryOrForm("splitText", bool.Parse, false); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, splitText ? new DnsTXTRecordData(DecodeCharacterStrings(text)) : new DnsTXTRecordData(text)); } break; case DnsResourceRecordType.RP: { string mailbox = request.GetQueryOrForm("mailbox", "").TrimEnd('.'); string txtDomain = request.GetQueryOrForm("txtDomain", "").TrimEnd('.'); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, 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('.'); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, 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('.'); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsNAPTRRecordData(order, preference, flags, services, regexp, replacement)); } break; case DnsResourceRecordType.DNAME: _dnsWebService.DnsServer.AuthZoneManager.DeleteRecords(zoneInfo.Name, domain, type); break; case DnsResourceRecordType.DS: { ushort keyTag = request.GetQueryOrForm("keyTag", ushort.Parse); DnssecAlgorithm algorithm = Enum.Parse(request.GetQueryOrForm("algorithm").Replace('-', '_'), true); DnssecDigestType digestType = Enum.Parse(request.GetQueryOrForm("digestType").Replace('-', '_'), true); byte[] digest = Convert.FromHexString(request.GetQueryOrFormAlt("digest", "value")); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsDSRecordData(keyTag, algorithm, digestType, digest)); } break; case DnsResourceRecordType.SSHFP: { DnsSSHFPAlgorithm sshfpAlgorithm = request.GetQueryOrFormEnum("sshfpAlgorithm"); DnsSSHFPFingerprintType sshfpFingerprintType = request.GetQueryOrFormEnum("sshfpFingerprintType"); byte[] sshfpFingerprint = request.GetQueryOrForm("sshfpFingerprint", Convert.FromHexString); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsSSHFPRecordData(sshfpAlgorithm, sshfpFingerprintType, sshfpFingerprint)); } break; case DnsResourceRecordType.TLSA: { DnsTLSACertificateUsage tlsaCertificateUsage = Enum.Parse(request.GetQueryOrForm("tlsaCertificateUsage").Replace('-', '_'), true); DnsTLSASelector tlsaSelector = request.GetQueryOrFormEnum("tlsaSelector"); DnsTLSAMatchingType tlsaMatchingType = Enum.Parse(request.GetQueryOrForm("tlsaMatchingType").Replace('-', '_'), true); string tlsaCertificateAssociationData = request.GetQueryOrForm("tlsaCertificateAssociationData"); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, 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"); Dictionary svcParams; if (strSvcParams.Equals("false", StringComparison.OrdinalIgnoreCase)) { svcParams = new Dictionary(0); } else { string[] strSvcParamsParts = strSvcParams.Split('|'); svcParams = new Dictionary(strSvcParamsParts.Length / 2); for (int i = 0; i < strSvcParamsParts.Length; i += 2) { DnsSvcParamKey svcParamKey = Enum.Parse(strSvcParamsParts[i].Replace('-', '_'), true); DnsSvcParamValue svcParamValue = DnsSvcParamValue.Parse(svcParamKey, strSvcParamsParts[i + 1]); svcParams.Add(svcParamKey, svcParamValue); } } _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsSVCBRecordData(svcPriority, targetName, 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); }); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, 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"); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsCAARecordData(flags, tag, value)); } break; case DnsResourceRecordType.ANAME: { string aname = request.GetQueryOrFormAlt("aname", "value").TrimEnd('.'); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsANAMERecordData(aname)); } break; case DnsResourceRecordType.FWD: { DnsTransportProtocol protocol = request.GetQueryOrFormEnum("protocol", DnsTransportProtocol.Udp); string forwarder = request.GetQueryOrFormAlt("forwarder", "value"); _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, DnsForwarderRecordData.CreatePartialRecordData(protocol, forwarder)); } break; case DnsResourceRecordType.APP: _dnsWebService.DnsServer.AuthZoneManager.DeleteRecords(zoneInfo.Name, domain, type); 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("Failed to delete the record."); } 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", uint.Parse, _defaultRecordTtl); bool disable = request.GetQueryOrForm("disable", bool.Parse, false); string comments = request.QueryOrForm("comments"); uint expiryTtl = request.GetQueryOrForm("expiryTtl", uint.Parse, 0u); DnsResourceRecordType type = request.GetQueryOrFormEnum("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)) 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", uint.Parse); uint retry = request.GetQueryOrForm("retry", uint.Parse); uint expire = request.GetQueryOrForm("expire", uint.Parse); uint minimum = request.GetQueryOrForm("minimum", uint.Parse); 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(request.GetQueryOrForm("algorithm").Replace('-', '_'), true); DnssecAlgorithm newAlgorithm = Enum.Parse(request.GetQueryOrForm("newAlgorithm", algorithm.ToString()).Replace('-', '_'), true); DnssecDigestType digestType = Enum.Parse(request.GetQueryOrForm("digestType").Replace('-', '_'), true); DnssecDigestType newDigestType = Enum.Parse(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("sshfpAlgorithm"); DnsSSHFPAlgorithm newSshfpAlgorithm = request.GetQueryOrFormEnum("newSshfpAlgorithm", sshfpAlgorithm); DnsSSHFPFingerprintType sshfpFingerprintType = request.GetQueryOrFormEnum("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(request.GetQueryOrForm("tlsaCertificateUsage").Replace('-', '_'), true); DnsTLSACertificateUsage newTlsaCertificateUsage = Enum.Parse(request.GetQueryOrForm("newTlsaCertificateUsage", tlsaCertificateUsage.ToString()).Replace('-', '_'), true); DnsTLSASelector tlsaSelector = request.GetQueryOrFormEnum("tlsaSelector"); DnsTLSASelector newTlsaSelector = request.GetQueryOrFormEnum("newTlsaSelector", tlsaSelector); DnsTLSAMatchingType tlsaMatchingType = Enum.Parse(request.GetQueryOrForm("tlsaMatchingType").Replace('-', '_'), true); DnsTLSAMatchingType newTlsaMatchingType = Enum.Parse(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 svcParams; if (strSvcParams.Equals("false", StringComparison.OrdinalIgnoreCase)) { svcParams = new Dictionary(0); } else { string[] strSvcParamsParts = strSvcParams.Split('|'); svcParams = new Dictionary(strSvcParamsParts.Length / 2); for (int i = 0; i < strSvcParamsParts.Length; i += 2) { DnsSvcParamKey svcParamKey = Enum.Parse(strSvcParamsParts[i].Replace('-', '_'), true); DnsSvcParamValue svcParamValue = DnsSvcParamValue.Parse(svcParamKey, strSvcParamsParts[i + 1]); svcParams.Add(svcParamKey, svcParamValue); } } Dictionary newSvcParams; if (strNewSvcParams.Equals("false", StringComparison.OrdinalIgnoreCase)) { newSvcParams = new Dictionary(0); } else { string[] strSvcParamsParts = strNewSvcParams.Split('|'); newSvcParams = new Dictionary(strSvcParamsParts.Length / 2); for (int i = 0; i < strSvcParamsParts.Length; i += 2) { DnsSvcParamKey svcParamKey = Enum.Parse(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 } }