StatsManager.cs 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437
  1. /*
  2. Technitium DNS Server
  3. Copyright (C) 2020 Shreyas Zare (shreyas@technitium.com)
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. using System;
  16. using System.Collections.Concurrent;
  17. using System.Collections.Generic;
  18. using System.IO;
  19. using System.Net;
  20. using System.Text;
  21. using System.Threading;
  22. using TechnitiumLibrary.IO;
  23. using TechnitiumLibrary.Net;
  24. using TechnitiumLibrary.Net.Dns;
  25. namespace DnsServerCore.Dns
  26. {
  27. public enum StatsResponseCode
  28. {
  29. NoError = 1,
  30. ServerFailure = 2,
  31. NameError = 3,
  32. Refused = 4
  33. }
  34. public enum StatsResponseType
  35. {
  36. Authoritative = 1,
  37. Recursive = 2,
  38. Cached = 3,
  39. Blocked = 4
  40. }
  41. public class StatsManager : IDisposable
  42. {
  43. #region variables
  44. readonly string _statsFolder;
  45. readonly LogManager _log;
  46. readonly StatCounter[] _lastHourStatCounters = new StatCounter[60];
  47. readonly StatCounter[] _lastHourStatCountersCopy = new StatCounter[60];
  48. readonly ConcurrentDictionary<DateTime, HourlyStats> _hourlyStatsCache = new ConcurrentDictionary<DateTime, HourlyStats>();
  49. readonly ConcurrentDictionary<DateTime, StatCounter> _dailyStatsCache = new ConcurrentDictionary<DateTime, StatCounter>();
  50. readonly Timer _maintenanceTimer;
  51. const int MAINTENANCE_TIMER_INITIAL_INTERVAL = 10000;
  52. const int MAINTENANCE_TIMER_INTERVAL = 10000;
  53. readonly BlockingCollection<StatsQueueItem> _queue = new BlockingCollection<StatsQueueItem>();
  54. readonly Thread _consumerThread;
  55. #endregion
  56. #region constructor
  57. public StatsManager(string statsFolder, LogManager log)
  58. {
  59. _statsFolder = statsFolder;
  60. _log = log;
  61. //load stats
  62. LoadLastHourStats();
  63. //do first maintenance
  64. DoMaintenance();
  65. //start periodic maintenance timer
  66. _maintenanceTimer = new Timer(delegate (object state)
  67. {
  68. try
  69. {
  70. DoMaintenance();
  71. }
  72. catch (Exception ex)
  73. {
  74. _log.Write(ex);
  75. }
  76. }, null, MAINTENANCE_TIMER_INITIAL_INTERVAL, MAINTENANCE_TIMER_INTERVAL);
  77. //stats consumer thread
  78. _consumerThread = new Thread(delegate ()
  79. {
  80. try
  81. {
  82. foreach (StatsQueueItem item in _queue.GetConsumingEnumerable())
  83. {
  84. StatCounter statCounter = _lastHourStatCounters[item._dateTime.Minute];
  85. if (statCounter != null)
  86. statCounter.Update(item._query, item._responseCode, item._responseType, item._clientIpAddress);
  87. }
  88. }
  89. catch (Exception ex)
  90. {
  91. _log.Write(ex);
  92. }
  93. });
  94. _consumerThread.Name = "Stats";
  95. _consumerThread.IsBackground = true;
  96. _consumerThread.Start();
  97. }
  98. #endregion
  99. #region IDisposable
  100. private bool _disposed = false;
  101. private readonly object _disposeLock = new object();
  102. protected virtual void Dispose(bool disposing)
  103. {
  104. lock (_disposeLock)
  105. {
  106. if (_disposed)
  107. return;
  108. if (disposing)
  109. {
  110. if (_maintenanceTimer != null)
  111. _maintenanceTimer.Dispose();
  112. //do last maintenance
  113. DoMaintenance();
  114. }
  115. _disposed = true;
  116. }
  117. }
  118. public void Dispose()
  119. {
  120. Dispose(true);
  121. }
  122. #endregion
  123. #region private
  124. private void LoadLastHourStats()
  125. {
  126. DateTime currentDateTime = DateTime.UtcNow;
  127. DateTime lastHourDateTime = currentDateTime.AddMinutes(-60);
  128. HourlyStats lastHourlyStats = null;
  129. DateTime lastHourlyStatsDateTime = new DateTime();
  130. for (int i = 0; i < 60; i++)
  131. {
  132. DateTime lastDateTime = lastHourDateTime.AddMinutes(i);
  133. if ((lastHourlyStats == null) || (lastDateTime.Hour != lastHourlyStatsDateTime.Hour))
  134. {
  135. lastHourlyStats = LoadHourlyStats(lastDateTime);
  136. lastHourlyStatsDateTime = lastDateTime;
  137. }
  138. _lastHourStatCounters[lastDateTime.Minute] = lastHourlyStats.MinuteStats[lastDateTime.Minute];
  139. _lastHourStatCountersCopy[lastDateTime.Minute] = _lastHourStatCounters[lastDateTime.Minute];
  140. }
  141. }
  142. private void DoMaintenance()
  143. {
  144. //load new stats counter 5 min ahead of current time
  145. DateTime currentDateTime = DateTime.UtcNow;
  146. for (int i = 0; i < 5; i++)
  147. {
  148. int minute = currentDateTime.AddMinutes(i).Minute;
  149. StatCounter statCounter = _lastHourStatCounters[minute];
  150. if ((statCounter == null) || statCounter.IsLocked)
  151. _lastHourStatCounters[minute] = new StatCounter();
  152. }
  153. //save data upto last 5 mins
  154. DateTime last5MinDateTime = currentDateTime.AddMinutes(-5);
  155. for (int i = 0; i < 5; i++)
  156. {
  157. DateTime lastDateTime = last5MinDateTime.AddMinutes(i);
  158. StatCounter lastStatCounter = _lastHourStatCounters[lastDateTime.Minute];
  159. if ((lastStatCounter != null) && !lastStatCounter.IsLocked)
  160. {
  161. //load hourly stats data
  162. HourlyStats hourlyStats = LoadHourlyStats(lastDateTime);
  163. //update hourly stats file
  164. lastStatCounter.Lock();
  165. hourlyStats.UpdateStat(lastDateTime, lastStatCounter);
  166. //save hourly stats
  167. SaveHourlyStats(lastDateTime, hourlyStats);
  168. //keep copy for api
  169. _lastHourStatCountersCopy[lastDateTime.Minute] = lastStatCounter;
  170. }
  171. }
  172. //load previous day stats to auto create daily stats file
  173. LoadDailyStats(currentDateTime.AddDays(-1));
  174. //remove old data from hourly stats cache
  175. {
  176. DateTime threshold = DateTime.UtcNow.AddHours(-24);
  177. threshold = new DateTime(threshold.Year, threshold.Month, threshold.Day, threshold.Hour, 0, 0, DateTimeKind.Utc);
  178. List<DateTime> _keysToRemove = new List<DateTime>();
  179. foreach (KeyValuePair<DateTime, HourlyStats> item in _hourlyStatsCache)
  180. {
  181. if (item.Key < threshold)
  182. _keysToRemove.Add(item.Key);
  183. }
  184. foreach (DateTime key in _keysToRemove)
  185. _hourlyStatsCache.TryRemove(key, out _);
  186. }
  187. //remove old data from daily stats cache
  188. {
  189. DateTime threshold = DateTime.UtcNow.AddMonths(-12);
  190. threshold = new DateTime(threshold.Year, threshold.Month, 1, 0, 0, 0, DateTimeKind.Utc);
  191. List<DateTime> _keysToRemove = new List<DateTime>();
  192. foreach (KeyValuePair<DateTime, StatCounter> item in _dailyStatsCache)
  193. {
  194. if (item.Key < threshold)
  195. _keysToRemove.Add(item.Key);
  196. }
  197. foreach (DateTime key in _keysToRemove)
  198. _dailyStatsCache.TryRemove(key, out _);
  199. }
  200. }
  201. private HourlyStats LoadHourlyStats(DateTime dateTime)
  202. {
  203. DateTime hourlyDateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 0, 0, 0, DateTimeKind.Utc);
  204. if (!_hourlyStatsCache.TryGetValue(hourlyDateTime, out HourlyStats hourlyStats))
  205. {
  206. string hourlyStatsFile = Path.Combine(_statsFolder, dateTime.ToString("yyyyMMddHH") + ".stat");
  207. if (File.Exists(hourlyStatsFile))
  208. {
  209. try
  210. {
  211. using (FileStream fS = new FileStream(hourlyStatsFile, FileMode.Open, FileAccess.Read))
  212. {
  213. hourlyStats = new HourlyStats(new BinaryReader(fS));
  214. }
  215. }
  216. catch (Exception ex)
  217. {
  218. _log.Write(ex);
  219. hourlyStats = new HourlyStats();
  220. }
  221. }
  222. else
  223. {
  224. hourlyStats = new HourlyStats();
  225. }
  226. if (!_hourlyStatsCache.TryAdd(hourlyDateTime, hourlyStats))
  227. {
  228. if (!_hourlyStatsCache.TryGetValue(hourlyDateTime, out hourlyStats))
  229. throw new DnsServerException("Unable to load hourly stats.");
  230. }
  231. }
  232. return hourlyStats;
  233. }
  234. private StatCounter LoadDailyStats(DateTime dateTime)
  235. {
  236. DateTime dailyDateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0, DateTimeKind.Utc);
  237. if (!_dailyStatsCache.TryGetValue(dailyDateTime, out StatCounter dailyStats))
  238. {
  239. string dailyStatsFile = Path.Combine(_statsFolder, dateTime.ToString("yyyyMMdd") + ".dstat");
  240. if (File.Exists(dailyStatsFile))
  241. {
  242. try
  243. {
  244. using (FileStream fS = new FileStream(dailyStatsFile, FileMode.Open, FileAccess.Read))
  245. {
  246. dailyStats = new StatCounter(new BinaryReader(fS));
  247. }
  248. }
  249. catch (Exception ex)
  250. {
  251. _log.Write(ex);
  252. }
  253. }
  254. if (dailyStats == null)
  255. {
  256. dailyStats = new StatCounter();
  257. dailyStats.Lock();
  258. for (int hour = 0; hour < 24; hour++) //hours
  259. {
  260. HourlyStats hourlyStats = LoadHourlyStats(dailyDateTime.AddHours(hour));
  261. dailyStats.Merge(hourlyStats.HourStat);
  262. }
  263. if (dailyStats.TotalQueries > 0)
  264. SaveDailyStats(dailyDateTime, dailyStats);
  265. }
  266. if (!_dailyStatsCache.TryAdd(dailyDateTime, dailyStats))
  267. {
  268. if (!_dailyStatsCache.TryGetValue(dailyDateTime, out dailyStats))
  269. throw new DnsServerException("Unable to load daily stats.");
  270. }
  271. }
  272. return dailyStats;
  273. }
  274. private void SaveHourlyStats(DateTime dateTime, HourlyStats hourlyStats)
  275. {
  276. string hourlyStatsFile = Path.Combine(_statsFolder, dateTime.ToString("yyyyMMddHH") + ".stat");
  277. try
  278. {
  279. using (FileStream fS = new FileStream(hourlyStatsFile, FileMode.Create, FileAccess.Write))
  280. {
  281. hourlyStats.WriteTo(new BinaryWriter(fS));
  282. }
  283. }
  284. catch (Exception ex)
  285. {
  286. _log.Write(ex);
  287. }
  288. }
  289. private void SaveDailyStats(DateTime dateTime, StatCounter dailyStats)
  290. {
  291. string dailyStatsFile = Path.Combine(_statsFolder, dateTime.ToString("yyyyMMdd") + ".dstat");
  292. try
  293. {
  294. using (FileStream fS = new FileStream(dailyStatsFile, FileMode.Create, FileAccess.Write))
  295. {
  296. dailyStats.WriteTo(new BinaryWriter(fS));
  297. }
  298. }
  299. catch (Exception ex)
  300. {
  301. _log.Write(ex);
  302. }
  303. }
  304. #endregion
  305. #region public
  306. public void Update(DnsDatagram response, IPAddress clientIpAddress)
  307. {
  308. StatsResponseCode responseCode;
  309. switch (response.RCODE)
  310. {
  311. case DnsResponseCode.NoError:
  312. responseCode = StatsResponseCode.NoError;
  313. break;
  314. case DnsResponseCode.ServerFailure:
  315. responseCode = StatsResponseCode.ServerFailure;
  316. break;
  317. case DnsResponseCode.NameError:
  318. responseCode = StatsResponseCode.NameError;
  319. break;
  320. case DnsResponseCode.Refused:
  321. responseCode = StatsResponseCode.Refused;
  322. break;
  323. default:
  324. return;
  325. }
  326. StatsResponseType responseType;
  327. if (response.Tag == null)
  328. responseType = StatsResponseType.Recursive;
  329. else
  330. responseType = (StatsResponseType)response.Tag;
  331. StatsQueueItem item;
  332. if (response.QDCOUNT > 0)
  333. item = new StatsQueueItem(response.Question[0], responseCode, responseType, clientIpAddress);
  334. else
  335. item = new StatsQueueItem(new DnsQuestionRecord("", DnsResourceRecordType.ANY, DnsClass.IN), responseCode, responseType, clientIpAddress);
  336. _queue.Add(item);
  337. }
  338. public Dictionary<string, List<KeyValuePair<string, int>>> GetLastHourStats()
  339. {
  340. StatCounter totalStatCounter = new StatCounter();
  341. totalStatCounter.Lock();
  342. List<KeyValuePair<string, int>> totalQueriesPerInterval = new List<KeyValuePair<string, int>>(60);
  343. List<KeyValuePair<string, int>> totalNoErrorPerInterval = new List<KeyValuePair<string, int>>(60);
  344. List<KeyValuePair<string, int>> totalServerFailurePerInterval = new List<KeyValuePair<string, int>>(60);
  345. List<KeyValuePair<string, int>> totalNameErrorPerInterval = new List<KeyValuePair<string, int>>(60);
  346. List<KeyValuePair<string, int>> totalRefusedPerInterval = new List<KeyValuePair<string, int>>(60);
  347. List<KeyValuePair<string, int>> totalAuthHitPerInterval = new List<KeyValuePair<string, int>>(60);
  348. List<KeyValuePair<string, int>> totalRecursionsPerInterval = new List<KeyValuePair<string, int>>(60);
  349. List<KeyValuePair<string, int>> totalCacheHitPerInterval = new List<KeyValuePair<string, int>>(60);
  350. List<KeyValuePair<string, int>> totalBlockedPerInterval = new List<KeyValuePair<string, int>>(60);
  351. List<KeyValuePair<string, int>> totalClientsPerInterval = new List<KeyValuePair<string, int>>(60);
  352. DateTime lastHourDateTime = DateTime.UtcNow.AddMinutes(-60);
  353. lastHourDateTime = new DateTime(lastHourDateTime.Year, lastHourDateTime.Month, lastHourDateTime.Day, lastHourDateTime.Hour, lastHourDateTime.Minute, 0, DateTimeKind.Utc);
  354. for (int minute = 0; minute < 60; minute++)
  355. {
  356. DateTime lastDateTime = lastHourDateTime.AddMinutes(minute);
  357. string label = lastDateTime.ToLocalTime().ToString("HH:mm");
  358. StatCounter statCounter = _lastHourStatCountersCopy[lastDateTime.Minute];
  359. if ((statCounter != null) && statCounter.IsLocked)
  360. {
  361. totalStatCounter.Merge(statCounter);
  362. totalQueriesPerInterval.Add(new KeyValuePair<string, int>(label, statCounter.TotalQueries));
  363. totalNoErrorPerInterval.Add(new KeyValuePair<string, int>(label, statCounter.TotalNoError));
  364. totalServerFailurePerInterval.Add(new KeyValuePair<string, int>(label, statCounter.TotalServerFailure));
  365. totalNameErrorPerInterval.Add(new KeyValuePair<string, int>(label, statCounter.TotalNameError));
  366. totalRefusedPerInterval.Add(new KeyValuePair<string, int>(label, statCounter.TotalRefused));
  367. totalAuthHitPerInterval.Add(new KeyValuePair<string, int>(label, statCounter.TotalAuthoritative));
  368. totalRecursionsPerInterval.Add(new KeyValuePair<string, int>(label, statCounter.TotalRecursive));
  369. totalCacheHitPerInterval.Add(new KeyValuePair<string, int>(label, statCounter.TotalCached));
  370. totalBlockedPerInterval.Add(new KeyValuePair<string, int>(label, statCounter.TotalBlocked));
  371. totalClientsPerInterval.Add(new KeyValuePair<string, int>(label, statCounter.TotalClients));
  372. }
  373. else
  374. {
  375. totalQueriesPerInterval.Add(new KeyValuePair<string, int>(label, 0));
  376. totalNoErrorPerInterval.Add(new KeyValuePair<string, int>(label, 0));
  377. totalServerFailurePerInterval.Add(new KeyValuePair<string, int>(label, 0));
  378. totalNameErrorPerInterval.Add(new KeyValuePair<string, int>(label, 0));
  379. totalRefusedPerInterval.Add(new KeyValuePair<string, int>(label, 0));
  380. totalAuthHitPerInterval.Add(new KeyValuePair<string, int>(label, 0));
  381. totalRecursionsPerInterval.Add(new KeyValuePair<string, int>(label, 0));
  382. totalCacheHitPerInterval.Add(new KeyValuePair<string, int>(label, 0));
  383. totalBlockedPerInterval.Add(new KeyValuePair<string, int>(label, 0));
  384. totalClientsPerInterval.Add(new KeyValuePair<string, int>(label, 0));
  385. }
  386. }
  387. Dictionary<string, List<KeyValuePair<string, int>>> data = new Dictionary<string, List<KeyValuePair<string, int>>>();
  388. {
  389. List<KeyValuePair<string, int>> stats = new List<KeyValuePair<string, int>>(10);
  390. stats.Add(new KeyValuePair<string, int>("totalQueries", totalStatCounter.TotalQueries));
  391. stats.Add(new KeyValuePair<string, int>("totalNoError", totalStatCounter.TotalNoError));
  392. stats.Add(new KeyValuePair<string, int>("totalServerFailure", totalStatCounter.TotalServerFailure));
  393. stats.Add(new KeyValuePair<string, int>("totalNameError", totalStatCounter.TotalNameError));
  394. stats.Add(new KeyValuePair<string, int>("totalRefused", totalStatCounter.TotalRefused));
  395. stats.Add(new KeyValuePair<string, int>("totalAuthoritative", totalStatCounter.TotalAuthoritative));
  396. stats.Add(new KeyValuePair<string, int>("totalRecursive", totalStatCounter.TotalRecursive));
  397. stats.Add(new KeyValuePair<string, int>("totalCached", totalStatCounter.TotalCached));
  398. stats.Add(new KeyValuePair<string, int>("totalBlocked", totalStatCounter.TotalBlocked));
  399. stats.Add(new KeyValuePair<string, int>("totalClients", totalStatCounter.TotalClients));
  400. data.Add("stats", stats);
  401. }
  402. data.Add("totalQueriesPerInterval", totalQueriesPerInterval);
  403. data.Add("totalNoErrorPerInterval", totalNoErrorPerInterval);
  404. data.Add("totalServerFailurePerInterval", totalServerFailurePerInterval);
  405. data.Add("totalNameErrorPerInterval", totalNameErrorPerInterval);
  406. data.Add("totalRefusedPerInterval", totalRefusedPerInterval);
  407. data.Add("totalAuthHitPerInterval", totalAuthHitPerInterval);
  408. data.Add("totalRecursionsPerInterval", totalRecursionsPerInterval);
  409. data.Add("totalCacheHitPerInterval", totalCacheHitPerInterval);
  410. data.Add("totalBlockedPerInterval", totalBlockedPerInterval);
  411. data.Add("totalClientsPerInterval", totalClientsPerInterval);
  412. data.Add("topDomains", totalStatCounter.GetTopDomains(10));
  413. data.Add("topBlockedDomains", totalStatCounter.GetTopBlockedDomains(10));
  414. data.Add("topClients", totalStatCounter.GetTopClients(10));
  415. data.Add("queryTypes", totalStatCounter.GetTopQueryTypes(5));
  416. return data;
  417. }
  418. public Dictionary<string, List<KeyValuePair<string, int>>> GetLastDayStats()
  419. {
  420. StatCounter totalStatCounter = new StatCounter();
  421. totalStatCounter.Lock();
  422. List<KeyValuePair<string, int>> totalQueriesPerInterval = new List<KeyValuePair<string, int>>();
  423. List<KeyValuePair<string, int>> totalNoErrorPerInterval = new List<KeyValuePair<string, int>>();
  424. List<KeyValuePair<string, int>> totalServerFailurePerInterval = new List<KeyValuePair<string, int>>();
  425. List<KeyValuePair<string, int>> totalNameErrorPerInterval = new List<KeyValuePair<string, int>>();
  426. List<KeyValuePair<string, int>> totalRefusedPerInterval = new List<KeyValuePair<string, int>>();
  427. List<KeyValuePair<string, int>> totalAuthHitPerInterval = new List<KeyValuePair<string, int>>();
  428. List<KeyValuePair<string, int>> totalRecursionsPerInterval = new List<KeyValuePair<string, int>>();
  429. List<KeyValuePair<string, int>> totalCacheHitPerInterval = new List<KeyValuePair<string, int>>();
  430. List<KeyValuePair<string, int>> totalBlockedPerInterval = new List<KeyValuePair<string, int>>();
  431. List<KeyValuePair<string, int>> totalClientsPerInterval = new List<KeyValuePair<string, int>>();
  432. DateTime lastDayDateTime = DateTime.UtcNow.AddHours(-24);
  433. lastDayDateTime = new DateTime(lastDayDateTime.Year, lastDayDateTime.Month, lastDayDateTime.Day, lastDayDateTime.Hour, 0, 0, DateTimeKind.Utc);
  434. for (int hour = 0; hour < 24; hour++)
  435. {
  436. DateTime lastDateTime = lastDayDateTime.AddHours(hour);
  437. string label = lastDateTime.ToLocalTime().ToString("MM/dd HH") + ":00";
  438. HourlyStats hourlyStats = LoadHourlyStats(lastDateTime);
  439. StatCounter hourlyStatCounter = hourlyStats.HourStat;
  440. totalStatCounter.Merge(hourlyStatCounter);
  441. totalQueriesPerInterval.Add(new KeyValuePair<string, int>(label, hourlyStatCounter.TotalQueries));
  442. totalNoErrorPerInterval.Add(new KeyValuePair<string, int>(label, hourlyStatCounter.TotalNoError));
  443. totalServerFailurePerInterval.Add(new KeyValuePair<string, int>(label, hourlyStatCounter.TotalServerFailure));
  444. totalNameErrorPerInterval.Add(new KeyValuePair<string, int>(label, hourlyStatCounter.TotalNameError));
  445. totalRefusedPerInterval.Add(new KeyValuePair<string, int>(label, hourlyStatCounter.TotalRefused));
  446. totalAuthHitPerInterval.Add(new KeyValuePair<string, int>(label, hourlyStatCounter.TotalAuthoritative));
  447. totalRecursionsPerInterval.Add(new KeyValuePair<string, int>(label, hourlyStatCounter.TotalRecursive));
  448. totalCacheHitPerInterval.Add(new KeyValuePair<string, int>(label, hourlyStatCounter.TotalCached));
  449. totalBlockedPerInterval.Add(new KeyValuePair<string, int>(label, hourlyStatCounter.TotalBlocked));
  450. totalClientsPerInterval.Add(new KeyValuePair<string, int>(label, hourlyStatCounter.TotalClients));
  451. }
  452. Dictionary<string, List<KeyValuePair<string, int>>> data = new Dictionary<string, List<KeyValuePair<string, int>>>();
  453. {
  454. List<KeyValuePair<string, int>> stats = new List<KeyValuePair<string, int>>(6);
  455. stats.Add(new KeyValuePair<string, int>("totalQueries", totalStatCounter.TotalQueries));
  456. stats.Add(new KeyValuePair<string, int>("totalNoError", totalStatCounter.TotalNoError));
  457. stats.Add(new KeyValuePair<string, int>("totalServerFailure", totalStatCounter.TotalServerFailure));
  458. stats.Add(new KeyValuePair<string, int>("totalNameError", totalStatCounter.TotalNameError));
  459. stats.Add(new KeyValuePair<string, int>("totalRefused", totalStatCounter.TotalRefused));
  460. stats.Add(new KeyValuePair<string, int>("totalAuthoritative", totalStatCounter.TotalAuthoritative));
  461. stats.Add(new KeyValuePair<string, int>("totalRecursive", totalStatCounter.TotalRecursive));
  462. stats.Add(new KeyValuePair<string, int>("totalCached", totalStatCounter.TotalCached));
  463. stats.Add(new KeyValuePair<string, int>("totalBlocked", totalStatCounter.TotalBlocked));
  464. stats.Add(new KeyValuePair<string, int>("totalClients", totalStatCounter.TotalClients));
  465. data.Add("stats", stats);
  466. }
  467. data.Add("totalQueriesPerInterval", totalQueriesPerInterval);
  468. data.Add("totalNoErrorPerInterval", totalNoErrorPerInterval);
  469. data.Add("totalServerFailurePerInterval", totalServerFailurePerInterval);
  470. data.Add("totalNameErrorPerInterval", totalNameErrorPerInterval);
  471. data.Add("totalRefusedPerInterval", totalRefusedPerInterval);
  472. data.Add("totalAuthHitPerInterval", totalAuthHitPerInterval);
  473. data.Add("totalRecursionsPerInterval", totalRecursionsPerInterval);
  474. data.Add("totalCacheHitPerInterval", totalCacheHitPerInterval);
  475. data.Add("totalBlockedPerInterval", totalBlockedPerInterval);
  476. data.Add("totalClientsPerInterval", totalClientsPerInterval);
  477. data.Add("topDomains", totalStatCounter.GetTopDomains(10));
  478. data.Add("topBlockedDomains", totalStatCounter.GetTopBlockedDomains(10));
  479. data.Add("topClients", totalStatCounter.GetTopClients(10));
  480. data.Add("queryTypes", totalStatCounter.GetTopQueryTypes(5));
  481. return data;
  482. }
  483. public Dictionary<string, List<KeyValuePair<string, int>>> GetLastWeekStats()
  484. {
  485. StatCounter totalStatCounter = new StatCounter();
  486. totalStatCounter.Lock();
  487. List<KeyValuePair<string, int>> totalQueriesPerInterval = new List<KeyValuePair<string, int>>();
  488. List<KeyValuePair<string, int>> totalNoErrorPerInterval = new List<KeyValuePair<string, int>>();
  489. List<KeyValuePair<string, int>> totalServerFailurePerInterval = new List<KeyValuePair<string, int>>();
  490. List<KeyValuePair<string, int>> totalNameErrorPerInterval = new List<KeyValuePair<string, int>>();
  491. List<KeyValuePair<string, int>> totalRefusedPerInterval = new List<KeyValuePair<string, int>>();
  492. List<KeyValuePair<string, int>> totalAuthHitPerInterval = new List<KeyValuePair<string, int>>();
  493. List<KeyValuePair<string, int>> totalRecursionsPerInterval = new List<KeyValuePair<string, int>>();
  494. List<KeyValuePair<string, int>> totalCacheHitPerInterval = new List<KeyValuePair<string, int>>();
  495. List<KeyValuePair<string, int>> totalBlockedPerInterval = new List<KeyValuePair<string, int>>();
  496. List<KeyValuePair<string, int>> totalClientsPerInterval = new List<KeyValuePair<string, int>>();
  497. DateTime lastWeekDateTime = DateTime.UtcNow.AddDays(-7);
  498. lastWeekDateTime = new DateTime(lastWeekDateTime.Year, lastWeekDateTime.Month, lastWeekDateTime.Day, 0, 0, 0, DateTimeKind.Utc);
  499. for (int day = 0; day < 7; day++) //days
  500. {
  501. DateTime lastDayDateTime = lastWeekDateTime.AddDays(day);
  502. string label = lastDayDateTime.ToLocalTime().ToString("MM/dd");
  503. StatCounter dailyStatCounter = LoadDailyStats(lastDayDateTime);
  504. totalStatCounter.Merge(dailyStatCounter);
  505. totalQueriesPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalQueries));
  506. totalNoErrorPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalNoError));
  507. totalServerFailurePerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalServerFailure));
  508. totalNameErrorPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalNameError));
  509. totalRefusedPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalRefused));
  510. totalAuthHitPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalAuthoritative));
  511. totalRecursionsPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalRecursive));
  512. totalCacheHitPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalCached));
  513. totalBlockedPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalBlocked));
  514. totalClientsPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalClients));
  515. }
  516. Dictionary<string, List<KeyValuePair<string, int>>> data = new Dictionary<string, List<KeyValuePair<string, int>>>();
  517. {
  518. List<KeyValuePair<string, int>> stats = new List<KeyValuePair<string, int>>(6);
  519. stats.Add(new KeyValuePair<string, int>("totalQueries", totalStatCounter.TotalQueries));
  520. stats.Add(new KeyValuePair<string, int>("totalNoError", totalStatCounter.TotalNoError));
  521. stats.Add(new KeyValuePair<string, int>("totalServerFailure", totalStatCounter.TotalServerFailure));
  522. stats.Add(new KeyValuePair<string, int>("totalNameError", totalStatCounter.TotalNameError));
  523. stats.Add(new KeyValuePair<string, int>("totalRefused", totalStatCounter.TotalRefused));
  524. stats.Add(new KeyValuePair<string, int>("totalAuthoritative", totalStatCounter.TotalAuthoritative));
  525. stats.Add(new KeyValuePair<string, int>("totalRecursive", totalStatCounter.TotalRecursive));
  526. stats.Add(new KeyValuePair<string, int>("totalCached", totalStatCounter.TotalCached));
  527. stats.Add(new KeyValuePair<string, int>("totalBlocked", totalStatCounter.TotalBlocked));
  528. stats.Add(new KeyValuePair<string, int>("totalClients", totalStatCounter.TotalClients));
  529. data.Add("stats", stats);
  530. }
  531. data.Add("totalQueriesPerInterval", totalQueriesPerInterval);
  532. data.Add("totalNoErrorPerInterval", totalNoErrorPerInterval);
  533. data.Add("totalServerFailurePerInterval", totalServerFailurePerInterval);
  534. data.Add("totalNameErrorPerInterval", totalNameErrorPerInterval);
  535. data.Add("totalRefusedPerInterval", totalRefusedPerInterval);
  536. data.Add("totalAuthHitPerInterval", totalAuthHitPerInterval);
  537. data.Add("totalRecursionsPerInterval", totalRecursionsPerInterval);
  538. data.Add("totalCacheHitPerInterval", totalCacheHitPerInterval);
  539. data.Add("totalBlockedPerInterval", totalBlockedPerInterval);
  540. data.Add("totalClientsPerInterval", totalClientsPerInterval);
  541. data.Add("topDomains", totalStatCounter.GetTopDomains(10));
  542. data.Add("topBlockedDomains", totalStatCounter.GetTopBlockedDomains(10));
  543. data.Add("topClients", totalStatCounter.GetTopClients(10));
  544. data.Add("queryTypes", totalStatCounter.GetTopQueryTypes(5));
  545. return data;
  546. }
  547. public Dictionary<string, List<KeyValuePair<string, int>>> GetLastMonthStats()
  548. {
  549. StatCounter totalStatCounter = new StatCounter();
  550. totalStatCounter.Lock();
  551. List<KeyValuePair<string, int>> totalQueriesPerInterval = new List<KeyValuePair<string, int>>();
  552. List<KeyValuePair<string, int>> totalNoErrorPerInterval = new List<KeyValuePair<string, int>>();
  553. List<KeyValuePair<string, int>> totalServerFailurePerInterval = new List<KeyValuePair<string, int>>();
  554. List<KeyValuePair<string, int>> totalNameErrorPerInterval = new List<KeyValuePair<string, int>>();
  555. List<KeyValuePair<string, int>> totalRefusedPerInterval = new List<KeyValuePair<string, int>>();
  556. List<KeyValuePair<string, int>> totalAuthHitPerInterval = new List<KeyValuePair<string, int>>();
  557. List<KeyValuePair<string, int>> totalRecursionsPerInterval = new List<KeyValuePair<string, int>>();
  558. List<KeyValuePair<string, int>> totalCacheHitPerInterval = new List<KeyValuePair<string, int>>();
  559. List<KeyValuePair<string, int>> totalBlockedPerInterval = new List<KeyValuePair<string, int>>();
  560. List<KeyValuePair<string, int>> totalClientsPerInterval = new List<KeyValuePair<string, int>>();
  561. DateTime lastMonthDateTime = DateTime.UtcNow.AddDays(-31);
  562. lastMonthDateTime = new DateTime(lastMonthDateTime.Year, lastMonthDateTime.Month, lastMonthDateTime.Day, 0, 0, 0, DateTimeKind.Utc);
  563. for (int day = 0; day < 31; day++) //days
  564. {
  565. DateTime lastDayDateTime = lastMonthDateTime.AddDays(day);
  566. string label = lastDayDateTime.ToLocalTime().ToString("MM/dd");
  567. StatCounter dailyStatCounter = LoadDailyStats(lastDayDateTime);
  568. totalStatCounter.Merge(dailyStatCounter);
  569. totalQueriesPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalQueries));
  570. totalNoErrorPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalNoError));
  571. totalServerFailurePerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalServerFailure));
  572. totalNameErrorPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalNameError));
  573. totalRefusedPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalRefused));
  574. totalAuthHitPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalAuthoritative));
  575. totalRecursionsPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalRecursive));
  576. totalCacheHitPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalCached));
  577. totalBlockedPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalBlocked));
  578. totalClientsPerInterval.Add(new KeyValuePair<string, int>(label, dailyStatCounter.TotalClients));
  579. }
  580. Dictionary<string, List<KeyValuePair<string, int>>> data = new Dictionary<string, List<KeyValuePair<string, int>>>();
  581. {
  582. List<KeyValuePair<string, int>> stats = new List<KeyValuePair<string, int>>(6);
  583. stats.Add(new KeyValuePair<string, int>("totalQueries", totalStatCounter.TotalQueries));
  584. stats.Add(new KeyValuePair<string, int>("totalNoError", totalStatCounter.TotalNoError));
  585. stats.Add(new KeyValuePair<string, int>("totalServerFailure", totalStatCounter.TotalServerFailure));
  586. stats.Add(new KeyValuePair<string, int>("totalNameError", totalStatCounter.TotalNameError));
  587. stats.Add(new KeyValuePair<string, int>("totalRefused", totalStatCounter.TotalRefused));
  588. stats.Add(new KeyValuePair<string, int>("totalAuthoritative", totalStatCounter.TotalAuthoritative));
  589. stats.Add(new KeyValuePair<string, int>("totalRecursive", totalStatCounter.TotalRecursive));
  590. stats.Add(new KeyValuePair<string, int>("totalCached", totalStatCounter.TotalCached));
  591. stats.Add(new KeyValuePair<string, int>("totalBlocked", totalStatCounter.TotalBlocked));
  592. stats.Add(new KeyValuePair<string, int>("totalClients", totalStatCounter.TotalClients));
  593. data.Add("stats", stats);
  594. }
  595. data.Add("totalQueriesPerInterval", totalQueriesPerInterval);
  596. data.Add("totalNoErrorPerInterval", totalNoErrorPerInterval);
  597. data.Add("totalServerFailurePerInterval", totalServerFailurePerInterval);
  598. data.Add("totalNameErrorPerInterval", totalNameErrorPerInterval);
  599. data.Add("totalRefusedPerInterval", totalRefusedPerInterval);
  600. data.Add("totalAuthHitPerInterval", totalAuthHitPerInterval);
  601. data.Add("totalRecursionsPerInterval", totalRecursionsPerInterval);
  602. data.Add("totalCacheHitPerInterval", totalCacheHitPerInterval);
  603. data.Add("totalBlockedPerInterval", totalBlockedPerInterval);
  604. data.Add("totalClientsPerInterval", totalClientsPerInterval);
  605. data.Add("topDomains", totalStatCounter.GetTopDomains(10));
  606. data.Add("topBlockedDomains", totalStatCounter.GetTopBlockedDomains(10));
  607. data.Add("topClients", totalStatCounter.GetTopClients(10));
  608. data.Add("queryTypes", totalStatCounter.GetTopQueryTypes(5));
  609. return data;
  610. }
  611. public Dictionary<string, List<KeyValuePair<string, int>>> GetLastYearStats()
  612. {
  613. StatCounter totalStatCounter = new StatCounter();
  614. totalStatCounter.Lock();
  615. List<KeyValuePair<string, int>> totalQueriesPerInterval = new List<KeyValuePair<string, int>>();
  616. List<KeyValuePair<string, int>> totalNoErrorPerInterval = new List<KeyValuePair<string, int>>();
  617. List<KeyValuePair<string, int>> totalServerFailurePerInterval = new List<KeyValuePair<string, int>>();
  618. List<KeyValuePair<string, int>> totalNameErrorPerInterval = new List<KeyValuePair<string, int>>();
  619. List<KeyValuePair<string, int>> totalRefusedPerInterval = new List<KeyValuePair<string, int>>();
  620. List<KeyValuePair<string, int>> totalAuthHitPerInterval = new List<KeyValuePair<string, int>>();
  621. List<KeyValuePair<string, int>> totalRecursionsPerInterval = new List<KeyValuePair<string, int>>();
  622. List<KeyValuePair<string, int>> totalCacheHitPerInterval = new List<KeyValuePair<string, int>>();
  623. List<KeyValuePair<string, int>> totalBlockedPerInterval = new List<KeyValuePair<string, int>>();
  624. List<KeyValuePair<string, int>> totalClientsPerInterval = new List<KeyValuePair<string, int>>();
  625. DateTime lastYearDateTime = DateTime.UtcNow.AddMonths(-12);
  626. lastYearDateTime = new DateTime(lastYearDateTime.Year, lastYearDateTime.Month, 1, 0, 0, 0, DateTimeKind.Utc);
  627. for (int month = 0; month < 12; month++) //months
  628. {
  629. StatCounter monthlyStatCounter = new StatCounter();
  630. monthlyStatCounter.Lock();
  631. DateTime lastMonthDateTime = lastYearDateTime.AddMonths(month);
  632. string label = lastMonthDateTime.ToLocalTime().ToString("MM/yyyy");
  633. int days = DateTime.DaysInMonth(lastMonthDateTime.Year, lastMonthDateTime.Month);
  634. for (int day = 0; day < days; day++) //days
  635. {
  636. StatCounter dailyStatCounter = LoadDailyStats(lastMonthDateTime.AddDays(day));
  637. monthlyStatCounter.Merge(dailyStatCounter);
  638. }
  639. totalStatCounter.Merge(monthlyStatCounter);
  640. totalQueriesPerInterval.Add(new KeyValuePair<string, int>(label, monthlyStatCounter.TotalQueries));
  641. totalNoErrorPerInterval.Add(new KeyValuePair<string, int>(label, monthlyStatCounter.TotalNoError));
  642. totalServerFailurePerInterval.Add(new KeyValuePair<string, int>(label, monthlyStatCounter.TotalServerFailure));
  643. totalNameErrorPerInterval.Add(new KeyValuePair<string, int>(label, monthlyStatCounter.TotalNameError));
  644. totalRefusedPerInterval.Add(new KeyValuePair<string, int>(label, monthlyStatCounter.TotalRefused));
  645. totalAuthHitPerInterval.Add(new KeyValuePair<string, int>(label, monthlyStatCounter.TotalAuthoritative));
  646. totalRecursionsPerInterval.Add(new KeyValuePair<string, int>(label, monthlyStatCounter.TotalRecursive));
  647. totalCacheHitPerInterval.Add(new KeyValuePair<string, int>(label, monthlyStatCounter.TotalCached));
  648. totalBlockedPerInterval.Add(new KeyValuePair<string, int>(label, monthlyStatCounter.TotalBlocked));
  649. totalClientsPerInterval.Add(new KeyValuePair<string, int>(label, monthlyStatCounter.TotalClients));
  650. }
  651. Dictionary<string, List<KeyValuePair<string, int>>> data = new Dictionary<string, List<KeyValuePair<string, int>>>();
  652. {
  653. List<KeyValuePair<string, int>> stats = new List<KeyValuePair<string, int>>(6);
  654. stats.Add(new KeyValuePair<string, int>("totalQueries", totalStatCounter.TotalQueries));
  655. stats.Add(new KeyValuePair<string, int>("totalNoError", totalStatCounter.TotalNoError));
  656. stats.Add(new KeyValuePair<string, int>("totalServerFailure", totalStatCounter.TotalServerFailure));
  657. stats.Add(new KeyValuePair<string, int>("totalNameError", totalStatCounter.TotalNameError));
  658. stats.Add(new KeyValuePair<string, int>("totalRefused", totalStatCounter.TotalRefused));
  659. stats.Add(new KeyValuePair<string, int>("totalAuthoritative", totalStatCounter.TotalAuthoritative));
  660. stats.Add(new KeyValuePair<string, int>("totalRecursive", totalStatCounter.TotalRecursive));
  661. stats.Add(new KeyValuePair<string, int>("totalCached", totalStatCounter.TotalCached));
  662. stats.Add(new KeyValuePair<string, int>("totalBlocked", totalStatCounter.TotalBlocked));
  663. stats.Add(new KeyValuePair<string, int>("totalClients", totalStatCounter.TotalClients));
  664. data.Add("stats", stats);
  665. }
  666. data.Add("totalQueriesPerInterval", totalQueriesPerInterval);
  667. data.Add("totalNoErrorPerInterval", totalNoErrorPerInterval);
  668. data.Add("totalServerFailurePerInterval", totalServerFailurePerInterval);
  669. data.Add("totalNameErrorPerInterval", totalNameErrorPerInterval);
  670. data.Add("totalRefusedPerInterval", totalRefusedPerInterval);
  671. data.Add("totalAuthHitPerInterval", totalAuthHitPerInterval);
  672. data.Add("totalRecursionsPerInterval", totalRecursionsPerInterval);
  673. data.Add("totalCacheHitPerInterval", totalCacheHitPerInterval);
  674. data.Add("totalBlockedPerInterval", totalBlockedPerInterval);
  675. data.Add("totalClientsPerInterval", totalClientsPerInterval);
  676. data.Add("topDomains", totalStatCounter.GetTopDomains(10));
  677. data.Add("topBlockedDomains", totalStatCounter.GetTopBlockedDomains(10));
  678. data.Add("topClients", totalStatCounter.GetTopClients(10));
  679. data.Add("queryTypes", totalStatCounter.GetTopQueryTypes(5));
  680. return data;
  681. }
  682. public List<KeyValuePair<DnsQuestionRecord, int>> GetLastHourEligibleQueries(int minimumHitsPerHour)
  683. {
  684. StatCounter totalStatCounter = new StatCounter();
  685. totalStatCounter.Lock();
  686. DateTime lastHourDateTime = DateTime.UtcNow.AddMinutes(-60);
  687. lastHourDateTime = new DateTime(lastHourDateTime.Year, lastHourDateTime.Month, lastHourDateTime.Day, lastHourDateTime.Hour, lastHourDateTime.Minute, 0, DateTimeKind.Utc);
  688. for (int minute = 0; minute < 60; minute++)
  689. {
  690. DateTime lastDateTime = lastHourDateTime.AddMinutes(minute);
  691. StatCounter statCounter = _lastHourStatCountersCopy[lastDateTime.Minute];
  692. if ((statCounter != null) && statCounter.IsLocked)
  693. totalStatCounter.Merge(statCounter);
  694. }
  695. return totalStatCounter.GetEligibleQueries(minimumHitsPerHour);
  696. }
  697. #endregion
  698. class HourlyStats
  699. {
  700. #region variables
  701. readonly StatCounter _hourStat; //calculated value
  702. readonly StatCounter[] _minuteStats = new StatCounter[60];
  703. #endregion
  704. #region constructor
  705. public HourlyStats()
  706. {
  707. _hourStat = new StatCounter();
  708. _hourStat.Lock();
  709. }
  710. public HourlyStats(BinaryReader bR)
  711. {
  712. if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "HS") //format
  713. throw new InvalidDataException("HourlyStats format is invalid.");
  714. byte version = bR.ReadByte();
  715. switch (version)
  716. {
  717. case 1:
  718. _hourStat = new StatCounter();
  719. _hourStat.Lock();
  720. for (int i = 0; i < _minuteStats.Length; i++)
  721. {
  722. _minuteStats[i] = new StatCounter(bR);
  723. _hourStat.Merge(_minuteStats[i]);
  724. }
  725. break;
  726. default:
  727. throw new InvalidDataException("HourlyStats version not supported.");
  728. }
  729. }
  730. #endregion
  731. #region public
  732. public void UpdateStat(DateTime dateTime, StatCounter minuteStat)
  733. {
  734. if (!minuteStat.IsLocked)
  735. throw new DnsServerException("StatCounter must be locked.");
  736. _hourStat.Merge(minuteStat);
  737. _minuteStats[dateTime.Minute] = minuteStat;
  738. }
  739. public void WriteTo(BinaryWriter bW)
  740. {
  741. bW.Write(Encoding.ASCII.GetBytes("HS")); //format
  742. bW.Write((byte)1); //version
  743. for (int i = 0; i < _minuteStats.Length; i++)
  744. {
  745. if (_minuteStats[i] == null)
  746. {
  747. _minuteStats[i] = new StatCounter();
  748. _minuteStats[i].Lock();
  749. }
  750. _minuteStats[i].WriteTo(bW);
  751. }
  752. }
  753. #endregion
  754. #region properties
  755. public StatCounter HourStat
  756. { get { return _hourStat; } }
  757. public StatCounter[] MinuteStats
  758. {
  759. get { return _minuteStats; }
  760. }
  761. #endregion
  762. }
  763. class StatCounter
  764. {
  765. #region variables
  766. volatile bool _locked;
  767. int _totalQueries;
  768. int _totalNoError;
  769. int _totalServerFailure;
  770. int _totalNameError;
  771. int _totalRefused;
  772. int _totalAuthoritative;
  773. int _totalRecursive;
  774. int _totalCached;
  775. int _totalBlocked;
  776. readonly ConcurrentDictionary<string, Counter> _queryDomains = new ConcurrentDictionary<string, Counter>();
  777. readonly ConcurrentDictionary<string, Counter> _queryBlockedDomains = new ConcurrentDictionary<string, Counter>();
  778. readonly ConcurrentDictionary<DnsResourceRecordType, Counter> _queryTypes = new ConcurrentDictionary<DnsResourceRecordType, Counter>();
  779. readonly ConcurrentDictionary<IPAddress, Counter> _clientIpAddresses = new ConcurrentDictionary<IPAddress, Counter>();
  780. readonly ConcurrentDictionary<DnsQuestionRecord, Counter> _queries = new ConcurrentDictionary<DnsQuestionRecord, Counter>();
  781. #endregion
  782. #region constructor
  783. public StatCounter()
  784. { }
  785. public StatCounter(BinaryReader bR)
  786. {
  787. if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "SC") //format
  788. throw new InvalidDataException("StatCounter format is invalid.");
  789. byte version = bR.ReadByte();
  790. switch (version)
  791. {
  792. case 1:
  793. case 2:
  794. case 3:
  795. _totalQueries = bR.ReadInt32();
  796. _totalNoError = bR.ReadInt32();
  797. _totalServerFailure = bR.ReadInt32();
  798. _totalNameError = bR.ReadInt32();
  799. _totalRefused = bR.ReadInt32();
  800. if (version >= 3)
  801. {
  802. _totalAuthoritative = bR.ReadInt32();
  803. _totalRecursive = bR.ReadInt32();
  804. _totalCached = bR.ReadInt32();
  805. _totalBlocked = bR.ReadInt32();
  806. }
  807. else
  808. {
  809. _totalBlocked = bR.ReadInt32();
  810. if (version >= 2)
  811. _totalCached = bR.ReadInt32();
  812. }
  813. {
  814. int count = bR.ReadInt32();
  815. for (int i = 0; i < count; i++)
  816. _queryDomains.TryAdd(bR.ReadShortString(), new Counter(bR.ReadInt32()));
  817. }
  818. {
  819. int count = bR.ReadInt32();
  820. for (int i = 0; i < count; i++)
  821. _queryBlockedDomains.TryAdd(bR.ReadShortString(), new Counter(bR.ReadInt32()));
  822. }
  823. {
  824. int count = bR.ReadInt32();
  825. for (int i = 0; i < count; i++)
  826. _queryTypes.TryAdd((DnsResourceRecordType)bR.ReadUInt16(), new Counter(bR.ReadInt32()));
  827. }
  828. {
  829. int count = bR.ReadInt32();
  830. for (int i = 0; i < count; i++)
  831. _clientIpAddresses.TryAdd(IPAddressExtension.Parse(bR), new Counter(bR.ReadInt32()));
  832. }
  833. break;
  834. default:
  835. throw new InvalidDataException("StatCounter version not supported.");
  836. }
  837. _locked = true;
  838. }
  839. #endregion
  840. #region private
  841. private static List<KeyValuePair<string, int>> GetTopList(List<KeyValuePair<string, int>> list, int limit)
  842. {
  843. list.Sort(delegate (KeyValuePair<string, int> item1, KeyValuePair<string, int> item2)
  844. {
  845. return item2.Value.CompareTo(item1.Value);
  846. });
  847. if (list.Count > limit)
  848. list.RemoveRange(limit, list.Count - limit);
  849. return list;
  850. }
  851. private static Counter GetNewCounter<T>(T key)
  852. {
  853. return new Counter();
  854. }
  855. #endregion
  856. #region public
  857. public void Lock()
  858. {
  859. _locked = true;
  860. }
  861. public void Update(DnsQuestionRecord query, StatsResponseCode responseCode, StatsResponseType responseType, IPAddress clientIpAddress)
  862. {
  863. if (_locked)
  864. return;
  865. if (clientIpAddress.IsIPv4MappedToIPv6)
  866. clientIpAddress = clientIpAddress.MapToIPv4();
  867. string domain = query.Name.ToLower();
  868. Interlocked.Increment(ref _totalQueries);
  869. switch (responseCode)
  870. {
  871. case StatsResponseCode.NoError:
  872. if (responseType != StatsResponseType.Blocked) //skip blocked domains
  873. {
  874. _queryDomains.GetOrAdd(domain, GetNewCounter).Increment();
  875. _queries.GetOrAdd(query, GetNewCounter).Increment();
  876. }
  877. Interlocked.Increment(ref _totalNoError);
  878. break;
  879. case StatsResponseCode.ServerFailure:
  880. Interlocked.Increment(ref _totalServerFailure);
  881. break;
  882. case StatsResponseCode.NameError:
  883. Interlocked.Increment(ref _totalNameError);
  884. break;
  885. case StatsResponseCode.Refused:
  886. Interlocked.Increment(ref _totalRefused);
  887. break;
  888. }
  889. switch (responseType)
  890. {
  891. case StatsResponseType.Authoritative:
  892. Interlocked.Increment(ref _totalAuthoritative);
  893. break;
  894. case StatsResponseType.Recursive: //recursion
  895. Interlocked.Increment(ref _totalRecursive);
  896. break;
  897. case StatsResponseType.Cached:
  898. Interlocked.Increment(ref _totalCached);
  899. break;
  900. case StatsResponseType.Blocked:
  901. _queryBlockedDomains.GetOrAdd(domain, GetNewCounter).Increment();
  902. Interlocked.Increment(ref _totalBlocked);
  903. break;
  904. }
  905. _queryTypes.GetOrAdd(query.Type, GetNewCounter).Increment();
  906. _clientIpAddresses.GetOrAdd(clientIpAddress, GetNewCounter).Increment();
  907. }
  908. public void Merge(StatCounter statCounter)
  909. {
  910. if (!_locked || !statCounter._locked)
  911. throw new DnsServerException("StatCounter must be locked.");
  912. _totalQueries += statCounter._totalQueries;
  913. _totalNoError += statCounter._totalNoError;
  914. _totalServerFailure += statCounter._totalServerFailure;
  915. _totalNameError += statCounter._totalNameError;
  916. _totalRefused += statCounter._totalRefused;
  917. _totalAuthoritative += statCounter._totalAuthoritative;
  918. _totalRecursive += statCounter._totalRecursive;
  919. _totalCached += statCounter._totalCached;
  920. _totalBlocked += statCounter._totalBlocked;
  921. foreach (KeyValuePair<string, Counter> queryDomain in statCounter._queryDomains)
  922. _queryDomains.GetOrAdd(queryDomain.Key, GetNewCounter).Merge(queryDomain.Value);
  923. foreach (KeyValuePair<string, Counter> queryBlockedDomain in statCounter._queryBlockedDomains)
  924. _queryBlockedDomains.GetOrAdd(queryBlockedDomain.Key, GetNewCounter).Merge(queryBlockedDomain.Value);
  925. foreach (KeyValuePair<DnsResourceRecordType, Counter> queryType in statCounter._queryTypes)
  926. _queryTypes.GetOrAdd(queryType.Key, GetNewCounter).Merge(queryType.Value);
  927. foreach (KeyValuePair<IPAddress, Counter> clientIpAddress in statCounter._clientIpAddresses)
  928. _clientIpAddresses.GetOrAdd(clientIpAddress.Key, GetNewCounter).Merge(clientIpAddress.Value);
  929. foreach (KeyValuePair<DnsQuestionRecord, Counter> query in statCounter._queries)
  930. _queries.GetOrAdd(query.Key, GetNewCounter).Merge(query.Value);
  931. }
  932. public void WriteTo(BinaryWriter bW)
  933. {
  934. if (!_locked)
  935. throw new DnsServerException("StatCounter must be locked.");
  936. bW.Write(Encoding.ASCII.GetBytes("SC")); //format
  937. bW.Write((byte)3); //version
  938. bW.Write(_totalQueries);
  939. bW.Write(_totalNoError);
  940. bW.Write(_totalServerFailure);
  941. bW.Write(_totalNameError);
  942. bW.Write(_totalRefused);
  943. bW.Write(_totalAuthoritative);
  944. bW.Write(_totalRecursive);
  945. bW.Write(_totalCached);
  946. bW.Write(_totalBlocked);
  947. {
  948. bW.Write(_queryDomains.Count);
  949. foreach (KeyValuePair<string, Counter> queryDomain in _queryDomains)
  950. {
  951. bW.WriteShortString(queryDomain.Key);
  952. bW.Write(queryDomain.Value.Count);
  953. }
  954. }
  955. {
  956. bW.Write(_queryBlockedDomains.Count);
  957. foreach (KeyValuePair<string, Counter> queryBlockedDomain in _queryBlockedDomains)
  958. {
  959. bW.WriteShortString(queryBlockedDomain.Key);
  960. bW.Write(queryBlockedDomain.Value.Count);
  961. }
  962. }
  963. {
  964. bW.Write(_queryTypes.Count);
  965. foreach (KeyValuePair<DnsResourceRecordType, Counter> queryType in _queryTypes)
  966. {
  967. bW.Write((ushort)queryType.Key);
  968. bW.Write(queryType.Value.Count);
  969. }
  970. }
  971. {
  972. bW.Write(_clientIpAddresses.Count);
  973. foreach (KeyValuePair<IPAddress, Counter> clientIpAddress in _clientIpAddresses)
  974. {
  975. clientIpAddress.Key.WriteTo(bW);
  976. bW.Write(clientIpAddress.Value.Count);
  977. }
  978. }
  979. }
  980. public List<KeyValuePair<string, int>> GetTopDomains(int limit)
  981. {
  982. List<KeyValuePair<string, int>> topDomains = new List<KeyValuePair<string, int>>(10);
  983. foreach (KeyValuePair<string, Counter> item in _queryDomains)
  984. topDomains.Add(new KeyValuePair<string, int>(item.Key, item.Value.Count));
  985. return GetTopList(topDomains, limit);
  986. }
  987. public List<KeyValuePair<string, int>> GetTopBlockedDomains(int limit)
  988. {
  989. List<KeyValuePair<string, int>> topBlockedDomains = new List<KeyValuePair<string, int>>(10);
  990. foreach (KeyValuePair<string, Counter> item in _queryBlockedDomains)
  991. topBlockedDomains.Add(new KeyValuePair<string, int>(item.Key, item.Value.Count));
  992. return GetTopList(topBlockedDomains, limit);
  993. }
  994. public List<KeyValuePair<string, int>> GetTopClients(int limit)
  995. {
  996. List<KeyValuePair<string, int>> topClients = new List<KeyValuePair<string, int>>(10);
  997. foreach (KeyValuePair<IPAddress, Counter> item in _clientIpAddresses)
  998. topClients.Add(new KeyValuePair<string, int>(item.Key.ToString(), item.Value.Count));
  999. return GetTopList(topClients, limit);
  1000. }
  1001. public List<KeyValuePair<string, int>> GetTopQueryTypes(int limit)
  1002. {
  1003. List<KeyValuePair<string, int>> queryTypes = new List<KeyValuePair<string, int>>(10);
  1004. foreach (KeyValuePair<DnsResourceRecordType, Counter> item in _queryTypes)
  1005. queryTypes.Add(new KeyValuePair<string, int>(item.Key.ToString(), item.Value.Count));
  1006. queryTypes.Sort(delegate (KeyValuePair<string, int> item1, KeyValuePair<string, int> item2)
  1007. {
  1008. return item2.Value.CompareTo(item1.Value);
  1009. });
  1010. if (queryTypes.Count > limit)
  1011. {
  1012. int othersCount = 0;
  1013. for (int i = limit; i < queryTypes.Count; i++)
  1014. othersCount += queryTypes[i].Value;
  1015. queryTypes.RemoveRange((limit - 1), queryTypes.Count - (limit - 1));
  1016. queryTypes.Add(new KeyValuePair<string, int>("Others", othersCount));
  1017. }
  1018. return queryTypes;
  1019. }
  1020. public List<KeyValuePair<DnsQuestionRecord, int>> GetEligibleQueries(int minimumHits)
  1021. {
  1022. List<KeyValuePair<DnsQuestionRecord, int>> eligibleQueries = new List<KeyValuePair<DnsQuestionRecord, int>>(100);
  1023. foreach (KeyValuePair<DnsQuestionRecord, Counter> item in _queries)
  1024. {
  1025. if (item.Value.Count >= minimumHits)
  1026. eligibleQueries.Add(new KeyValuePair<DnsQuestionRecord, int>(item.Key, item.Value.Count));
  1027. }
  1028. return eligibleQueries;
  1029. }
  1030. #endregion
  1031. #region properties
  1032. public bool IsLocked
  1033. { get { return _locked; } }
  1034. public int TotalQueries
  1035. { get { return _totalQueries; } }
  1036. public int TotalNoError
  1037. { get { return _totalNoError; } }
  1038. public int TotalServerFailure
  1039. { get { return _totalServerFailure; } }
  1040. public int TotalNameError
  1041. { get { return _totalNameError; } }
  1042. public int TotalRefused
  1043. { get { return _totalRefused; } }
  1044. public int TotalAuthoritative
  1045. { get { return _totalAuthoritative; } }
  1046. public int TotalRecursive
  1047. { get { return _totalRecursive; } }
  1048. public int TotalCached
  1049. { get { return _totalCached; } }
  1050. public int TotalBlocked
  1051. { get { return _totalBlocked; } }
  1052. public int TotalClients
  1053. { get { return _clientIpAddresses.Count; } }
  1054. #endregion
  1055. class Counter
  1056. {
  1057. #region variables
  1058. int _count;
  1059. #endregion
  1060. #region constructor
  1061. public Counter()
  1062. { }
  1063. public Counter(int count)
  1064. {
  1065. _count = count;
  1066. }
  1067. #endregion
  1068. #region public
  1069. public void Increment()
  1070. {
  1071. Interlocked.Increment(ref _count);
  1072. }
  1073. public void Merge(Counter counter)
  1074. {
  1075. _count += counter._count;
  1076. }
  1077. #endregion
  1078. #region properties
  1079. public int Count
  1080. { get { return _count; } }
  1081. #endregion
  1082. }
  1083. }
  1084. class StatsQueueItem
  1085. {
  1086. #region variables
  1087. public readonly DateTime _dateTime;
  1088. public readonly DnsQuestionRecord _query;
  1089. public readonly StatsResponseCode _responseCode;
  1090. public readonly StatsResponseType _responseType;
  1091. public readonly IPAddress _clientIpAddress;
  1092. #endregion
  1093. #region constructor
  1094. public StatsQueueItem(DnsQuestionRecord query, StatsResponseCode responseCode, StatsResponseType responseType, IPAddress clientIpAddress)
  1095. {
  1096. _dateTime = DateTime.UtcNow;
  1097. _query = query;
  1098. _responseCode = responseCode;
  1099. _responseType = responseType;
  1100. _clientIpAddress = clientIpAddress;
  1101. }
  1102. #endregion
  1103. }
  1104. }
  1105. }