/*
Technitium DNS Server
Copyright (C) 2021 Shreyas Zare (shreyas@technitium.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
$(function () {
$("input[type=radio][name=rdAddZoneType]").change(function () {
$("#divAddZonePrimaryNameServerAddresses").hide();
$("#divAddZoneZoneTransferProtocol").hide();
$("#divAddZoneTsigKeyName").hide();
$("#divAddZoneForwarderProtocol").hide();
$("#divAddZoneForwarder").hide();
var zoneType = $('input[name=rdAddZoneType]:checked').val();
switch (zoneType) {
case "Primary":
break;
case "Secondary":
$("#divAddZonePrimaryNameServerAddresses").show();
$("#divAddZoneZoneTransferProtocol").show();
$("#divAddZoneTsigKeyName").show();
break;
case "Stub":
$("#divAddZonePrimaryNameServerAddresses").show();
break;
case "Forwarder":
$("#divAddZoneForwarderProtocol").show();
$("#divAddZoneForwarder").show();
break;
}
});
$("input[type=radio][name=rdAddZoneForwarderProtocol]").change(function () {
var protocol = $('input[name=rdAddZoneForwarderProtocol]:checked').val();
switch (protocol) {
case "Udp":
case "Tcp":
$("#txtAddZoneForwarder").attr("placeholder", "8.8.8.8 or [2620:fe::10]")
break;
case "Tls":
$("#txtAddZoneForwarder").attr("placeholder", "dns.quad9.net (9.9.9.9:853)")
break;
case "Https":
case "HttpsJson":
$("#txtAddZoneForwarder").attr("placeholder", "https://cloudflare-dns.com/dns-query (1.1.1.1)")
break;
}
});
$("input[type=radio][name=rdZoneTransfer]").change(function () {
var zoneTransfer = $('input[name=rdZoneTransfer]:checked').val();
if (zoneTransfer === "AllowOnlySpecifiedNameServers") {
$("#txtZoneTransferNameServers").prop("disabled", false);
}
else {
$("#txtZoneTransferNameServers").prop("disabled", true);
}
});
$("input[type=radio][name=rdZoneNotify]").change(function () {
var zoneTransfer = $('input[name=rdZoneNotify]:checked').val();
if (zoneTransfer === "SpecifiedNameServers") {
$("#txtZoneNotifyNameServers").prop("disabled", false);
}
else {
$("#txtZoneNotifyNameServers").prop("disabled", true);
}
});
$("#chkAddEditRecordDataPtr").click(function () {
var addPtrRecord = $("#chkAddEditRecordDataPtr").prop('checked');
$("#chkAddEditRecordDataCreatePtrZone").prop('disabled', !addPtrRecord);
});
$("input[type=radio][name=rdAddEditRecordDataForwarderProtocol]").change(updateAddEditFormForwarderPlaceholder);
$("#optAddEditRecordDataAppName").change(function () {
if (appsList == null)
return;
var appName = $("#optAddEditRecordDataAppName").val();
var optClassPaths = "";
for (var i = 0; i < appsList.length; i++) {
if (appsList[i].name == appName) {
for (var j = 0; j < appsList[i].appRecordRequestHandlers.length; j++) {
optClassPaths += "";
}
}
}
$("#optAddEditRecordDataClassPath").html(optClassPaths);
$("#txtAddEditRecordDataData").val("");
});
$("#optAddEditRecordDataClassPath").change(function () {
if (appsList == null)
return;
var appName = $("#optAddEditRecordDataAppName").val();
var classPath = $("#optAddEditRecordDataClassPath").val();
for (var i = 0; i < appsList.length; i++) {
if (appsList[i].name == appName) {
for (var j = 0; j < appsList[i].appRecordRequestHandlers.length; j++) {
if (appsList[i].appRecordRequestHandlers[j].classPath == classPath) {
$("#txtAddEditRecordDataData").val(appsList[i].appRecordRequestHandlers[j].recordDataTemplate);
return;
}
}
}
}
$("#txtAddEditRecordDataData").val("");
});
$("#optZoneOptionsQuickTsigKeyNames").change(function () {
var selectedOption = $("#optZoneOptionsQuickTsigKeyNames").val();
switch (selectedOption) {
case "blank":
break;
case "none":
$("#txtZoneOptionsZoneTransferTsigKeyNames").val("");
break;
default:
var existingList = $("#txtZoneOptionsZoneTransferTsigKeyNames").val();
if (existingList.indexOf(selectedOption) < 0) {
existingList += selectedOption + "\n";
$("#txtZoneOptionsZoneTransferTsigKeyNames").val(existingList);
}
break;
}
});
});
function refreshZones(checkDisplay) {
if (checkDisplay == null)
checkDisplay = false;
var divViewZones = $("#divViewZones");
if (checkDisplay && (divViewZones.css('display') === "none"))
return;
var divViewZonesLoader = $("#divViewZonesLoader");
var divEditZone = $("#divEditZone");
divViewZones.hide();
divEditZone.hide();
divViewZonesLoader.show();
HTTPRequest({
url: "/api/listZones?token=" + token,
success: function (responseJSON) {
var zones = responseJSON.response.zones;
var tableHtmlRows = "";
for (var i = 0; i < zones.length; i++) {
var id = Math.floor(Math.random() * 10000);
var name = zones[i].name;
if (name === "")
name = ".";
var type;
if (zones[i].internal)
type = "Internal";
else
type = "" + zones[i].type + "";
var status;
if (zones[i].disabled)
status = "Disabled";
else if (zones[i].isExpired)
status = "Expired";
else
status = "Enabled";
var expiry = zones[i].expiry;
if (expiry == null)
expiry = " ";
else
expiry = moment(expiry).local().format("YYYY-MM-DD HH:mm");
var isReadOnlyZone = zones[i].internal;
var disableOptions;
switch (zones[i].type) {
case "Primary":
case "Secondary":
disableOptions = zones[i].internal;
break;
default:
disableOptions = true;
break;
}
tableHtmlRows += "
";
}
$("#tableZonesBody").html(tableHtmlRows);
if (zones.length > 0)
$("#tableZonesFooter").html("
Total Zones: " + zones.length + "
");
else
$("#tableZonesFooter").html("
No Zones Found
");
divViewZonesLoader.hide();
divViewZones.show();
},
error: function () {
divViewZonesLoader.hide();
divViewZones.show();
},
invalidToken: function () {
showPageLogin();
},
objLoaderPlaceholder: divViewZonesLoader
});
}
function enableZone(objBtn, domain) {
var btn = $(objBtn);
var id = btn.attr("data-id");
btn.button('loading');
HTTPRequest({
url: "/api/enableZone?token=" + token + "&domain=" + domain,
success: function (responseJSON) {
btn.button('reset');
$("#btnEnableZone" + id).hide();
$("#btnDisableZone" + id).show();
$("#tdStatus" + id).attr("class", "label label-success");
$("#tdStatus" + id).html("Enabled");
showAlert("success", "Zone Enabled!", "Zone '" + domain + "' was enabled successfully.");
},
error: function () {
btn.button('reset');
},
invalidToken: function () {
showPageLogin();
}
});
}
function disableZone(objBtn, domain) {
if (!confirm("Are you sure you want to disable the zone '" + domain + "'?"))
return false;
var btn = $(objBtn);
var id = btn.attr("data-id");
btn.button('loading');
HTTPRequest({
url: "/api/disableZone?token=" + token + "&domain=" + domain,
success: function (responseJSON) {
btn.button('reset');
$("#btnEnableZone" + id).show();
$("#btnDisableZone" + id).hide();
$("#tdStatus" + id).attr("class", "label label-warning");
$("#tdStatus" + id).html("Disabled");
showAlert("success", "Zone Disabled!", "Zone '" + domain + "' was disabled successfully.");
},
error: function () {
btn.button('reset');
},
invalidToken: function () {
showPageLogin();
}
});
}
function deleteZone(objBtn, domain, editZone) {
if (!confirm("Are you sure you want to permanently delete the zone '" + domain + "' and all its records?"))
return false;
if (editZone == null)
editZone = false;
var btn = $(objBtn);
var id = btn.attr("data-id");
btn.button('loading');
HTTPRequest({
url: "/api/deleteZone?token=" + token + "&domain=" + domain,
success: function (responseJSON) {
if (editZone) {
btn.button('reset');
refreshZones();
}
else {
$("#trZone" + id).remove();
var totalZones = $('#tableZones >tbody >tr').length;
if (totalZones > 0)
$("#tableZonesFooter").html("
Total Zones: " + totalZones + "
");
else
$("#tableZonesFooter").html("
No Zones Found
");
}
showAlert("success", "Zone Deleted!", "Zone '" + domain + "' was deleted successfully.");
},
error: function () {
btn.button('reset');
},
invalidToken: function () {
showPageLogin();
}
});
}
function showZoneOptions(domain) {
var divZoneOptionsAlert = $("#divZoneOptionsAlert");
var divZoneOptionsLoader = $("#divZoneOptionsLoader");
var divZoneOptions = $("#divZoneOptions");
divZoneOptionsLoader.show();
divZoneOptions.hide();
$("#modalZoneOptions").modal("show");
HTTPRequest({
url: "/api/zone/options/get?token=" + token + "&domain=" + domain,
success: function (responseJSON) {
if (responseJSON.response.name === "")
responseJSON.response.name = ".";
$("#lblZoneOptionsZoneName").text(responseJSON.response.name);
$("#txtZoneTransferNameServers").prop("disabled", true);
$("#txtZoneNotifyNameServers").prop("disabled", true);
switch (responseJSON.response.zoneTransfer) {
case "Allow":
$("#rdZoneTransferAllow").prop("checked", true);
break;
case "AllowOnlyZoneNameServers":
$("#rdZoneTransferAllowOnlyZoneNameServers").prop("checked", true);
break;
case "AllowOnlySpecifiedNameServers":
$("#rdZoneTransferAllowOnlySpecifiedNameServers").prop("checked", true);
$("#txtZoneTransferNameServers").prop("disabled", false);
break;
case "Deny":
default:
$("#rdZoneTransferDeny").prop("checked", true);
break;
}
{
var value = "";
for (var i = 0; i < responseJSON.response.zoneTransferNameServers.length; i++)
value += responseJSON.response.zoneTransferNameServers[i] + "\r\n";
$("#txtZoneTransferNameServers").val(value);
}
switch (responseJSON.response.notify) {
case "ZoneNameServers":
$("#rdZoneNotifyZoneNameServers").prop("checked", true);
break;
case "SpecifiedNameServers":
$("#rdZoneNotifySpecifiedNameServers").prop("checked", true);
$("#txtZoneNotifyNameServers").prop("disabled", false);
break;
case "None":
default:
$("#rdZoneNotifyNone").prop("checked", true);
break;
}
{
var value = "";
for (var i = 0; i < responseJSON.response.notifyNameServers.length; i++)
value += responseJSON.response.notifyNameServers[i] + "\r\n";
$("#txtZoneNotifyNameServers").val(value);
}
{
var value = "";
if (responseJSON.response.zoneTransferTsigKeyNames != null) {
for (var i = 0; i < responseJSON.response.zoneTransferTsigKeyNames.length; i++) {
value += responseJSON.response.zoneTransferTsigKeyNames[i] + "\r\n";
}
}
$("#txtZoneOptionsZoneTransferTsigKeyNames").val(value);
}
{
var options = "";
if (responseJSON.response.availableTsigKeyNames != null) {
for (var i = 0; i < responseJSON.response.availableTsigKeyNames.length; i++) {
options += "";
}
}
$("#optZoneOptionsQuickTsigKeyNames").html(options);
}
divZoneOptionsLoader.hide();
divZoneOptions.show();
},
error: function () {
divZoneOptionsLoader.hide();
},
invalidToken: function () {
$("#modalZoneOptions").modal("hide");
showPageLogin();
},
objAlertPlaceholder: divZoneOptionsAlert,
objLoaderPlaceholder: divZoneOptionsLoader
});
}
function saveZoneOptions() {
var divZoneOptionsAlert = $("#divZoneOptionsAlert");
var divZoneOptionsLoader = $("#divZoneOptionsLoader");
var domain = $("#lblZoneOptionsZoneName").text();
var zoneTransfer = $("input[name=rdZoneTransfer]:checked").val();
var zoneTransferNameServers = cleanTextList($("#txtZoneTransferNameServers").val());
if ((zoneTransferNameServers.length === 0) || (zoneTransferNameServers === ","))
zoneTransferNameServers = false;
else
$("#txtZoneTransferNameServers").val(zoneTransferNameServers.replace(/,/g, "\n"));
var notify = $("input[name=rdZoneNotify]:checked").val();
var notifyNameServers = cleanTextList($("#txtZoneNotifyNameServers").val());
if ((notifyNameServers.length === 0) || (notifyNameServers === ","))
notifyNameServers = false;
else
$("#txtZoneNotifyNameServers").val(notifyNameServers.replace(/,/g, "\n"));
var zoneTransferTsigKeyNames = cleanTextList($("#txtZoneOptionsZoneTransferTsigKeyNames").val());
if ((zoneTransferTsigKeyNames.length === 0) || (zoneTransferTsigKeyNames === ","))
zoneTransferTsigKeyNames = false;
else
$("#txtZoneOptionsZoneTransferTsigKeyNames").val(zoneTransferTsigKeyNames.replace(/,/g, "\n"));
var btn = $("#btnSaveZoneOptions");
btn.button('loading');
HTTPRequest({
url: "/api/zone/options/set?token=" + token + "&domain=" + domain
+ "&zoneTransfer=" + zoneTransfer + "&zoneTransferNameServers=" + encodeURIComponent(zoneTransferNameServers)
+ "¬ify=" + notify + "¬ifyNameServers=" + encodeURIComponent(notifyNameServers)
+ "&zoneTransferTsigKeyNames=" + encodeURIComponent(zoneTransferTsigKeyNames),
success: function (responseJSON) {
btn.button('reset');
$("#modalZoneOptions").modal("hide");
showAlert("success", "Options Saved!", "Zone options were saved successfully.");
},
error: function () {
btn.button('reset');
divZoneOptionsLoader.hide();
},
invalidToken: function () {
btn.button('reset');
$("#modalZoneOptions").modal("hide");
showPageLogin();
},
objAlertPlaceholder: divZoneOptionsAlert,
objLoaderPlaceholder: divZoneOptionsLoader
});
}
function resyncZone(objBtn, domain) {
if ($("#titleEditZoneType").text() == "Secondary") {
if (!confirm("The resync action will perform a full zone transfer (AXFR). You will need to check the logs to confirm if the resync action was successful.\r\n\r\nAre you sure you want to resync the '" + domain + "' zone?"))
return false;
}
else {
if (!confirm("The resync action will perform a full zone refresh. You will need to check the logs to confirm if the resync action was successful.\r\n\r\nAre you sure you want to resync the '" + domain + "' zone?"))
return false;
}
var btn = $(objBtn);
btn.button('loading');
HTTPRequest({
url: "/api/zone/resync?token=" + token + "&domain=" + domain,
success: function (responseJSON) {
btn.button('reset');
showAlert("success", "Resync Triggered!", "Zone '" + domain + "' resync was triggered successfully. Please check the Logs for confirmation.");
},
error: function () {
btn.button('reset');
},
invalidToken: function () {
btn.button('reset');
showPageLogin();
}
});
}
function showAddZoneModal() {
$("#divAddZoneAlert").html("");
$("#txtAddZone").val("");
$("#rdAddZoneTypePrimary").prop("checked", true);
$("#txtAddZonePrimaryNameServerAddresses").val("");
$("#rdAddZoneZoneTransferProtocolTcp").prop("checked", true);
$("#optAddZoneTsigKeyName").val("");
$("input[name=rdAddZoneForwarderProtocol]:radio").attr("disabled", false);
$("#rdAddZoneForwarderProtocolUdp").prop("checked", true);
$("#chkAddZoneForwarderThisServer").prop('checked', false);
$("#txtAddZoneForwarder").prop("disabled", false);
$("#txtAddZoneForwarder").attr("placeholder", "8.8.8.8 or [2620:fe::10]")
$("#txtAddZoneForwarder").val("");
$("#divAddZonePrimaryNameServerAddresses").hide();
$("#divAddZoneZoneTransferProtocol").hide();
$("#divAddZoneTsigKeyName").hide();
$("#divAddZoneForwarderProtocol").hide();
$("#divAddZoneForwarder").hide();
$("#btnAddZone").button('reset');
$("#modalAddZone").modal("show");
setTimeout(function () {
$("#txtAddZone").focus();
}, 1000);
}
function updateAddZoneFormForwarderThisServer() {
var useThisServer = $("#chkAddZoneForwarderThisServer").prop('checked');
if (useThisServer) {
$("input[name=rdAddZoneForwarderProtocol]:radio").attr("disabled", true);
$("#rdAddZoneForwarderProtocolUdp").prop("checked", true);
$("#txtAddZoneForwarder").attr("placeholder", "8.8.8.8 or [2620:fe::10]")
$("#txtAddZoneForwarder").prop("disabled", true);
$("#txtAddZoneForwarder").val("this-server");
}
else {
$("input[name=rdAddZoneForwarderProtocol]:radio").attr("disabled", false);
$("#txtAddZoneForwarder").prop("disabled", false);
$("#txtAddZoneForwarder").val("");
}
}
function addZone() {
var divAddZoneAlert = $("#divAddZoneAlert");
var domain = $("#txtAddZone").val();
if ((domain == null) || (domain === "")) {
showAlert("warning", "Missing!", "Please enter a domain name to add zone.", divAddZoneAlert);
$("#txtAddZone").focus();
return;
}
var type = $('input[name=rdAddZoneType]:checked').val();
var parameters;
switch (type) {
case "Secondary":
var tsigKeyName = $("#optAddZoneTsigKeyName").val();
parameters = "&primaryNameServerAddresses=" + encodeURIComponent(cleanTextList($("#txtAddZonePrimaryNameServerAddresses").val()));
parameters += "&zoneTransferProtocol=" + $('input[name=rdAddZoneZoneTransferProtocol]:checked').val();
parameters += "&tsigKeyName=" + encodeURIComponent(tsigKeyName);
break;
case "Stub":
parameters = "&primaryNameServerAddresses=" + encodeURIComponent(cleanTextList($("#txtAddZonePrimaryNameServerAddresses").val()));
break;
case "Forwarder":
var forwarder = $("#txtAddZoneForwarder").val();
if ((forwarder == null) || (forwarder === "")) {
showAlert("warning", "Missing!", "Please enter a forwarder server name to add zone.", divAddZoneAlert);
$("#divAddZoneForwarder").focus();
return;
}
parameters = "&protocol=" + $('input[name=rdAddZoneForwarderProtocol]:checked').val() + "&forwarder=" + encodeURIComponent(forwarder);
break;
default:
parameters = "";
break;
}
var btn = $("#btnAddZone").button('loading');
HTTPRequest({
url: "/api/createZone?token=" + token + "&domain=" + domain + "&type=" + type + parameters,
success: function (responseJSON) {
$("#modalAddZone").modal("hide");
showEditZone(responseJSON.response.domain);
showAlert("success", "Zone Added!", "Zone was added successfully.");
},
error: function () {
btn.button('reset');
},
invalidToken: function () {
showPageLogin();
},
objAlertPlaceholder: divAddZoneAlert
});
}
function showEditZone(domain) {
var divViewZonesLoader = $("#divViewZonesLoader");
var divViewZones = $("#divViewZones");
var divEditZone = $("#divEditZone");
divViewZones.hide();
divEditZone.hide();
divViewZonesLoader.show();
HTTPRequest({
url: "/api/getRecords?token=" + token + "&domain=" + domain,
success: function (responseJSON) {
var type;
if (responseJSON.response.zone.internal)
type = "Internal";
else
type = responseJSON.response.zone.type;
var status;
if (responseJSON.response.zone.disabled)
status = "Disabled";
else if (responseJSON.response.zone.isExpired)
status = "Expired";
else
status = "Enabled";
var expiry = responseJSON.response.zone.expiry;
if (expiry == null)
expiry = " ";
else
expiry = "Expiry: " + moment(expiry).local().format("YYYY-MM-DD HH:mm:ss");
$("#titleEditZoneType").html(type);
$("#tdStatusEditZone").html(status);
$("#titleEditZoneExpiry").html(expiry);
if (responseJSON.response.zone.internal)
$("#titleEditZoneType").attr("class", "label label-default");
else
$("#titleEditZoneType").attr("class", "label label-primary");
switch (status) {
case "Disabled":
$("#tdStatusEditZone").attr("class", "label label-warning");
break;
case "Expired":
$("#tdStatusEditZone").attr("class", "label label-danger");
break;
case "Enabled":
$("#tdStatusEditZone").attr("class", "label label-success");
break;
}
switch (type) {
case "Internal":
case "Secondary":
case "Stub":
$("#btnEditZoneAddRecord").hide();
break;
case "Forwarder":
$("#btnEditZoneAddRecord").show();
$("#optEditRecordTypeFwd").show();
$("#optEditRecordTypeApp").hide();
break;
default:
$("#btnEditZoneAddRecord").show();
$("#optEditRecordTypeFwd").hide();
$("#optEditRecordTypeApp").show();
break;
}
if (responseJSON.response.zone.internal) {
$("#btnEnableZoneEditZone").hide();
$("#btnDisableZoneEditZone").hide();
$("#btnEditZoneDeleteZone").hide();
}
else if (responseJSON.response.zone.disabled) {
$("#btnEnableZoneEditZone").show();
$("#btnDisableZoneEditZone").hide();
$("#btnEditZoneDeleteZone").show();
}
else {
$("#btnEnableZoneEditZone").hide();
$("#btnDisableZoneEditZone").show();
$("#btnEditZoneDeleteZone").show();
}
switch (type) {
case "Secondary":
case "Stub":
$("#btnZoneResync").show();
break;
default:
$("#btnZoneResync").hide();
break;
}
switch (type) {
case "Primary":
case "Secondary":
$("#btnZoneOptions").show();
break;
default:
$("#btnZoneOptions").hide();
break;
}
var records = responseJSON.response.records;
var tableHtmlRows = "";
for (var i = 0; i < records.length; i++) {
var id = Math.floor(Math.random() * 10000);
var name = records[i].name.toLowerCase();
if (name === "")
name = ".";
if (name === domain)
name = "@";
else
name = name.replace("." + domain, "");
tableHtmlRows += "
" + htmlEncode(name) + "
";
tableHtmlRows += "
" + records[i].type + "
";
tableHtmlRows += "
" + records[i].ttl + "
";
var additionalDataAttributes = "";
switch (records[i].type.toUpperCase()) {
case "A":
case "CNAME":
case "DNAME":
case "PTR":
case "TXT":
case "AAAA":
case "ANAME":
tableHtmlRows += "