mirror of
https://github.com/TechnitiumSoftware/DnsServer.git
synced 2026-03-02 23:09:15 +00:00
863 lines
36 KiB
C#
863 lines
36 KiB
C#
/*
|
|
Technitium DNS Server
|
|
Copyright (C) 2025 Shreyas Zare (shreyas@technitium.com)
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
using DnsServerCore.Auth;
|
|
using DnsServerCore.HttpApi.Models;
|
|
using Microsoft.AspNetCore.Http;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Net;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using TechnitiumLibrary.Net.Dns;
|
|
using TechnitiumLibrary.Net.Dns.ResourceRecords;
|
|
|
|
namespace DnsServerCore
|
|
{
|
|
public partial class DnsWebService
|
|
{
|
|
class WebServiceDashboardApi
|
|
{
|
|
#region variables
|
|
|
|
readonly DnsWebService _dnsWebService;
|
|
|
|
const int CLUSTER_NODE_DASHBOARD_STATS_API_TIMEOUT = 10000;
|
|
|
|
#endregion
|
|
|
|
#region constructor
|
|
|
|
public WebServiceDashboardApi(DnsWebService dnsWebService)
|
|
{
|
|
_dnsWebService = dnsWebService;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region private
|
|
|
|
private static void WriteChartDataSet(Utf8JsonWriter jsonWriter, DashboardStats.DataSet dataSet, string backgroundColor, string borderColor)
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("label", dataSet.Label);
|
|
jsonWriter.WriteString("backgroundColor", backgroundColor);
|
|
jsonWriter.WriteString("borderColor", borderColor);
|
|
jsonWriter.WriteNumber("borderWidth", 2);
|
|
jsonWriter.WriteBoolean("fill", true);
|
|
|
|
jsonWriter.WritePropertyName("data");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (long value in dataSet.Data)
|
|
jsonWriter.WriteNumberValue(value);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
private async Task ResolvePtrTopClientsAsync(DashboardStats.TopClientStats[] topClients)
|
|
{
|
|
IDictionary<string, string> dhcpClientIpMap = _dnsWebService._dhcpServer.GetAddressHostNameMap();
|
|
|
|
async Task ResolvePtrAsync(DashboardStats.TopClientStats item)
|
|
{
|
|
string ip = item.Name;
|
|
|
|
if (dhcpClientIpMap.TryGetValue(ip, out string dhcpDomain))
|
|
{
|
|
item.Domain = dhcpDomain;
|
|
return;
|
|
}
|
|
|
|
IPAddress address = IPAddress.Parse(ip);
|
|
|
|
if (IPAddress.IsLoopback(address))
|
|
{
|
|
item.Domain = "localhost";
|
|
return;
|
|
}
|
|
|
|
DnsDatagram ptrResponse = await _dnsWebService._dnsServer.DirectQueryAsync(new DnsQuestionRecord(address, DnsClass.IN), 500);
|
|
if (ptrResponse.Answer.Count > 0)
|
|
{
|
|
IReadOnlyList<string> ptrDomains = DnsClient.ParseResponsePTR(ptrResponse);
|
|
if (ptrDomains.Count > 0)
|
|
{
|
|
item.Domain = ptrDomains[0];
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
List<Task> resolverTasks = new List<Task>(topClients.Length);
|
|
|
|
foreach (DashboardStats.TopClientStats item in topClients)
|
|
{
|
|
if (string.IsNullOrEmpty(item.Domain))
|
|
resolverTasks.Add(ResolvePtrAsync(item));
|
|
}
|
|
|
|
foreach (Task resolverTask in resolverTasks)
|
|
{
|
|
try
|
|
{
|
|
await resolverTask;
|
|
}
|
|
catch
|
|
{ }
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region public
|
|
|
|
public async Task GetStats(HttpContext context)
|
|
{
|
|
User sessionUser = _dnsWebService.GetSessionUser(context);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Dashboard, sessionUser, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
DashboardStatsType type = request.GetQueryOrFormEnum("type", DashboardStatsType.LastHour);
|
|
bool utcFormat = request.GetQueryOrForm("utc", bool.Parse, false);
|
|
|
|
bool isLanguageEnUs = true;
|
|
string acceptLanguage = request.Headers.AcceptLanguage;
|
|
if (!string.IsNullOrEmpty(acceptLanguage))
|
|
isLanguageEnUs = acceptLanguage.StartsWith("en-us", StringComparison.OrdinalIgnoreCase);
|
|
|
|
bool dontTrimQueryTypeData = request.GetQueryOrForm("dontTrimQueryTypeData", bool.Parse, false);
|
|
|
|
DateTime startDate = default;
|
|
DateTime endDate = default;
|
|
|
|
if (type == DashboardStatsType.Custom)
|
|
{
|
|
string strStartDate = request.GetQueryOrForm("start");
|
|
string strEndDate = request.GetQueryOrForm("end");
|
|
|
|
if (!DateTime.TryParse(strStartDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out startDate))
|
|
throw new DnsWebServiceException("Invalid start date format.");
|
|
|
|
if (!DateTime.TryParse(strEndDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out endDate))
|
|
throw new DnsWebServiceException("Invalid end date format.");
|
|
|
|
if (startDate > endDate)
|
|
throw new DnsWebServiceException("Start date must be less than or equal to end date.");
|
|
}
|
|
|
|
List<Task<DashboardStats>> tasks = null;
|
|
|
|
if (_dnsWebService._clusterManager.ClusterInitialized)
|
|
{
|
|
string node = request.GetQueryOrForm("node", null);
|
|
if ("cluster".Equals(node, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
IReadOnlyDictionary<int, Cluster.ClusterNode> clusterNodes = _dnsWebService._clusterManager.ClusterNodes;
|
|
tasks = new List<Task<DashboardStats>>(clusterNodes.Count);
|
|
|
|
foreach (KeyValuePair<int, Cluster.ClusterNode> clusterNode in clusterNodes)
|
|
{
|
|
if (clusterNode.Value.State == Cluster.ClusterNodeState.Self)
|
|
continue;
|
|
|
|
tasks.Add(TechnitiumLibrary.TaskExtensions.TimeoutAsync(delegate (CancellationToken cancellationToken1)
|
|
{
|
|
return clusterNode.Value.GetDashboardStatsAsync(sessionUser, type, utcFormat, acceptLanguage, true, startDate, endDate, cancellationToken1);
|
|
}, CLUSTER_NODE_DASHBOARD_STATS_API_TIMEOUT));
|
|
}
|
|
}
|
|
}
|
|
|
|
DashboardStats dashboardStats;
|
|
string labelFormat;
|
|
|
|
switch (type)
|
|
{
|
|
case DashboardStatsType.LastHour:
|
|
dashboardStats = _dnsWebService._dnsServer.StatsManager.GetLastHourMinuteWiseStats(utcFormat);
|
|
labelFormat = "HH:mm";
|
|
break;
|
|
|
|
case DashboardStatsType.LastDay:
|
|
dashboardStats = _dnsWebService._dnsServer.StatsManager.GetLastDayHourWiseStats(utcFormat);
|
|
|
|
if (isLanguageEnUs)
|
|
labelFormat = "MM/DD HH:00";
|
|
else
|
|
labelFormat = "DD/MM HH:00";
|
|
|
|
break;
|
|
|
|
case DashboardStatsType.LastWeek:
|
|
dashboardStats = _dnsWebService._dnsServer.StatsManager.GetLastWeekDayWiseStats(utcFormat);
|
|
|
|
if (isLanguageEnUs)
|
|
labelFormat = "MM/DD";
|
|
else
|
|
labelFormat = "DD/MM";
|
|
|
|
break;
|
|
|
|
case DashboardStatsType.LastMonth:
|
|
dashboardStats = _dnsWebService._dnsServer.StatsManager.GetLastMonthDayWiseStats(utcFormat);
|
|
|
|
if (isLanguageEnUs)
|
|
labelFormat = "MM/DD";
|
|
else
|
|
labelFormat = "DD/MM";
|
|
|
|
break;
|
|
|
|
case DashboardStatsType.LastYear:
|
|
labelFormat = "MM/YYYY";
|
|
dashboardStats = _dnsWebService._dnsServer.StatsManager.GetLastYearMonthWiseStats(utcFormat);
|
|
break;
|
|
|
|
case DashboardStatsType.Custom:
|
|
TimeSpan duration = endDate - startDate;
|
|
|
|
if ((Convert.ToInt32(duration.TotalDays) + 1) > 7)
|
|
{
|
|
dashboardStats = _dnsWebService._dnsServer.StatsManager.GetDayWiseStats(startDate, endDate, utcFormat);
|
|
|
|
if (isLanguageEnUs)
|
|
labelFormat = "MM/DD";
|
|
else
|
|
labelFormat = "DD/MM";
|
|
}
|
|
else if ((Convert.ToInt32(duration.TotalHours) + 1) > 3)
|
|
{
|
|
dashboardStats = _dnsWebService._dnsServer.StatsManager.GetHourWiseStats(startDate, endDate, utcFormat);
|
|
|
|
if (isLanguageEnUs)
|
|
labelFormat = "MM/DD HH:00";
|
|
else
|
|
labelFormat = "DD/MM HH:00";
|
|
}
|
|
else
|
|
{
|
|
dashboardStats = _dnsWebService._dnsServer.StatsManager.GetMinuteWiseStats(startDate, endDate, utcFormat);
|
|
|
|
if (isLanguageEnUs)
|
|
labelFormat = "MM/DD HH:mm";
|
|
else
|
|
labelFormat = "DD/MM HH:mm";
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
throw new DnsWebServiceException("Unknown stats type requested: " + type.ToString());
|
|
}
|
|
|
|
//add extra stats
|
|
{
|
|
dashboardStats.Stats.Zones = _dnsWebService._dnsServer.AuthZoneManager.TotalZones;
|
|
dashboardStats.Stats.CachedEntries = _dnsWebService._dnsServer.CacheZoneManager.TotalEntries;
|
|
dashboardStats.Stats.AllowedZones = _dnsWebService._dnsServer.AllowedZoneManager.TotalZonesAllowed;
|
|
dashboardStats.Stats.BlockedZones = _dnsWebService._dnsServer.BlockedZoneManager.TotalZonesBlocked;
|
|
dashboardStats.Stats.AllowListZones = _dnsWebService._dnsServer.BlockListZoneManager.TotalZonesAllowed;
|
|
dashboardStats.Stats.BlockListZones = _dnsWebService._dnsServer.BlockListZoneManager.TotalZonesBlocked;
|
|
}
|
|
|
|
if (tasks is not null)
|
|
{
|
|
foreach (Task<DashboardStats> task in tasks)
|
|
{
|
|
try
|
|
{
|
|
dashboardStats.Merge(await task, 10);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_dnsWebService._log.Write(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!dontTrimQueryTypeData)
|
|
dashboardStats.QueryTypeChartData.Trim(10); //trim query type data
|
|
|
|
Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
|
|
|
|
//stats
|
|
{
|
|
jsonWriter.WritePropertyName("stats");
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteNumber("totalQueries", dashboardStats.Stats.TotalQueries);
|
|
jsonWriter.WriteNumber("totalNoError", dashboardStats.Stats.TotalNoError);
|
|
jsonWriter.WriteNumber("totalServerFailure", dashboardStats.Stats.TotalServerFailure);
|
|
jsonWriter.WriteNumber("totalNxDomain", dashboardStats.Stats.TotalNxDomain);
|
|
jsonWriter.WriteNumber("totalRefused", dashboardStats.Stats.TotalRefused);
|
|
|
|
jsonWriter.WriteNumber("totalAuthoritative", dashboardStats.Stats.TotalAuthoritative);
|
|
jsonWriter.WriteNumber("totalRecursive", dashboardStats.Stats.TotalRecursive);
|
|
jsonWriter.WriteNumber("totalCached", dashboardStats.Stats.TotalCached);
|
|
jsonWriter.WriteNumber("totalBlocked", dashboardStats.Stats.TotalBlocked);
|
|
jsonWriter.WriteNumber("totalDropped", dashboardStats.Stats.TotalDropped);
|
|
|
|
jsonWriter.WriteNumber("totalClients", dashboardStats.Stats.TotalClients);
|
|
|
|
jsonWriter.WriteNumber("zones", dashboardStats.Stats.Zones);
|
|
jsonWriter.WriteNumber("cachedEntries", dashboardStats.Stats.CachedEntries);
|
|
jsonWriter.WriteNumber("allowedZones", dashboardStats.Stats.AllowedZones);
|
|
jsonWriter.WriteNumber("blockedZones", dashboardStats.Stats.BlockedZones);
|
|
jsonWriter.WriteNumber("allowListZones", dashboardStats.Stats.AllowListZones);
|
|
jsonWriter.WriteNumber("blockListZones", dashboardStats.Stats.BlockListZones);
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
//main chart
|
|
{
|
|
jsonWriter.WritePropertyName("mainChartData");
|
|
jsonWriter.WriteStartObject();
|
|
|
|
//label format
|
|
{
|
|
jsonWriter.WriteString("labelFormat", labelFormat);
|
|
}
|
|
|
|
//label
|
|
{
|
|
jsonWriter.WritePropertyName("labels");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (string label in dashboardStats.MainChartData.Labels)
|
|
jsonWriter.WriteStringValue(label);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
//datasets
|
|
{
|
|
jsonWriter.WritePropertyName("datasets");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DashboardStats.DataSet dataSet in dashboardStats.MainChartData.DataSets)
|
|
{
|
|
string backgroundColor;
|
|
string borderColor;
|
|
|
|
switch (dataSet.Label)
|
|
{
|
|
case "Total":
|
|
backgroundColor = "rgba(102, 153, 255, 0.1)";
|
|
borderColor = "rgb(102, 153, 255)";
|
|
break;
|
|
|
|
case "No Error":
|
|
backgroundColor = "rgba(92, 184, 92, 0.1)";
|
|
borderColor = "rgb(92, 184, 92)";
|
|
break;
|
|
|
|
case "Server Failure":
|
|
backgroundColor = "rgba(217, 83, 79, 0.1)";
|
|
borderColor = "rgb(217, 83, 79)";
|
|
break;
|
|
|
|
case "NX Domain":
|
|
backgroundColor = "rgba(120, 120, 120, 0.1)";
|
|
borderColor = "rgb(120, 120, 120)";
|
|
break;
|
|
|
|
case "Refused":
|
|
backgroundColor = "rgba(91, 192, 222, 0.1)";
|
|
borderColor = "rgb(91, 192, 222)";
|
|
break;
|
|
|
|
case "Authoritative":
|
|
backgroundColor = "rgba(150, 150, 0, 0.1)";
|
|
borderColor = "rgb(150, 150, 0)";
|
|
break;
|
|
|
|
case "Recursive":
|
|
backgroundColor = "rgba(23, 162, 184, 0.1)";
|
|
borderColor = "rgb(23, 162, 184)";
|
|
break;
|
|
|
|
case "Cached":
|
|
backgroundColor = "rgba(111, 84, 153, 0.1)";
|
|
borderColor = "rgb(111, 84, 153)";
|
|
break;
|
|
|
|
case "Blocked":
|
|
backgroundColor = "rgba(255, 165, 0, 0.1)";
|
|
borderColor = "rgb(255, 165, 0)";
|
|
break;
|
|
|
|
case "Dropped":
|
|
backgroundColor = "rgba(30, 30, 30, 0.1)";
|
|
borderColor = "rgb(30, 30, 30)";
|
|
break;
|
|
|
|
case "Clients":
|
|
backgroundColor = "rgba(51, 122, 183, 0.1)";
|
|
borderColor = "rgb(51, 122, 183)";
|
|
break;
|
|
|
|
default:
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
WriteChartDataSet(jsonWriter, dataSet, backgroundColor, borderColor);
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
//query response chart
|
|
{
|
|
jsonWriter.WritePropertyName("queryResponseChartData");
|
|
jsonWriter.WriteStartObject();
|
|
|
|
//labels
|
|
{
|
|
jsonWriter.WritePropertyName("labels");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (string label in dashboardStats.QueryResponseChartData.Labels)
|
|
jsonWriter.WriteStringValue(label);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
//datasets
|
|
{
|
|
jsonWriter.WritePropertyName("datasets");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WritePropertyName("data");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (long value in dashboardStats.QueryResponseChartData.DataSets[0].Data)
|
|
jsonWriter.WriteNumberValue(value);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
|
|
jsonWriter.WritePropertyName("backgroundColor");
|
|
jsonWriter.WriteStartArray();
|
|
jsonWriter.WriteStringValue("rgba(150, 150, 0, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(23, 162, 184, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(111, 84, 153, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(255, 165, 0, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(7, 7, 7, 0.5)");
|
|
jsonWriter.WriteEndArray();
|
|
|
|
jsonWriter.WriteEndObject();
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
//query type chart
|
|
{
|
|
jsonWriter.WritePropertyName("queryTypeChartData");
|
|
jsonWriter.WriteStartObject();
|
|
|
|
//labels
|
|
{
|
|
jsonWriter.WritePropertyName("labels");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (string label in dashboardStats.QueryTypeChartData.Labels)
|
|
jsonWriter.WriteStringValue(label);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
//datasets
|
|
{
|
|
jsonWriter.WritePropertyName("datasets");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WritePropertyName("data");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (long value in dashboardStats.QueryTypeChartData.DataSets[0].Data)
|
|
jsonWriter.WriteNumberValue(value);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
|
|
jsonWriter.WritePropertyName("backgroundColor");
|
|
jsonWriter.WriteStartArray();
|
|
jsonWriter.WriteStringValue("rgba(102, 153, 255, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(92, 184, 92, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(7, 7, 7, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(91, 192, 222, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(150, 150, 0, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(23, 162, 184, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(111, 84, 153, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(255, 165, 0, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(51, 122, 183, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(150, 150, 150, 0.5)");
|
|
jsonWriter.WriteEndArray();
|
|
|
|
jsonWriter.WriteEndObject();
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
//protocol type chart
|
|
{
|
|
jsonWriter.WritePropertyName("protocolTypeChartData");
|
|
jsonWriter.WriteStartObject();
|
|
|
|
//labels
|
|
{
|
|
jsonWriter.WritePropertyName("labels");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (string label in dashboardStats.ProtocolTypeChartData.Labels)
|
|
jsonWriter.WriteStringValue(label);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
//datasets
|
|
{
|
|
jsonWriter.WritePropertyName("datasets");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WritePropertyName("data");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (long value in dashboardStats.ProtocolTypeChartData.DataSets[0].Data)
|
|
jsonWriter.WriteNumberValue(value);
|
|
|
|
jsonWriter.WriteEndArray();
|
|
|
|
jsonWriter.WritePropertyName("backgroundColor");
|
|
jsonWriter.WriteStartArray();
|
|
jsonWriter.WriteStringValue("rgba(111, 84, 153, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(150, 150, 0, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(23, 162, 184, 0.5)"); ;
|
|
jsonWriter.WriteStringValue("rgba(255, 165, 0, 0.5)");
|
|
jsonWriter.WriteStringValue("rgba(91, 192, 222, 0.5)");
|
|
jsonWriter.WriteEndArray();
|
|
|
|
jsonWriter.WriteEndObject();
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
//top clients
|
|
{
|
|
await ResolvePtrTopClientsAsync(dashboardStats.TopClients);
|
|
|
|
jsonWriter.WritePropertyName("topClients");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DashboardStats.TopClientStats item in dashboardStats.TopClients)
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("name", item.Name);
|
|
|
|
if (!string.IsNullOrEmpty(item.Domain))
|
|
jsonWriter.WriteString("domain", item.Domain);
|
|
|
|
jsonWriter.WriteNumber("hits", item.Hits);
|
|
|
|
IPAddress ip = IPAddress.Parse(item.Name);
|
|
jsonWriter.WriteBoolean("rateLimited", item.RateLimited || _dnsWebService._dnsServer.HasQpmLimitExceeded(ip, DnsTransportProtocol.Udp) || _dnsWebService._dnsServer.HasQpmLimitExceeded(ip, DnsTransportProtocol.Tcp));
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
//top domains
|
|
{
|
|
jsonWriter.WritePropertyName("topDomains");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DashboardStats.TopStats item in dashboardStats.TopDomains)
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("name", item.Name);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(item.Name, out string idn))
|
|
jsonWriter.WriteString("nameIdn", idn);
|
|
|
|
jsonWriter.WriteNumber("hits", item.Hits);
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
|
|
//top blocked domains
|
|
{
|
|
jsonWriter.WritePropertyName("topBlockedDomains");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DashboardStats.TopStats item in dashboardStats.TopBlockedDomains)
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("name", item.Name);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(item.Name, out string idn))
|
|
jsonWriter.WriteString("nameIdn", idn);
|
|
|
|
jsonWriter.WriteNumber("hits", item.Hits);
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
}
|
|
|
|
public async Task GetTopStats(HttpContext context)
|
|
{
|
|
User sessionUser = _dnsWebService.GetSessionUser(context);
|
|
|
|
if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Dashboard, sessionUser, PermissionFlag.View))
|
|
throw new DnsWebServiceException("Access was denied.");
|
|
|
|
HttpRequest request = context.Request;
|
|
|
|
DashboardStatsType type = request.GetQueryOrFormEnum("type", DashboardStatsType.LastHour);
|
|
DashboardTopStatsType statsType = request.GetQueryOrFormEnum<DashboardTopStatsType>("statsType");
|
|
int limit = request.GetQueryOrForm("limit", int.Parse, 1000);
|
|
|
|
DateTime startDate = default;
|
|
DateTime endDate = default;
|
|
|
|
if (type == DashboardStatsType.Custom)
|
|
{
|
|
string strStartDate = request.GetQueryOrForm("start");
|
|
string strEndDate = request.GetQueryOrForm("end");
|
|
|
|
if (!DateTime.TryParse(strStartDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out startDate))
|
|
throw new DnsWebServiceException("Invalid start date format.");
|
|
|
|
if (!DateTime.TryParse(strEndDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out endDate))
|
|
throw new DnsWebServiceException("Invalid end date format.");
|
|
|
|
if (startDate > endDate)
|
|
throw new DnsWebServiceException("Start date must be less than or equal to end date.");
|
|
}
|
|
|
|
List<Task<DashboardStats>> tasks = null;
|
|
|
|
if (_dnsWebService._clusterManager.ClusterInitialized)
|
|
{
|
|
string node = request.GetQueryOrForm("node", null);
|
|
if ("cluster".Equals(node, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
IReadOnlyDictionary<int, Cluster.ClusterNode> clusterNodes = _dnsWebService._clusterManager.ClusterNodes;
|
|
tasks = new List<Task<DashboardStats>>(clusterNodes.Count);
|
|
|
|
foreach (KeyValuePair<int, Cluster.ClusterNode> clusterNode in clusterNodes)
|
|
{
|
|
if (clusterNode.Value.State == Cluster.ClusterNodeState.Self)
|
|
continue;
|
|
|
|
tasks.Add(TechnitiumLibrary.TaskExtensions.TimeoutAsync(delegate (CancellationToken cancellationToken1)
|
|
{
|
|
return clusterNode.Value.GetDashboardTopStatsAsync(sessionUser, statsType, limit, type, startDate, endDate, cancellationToken1);
|
|
}, CLUSTER_NODE_DASHBOARD_STATS_API_TIMEOUT));
|
|
}
|
|
}
|
|
}
|
|
|
|
DashboardStats topStatsData;
|
|
|
|
switch (type)
|
|
{
|
|
case DashboardStatsType.LastHour:
|
|
topStatsData = _dnsWebService._dnsServer.StatsManager.GetLastHourTopStats(statsType, limit);
|
|
break;
|
|
|
|
case DashboardStatsType.LastDay:
|
|
topStatsData = _dnsWebService._dnsServer.StatsManager.GetLastDayTopStats(statsType, limit);
|
|
break;
|
|
|
|
case DashboardStatsType.LastWeek:
|
|
topStatsData = _dnsWebService._dnsServer.StatsManager.GetLastWeekTopStats(statsType, limit);
|
|
break;
|
|
|
|
case DashboardStatsType.LastMonth:
|
|
topStatsData = _dnsWebService._dnsServer.StatsManager.GetLastMonthTopStats(statsType, limit);
|
|
break;
|
|
|
|
case DashboardStatsType.LastYear:
|
|
topStatsData = _dnsWebService._dnsServer.StatsManager.GetLastYearTopStats(statsType, limit);
|
|
break;
|
|
|
|
case DashboardStatsType.Custom:
|
|
TimeSpan duration = endDate - startDate;
|
|
|
|
if ((Convert.ToInt32(duration.TotalDays) + 1) > 7)
|
|
topStatsData = _dnsWebService._dnsServer.StatsManager.GetDayWiseTopStats(startDate, endDate, statsType, limit);
|
|
else if ((Convert.ToInt32(duration.TotalHours) + 1) > 3)
|
|
topStatsData = _dnsWebService._dnsServer.StatsManager.GetHourWiseTopStats(startDate, endDate, statsType, limit);
|
|
else
|
|
topStatsData = _dnsWebService._dnsServer.StatsManager.GetMinuteWiseTopStats(startDate, endDate, statsType, limit);
|
|
|
|
break;
|
|
|
|
default:
|
|
throw new DnsWebServiceException("Unknown stats type requested: " + type.ToString());
|
|
}
|
|
|
|
if (tasks is not null)
|
|
{
|
|
foreach (Task<DashboardStats> task in tasks)
|
|
{
|
|
try
|
|
{
|
|
topStatsData.Merge(await task, limit);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_dnsWebService._log.Write(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
|
|
|
|
switch (statsType)
|
|
{
|
|
case DashboardTopStatsType.TopClients:
|
|
{
|
|
bool noReverseLookup = request.GetQueryOrForm("noReverseLookup", bool.Parse, false);
|
|
bool onlyRateLimitedClients = request.GetQueryOrForm("onlyRateLimitedClients", bool.Parse, false);
|
|
|
|
if (!noReverseLookup)
|
|
await ResolvePtrTopClientsAsync(topStatsData.TopClients);
|
|
|
|
jsonWriter.WritePropertyName("topClients");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DashboardStats.TopClientStats item in topStatsData.TopClients)
|
|
{
|
|
IPAddress ip = IPAddress.Parse(item.Name);
|
|
bool rateLimited = item.RateLimited || _dnsWebService._dnsServer.HasQpmLimitExceeded(ip, DnsTransportProtocol.Udp) || _dnsWebService._dnsServer.HasQpmLimitExceeded(ip, DnsTransportProtocol.Tcp);
|
|
|
|
if (onlyRateLimitedClients && !rateLimited)
|
|
continue;
|
|
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("name", item.Name);
|
|
|
|
if (!string.IsNullOrEmpty(item.Domain))
|
|
jsonWriter.WriteString("domain", item.Domain);
|
|
|
|
jsonWriter.WriteNumber("hits", item.Hits);
|
|
jsonWriter.WriteBoolean("rateLimited", rateLimited);
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
break;
|
|
|
|
case DashboardTopStatsType.TopDomains:
|
|
{
|
|
jsonWriter.WritePropertyName("topDomains");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DashboardStats.TopStats item in topStatsData.TopDomains)
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("name", item.Name);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(item.Name, out string idn))
|
|
jsonWriter.WriteString("nameIdn", idn);
|
|
|
|
jsonWriter.WriteNumber("hits", item.Hits);
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
break;
|
|
|
|
case DashboardTopStatsType.TopBlockedDomains:
|
|
{
|
|
jsonWriter.WritePropertyName("topBlockedDomains");
|
|
jsonWriter.WriteStartArray();
|
|
|
|
foreach (DashboardStats.TopStats item in topStatsData.TopBlockedDomains)
|
|
{
|
|
jsonWriter.WriteStartObject();
|
|
|
|
jsonWriter.WriteString("name", item.Name);
|
|
|
|
if (DnsClient.TryConvertDomainNameToUnicode(item.Name, out string idn))
|
|
jsonWriter.WriteString("nameIdn", idn);
|
|
|
|
jsonWriter.WriteNumber("hits", item.Hits);
|
|
|
|
jsonWriter.WriteEndObject();
|
|
}
|
|
|
|
jsonWriter.WriteEndArray();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
}
|