123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- /*
- Technitium DNS Server
- Copyright (C) 2023 Shreyas Zare (shreyas@technitium.com)
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- using DnsServerCore.Auth;
- using DnsServerCore.Dns;
- using Microsoft.AspNetCore.Http;
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Net;
- using System.Text.Json;
- using System.Threading.Tasks;
- using TechnitiumLibrary.Net.Dns;
- using TechnitiumLibrary.Net.Dns.ResourceRecords;
- namespace DnsServerCore
- {
- class WebServiceDashboardApi
- {
- #region variables
- readonly DnsWebService _dnsWebService;
- #endregion
- #region constructor
- public WebServiceDashboardApi(DnsWebService dnsWebService)
- {
- _dnsWebService = dnsWebService;
- }
- #endregion
- #region private
- private static void WriteChartDataSet(Utf8JsonWriter jsonWriter, string label, string backgroundColor, string borderColor, List<KeyValuePair<string, long>> statsPerInterval)
- {
- jsonWriter.WriteStartObject();
- jsonWriter.WriteString("label", label);
- jsonWriter.WriteString("backgroundColor", backgroundColor);
- jsonWriter.WriteString("borderColor", borderColor);
- jsonWriter.WriteNumber("borderWidth", 2);
- jsonWriter.WriteBoolean("fill", true);
- jsonWriter.WritePropertyName("data");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in statsPerInterval)
- jsonWriter.WriteNumberValue(item.Value);
- jsonWriter.WriteEndArray();
- jsonWriter.WriteEndObject();
- }
- private async Task<IDictionary<string, string>> ResolvePtrTopClientsAsync(List<KeyValuePair<string, long>> topClients)
- {
- IDictionary<string, string> dhcpClientIpMap = _dnsWebService.DhcpServer.GetAddressHostNameMap();
- async Task<KeyValuePair<string, string>> ResolvePtrAsync(string ip)
- {
- if (dhcpClientIpMap.TryGetValue(ip, out string dhcpDomain))
- return new KeyValuePair<string, string>(ip, dhcpDomain);
- IPAddress address = IPAddress.Parse(ip);
- if (IPAddress.IsLoopback(address))
- return new KeyValuePair<string, string>(ip, "localhost");
- 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)
- return new KeyValuePair<string, string>(ip, ptrDomains[0]);
- }
- return new KeyValuePair<string, string>(ip, null);
- }
- List<Task<KeyValuePair<string, string>>> resolverTasks = new List<Task<KeyValuePair<string, string>>>();
- foreach (KeyValuePair<string, long> item in topClients)
- {
- resolverTasks.Add(ResolvePtrAsync(item.Key));
- }
- Dictionary<string, string> result = new Dictionary<string, string>();
- foreach (Task<KeyValuePair<string, string>> resolverTask in resolverTasks)
- {
- try
- {
- KeyValuePair<string, string> ptrResult = await resolverTask;
- result[ptrResult.Key] = ptrResult.Value;
- }
- catch
- { }
- }
- return result;
- }
- #endregion
- #region public
- public async Task GetStats(HttpContext context)
- {
- if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Dashboard, context.GetCurrentSession().User, PermissionFlag.View))
- throw new DnsWebServiceException("Access was denied.");
- HttpRequest request = context.Request;
- string strType = request.GetQueryOrForm("type", "lastHour");
- bool utcFormat = request.GetQueryOrForm("utc", bool.Parse, false);
- bool isLanguageEnUs = true;
- string acceptLanguage = request.Headers["Accept-Language"];
- if (!string.IsNullOrEmpty(acceptLanguage))
- isLanguageEnUs = acceptLanguage.StartsWith("en-us", StringComparison.OrdinalIgnoreCase);
- Dictionary<string, List<KeyValuePair<string, long>>> data;
- string labelFormat;
- switch (strType.ToLower())
- {
- case "lasthour":
- data = _dnsWebService.DnsServer.StatsManager.GetLastHourMinuteWiseStats(utcFormat);
- labelFormat = "HH:mm";
- break;
- case "lastday":
- data = _dnsWebService.DnsServer.StatsManager.GetLastDayHourWiseStats(utcFormat);
- if (isLanguageEnUs)
- labelFormat = "MM/DD HH:00";
- else
- labelFormat = "DD/MM HH:00";
- break;
- case "lastweek":
- data = _dnsWebService.DnsServer.StatsManager.GetLastWeekDayWiseStats(utcFormat);
- if (isLanguageEnUs)
- labelFormat = "MM/DD";
- else
- labelFormat = "DD/MM";
- break;
- case "lastmonth":
- data = _dnsWebService.DnsServer.StatsManager.GetLastMonthDayWiseStats(utcFormat);
- if (isLanguageEnUs)
- labelFormat = "MM/DD";
- else
- labelFormat = "DD/MM";
- break;
- case "lastyear":
- labelFormat = "MM/YYYY";
- data = _dnsWebService.DnsServer.StatsManager.GetLastYearMonthWiseStats(utcFormat);
- break;
- case "custom":
- string strStartDate = request.GetQueryOrForm("start");
- string strEndDate = request.GetQueryOrForm("end");
- if (!DateTime.TryParse(strStartDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime startDate))
- throw new DnsWebServiceException("Invalid start date format.");
- if (!DateTime.TryParse(strEndDate, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime 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.");
- if ((Convert.ToInt32((endDate - startDate).TotalDays) + 1) > 7)
- {
- data = _dnsWebService.DnsServer.StatsManager.GetDayWiseStats(startDate, endDate, utcFormat);
- if (isLanguageEnUs)
- labelFormat = "MM/DD";
- else
- labelFormat = "DD/MM";
- }
- else
- {
- data = _dnsWebService.DnsServer.StatsManager.GetHourWiseStats(startDate, endDate, utcFormat);
- if (isLanguageEnUs)
- labelFormat = "MM/DD HH:00";
- else
- labelFormat = "DD/MM HH:00";
- }
- break;
- default:
- throw new DnsWebServiceException("Unknown stats type requested: " + strType);
- }
- Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
- //stats
- {
- List<KeyValuePair<string, long>> stats = data["stats"];
- jsonWriter.WritePropertyName("stats");
- jsonWriter.WriteStartObject();
- foreach (KeyValuePair<string, long> item in stats)
- jsonWriter.WriteNumber(item.Key, item.Value);
- jsonWriter.WriteNumber("zones", _dnsWebService.DnsServer.AuthZoneManager.TotalZones);
- jsonWriter.WriteNumber("cachedEntries", _dnsWebService.DnsServer.CacheZoneManager.TotalEntries);
- jsonWriter.WriteNumber("allowedZones", _dnsWebService.DnsServer.AllowedZoneManager.TotalZonesAllowed);
- jsonWriter.WriteNumber("blockedZones", _dnsWebService.DnsServer.BlockedZoneManager.TotalZonesBlocked);
- jsonWriter.WriteNumber("allowListZones", _dnsWebService.DnsServer.BlockListZoneManager.TotalZonesAllowed);
- jsonWriter.WriteNumber("blockListZones", _dnsWebService.DnsServer.BlockListZoneManager.TotalZonesBlocked);
- jsonWriter.WriteEndObject();
- }
- //main chart
- {
- jsonWriter.WritePropertyName("mainChartData");
- jsonWriter.WriteStartObject();
- //label format
- {
- jsonWriter.WriteString("labelFormat", labelFormat);
- }
- //label
- {
- List<KeyValuePair<string, long>> statsPerInterval = data["totalQueriesPerInterval"];
- jsonWriter.WritePropertyName("labels");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in statsPerInterval)
- jsonWriter.WriteStringValue(item.Key);
- jsonWriter.WriteEndArray();
- }
- //datasets
- {
- jsonWriter.WritePropertyName("datasets");
- jsonWriter.WriteStartArray();
- WriteChartDataSet(jsonWriter, "Total", "rgba(102, 153, 255, 0.1)", "rgb(102, 153, 255)", data["totalQueriesPerInterval"]);
- WriteChartDataSet(jsonWriter, "No Error", "rgba(92, 184, 92, 0.1)", "rgb(92, 184, 92)", data["totalNoErrorPerInterval"]);
- WriteChartDataSet(jsonWriter, "Server Failure", "rgba(217, 83, 79, 0.1)", "rgb(217, 83, 79)", data["totalServerFailurePerInterval"]);
- WriteChartDataSet(jsonWriter, "NX Domain", "rgba(7, 7, 7, 0.1)", "rgb(7, 7, 7)", data["totalNxDomainPerInterval"]);
- WriteChartDataSet(jsonWriter, "Refused", "rgba(91, 192, 222, 0.1)", "rgb(91, 192, 222)", data["totalRefusedPerInterval"]);
- WriteChartDataSet(jsonWriter, "Authoritative", "rgba(150, 150, 0, 0.1)", "rgb(150, 150, 0)", data["totalAuthHitPerInterval"]);
- WriteChartDataSet(jsonWriter, "Recursive", "rgba(23, 162, 184, 0.1)", "rgb(23, 162, 184)", data["totalRecursionsPerInterval"]);
- WriteChartDataSet(jsonWriter, "Cached", "rgba(111, 84, 153, 0.1)", "rgb(111, 84, 153)", data["totalCacheHitPerInterval"]);
- WriteChartDataSet(jsonWriter, "Blocked", "rgba(255, 165, 0, 0.1)", "rgb(255, 165, 0)", data["totalBlockedPerInterval"]);
- WriteChartDataSet(jsonWriter, "Clients", "rgba(51, 122, 183, 0.1)", "rgb(51, 122, 183)", data["totalClientsPerInterval"]);
- jsonWriter.WriteEndArray();
- }
- jsonWriter.WriteEndObject();
- }
- //query response chart
- {
- jsonWriter.WritePropertyName("queryResponseChartData");
- jsonWriter.WriteStartObject();
- List<KeyValuePair<string, long>> stats = data["stats"];
- //labels
- {
- jsonWriter.WritePropertyName("labels");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in stats)
- {
- switch (item.Key)
- {
- case "totalAuthoritative":
- jsonWriter.WriteStringValue("Authoritative");
- break;
- case "totalRecursive":
- jsonWriter.WriteStringValue("Recursive");
- break;
- case "totalCached":
- jsonWriter.WriteStringValue("Cached");
- break;
- case "totalBlocked":
- jsonWriter.WriteStringValue("Blocked");
- break;
- }
- }
- jsonWriter.WriteEndArray();
- }
- //datasets
- {
- jsonWriter.WritePropertyName("datasets");
- jsonWriter.WriteStartArray();
- jsonWriter.WriteStartObject();
- jsonWriter.WritePropertyName("data");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in stats)
- {
- switch (item.Key)
- {
- case "totalAuthoritative":
- case "totalRecursive":
- case "totalCached":
- case "totalBlocked":
- jsonWriter.WriteNumberValue(item.Value);
- break;
- }
- }
- 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.WriteEndArray();
- jsonWriter.WriteEndObject();
- jsonWriter.WriteEndArray();
- }
- jsonWriter.WriteEndObject();
- }
- //query type chart
- {
- jsonWriter.WritePropertyName("queryTypeChartData");
- jsonWriter.WriteStartObject();
- List<KeyValuePair<string, long>> queryTypes = data["queryTypes"];
- //labels
- {
- jsonWriter.WritePropertyName("labels");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in queryTypes)
- jsonWriter.WriteStringValue(item.Key);
- jsonWriter.WriteEndArray();
- }
- //datasets
- {
- jsonWriter.WritePropertyName("datasets");
- jsonWriter.WriteStartArray();
- jsonWriter.WriteStartObject();
- jsonWriter.WritePropertyName("data");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in queryTypes)
- jsonWriter.WriteNumberValue(item.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();
- }
- //top clients
- {
- List<KeyValuePair<string, long>> topClients = data["topClients"];
- IDictionary<string, string> clientIpMap = await ResolvePtrTopClientsAsync(topClients);
- jsonWriter.WritePropertyName("topClients");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in topClients)
- {
- jsonWriter.WriteStartObject();
- jsonWriter.WriteString("name", item.Key);
- if (clientIpMap.TryGetValue(item.Key, out string clientDomain) && !string.IsNullOrEmpty(clientDomain))
- jsonWriter.WriteString("domain", clientDomain);
- jsonWriter.WriteNumber("hits", item.Value);
- jsonWriter.WriteEndObject();
- }
- jsonWriter.WriteEndArray();
- }
- //top domains
- {
- List<KeyValuePair<string, long>> topDomains = data["topDomains"];
- jsonWriter.WritePropertyName("topDomains");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in topDomains)
- {
- jsonWriter.WriteStartObject();
- jsonWriter.WriteString("name", item.Key);
- if (item.Key.Contains("xn--", StringComparison.OrdinalIgnoreCase))
- jsonWriter.WriteString("nameIdn", DnsClient.ConvertDomainNameToUnicode(item.Key));
- jsonWriter.WriteNumber("hits", item.Value);
- jsonWriter.WriteEndObject();
- }
- jsonWriter.WriteEndArray();
- }
- //top blocked domains
- {
- List<KeyValuePair<string, long>> topBlockedDomains = data["topBlockedDomains"];
- jsonWriter.WritePropertyName("topBlockedDomains");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in topBlockedDomains)
- {
- jsonWriter.WriteStartObject();
- jsonWriter.WriteString("name", item.Key);
- if (item.Key.Contains("xn--", StringComparison.OrdinalIgnoreCase))
- jsonWriter.WriteString("nameIdn", DnsClient.ConvertDomainNameToUnicode(item.Key));
- jsonWriter.WriteNumber("hits", item.Value);
- jsonWriter.WriteEndObject();
- }
- jsonWriter.WriteEndArray();
- }
- }
- public async Task GetTopStats(HttpContext context)
- {
- if (!_dnsWebService._authManager.IsPermitted(PermissionSection.Dashboard, context.GetCurrentSession().User, PermissionFlag.View))
- throw new DnsWebServiceException("Access was denied.");
- HttpRequest request = context.Request;
- string strType = request.GetQueryOrForm("type", "lastHour");
- TopStatsType statsType = request.GetQueryOrFormEnum<TopStatsType>("statsType");
- int limit = request.GetQueryOrForm("limit", int.Parse, 1000);
- List<KeyValuePair<string, long>> topStatsData;
- switch (strType.ToLower())
- {
- case "lasthour":
- topStatsData = _dnsWebService.DnsServer.StatsManager.GetLastHourTopStats(statsType, limit);
- break;
- case "lastday":
- topStatsData = _dnsWebService.DnsServer.StatsManager.GetLastDayTopStats(statsType, limit);
- break;
- case "lastweek":
- topStatsData = _dnsWebService.DnsServer.StatsManager.GetLastWeekTopStats(statsType, limit);
- break;
- case "lastmonth":
- topStatsData = _dnsWebService.DnsServer.StatsManager.GetLastMonthTopStats(statsType, limit);
- break;
- case "lastyear":
- topStatsData = _dnsWebService.DnsServer.StatsManager.GetLastYearTopStats(statsType, limit);
- break;
- case "custom":
- string strStartDate = request.GetQueryOrForm("start");
- string strEndDate = request.GetQueryOrForm("end");
- if (!DateTime.TryParseExact(strStartDate, "yyyy-M-d", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime startDate))
- throw new DnsWebServiceException("Invalid start date format.");
- if (!DateTime.TryParseExact(strEndDate, "yyyy-M-d", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime 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.");
- if ((Convert.ToInt32((endDate - startDate).TotalDays) + 1) > 7)
- topStatsData = _dnsWebService.DnsServer.StatsManager.GetDayWiseTopStats(startDate, endDate, statsType, limit);
- else
- topStatsData = _dnsWebService.DnsServer.StatsManager.GetHourWiseTopStats(startDate, endDate, statsType, limit);
- break;
- default:
- throw new DnsWebServiceException("Unknown stats type requested: " + strType);
- }
- Utf8JsonWriter jsonWriter = context.GetCurrentJsonWriter();
- switch (statsType)
- {
- case TopStatsType.TopClients:
- {
- IDictionary<string, string> clientIpMap = await ResolvePtrTopClientsAsync(topStatsData);
- jsonWriter.WritePropertyName("topClients");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in topStatsData)
- {
- jsonWriter.WriteStartObject();
- jsonWriter.WriteString("name", item.Key);
- if (clientIpMap.TryGetValue(item.Key, out string clientDomain) && !string.IsNullOrEmpty(clientDomain))
- jsonWriter.WriteString("domain", clientDomain);
- jsonWriter.WriteNumber("hits", item.Value);
- jsonWriter.WriteEndObject();
- }
- jsonWriter.WriteEndArray();
- }
- break;
- case TopStatsType.TopDomains:
- {
- jsonWriter.WritePropertyName("topDomains");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in topStatsData)
- {
- jsonWriter.WriteStartObject();
- jsonWriter.WriteString("name", item.Key);
- if (item.Key.Contains("xn--", StringComparison.OrdinalIgnoreCase))
- jsonWriter.WriteString("nameIdn", DnsClient.ConvertDomainNameToUnicode(item.Key));
- jsonWriter.WriteNumber("hits", item.Value);
- jsonWriter.WriteEndObject();
- }
- jsonWriter.WriteEndArray();
- }
- break;
- case TopStatsType.TopBlockedDomains:
- {
- jsonWriter.WritePropertyName("topBlockedDomains");
- jsonWriter.WriteStartArray();
- foreach (KeyValuePair<string, long> item in topStatsData)
- {
- jsonWriter.WriteStartObject();
- jsonWriter.WriteString("name", item.Key);
- if (item.Key.Contains("xn--", StringComparison.OrdinalIgnoreCase))
- jsonWriter.WriteString("nameIdn", DnsClient.ConvertDomainNameToUnicode(item.Key));
- jsonWriter.WriteNumber("hits", item.Value);
- jsonWriter.WriteEndObject();
- }
- jsonWriter.WriteEndArray();
- }
- break;
- default:
- throw new NotSupportedException();
- }
- }
- #endregion
- }
- }
|