From 86653de0cd26321c4cd1f80b5e62eafa6f895eda Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 15 Sep 2024 15:14:12 +0530 Subject: [PATCH] WebServiceZonesApi: updated API to support new record types and features. --- DnsServerCore/WebServiceZonesApi.cs | 1408 ++++++++++++++++----------- 1 file changed, 813 insertions(+), 595 deletions(-) diff --git a/DnsServerCore/WebServiceZonesApi.cs b/DnsServerCore/WebServiceZonesApi.cs index 56af72d8..d67b5ff7 100644 --- a/DnsServerCore/WebServiceZonesApi.cs +++ b/DnsServerCore/WebServiceZonesApi.cs @@ -209,45 +209,16 @@ namespace DnsServerCore jsonWriter.WriteString("data", record.RDATA.ToString()); } - if (authoritativeZoneRecords) + if (authoritativeZoneRecords && (zoneInfo is not null)) { - SOARecordInfo authRecordInfo = record.GetAuthSOARecordInfo(); - - if (zoneInfo is not null) + switch (zoneInfo.Type) { - switch (zoneInfo.Type) - { - case AuthZoneType.Primary: - jsonWriter.WriteBoolean("useSerialDateScheme", authRecordInfo.UseSoaSerialDateScheme); - break; - - case AuthZoneType.Secondary: - jsonWriter.WriteBoolean("validateZone", authRecordInfo.ValidateZone); - break; - } + case AuthZoneType.Primary: + case AuthZoneType.Forwarder: + case AuthZoneType.Catalog: + jsonWriter.WriteBoolean("useSerialDateScheme", record.GetAuthSOARecordInfo().UseSoaSerialDateScheme); + break; } - - IReadOnlyList primaryNameServers = authRecordInfo.PrimaryNameServers; - if (primaryNameServers is not null) - { - string primaryAddresses = null; - - foreach (NameServerAddress primaryNameServer in primaryNameServers) - { - if (primaryAddresses == null) - primaryAddresses = primaryNameServer.OriginalAddress; - else - primaryAddresses = primaryAddresses + ", " + primaryNameServer.OriginalAddress; - } - - jsonWriter.WriteString("primaryAddresses", primaryAddresses); - } - - if (authRecordInfo.ZoneTransferProtocol != DnsTransportProtocol.Udp) - jsonWriter.WriteString("zoneTransferProtocol", authRecordInfo.ZoneTransferProtocol.ToString()); - - if (!string.IsNullOrEmpty(authRecordInfo.TsigKeyName)) - jsonWriter.WriteString("tsigKeyName", authRecordInfo.TsigKeyName); } } break; @@ -309,6 +280,24 @@ namespace DnsServerCore } 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) @@ -382,6 +371,34 @@ namespace DnsServerCore } 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) @@ -615,7 +632,7 @@ namespace DnsServerCore jsonWriter.WriteStartObject(); foreach (KeyValuePair svcParam in rdata.SvcParams) - jsonWriter.WriteString(svcParam.Key.ToString().ToLower().Replace('_', '-'), svcParam.Value.ToString()); + jsonWriter.WriteString(svcParam.Key.ToString().ToLowerInvariant().Replace('_', '-'), svcParam.Value.ToString()); jsonWriter.WriteEndObject(); @@ -690,6 +707,7 @@ namespace DnsServerCore { 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()); @@ -775,6 +793,8 @@ namespace DnsServerCore } jsonWriter.WriteString("lastUsedOn", authRecordInfo.LastUsedOn); + jsonWriter.WriteString("lastModified", authRecordInfo.LastModified); + jsonWriter.WriteNumber("expiryTtl", authRecordInfo.ExpiryTtl); } else { @@ -819,6 +839,21 @@ namespace DnsServerCore 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) { @@ -849,14 +884,61 @@ namespace DnsServerCore 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); - jsonWriter.WriteString("dnssecStatus", zoneInfo.DnssecStatus.ToString()); - jsonWriter.WriteNumber("soaSerial", (zoneInfo.GetApexRecords(DnsResourceRecordType.SOA)[0].RDATA as DnsSOARecordData).Serial); + 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; @@ -871,43 +953,9 @@ namespace DnsServerCore jsonWriter.WriteEndArray(); } - - break; - - case AuthZoneType.Secondary: - jsonWriter.WriteString("dnssecStatus", zoneInfo.DnssecStatus.ToString()); - jsonWriter.WriteNumber("soaSerial", (zoneInfo.GetApexRecords(DnsResourceRecordType.SOA)[0].RDATA as DnsSOARecordData).Serial); - jsonWriter.WriteString("expiry", zoneInfo.Expiry); - jsonWriter.WriteBoolean("isExpired", zoneInfo.IsExpired); - jsonWriter.WriteBoolean("validationFailed", zoneInfo.ValidationFailed); - jsonWriter.WriteBoolean("syncFailed", zoneInfo.SyncFailed); - - { - 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; - - case AuthZoneType.Stub: - jsonWriter.WriteNumber("soaSerial", (zoneInfo.GetApexRecords(DnsResourceRecordType.SOA)[0].RDATA as DnsSOARecordData).Serial); - jsonWriter.WriteString("expiry", zoneInfo.Expiry); - jsonWriter.WriteBoolean("isExpired", zoneInfo.IsExpired); - jsonWriter.WriteBoolean("syncFailed", zoneInfo.SyncFailed); break; } - jsonWriter.WriteString("lastModified", zoneInfo.LastModified); - jsonWriter.WriteBoolean("disabled", zoneInfo.Disabled); - jsonWriter.WriteEndObject(); } @@ -1143,6 +1191,33 @@ namespace DnsServerCore 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 catalogZones = _dnsWebService.DnsServer.AuthZoneManager.GetAllCatalogZones(); + + jsonWriter.WritePropertyName("catalogZoneNames"); + jsonWriter.WriteStartArray(); + + foreach (AuthZoneInfo catalogZone in catalogZones) + { + if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, catalogZone.Name, session.User, PermissionFlag.View)) + continue; + + if (catalogZone.Disabled) + continue; + + jsonWriter.WriteStringValue(catalogZone.Name); + } + + jsonWriter.WriteEndArray(); + } + public async Task CreateZoneAsync(HttpContext context) { UserSession session = context.GetCurrentSession(); @@ -1158,7 +1233,7 @@ namespace DnsServerCore if (IPAddress.TryParse(zoneName, out IPAddress ipAddress)) { - zoneName = ipAddress.GetReverseDomain().ToLower(); + zoneName = ipAddress.GetReverseDomain().ToLowerInvariant(); } else if (zoneName.Contains('/')) { @@ -1182,8 +1257,18 @@ namespace DnsServerCore case AuthZoneType.Primary: { bool useSoaSerialDateScheme = request.GetQueryOrForm("useSoaSerialDateScheme", bool.Parse, _dnsWebService.DnsServer.AuthZoneManager.UseSoaSerialDateScheme); + string catalogZoneName = request.GetQueryOrForm("catalog", null); - zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CreatePrimaryZone(zoneName, _dnsWebService.DnsServer.ServerDomain, false, useSoaSerialDateScheme); + AuthZoneInfo catalogZoneInfo = null; + + if (catalogZoneName is not null) + { + catalogZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(catalogZoneName); + if (catalogZoneInfo is null) + throw new DnsServerException("No such Catalog zone was found: " + catalogZoneName); + } + + zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CreatePrimaryZone(zoneName, useSoaSerialDateScheme); if (zoneInfo is null) throw new DnsWebServiceException("Zone already exists: " + zoneName); @@ -1193,22 +1278,25 @@ namespace DnsServerCore _dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); _dnsWebService._authManager.SaveConfigFile(); - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Authoritative primary zone was created: " + zoneName); - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); + //add membership for catalog zone + if (catalogZoneInfo is not null) + _dnsWebService.DnsServer.AuthZoneManager.AddCatalogMemberZone(catalogZoneInfo.Name, zoneInfo); + + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Authoritative Primary zone was created: " + zoneInfo.DisplayName); } break; case AuthZoneType.Secondary: { string primaryNameServerAddresses = request.GetQueryOrForm("primaryNameServerAddresses", null); - DnsTransportProtocol zoneTransferProtocol = request.GetQueryOrFormEnum("zoneTransferProtocol", DnsTransportProtocol.Tcp); - string tsigKeyName = request.GetQueryOrForm("tsigKeyName", null); + DnsTransportProtocol primaryZoneTransferProtocol = request.GetQueryOrFormEnum("zoneTransferProtocol", DnsTransportProtocol.Tcp); + string primaryZoneTransferTsigKeyName = request.GetQueryOrForm("tsigKeyName", null); bool validateZone = request.GetQueryOrForm("validateZone", bool.Parse, false); - if (zoneTransferProtocol == DnsTransportProtocol.Quic) + if (primaryZoneTransferProtocol == DnsTransportProtocol.Quic) DnsWebService.ValidateQuicSupport(); - zoneInfo = await _dnsWebService.DnsServer.AuthZoneManager.CreateSecondaryZoneAsync(zoneName, primaryNameServerAddresses, zoneTransferProtocol, tsigKeyName, validateZone); + zoneInfo = await _dnsWebService.DnsServer.AuthZoneManager.CreateSecondaryZoneAsync(zoneName, primaryNameServerAddresses, primaryZoneTransferProtocol, primaryZoneTransferTsigKeyName, validateZone); if (zoneInfo is null) throw new DnsWebServiceException("Zone already exists: " + zoneName); @@ -1218,14 +1306,23 @@ namespace DnsServerCore _dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); _dnsWebService._authManager.SaveConfigFile(); - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Authoritative secondary zone was created: " + zoneName); - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + 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 DnsServerException("No such Catalog zone was found: " + catalogZoneName); + } zoneInfo = await _dnsWebService.DnsServer.AuthZoneManager.CreateStubZoneAsync(zoneName, primaryNameServerAddresses); if (zoneInfo is null) @@ -1237,8 +1334,11 @@ namespace DnsServerCore _dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); _dnsWebService._authManager.SaveConfigFile(); - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Stub zone was created: " + zoneName); - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); + //add membership for catalog zone + if (catalogZoneInfo is not null) + _dnsWebService.DnsServer.AuthZoneManager.AddCatalogMemberZone(catalogZoneInfo.Name, zoneInfo); + + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Stub zone was created: " + zoneInfo.DisplayName); } break; @@ -1265,15 +1365,18 @@ namespace DnsServerCore break; } - switch (forwarderProtocol) - { - case DnsTransportProtocol.HttpsJson: - forwarderProtocol = DnsTransportProtocol.Https; - break; + if (forwarderProtocol == DnsTransportProtocol.Quic) + DnsWebService.ValidateQuicSupport(); - case DnsTransportProtocol.Quic: - DnsWebService.ValidateQuicSupport(); - break; + 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 DnsServerException("No such Catalog zone was found: " + catalogZoneName); } zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CreateForwarderZone(zoneName, forwarderProtocol, forwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword, null); @@ -1286,8 +1389,73 @@ namespace DnsServerCore _dnsWebService._authManager.SetPermission(PermissionSection.Zones, zoneInfo.Name, _dnsWebService._authManager.GetGroup(Group.DNS_ADMINISTRATORS), PermissionFlag.ViewModifyDelete); _dnsWebService._authManager.SaveConfigFile(); - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Forwarder zone was created: " + zoneName); - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); + //add membership for catalog zone + if (catalogZoneInfo is not null) + _dnsWebService.DnsServer.AuthZoneManager.AddCatalogMemberZone(catalogZoneInfo.Name, zoneInfo); + + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + 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(), "[" + 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(), "[" + 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(), "[" + session.User.Username + "] Secondary Catalog zone was created: " + zoneInfo.DisplayName); } break; @@ -1317,7 +1485,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + zoneName); + throw new DnsWebServiceException("No such zone was found: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -1414,9 +1582,7 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.ImportRecords(zoneInfo.Name, newRecords, overwrite, overwriteSoaSerial); - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Total " + newRecords.Count + " record(s) were imported successfully into authoritative zone: " + zoneInfo.Name); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Total " + newRecords.Count + " record(s) were imported successfully into " + zoneInfo.TypeName + " zone: " + zoneInfo.DisplayName); } public async Task ExportZoneAsync(HttpContext context) @@ -1434,7 +1600,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + zoneName); + throw new DnsWebServiceException("No such zone was found: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -1467,7 +1633,7 @@ namespace DnsServerCore HttpResponse response = context.Response; response.ContentType = "text/plain"; - response.Headers.ContentDisposition = "attachment;filename=" + (zoneInfo.Name == "." ? "root.zone" : zoneInfo.Name + ".zone"); + response.Headers.ContentDisposition = "attachment;filename=" + (zoneInfo.Name.Length == 0 ? "root.zone" : zoneInfo.Name + ".zone"); await using (StreamWriter sW = new StreamWriter(response.Body)) { @@ -1500,23 +1666,29 @@ namespace DnsServerCore AuthZoneInfo sourceZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(sourceZoneName); if (sourceZoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + sourceZoneName); + 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, sourceZoneName); + AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CloneZone(zoneName, sourceZoneInfo.Name); - //set permissions + //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(); - //save zone - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); - - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] " + sourceZoneInfo.Type.ToString() + " zone '" + sourceZoneInfo.Name + "' was cloned as '" + zoneName + "' sucessfully."); + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] " + sourceZoneInfo.TypeName + " zone '" + sourceZoneInfo.DisplayName + "' was cloned as '" + zoneInfo.DisplayName + "' sucessfully."); } public void ConvertZone(HttpContext context) @@ -1536,7 +1708,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + zoneName); + throw new DnsWebServiceException("No such zone was found: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -1545,9 +1717,8 @@ namespace DnsServerCore throw new DnsWebServiceException("Access was denied."); _dnsWebService.DnsServer.AuthZoneManager.ConvertZoneType(zoneInfo.Name, type); - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] " + zoneInfo.Type.ToString() + " zone '" + zoneInfo.Name + "' was converted to " + type.ToString() + " zone sucessfully."); + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] " + zoneInfo.TypeName + " zone '" + zoneInfo.DisplayName + "' was converted to " + AuthZoneInfo.GetZoneTypeName(type) + " zone sucessfully."); } public void SignPrimaryZone(HttpContext context) @@ -1628,8 +1799,6 @@ namespace DnsServerCore } _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Primary zone was signed successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void UnsignPrimaryZone(HttpContext context) @@ -1650,8 +1819,6 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.UnsignPrimaryZone(zoneName); _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Primary zone was unsigned successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void GetPrimaryZoneDsInfo(HttpContext context) @@ -1668,7 +1835,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + zoneName); + throw new DnsWebServiceException("No such zone was found: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -1679,10 +1846,10 @@ namespace DnsServerCore if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.View)) throw new DnsWebServiceException("Access was denied."); - if (zoneInfo.DnssecStatus == AuthZoneDnssecStatus.Unsigned) + if (zoneInfo.ApexZone.DnssecStatus == AuthZoneDnssecStatus.Unsigned) throw new DnsWebServiceException("The zone must be signed with DNSSEC."); - IReadOnlyList dnsKeyRecords = zoneInfo.GetApexRecords(DnsResourceRecordType.DNSKEY); + IReadOnlyList dnsKeyRecords = zoneInfo.ApexZone.GetRecords(DnsResourceRecordType.DNSKEY); Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter(); @@ -1690,7 +1857,7 @@ namespace DnsServerCore jsonWriter.WriteString("type", zoneInfo.Type.ToString()); jsonWriter.WriteBoolean("internal", zoneInfo.Internal); jsonWriter.WriteBoolean("disabled", zoneInfo.Disabled); - jsonWriter.WriteString("dnssecStatus", zoneInfo.DnssecStatus.ToString()); + jsonWriter.WriteString("dnssecStatus", zoneInfo.ApexZone.DnssecStatus.ToString()); jsonWriter.WritePropertyName("dsRecords"); jsonWriter.WriteStartArray(); @@ -1767,7 +1934,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + zoneName); + throw new DnsWebServiceException("No such zone was found: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -1784,11 +1951,11 @@ namespace DnsServerCore jsonWriter.WriteString("type", zoneInfo.Type.ToString()); jsonWriter.WriteBoolean("internal", zoneInfo.Internal); jsonWriter.WriteBoolean("disabled", zoneInfo.Disabled); - jsonWriter.WriteString("dnssecStatus", zoneInfo.DnssecStatus.ToString()); + jsonWriter.WriteString("dnssecStatus", zoneInfo.ApexZone.DnssecStatus.ToString()); - if (zoneInfo.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3) + if (zoneInfo.ApexZone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3) { - IReadOnlyList nsec3ParamRecords = zoneInfo.GetApexRecords(DnsResourceRecordType.NSEC3PARAM); + IReadOnlyList nsec3ParamRecords = zoneInfo.ApexZone.GetRecords(DnsResourceRecordType.NSEC3PARAM); DnsNSEC3PARAMRecordData nsec3Param = nsec3ParamRecords[0].RDATA as DnsNSEC3PARAMRecordData; jsonWriter.WriteNumber("nsec3Iterations", nsec3Param.Iterations); @@ -1870,8 +2037,6 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.ConvertPrimaryZoneToNSEC(zoneName); _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Primary zone was converted to NSEC successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void ConvertPrimaryZoneToNSEC3(HttpContext context) @@ -1897,8 +2062,6 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.ConvertPrimaryZoneToNSEC3(zoneName, iterations, saltLength); _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Primary zone was converted to NSEC3 successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void UpdatePrimaryZoneNSEC3Parameters(HttpContext context) @@ -1924,8 +2087,6 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.UpdatePrimaryZoneNSEC3Parameters(zoneName, iterations, saltLength); _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Primary zone NSEC3 parameters were updated successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void UpdatePrimaryZoneDnssecDnsKeyTtl(HttpContext context) @@ -1950,8 +2111,6 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.UpdatePrimaryZoneDnsKeyTtl(zoneName, dnsKeyTtl); _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Primary zone DNSKEY TTL was updated successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void GenerateAndAddPrimaryZoneDnssecPrivateKey(HttpContext context) @@ -1995,8 +2154,6 @@ namespace DnsServerCore } _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] DNSSEC private key was generated and added to the primary zone successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void UpdatePrimaryZoneDnssecPrivateKey(HttpContext context) @@ -2022,8 +2179,6 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.UpdatePrimaryZoneDnssecPrivateKey(zoneName, keyTag, rolloverDays); _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Primary zone DNSSEC private key config was updated successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void DeletePrimaryZoneDnssecPrivateKey(HttpContext context) @@ -2048,8 +2203,6 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.DeletePrimaryZoneDnssecPrivateKey(zoneName, keyTag); _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] DNSSEC private key was deleted from primary zone successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(HttpContext context) @@ -2070,8 +2223,6 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.PublishAllGeneratedPrimaryZoneDnssecPrivateKeys(zoneName); _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] All DNSSEC private keys from the primary zone were published successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void RolloverPrimaryZoneDnsKey(HttpContext context) @@ -2096,8 +2247,6 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.RolloverPrimaryZoneDnsKey(zoneName, keyTag); _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] The DNSKEY (" + keyTag + ") from the primary zone was rolled over successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void RetirePrimaryZoneDnsKey(HttpContext context) @@ -2122,8 +2271,6 @@ namespace DnsServerCore _dnsWebService.DnsServer.AuthZoneManager.RetirePrimaryZoneDnsKey(zoneName, keyTag); _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] The DNSKEY (" + keyTag + ") from the primary zone was retired successfully: " + zoneName); - - _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneName); } public void DeleteZone(HttpContext context) @@ -2140,7 +2287,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + zoneName); + throw new DnsWebServiceException("No such zone was found: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -2148,14 +2295,13 @@ namespace DnsServerCore if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, zoneInfo.Name, session.User, PermissionFlag.Delete)) throw new DnsWebServiceException("Access was denied."); - if (!_dnsWebService.DnsServer.AuthZoneManager.DeleteZone(zoneInfo.Name)) - throw new DnsWebServiceException("Failed to delete the zone: " + zoneInfo.Name); + 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(), "[" + session.User.Username + "] " + zoneInfo.Type.ToString() + " zone was deleted: " + zoneName); - _dnsWebService.DnsServer.AuthZoneManager.DeleteZoneFile(zoneInfo.Name); + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] " + zoneInfo.TypeName + " zone was deleted: " + zoneInfo.DisplayName); } public void EnableZone(HttpContext context) @@ -2172,7 +2318,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + zoneName); + throw new DnsWebServiceException("No such zone was found: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -2182,7 +2328,7 @@ namespace DnsServerCore zoneInfo.Disabled = false; - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] " + zoneInfo.Type.ToString() + " zone was enabled: " + zoneInfo.Name); + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] " + zoneInfo.TypeName + " zone was enabled: " + zoneInfo.DisplayName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); @@ -2204,7 +2350,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + zoneName); + throw new DnsWebServiceException("No such zone was found: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -2214,7 +2360,7 @@ namespace DnsServerCore zoneInfo.Disabled = true; - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] " + zoneInfo.Type.ToString() + " zone was disabled: " + zoneInfo.Name); + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] " + zoneInfo.TypeName + " zone was disabled: " + zoneInfo.DisplayName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); } @@ -2233,11 +2379,12 @@ namespace DnsServerCore 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 authoritative zone was found: " + zoneName); + throw new DnsWebServiceException("No such zone was found: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -2254,12 +2401,23 @@ namespace DnsServerCore jsonWriter.WriteString("type", zoneInfo.Type.ToString()); + if (zoneInfo.Type == AuthZoneType.Primary) + jsonWriter.WriteBoolean("internal", zoneInfo.Internal); + switch (zoneInfo.Type) { case AuthZoneType.Primary: - jsonWriter.WriteBoolean("internal", zoneInfo.Internal); - jsonWriter.WriteString("dnssecStatus", zoneInfo.DnssecStatus.ToString()); + 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; @@ -2274,45 +2432,133 @@ namespace DnsServerCore jsonWriter.WriteEndArray(); } - - break; - - case AuthZoneType.Secondary: - jsonWriter.WriteString("dnssecStatus", zoneInfo.DnssecStatus.ToString()); - - { - 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) + { + if (zoneInfo.ApexZone.CatalogZone is null) + { + jsonWriter.WriteBoolean("isSecondaryCatalogMember", true); + } + else + { + jsonWriter.WriteBoolean("isSecondaryCatalogMember", false); + jsonWriter.WriteBoolean("overrideCatalogQueryAccess", zoneInfo.OverrideCatalogQueryAccess); + } + } + break; + + case AuthZoneType.Secondary: + jsonWriter.WriteString("catalog", zoneInfo.CatalogZoneName); + + if (zoneInfo.CatalogZoneName is not null) + jsonWriter.WriteBoolean("overrideCatalogPrimaryNameServers", zoneInfo.OverrideCatalogPrimaryNameServers); + + break; + + case AuthZoneType.SecondaryForwarder: + jsonWriter.WriteString("catalog", zoneInfo.CatalogZoneName); + 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 switch (zoneInfo.Type) { case AuthZoneType.Primary: case AuthZoneType.Secondary: + case AuthZoneType.Stub: + case AuthZoneType.Forwarder: + case AuthZoneType.SecondaryForwarder: + case AuthZoneType.Catalog: + 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(); + break; + } + + //zone transfer & notify + switch (zoneInfo.Type) + { + case AuthZoneType.Primary: + case AuthZoneType.Secondary: + case AuthZoneType.Forwarder: + case AuthZoneType.Catalog: jsonWriter.WriteString("zoneTransfer", zoneInfo.ZoneTransfer.ToString()); - jsonWriter.WritePropertyName("zoneTransferNameServers"); + jsonWriter.WritePropertyName("zoneTransferNetworkACL"); { jsonWriter.WriteStartArray(); - if (zoneInfo.ZoneTransferNameServers is not null) + if (zoneInfo.ZoneTransferNetworkACL is not null) { - foreach (NetworkAddress networkAddress in zoneInfo.ZoneTransferNameServers) - jsonWriter.WriteStringValue(networkAddress.ToString()); + foreach (NetworkAccessControl nac in zoneInfo.ZoneTransferNetworkACL) + jsonWriter.WriteStringValue(nac.ToString()); } jsonWriter.WriteEndArray(); @@ -2349,21 +2595,23 @@ namespace DnsServerCore 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("updateIpAddresses"); + jsonWriter.WritePropertyName("updateNetworkACL"); { jsonWriter.WriteStartArray(); - if (zoneInfo.UpdateIpAddresses is not null) + if (zoneInfo.UpdateNetworkACL is not null) { - foreach (NetworkAddress networkAddress in zoneInfo.UpdateIpAddresses) - jsonWriter.WriteStringValue(networkAddress.ToString()); + foreach (NetworkAccessControl nac in zoneInfo.UpdateNetworkACL) + jsonWriter.WriteStringValue(nac.ToString()); } jsonWriter.WriteEndArray(); @@ -2408,6 +2656,27 @@ namespace DnsServerCore break; } + if (includeAvailableCatalogZoneNames) + { + IReadOnlyList catalogZones = _dnsWebService.DnsServer.AuthZoneManager.GetAllCatalogZones(); + + jsonWriter.WritePropertyName("availableCatalogZoneNames"); + jsonWriter.WriteStartArray(); + + foreach (AuthZoneInfo catalogZone in catalogZones) + { + if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Zones, catalogZone.Name, session.User, PermissionFlag.View)) + continue; + + if (catalogZone.Disabled) + continue; + + jsonWriter.WriteStringValue(catalogZone.Name); + } + + jsonWriter.WriteEndArray(); + } + if (includeAvailableTsigKeyNames) { jsonWriter.WritePropertyName("availableTsigKeyNames"); @@ -2441,7 +2710,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + zoneName); + throw new DnsWebServiceException("No such zone was found: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -2452,22 +2721,174 @@ namespace DnsServerCore 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.CatalogZoneName is not null) && (zoneInfo.ApexZone.CatalogZone is 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.CatalogZoneName is not null) && (zoneInfo.ApexZone.CatalogZone is null)) + break; //cannot set option for Stub 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.CatalogZoneName is not null) && (zoneInfo.ApexZone.CatalogZone is 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.CatalogZoneName is not null) && (zoneInfo.ApexZone.CatalogZone is 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.CatalogZoneName is not null) && (zoneInfo.ApexZone.CatalogZone is 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.CatalogZoneName is not null) && (zoneInfo.ApexZone.CatalogZone is 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 strZoneTransferNameServers = request.QueryOrForm("zoneTransferNameServers"); - if (strZoneTransferNameServers is not null) - { - if ((strZoneTransferNameServers.Length == 0) || strZoneTransferNameServers.Equals("false", StringComparison.OrdinalIgnoreCase)) - zoneInfo.ZoneTransferNameServers = null; - else - zoneInfo.ZoneTransferNameServers = strZoneTransferNameServers.Split(NetworkAddress.Parse, ','); - } - string strZoneTransferTsigKeyNames = request.QueryOrForm("zoneTransferTsigKeyNames"); if (strZoneTransferTsigKeyNames is not null) { @@ -2481,12 +2902,22 @@ namespace DnsServerCore Dictionary zoneTransferTsigKeyNames = new Dictionary(strZoneTransferTsigKeyNamesParts.Length); for (int i = 0; i < strZoneTransferTsigKeyNamesParts.Length; i++) - zoneTransferTsigKeyNames.Add(strZoneTransferTsigKeyNamesParts[i].ToLower(), null); + 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; @@ -2498,24 +2929,27 @@ namespace DnsServerCore else zoneInfo.NotifyNameServers = strNotifyNameServers.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 strUpdateIpAddresses = request.QueryOrForm("updateIpAddresses"); - if (strUpdateIpAddresses is not null) + string strUpdateNetworkACL = request.QueryOrForm("updateNetworkACL"); + if (strUpdateNetworkACL is not null) { - if ((strUpdateIpAddresses.Length == 0) || strUpdateIpAddresses.Equals("false", StringComparison.OrdinalIgnoreCase)) - zoneInfo.UpdateIpAddresses = null; + if ((strUpdateNetworkACL.Length == 0) || strUpdateNetworkACL.Equals("false", StringComparison.OrdinalIgnoreCase)) + zoneInfo.UpdateNetworkACL = null; else - zoneInfo.UpdateIpAddresses = strUpdateIpAddresses.Split(NetworkAddress.Parse, ','); + zoneInfo.UpdateNetworkACL = strUpdateNetworkACL.Split(NetworkAccessControl.Parse, ','); } break; } @@ -2538,8 +2972,8 @@ namespace DnsServerCore for (int i = 0; i < strUpdateSecurityPoliciesParts.Length; i += 3) { - string tsigKeyName = strUpdateSecurityPoliciesParts[i].ToLower(); - string domain = strUpdateSecurityPoliciesParts[i + 1].ToLower(); + 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)) @@ -2567,7 +3001,38 @@ namespace DnsServerCore break; } - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] " + zoneInfo.Type.ToString() + " zone options were updated successfully: " + zoneInfo.Name); + //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.Type == AuthZoneType.Stub) && (zoneInfo.CatalogZoneName is not null) && (zoneInfo.ApexZone.CatalogZone is 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)) + _dnsWebService.DnsServer.AuthZoneManager.AddCatalogMemberZone(catalogZoneName, zoneInfo); + else if (!catalogZoneName.Equals(oldCatalogZoneName, StringComparison.OrdinalIgnoreCase)) + _dnsWebService.DnsServer.AuthZoneManager.ChangeCatalogMemberZoneOwnership(zoneInfo, catalogZoneName); + } + } + + break; + } + + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] " + zoneInfo.TypeName + " zone options were updated successfully: " + zoneInfo.DisplayName); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); } @@ -2586,7 +3051,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + zoneName); + throw new DnsWebServiceException("No such zone was found: " + zoneName); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -2597,12 +3062,14 @@ namespace DnsServerCore switch (zoneInfo.Type) { case AuthZoneType.Secondary: + case AuthZoneType.SecondaryForwarder: + case AuthZoneType.SecondaryCatalog: case AuthZoneType.Stub: zoneInfo.TriggerResync(); break; default: - throw new DnsWebServiceException("Only Secondary and Stub zones support resync."); + throw new DnsWebServiceException("Only Secondary, Secondary Forwarder, Secondary Catalog, and Stub zones support resync."); } } @@ -2626,7 +3093,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.FindAuthZoneInfo(string.IsNullOrEmpty(zoneName) ? domain : zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + domain); + throw new DnsWebServiceException("No such zone was found: " + domain); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -2640,6 +3107,7 @@ namespace DnsServerCore 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; @@ -2670,7 +3138,7 @@ namespace DnsServerCore string ptrZone = Zone.GetReverseZone(ipAddress, type == DnsResourceRecordType.A ? 24 : 64); - reverseZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CreatePrimaryZone(ptrZone, _dnsWebService.DnsServer.ServerDomain, false); + reverseZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CreatePrimaryZone(ptrZone); if (reverseZoneInfo == null) throw new DnsServerException("Failed to create reverse zone to add PTR record: " + ptrZone); @@ -2682,12 +3150,16 @@ namespace DnsServerCore } if (reverseZoneInfo.Internal) - throw new DnsServerException("Reverse zone '" + reverseZoneInfo.Name + "' is an internal zone."); + throw new DnsServerException("Reverse zone '" + reverseZoneInfo.DisplayName + "' is an internal zone."); if ((reverseZoneInfo.Type != AuthZoneType.Primary) && (reverseZoneInfo.Type != AuthZoneType.Forwarder)) - throw new DnsServerException("Reverse zone '" + reverseZoneInfo.Name + "' is not a primary or forwarder zone."); + throw new DnsServerException("Reverse zone '" + reverseZoneInfo.DisplayName + "' is not a primary or forwarder zone."); - _dnsWebService.DnsServer.AuthZoneManager.SetRecords(reverseZoneInfo.Name, ptrDomain, DnsResourceRecordType.PTR, ttl, new DnsPTRRecordData[] { new DnsPTRRecordData(domain) }); + 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); } @@ -2695,18 +3167,6 @@ namespace DnsServerCore newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsARecordData(ipAddress)); else newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsAAAARecordData(ipAddress)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); - - bool updateSvcbHints = request.GetQueryOrForm("updateSvcbHints", bool.Parse, false); - if (updateSvcbHints) - UpdateSvcbAutoHints(zoneInfo.Name, domain, type == DnsResourceRecordType.A, type == DnsResourceRecordType.AAAA); } break; @@ -2719,14 +3179,6 @@ namespace DnsServerCore if (!string.IsNullOrEmpty(glueAddresses)) newRecord.SetGlueRecords(glueAddresses); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2736,7 +3188,7 @@ namespace DnsServerCore { 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 records."); + throw new DnsWebServiceException("Record already exists. Use overwrite option if you wish to overwrite existing record."); } string cname = request.GetQueryOrFormAlt("cname", "value").TrimEnd('.'); @@ -2746,10 +3198,7 @@ namespace DnsServerCore newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsCNAMERecordData(cname)); - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); + overwrite = true; //force SetRecord } break; @@ -2758,14 +3207,6 @@ namespace DnsServerCore string ptrName = request.GetQueryOrFormAlt("ptrName", "value").TrimEnd('.'); newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsPTRRecordData(ptrName)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2775,14 +3216,6 @@ namespace DnsServerCore string exchange = request.GetQueryOrFormAlt("exchange", "value").TrimEnd('.'); newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsMXRecordData(preference, exchange)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2792,14 +3225,15 @@ namespace DnsServerCore 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; - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; + case DnsResourceRecordType.RP: + { + string mailbox = request.GetQueryOrForm("mailbox", "").TrimEnd('.'); + string txtDomain = request.GetQueryOrForm("txtDomain", "").TrimEnd('.'); - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); + newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsRPRecordData(mailbox, txtDomain)); } break; @@ -2811,14 +3245,6 @@ namespace DnsServerCore string target = request.GetQueryOrFormAlt("target", "value").TrimEnd('.'); newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsSRVRecordData(priority, weight, port, target)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2832,14 +3258,6 @@ namespace DnsServerCore string replacement = request.GetQueryOrForm("naptrReplacement", "").TrimEnd('.'); newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsNAPTRRecordData(order, preference, flags, services, regexp, replacement)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2849,7 +3267,7 @@ namespace DnsServerCore { 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 records."); + throw new DnsWebServiceException("Record already exists. Use overwrite option if you wish to overwrite existing record."); } string dname = request.GetQueryOrFormAlt("dname", "value").TrimEnd('.'); @@ -2862,10 +3280,7 @@ namespace DnsServerCore newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsDNAMERecordData(dname)); - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); + overwrite = true; //force SetRecord } break; @@ -2877,14 +3292,6 @@ namespace DnsServerCore byte[] digest = request.GetQueryOrFormAlt("digest", "value", Convert.FromHexString); newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsDSRecordData(keyTag, algorithm, digestType, digest)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2895,14 +3302,6 @@ namespace DnsServerCore byte[] sshfpFingerprint = request.GetQueryOrForm("sshfpFingerprint", Convert.FromHexString); newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsSSHFPRecordData(sshfpAlgorithm, sshfpFingerprintType, sshfpFingerprint)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2914,14 +3313,6 @@ namespace DnsServerCore string tlsaCertificateAssociationData = request.GetQueryOrForm("tlsaCertificateAssociationData"); newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsTLSARecordData(tlsaCertificateUsage, tlsaSelector, tlsaMatchingType, tlsaCertificateAssociationData)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2956,9 +3347,6 @@ namespace DnsServerCore newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsSVCBRecordData(svcPriority, targetName, svcParams)); - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthSVCBRecordInfo().Comments = comments; - if (autoIpv4Hint) newRecord.GetAuthSVCBRecordInfo().AutoIpv4Hint = true; @@ -2967,11 +3355,6 @@ namespace DnsServerCore if (autoIpv4Hint || autoIpv6Hint) ResolveSvcbAutoHints(zoneInfo.Name, newRecord, autoIpv4Hint, autoIpv6Hint, svcParams); - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -2982,14 +3365,6 @@ namespace DnsServerCore 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)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -3000,14 +3375,6 @@ namespace DnsServerCore string value = request.GetQueryOrForm("value"); newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsCAARecordData(flags, tag, value)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -3016,14 +3383,6 @@ namespace DnsServerCore string aname = request.GetQueryOrFormAlt("aname", "value").TrimEnd('.'); newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsANAMERecordData(aname)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; @@ -3054,37 +3413,31 @@ namespace DnsServerCore } } - newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsForwarderRecordData(protocol, forwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword)); + byte priority = request.GetQueryOrForm("forwarderPriority", byte.Parse, byte.MinValue); - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; + if (protocol == DnsTransportProtocol.Quic) + DnsWebService.ValidateQuicSupport(); - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); + newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsForwarderRecordData(protocol, forwarder, 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", ""); - 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 records."); + 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)); - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); + overwrite = true; //force SetRecord } break; @@ -3100,20 +3453,36 @@ namespace DnsServerCore rdata = Convert.FromHexString(strRData); newRecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, DnsResourceRecord.ReadRecordDataFrom(type, rdata)); - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - if (overwrite) - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newRecord); - else - _dnsWebService.DnsServer.AuthZoneManager.AddRecord(zoneInfo.Name, newRecord); } break; } - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] New record was added to authoritative zone '" + zoneInfo.Name + "' successfully {record: " + newRecord.ToString() + "}"); + //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(), "[" + 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(); @@ -3145,7 +3514,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.FindAuthZoneInfo(string.IsNullOrEmpty(zoneName) ? domain : zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + domain); + throw new DnsWebServiceException("No such zone was found: " + domain); UserSession session = context.GetCurrentSession(); @@ -3189,7 +3558,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.FindAuthZoneInfo(string.IsNullOrEmpty(zoneName) ? domain : zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + domain); + throw new DnsWebServiceException("No such zone was found: " + domain); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -3214,7 +3583,7 @@ namespace DnsServerCore string ptrDomain = Zone.GetReverseZone(ipAddress, type == DnsResourceRecordType.A ? 32 : 128); AuthZoneInfo reverseZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.FindAuthZoneInfo(ptrDomain); - if ((reverseZoneInfo != null) && !reverseZoneInfo.Internal && (reverseZoneInfo.Type == AuthZoneType.Primary)) + 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) @@ -3276,6 +3645,15 @@ namespace DnsServerCore } 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); @@ -3400,7 +3778,7 @@ namespace DnsServerCore DnsTransportProtocol protocol = request.GetQueryOrFormEnum("protocol", DnsTransportProtocol.Udp); string forwarder = request.GetQueryOrFormAlt("forwarder", "value"); - _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, new DnsForwarderRecordData(protocol, forwarder)); + _dnsWebService.DnsServer.AuthZoneManager.DeleteRecord(zoneInfo.Name, domain, type, DnsForwarderRecordData.CreatePartialRecordData(protocol, forwarder)); } break; @@ -3425,7 +3803,7 @@ namespace DnsServerCore break; } - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Record was deleted from authoritative zone '" + zoneInfo.Name + "' successfully {domain: " + domain + "; type: " + type + ";}"); + _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Record was deleted from " + zoneInfo.TypeName + " zone '" + zoneInfo.DisplayName + "' successfully {domain: " + domain + "; type: " + type + ";}"); _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(zoneInfo.Name); } @@ -3450,7 +3828,7 @@ namespace DnsServerCore AuthZoneInfo zoneInfo = _dnsWebService.DnsServer.AuthZoneManager.FindAuthZoneInfo(string.IsNullOrEmpty(zoneName) ? domain : zoneName); if (zoneInfo is null) - throw new DnsWebServiceException("No such authoritative zone was found: " + domain); + throw new DnsWebServiceException("No such zone was found: " + domain); if (zoneInfo.Internal) throw new DnsWebServiceException("Access was denied to manage internal DNS Server zone."); @@ -3464,6 +3842,7 @@ namespace DnsServerCore 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; @@ -3491,7 +3870,7 @@ namespace DnsServerCore string ptrZone = Zone.GetReverseZone(newIpAddress, type == DnsResourceRecordType.A ? 24 : 64); - newReverseZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CreatePrimaryZone(ptrZone, _dnsWebService.DnsServer.ServerDomain, false); + newReverseZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.CreatePrimaryZone(ptrZone); if (newReverseZoneInfo is null) throw new DnsServerException("Failed to create reverse zone to add PTR record: " + ptrZone); @@ -3503,15 +3882,15 @@ namespace DnsServerCore } if (newReverseZoneInfo.Internal) - throw new DnsServerException("Reverse zone '" + newReverseZoneInfo.Name + "' is an internal zone."); + throw new DnsServerException("Reverse zone '" + newReverseZoneInfo.DisplayName + "' is an internal zone."); if ((newReverseZoneInfo.Type != AuthZoneType.Primary) && (newReverseZoneInfo.Type != AuthZoneType.Forwarder)) - throw new DnsServerException("Reverse zone '" + newReverseZoneInfo.Name + "' is not a primary or forwarder zone."); + throw new DnsServerException("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 != null) && !oldReverseZoneInfo.Internal && (oldReverseZoneInfo.Type == AuthZoneType.Primary)) + 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); @@ -3519,7 +3898,11 @@ namespace DnsServerCore } //add new PTR record and save reverse zone - _dnsWebService.DnsServer.AuthZoneManager.SetRecords(newReverseZoneInfo.Name, newPtrDomain, DnsResourceRecordType.PTR, ttl, new DnsPTRRecordData[] { new DnsPTRRecordData(domain) }); + 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); } @@ -3533,18 +3916,6 @@ namespace DnsServerCore oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsAAAARecordData(ipAddress)); newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsAAAARecordData(newIpAddress)); } - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); - - bool updateSvcbHints = request.GetQueryOrForm("updateSvcbHints", bool.Parse, false); - if (updateSvcbHints) - UpdateSvcbAutoHints(zoneInfo.Name, newDomain, type == DnsResourceRecordType.A, type == DnsResourceRecordType.AAAA); } break; @@ -3556,16 +3927,8 @@ namespace DnsServerCore oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsNSRecordData(nameServer)); newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsNSRecordData(newNameServer)); - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - if (request.TryGetQueryOrForm("glue", out string glueAddresses)) newRecord.SetGlueRecords(glueAddresses); - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3578,14 +3941,6 @@ namespace DnsServerCore oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsCNAMERecordData(cname)); newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsCNAMERecordData(cname)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3599,76 +3954,19 @@ namespace DnsServerCore uint expire = request.GetQueryOrForm("expire", uint.Parse); uint minimum = request.GetQueryOrForm("minimum", uint.Parse); - DnsResourceRecord newSOARecord = new DnsResourceRecord(domain, type, DnsClass.IN, ttl, new DnsSOARecordData(primaryNameServer, responsiblePerson, serial, refresh, retry, expire, minimum)); + 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: { - SOARecordInfo recordInfo = newSOARecord.GetAuthSOARecordInfo(); - if (request.TryGetQueryOrForm("useSerialDateScheme", bool.Parse, out bool useSerialDateScheme)) - recordInfo.UseSoaSerialDateScheme = useSerialDateScheme; - } - break; - - case AuthZoneType.Secondary: - { - SOARecordInfo recordInfo = newSOARecord.GetAuthSOARecordInfo(); - - if (request.TryGetQueryOrForm("validateZone", bool.Parse, out bool validateZone)) - recordInfo.ValidateZone = validateZone; - - if (request.TryGetQueryOrFormEnum("zoneTransferProtocol", out DnsTransportProtocol zoneTransferProtocol)) - { - if (zoneTransferProtocol == DnsTransportProtocol.Quic) - DnsWebService.ValidateQuicSupport(); - - recordInfo.ZoneTransferProtocol = zoneTransferProtocol; - } - - if (request.TryGetQueryOrForm("primaryAddresses", out string primaryAddresses)) - { - recordInfo.PrimaryNameServers = primaryAddresses.Split(delegate (string address) - { - NameServerAddress nameServer = NameServerAddress.Parse(address); - - if (nameServer.Protocol != zoneTransferProtocol) - nameServer = nameServer.ChangeProtocol(zoneTransferProtocol); - - return nameServer; - }, ','); - } - - if (request.TryGetQueryOrForm("tsigKeyName", out string tsigKeyName)) - recordInfo.TsigKeyName = tsigKeyName; - } - break; - - case AuthZoneType.Stub: - { - if (request.TryGetQueryOrForm("primaryAddresses", out string primaryAddresses)) - { - newSOARecord.GetAuthSOARecordInfo().PrimaryNameServers = primaryAddresses.Split(delegate (string address) - { - NameServerAddress nameServer = NameServerAddress.Parse(address); - - if (nameServer.Protocol != DnsTransportProtocol.Udp) - nameServer = nameServer.ChangeProtocol(DnsTransportProtocol.Udp); - - return nameServer; - }, ','); - } + newRecord.GetAuthSOARecordInfo().UseSoaSerialDateScheme = useSerialDateScheme; } break; } - - if (!string.IsNullOrEmpty(comments)) - newSOARecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.SetRecord(zoneInfo.Name, newSOARecord); - - newRecord = zoneInfo.GetApexRecords(DnsResourceRecordType.SOA)[0]; } break; @@ -3679,14 +3977,6 @@ namespace DnsServerCore oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsPTRRecordData(ptrName)); newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsPTRRecordData(newPtrName)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3700,14 +3990,6 @@ namespace DnsServerCore oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsMXRecordData(preference, exchange)); newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsMXRecordData(newPreference, newExchange)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3721,14 +4003,19 @@ namespace DnsServerCore 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; - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; + case DnsResourceRecordType.RP: + { + string mailbox = request.GetQueryOrForm("mailbox", "").TrimEnd('.'); + string newMailbox = request.GetQueryOrForm("newMailbox", mailbox).TrimEnd('.'); - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; + string txtDomain = request.GetQueryOrForm("txtDomain", "").TrimEnd('.'); + string newTxtDomain = request.GetQueryOrForm("newTxtDomain", txtDomain).TrimEnd('.'); - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); + 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; @@ -3748,14 +4035,6 @@ namespace DnsServerCore 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)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3781,14 +4060,6 @@ namespace DnsServerCore 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)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3804,14 +4075,6 @@ namespace DnsServerCore oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsDNAMERecordData(dname)); newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsDNAMERecordData(dname)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3831,14 +4094,6 @@ namespace DnsServerCore 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)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3855,14 +4110,6 @@ namespace DnsServerCore 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)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3882,14 +4129,6 @@ namespace DnsServerCore 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)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3951,12 +4190,6 @@ namespace DnsServerCore 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 (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - if (autoIpv4Hint) newRecord.GetAuthSVCBRecordInfo().AutoIpv4Hint = true; @@ -3965,8 +4198,6 @@ namespace DnsServerCore if (autoIpv4Hint || autoIpv6Hint) ResolveSvcbAutoHints(zoneInfo.Name, newRecord, autoIpv4Hint, autoIpv6Hint, newSvcParams); - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -3983,14 +4214,6 @@ namespace DnsServerCore 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)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -4007,14 +4230,6 @@ namespace DnsServerCore 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)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -4025,14 +4240,6 @@ namespace DnsServerCore oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsANAMERecordData(aname)); newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsANAMERecordData(newAName)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -4067,27 +4274,13 @@ namespace DnsServerCore } } - switch (newProtocol) - { - case DnsTransportProtocol.HttpsJson: - newProtocol = DnsTransportProtocol.Https; - break; + byte priority = request.GetQueryOrForm("forwarderPriority", byte.Parse, byte.MinValue); - case DnsTransportProtocol.Quic: - DnsWebService.ValidateQuicSupport(); - break; - } + if (newProtocol == DnsTransportProtocol.Quic) + DnsWebService.ValidateQuicSupport(); - oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsForwarderRecordData(protocol, forwarder)); - newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, 0, new DnsForwarderRecordData(newProtocol, newForwarder, dnssecValidation, proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); + 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; @@ -4099,14 +4292,6 @@ namespace DnsServerCore 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)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; @@ -4131,20 +4316,53 @@ namespace DnsServerCore oldRecord = new DnsResourceRecord(domain, type, DnsClass.IN, 0, new DnsUnknownRecordData(rdata)); newRecord = new DnsResourceRecord(newDomain, type, DnsClass.IN, ttl, new DnsUnknownRecordData(newRData)); - - if (disable) - newRecord.GetAuthGenericRecordInfo().Disabled = true; - - if (!string.IsNullOrEmpty(comments)) - newRecord.GetAuthGenericRecordInfo().Comments = comments; - - _dnsWebService.DnsServer.AuthZoneManager.UpdateRecord(zoneInfo.Name, oldRecord, newRecord); } break; } - _dnsWebService._log.Write(context.GetRemoteEndPoint(), "[" + session.User.Username + "] Record was updated for authoritative zone '" + zoneInfo.Name + "' successfully {" + (oldRecord is null ? "" : "oldRecord: " + oldRecord.ToString() + "; ") + "newRecord: " + newRecord.ToString() + "}"); + //update record info + GenericRecordInfo recordInfo = newRecord.GetAuthGenericRecordInfo(); + recordInfo.LastModified = DateTime.UtcNow; + recordInfo.ExpiryTtl = expiryTtl; + + if (disable) + recordInfo.Disabled = true; + + if (!string.IsNullOrEmpty(comments)) + 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; + } + + newRecord = zoneInfo.ApexZone.GetRecords(DnsResourceRecordType.SOA)[0]; //get updated record to return json + } + 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(), "[" + 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();