StatsManager.cs 96 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258
  1. /*
  2. Technitium DNS Server
  3. Copyright (C) 2024 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 DnsServerCore.ApplicationCommon;
  16. using System;
  17. using System.Collections.Concurrent;
  18. using System.Collections.Generic;
  19. using System.Globalization;
  20. using System.IO;
  21. using System.Net;
  22. using System.Net.Sockets;
  23. using System.Text;
  24. using System.Threading;
  25. using TechnitiumLibrary.IO;
  26. using TechnitiumLibrary.Net;
  27. using TechnitiumLibrary.Net.Dns;
  28. using TechnitiumLibrary.Net.Dns.ResourceRecords;
  29. namespace DnsServerCore.Dns
  30. {
  31. public enum TopStatsType
  32. {
  33. Unknown = 0,
  34. TopClients = 1,
  35. TopDomains = 2,
  36. TopBlockedDomains = 3
  37. }
  38. public sealed class StatsManager : IDisposable
  39. {
  40. #region variables
  41. const int DAILY_STATS_FILE_TOP_LIMIT = 1000;
  42. readonly static HourlyStats _emptyHourlyStats = new HourlyStats();
  43. readonly static StatCounter _emptyDailyStats = new StatCounter();
  44. readonly DnsServer _dnsServer;
  45. readonly string _statsFolder;
  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_PERIODIC_INTERVAL = 10000;
  53. readonly BlockingCollection<StatsQueueItem> _queue = new BlockingCollection<StatsQueueItem>();
  54. readonly Thread _consumerThread;
  55. readonly Timer _statsCleanupTimer;
  56. const int STATS_CLEANUP_TIMER_INITIAL_INTERVAL = 60 * 1000;
  57. const int STATS_CLEANUP_TIMER_PERIODIC_INTERVAL = 60 * 60 * 1000;
  58. bool _enableInMemoryStats;
  59. int _maxStatFileDays;
  60. #endregion
  61. #region constructor
  62. static StatsManager()
  63. {
  64. _emptyDailyStats.Lock();
  65. }
  66. public StatsManager(DnsServer dnsServer)
  67. {
  68. _dnsServer = dnsServer;
  69. _statsFolder = Path.Combine(dnsServer.ConfigFolder, "stats");
  70. if (!Directory.Exists(_statsFolder))
  71. Directory.CreateDirectory(_statsFolder);
  72. //load stats
  73. LoadLastHourStats();
  74. try
  75. {
  76. //do first maintenance
  77. DoMaintenance();
  78. }
  79. catch (Exception ex)
  80. {
  81. _dnsServer.LogManager?.Write(ex);
  82. }
  83. //start periodic maintenance timer
  84. _maintenanceTimer = new Timer(delegate (object state)
  85. {
  86. try
  87. {
  88. DoMaintenance();
  89. }
  90. catch (Exception ex)
  91. {
  92. _dnsServer.LogManager?.Write(ex);
  93. }
  94. }, null, MAINTENANCE_TIMER_INITIAL_INTERVAL, MAINTENANCE_TIMER_PERIODIC_INTERVAL);
  95. //stats consumer thread
  96. _consumerThread = new Thread(delegate ()
  97. {
  98. try
  99. {
  100. foreach (StatsQueueItem item in _queue.GetConsumingEnumerable())
  101. {
  102. StatCounter statCounter = _lastHourStatCounters[item._timestamp.Minute];
  103. if (statCounter is not null)
  104. {
  105. DnsQuestionRecord query;
  106. if ((item._request is not null) && (item._request.Question.Count > 0))
  107. query = item._request.Question[0];
  108. else
  109. query = null;
  110. DnsServerResponseType responseType;
  111. if (item._response is null)
  112. responseType = DnsServerResponseType.Dropped;
  113. else if (item._response.Tag is null)
  114. responseType = DnsServerResponseType.Recursive;
  115. else
  116. responseType = (DnsServerResponseType)item._response.Tag;
  117. statCounter.Update(query, item._response is null ? DnsResponseCode.NoError : item._response.RCODE, responseType, item._remoteEP.Address, item._protocol, item._rateLimited);
  118. }
  119. if ((item._request is null) || (item._response is null))
  120. continue; //skip dropped requests for apps to prevent DOS
  121. foreach (IDnsQueryLogger logger in _dnsServer.DnsApplicationManager.DnsQueryLoggers)
  122. {
  123. try
  124. {
  125. _ = logger.InsertLogAsync(item._timestamp, item._request, item._remoteEP, item._protocol, item._response);
  126. }
  127. catch (Exception ex)
  128. {
  129. LogManager log = dnsServer.LogManager;
  130. if (log is not null)
  131. log.Write(ex);
  132. }
  133. }
  134. }
  135. }
  136. catch (Exception ex)
  137. {
  138. _dnsServer.LogManager?.Write(ex);
  139. }
  140. });
  141. _consumerThread.Name = "Stats";
  142. _consumerThread.IsBackground = true;
  143. _consumerThread.Start();
  144. _statsCleanupTimer = new Timer(delegate (object state)
  145. {
  146. try
  147. {
  148. if (_maxStatFileDays < 1)
  149. return;
  150. DateTime cutoffDate = DateTime.UtcNow.AddDays(_maxStatFileDays * -1).Date;
  151. LogManager log = dnsServer.LogManager;
  152. //delete hourly logs
  153. {
  154. string[] hourlyStatsFiles = Directory.GetFiles(Path.Combine(_dnsServer.ConfigFolder, "stats"), "*.stat");
  155. foreach (string hourlyStatsFile in hourlyStatsFiles)
  156. {
  157. string hourlyStatsFileName = Path.GetFileNameWithoutExtension(hourlyStatsFile);
  158. if (!DateTime.TryParseExact(hourlyStatsFileName, "yyyyMMddHH", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime hourlyStatsFileDate))
  159. continue;
  160. if (hourlyStatsFileDate < cutoffDate)
  161. {
  162. try
  163. {
  164. File.Delete(hourlyStatsFile);
  165. if (log != null)
  166. log.Write("StatsManager cleanup deleted the hourly stats file: " + hourlyStatsFile);
  167. }
  168. catch (Exception ex)
  169. {
  170. if (log != null)
  171. log.Write(ex);
  172. }
  173. }
  174. }
  175. }
  176. //delete daily logs
  177. {
  178. string[] dailyStatsFiles = Directory.GetFiles(Path.Combine(_dnsServer.ConfigFolder, "stats"), "*.dstat");
  179. foreach (string dailyStatsFile in dailyStatsFiles)
  180. {
  181. string dailyStatsFileName = Path.GetFileNameWithoutExtension(dailyStatsFile);
  182. if (!DateTime.TryParseExact(dailyStatsFileName, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime dailyStatsFileDate))
  183. continue;
  184. if (dailyStatsFileDate < cutoffDate)
  185. {
  186. try
  187. {
  188. File.Delete(dailyStatsFile);
  189. if (log != null)
  190. log.Write("StatsManager cleanup deleted the daily stats file: " + dailyStatsFile);
  191. }
  192. catch (Exception ex)
  193. {
  194. if (log != null)
  195. log.Write(ex);
  196. }
  197. }
  198. }
  199. }
  200. }
  201. catch (Exception ex)
  202. {
  203. _dnsServer.LogManager?.Write(ex);
  204. }
  205. });
  206. _statsCleanupTimer.Change(STATS_CLEANUP_TIMER_INITIAL_INTERVAL, STATS_CLEANUP_TIMER_PERIODIC_INTERVAL);
  207. }
  208. #endregion
  209. #region IDisposable
  210. bool _disposed;
  211. readonly object _disposeLock = new object();
  212. private void Dispose(bool disposing)
  213. {
  214. lock (_disposeLock)
  215. {
  216. if (_disposed)
  217. return;
  218. if (disposing)
  219. {
  220. if (_maintenanceTimer != null)
  221. _maintenanceTimer.Dispose();
  222. //do last maintenance
  223. DoMaintenance();
  224. }
  225. _disposed = true;
  226. }
  227. }
  228. public void Dispose()
  229. {
  230. Dispose(true);
  231. }
  232. #endregion
  233. #region private
  234. private void LoadLastHourStats()
  235. {
  236. try
  237. {
  238. DateTime currentDateTime = DateTime.UtcNow;
  239. DateTime lastHourDateTime = currentDateTime.AddMinutes(-60);
  240. HourlyStats lastHourlyStats = null;
  241. DateTime lastHourlyStatsDateTime = new DateTime();
  242. for (int i = 0; i < 60; i++)
  243. {
  244. DateTime lastDateTime = lastHourDateTime.AddMinutes(i);
  245. if ((lastHourlyStats == null) || (lastDateTime.Hour != lastHourlyStatsDateTime.Hour))
  246. {
  247. lastHourlyStats = LoadHourlyStats(lastDateTime);
  248. lastHourlyStatsDateTime = lastDateTime;
  249. }
  250. _lastHourStatCounters[lastDateTime.Minute] = lastHourlyStats.MinuteStats[lastDateTime.Minute];
  251. _lastHourStatCountersCopy[lastDateTime.Minute] = _lastHourStatCounters[lastDateTime.Minute];
  252. }
  253. }
  254. catch (Exception ex)
  255. {
  256. _dnsServer.LogManager?.Write(ex);
  257. }
  258. }
  259. private void DoMaintenance()
  260. {
  261. //load new stats counter 5 min ahead of current time
  262. DateTime currentDateTime = DateTime.UtcNow;
  263. for (int i = 0; i < 5; i++)
  264. {
  265. int minute = currentDateTime.AddMinutes(i).Minute;
  266. StatCounter statCounter = _lastHourStatCounters[minute];
  267. if ((statCounter == null) || statCounter.IsLocked)
  268. _lastHourStatCounters[minute] = new StatCounter();
  269. }
  270. //save data upto last 5 mins
  271. DateTime last5MinDateTime = currentDateTime.AddMinutes(-5);
  272. for (int i = 0; i < 5; i++)
  273. {
  274. DateTime lastDateTime = last5MinDateTime.AddMinutes(i);
  275. StatCounter lastStatCounter = _lastHourStatCounters[lastDateTime.Minute];
  276. if ((lastStatCounter != null) && !lastStatCounter.IsLocked)
  277. {
  278. lastStatCounter.Lock();
  279. if (!_enableInMemoryStats)
  280. {
  281. //load hourly stats data
  282. HourlyStats hourlyStats = LoadHourlyStats(lastDateTime);
  283. //update hourly stats file
  284. hourlyStats.UpdateStat(lastDateTime, lastStatCounter);
  285. //save hourly stats
  286. SaveHourlyStats(lastDateTime, hourlyStats);
  287. }
  288. //keep copy for api
  289. _lastHourStatCountersCopy[lastDateTime.Minute] = lastStatCounter;
  290. }
  291. }
  292. //load previous day stats to auto create daily stats file
  293. LoadDailyStats(currentDateTime.AddDays(-1));
  294. //remove old data from hourly stats cache
  295. {
  296. DateTime threshold = DateTime.UtcNow.AddHours(-24);
  297. threshold = new DateTime(threshold.Year, threshold.Month, threshold.Day, threshold.Hour, 0, 0, DateTimeKind.Utc);
  298. List<DateTime> _keysToRemove = new List<DateTime>();
  299. foreach (KeyValuePair<DateTime, HourlyStats> item in _hourlyStatsCache)
  300. {
  301. if (item.Key < threshold)
  302. _keysToRemove.Add(item.Key);
  303. }
  304. foreach (DateTime key in _keysToRemove)
  305. _hourlyStatsCache.TryRemove(key, out _);
  306. }
  307. //unload minute stats data from hourly stats cache for data older than last hour
  308. {
  309. DateTime lastHourThreshold = DateTime.UtcNow.AddHours(-1);
  310. lastHourThreshold = new DateTime(lastHourThreshold.Year, lastHourThreshold.Month, lastHourThreshold.Day, lastHourThreshold.Hour, 0, 0, DateTimeKind.Utc);
  311. foreach (KeyValuePair<DateTime, HourlyStats> item in _hourlyStatsCache)
  312. {
  313. if (item.Key < lastHourThreshold)
  314. item.Value.UnloadMinuteStats();
  315. }
  316. }
  317. //remove old data from daily stats cache
  318. {
  319. DateTime threshold = DateTime.UtcNow.AddMonths(-12);
  320. threshold = new DateTime(threshold.Year, threshold.Month, 1, 0, 0, 0, DateTimeKind.Utc);
  321. List<DateTime> _keysToRemove = new List<DateTime>();
  322. foreach (KeyValuePair<DateTime, StatCounter> item in _dailyStatsCache)
  323. {
  324. if (item.Key < threshold)
  325. _keysToRemove.Add(item.Key);
  326. }
  327. foreach (DateTime key in _keysToRemove)
  328. _dailyStatsCache.TryRemove(key, out _);
  329. }
  330. }
  331. private HourlyStats LoadHourlyStats(DateTime dateTime)
  332. {
  333. if (_enableInMemoryStats)
  334. return _emptyHourlyStats;
  335. DateTime hourlyDateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 0, 0, 0, DateTimeKind.Utc);
  336. if (!_hourlyStatsCache.TryGetValue(hourlyDateTime, out HourlyStats hourlyStats))
  337. {
  338. string hourlyStatsFile = Path.Combine(_statsFolder, dateTime.ToString("yyyyMMddHH") + ".stat");
  339. if (File.Exists(hourlyStatsFile))
  340. {
  341. try
  342. {
  343. using (FileStream fS = new FileStream(hourlyStatsFile, FileMode.Open, FileAccess.Read))
  344. {
  345. hourlyStats = new HourlyStats(new BinaryReader(fS));
  346. }
  347. }
  348. catch (Exception ex)
  349. {
  350. LogManager log = _dnsServer.LogManager;
  351. if (log != null)
  352. log.Write(ex);
  353. hourlyStats = new HourlyStats();
  354. }
  355. }
  356. else
  357. {
  358. hourlyStats = new HourlyStats();
  359. }
  360. if (!_hourlyStatsCache.TryAdd(hourlyDateTime, hourlyStats))
  361. {
  362. if (!_hourlyStatsCache.TryGetValue(hourlyDateTime, out hourlyStats))
  363. throw new DnsServerException("Unable to load hourly stats.");
  364. }
  365. }
  366. return hourlyStats;
  367. }
  368. private StatCounter LoadDailyStats(DateTime dateTime)
  369. {
  370. if (_enableInMemoryStats)
  371. return _emptyDailyStats;
  372. DateTime dailyDateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0, DateTimeKind.Utc);
  373. if (!_dailyStatsCache.TryGetValue(dailyDateTime, out StatCounter dailyStats))
  374. {
  375. string dailyStatsFile = Path.Combine(_statsFolder, dateTime.ToString("yyyyMMdd") + ".dstat");
  376. if (File.Exists(dailyStatsFile))
  377. {
  378. try
  379. {
  380. using (FileStream fS = new FileStream(dailyStatsFile, FileMode.Open, FileAccess.Read))
  381. {
  382. dailyStats = new StatCounter(new BinaryReader(fS));
  383. }
  384. //check if existing file could be truncated to avoid loading unnecessary data in memory
  385. if (dailyStats.Truncate(DAILY_STATS_FILE_TOP_LIMIT))
  386. SaveDailyStats(dailyDateTime, dailyStats); //save truncated file
  387. }
  388. catch (Exception ex)
  389. {
  390. LogManager log = _dnsServer.LogManager;
  391. if (log != null)
  392. log.Write(ex);
  393. }
  394. }
  395. if (dailyStats == null)
  396. {
  397. dailyStats = new StatCounter();
  398. dailyStats.Lock();
  399. for (int hour = 0; hour < 24; hour++) //hours
  400. {
  401. HourlyStats hourlyStats = LoadHourlyStats(dailyDateTime.AddHours(hour));
  402. dailyStats.Merge(hourlyStats.HourStat);
  403. }
  404. if (dailyStats.TotalQueries > 0)
  405. {
  406. _ = dailyStats.Truncate(DAILY_STATS_FILE_TOP_LIMIT);
  407. SaveDailyStats(dailyDateTime, dailyStats);
  408. }
  409. }
  410. if (!_dailyStatsCache.TryAdd(dailyDateTime, dailyStats))
  411. {
  412. if (!_dailyStatsCache.TryGetValue(dailyDateTime, out dailyStats))
  413. throw new DnsServerException("Unable to load daily stats.");
  414. }
  415. }
  416. return dailyStats;
  417. }
  418. private void SaveHourlyStats(DateTime dateTime, HourlyStats hourlyStats)
  419. {
  420. string hourlyStatsFile = Path.Combine(_statsFolder, dateTime.ToString("yyyyMMddHH") + ".stat");
  421. try
  422. {
  423. using (FileStream fS = new FileStream(hourlyStatsFile, FileMode.Create, FileAccess.Write))
  424. {
  425. hourlyStats.WriteTo(new BinaryWriter(fS));
  426. }
  427. }
  428. catch (Exception ex)
  429. {
  430. LogManager log = _dnsServer.LogManager;
  431. if (log != null)
  432. log.Write(ex);
  433. }
  434. }
  435. private void SaveDailyStats(DateTime dateTime, StatCounter dailyStats)
  436. {
  437. string dailyStatsFile = Path.Combine(_statsFolder, dateTime.ToString("yyyyMMdd") + ".dstat");
  438. try
  439. {
  440. using (FileStream fS = new FileStream(dailyStatsFile, FileMode.Create, FileAccess.Write))
  441. {
  442. dailyStats.WriteTo(new BinaryWriter(fS));
  443. }
  444. }
  445. catch (Exception ex)
  446. {
  447. LogManager log = _dnsServer.LogManager;
  448. if (log != null)
  449. log.Write(ex);
  450. }
  451. }
  452. private void Flush()
  453. {
  454. //clear in memory stats
  455. for (int i = 0; i < _lastHourStatCountersCopy.Length; i++)
  456. _lastHourStatCountersCopy[i] = null;
  457. _hourlyStatsCache.Clear();
  458. _dailyStatsCache.Clear();
  459. }
  460. #endregion
  461. #region public
  462. public void ReloadStats()
  463. {
  464. Flush();
  465. LoadLastHourStats();
  466. }
  467. public void DeleteAllStats()
  468. {
  469. foreach (string hourlyStatsFile in Directory.GetFiles(Path.Combine(_dnsServer.ConfigFolder, "stats"), "*.stat", SearchOption.TopDirectoryOnly))
  470. {
  471. File.Delete(hourlyStatsFile);
  472. }
  473. foreach (string dailyStatsFile in Directory.GetFiles(Path.Combine(_dnsServer.ConfigFolder, "stats"), "*.dstat", SearchOption.TopDirectoryOnly))
  474. {
  475. File.Delete(dailyStatsFile);
  476. }
  477. Flush();
  478. }
  479. public void QueueUpdate(DnsDatagram request, IPEndPoint remoteEP, DnsTransportProtocol protocol, DnsDatagram response, bool rateLimited)
  480. {
  481. _queue.Add(new StatsQueueItem(request, remoteEP, protocol, response, rateLimited));
  482. }
  483. public Dictionary<string, List<KeyValuePair<string, long>>> GetLastHourMinuteWiseStats(bool utcFormat)
  484. {
  485. StatCounter totalStatCounter = new StatCounter();
  486. totalStatCounter.Lock();
  487. List<KeyValuePair<string, long>> totalQueriesPerInterval = new List<KeyValuePair<string, long>>(60);
  488. List<KeyValuePair<string, long>> totalNoErrorPerInterval = new List<KeyValuePair<string, long>>(60);
  489. List<KeyValuePair<string, long>> totalServerFailurePerInterval = new List<KeyValuePair<string, long>>(60);
  490. List<KeyValuePair<string, long>> totalNxDomainPerInterval = new List<KeyValuePair<string, long>>(60);
  491. List<KeyValuePair<string, long>> totalRefusedPerInterval = new List<KeyValuePair<string, long>>(60);
  492. List<KeyValuePair<string, long>> totalAuthHitPerInterval = new List<KeyValuePair<string, long>>(60);
  493. List<KeyValuePair<string, long>> totalRecursionsPerInterval = new List<KeyValuePair<string, long>>(60);
  494. List<KeyValuePair<string, long>> totalCacheHitPerInterval = new List<KeyValuePair<string, long>>(60);
  495. List<KeyValuePair<string, long>> totalBlockedPerInterval = new List<KeyValuePair<string, long>>(60);
  496. List<KeyValuePair<string, long>> totalDroppedPerInterval = new List<KeyValuePair<string, long>>(60);
  497. List<KeyValuePair<string, long>> totalClientsPerInterval = new List<KeyValuePair<string, long>>(60);
  498. DateTime lastHourDateTime = DateTime.UtcNow.AddMinutes(-60);
  499. lastHourDateTime = new DateTime(lastHourDateTime.Year, lastHourDateTime.Month, lastHourDateTime.Day, lastHourDateTime.Hour, lastHourDateTime.Minute, 0, DateTimeKind.Utc);
  500. for (int minute = 0; minute < 60; minute++)
  501. {
  502. DateTime lastDateTime = lastHourDateTime.AddMinutes(minute);
  503. string label;
  504. if (utcFormat)
  505. label = lastDateTime.AddMinutes(1).ToString("O");
  506. else
  507. label = lastDateTime.AddMinutes(1).ToLocalTime().ToString("HH:mm");
  508. StatCounter statCounter = _lastHourStatCountersCopy[lastDateTime.Minute];
  509. if ((statCounter != null) && statCounter.IsLocked)
  510. {
  511. totalStatCounter.Merge(statCounter);
  512. totalQueriesPerInterval.Add(new KeyValuePair<string, long>(label, statCounter.TotalQueries));
  513. totalNoErrorPerInterval.Add(new KeyValuePair<string, long>(label, statCounter.TotalNoError));
  514. totalServerFailurePerInterval.Add(new KeyValuePair<string, long>(label, statCounter.TotalServerFailure));
  515. totalNxDomainPerInterval.Add(new KeyValuePair<string, long>(label, statCounter.TotalNxDomain));
  516. totalRefusedPerInterval.Add(new KeyValuePair<string, long>(label, statCounter.TotalRefused));
  517. totalAuthHitPerInterval.Add(new KeyValuePair<string, long>(label, statCounter.TotalAuthoritative));
  518. totalRecursionsPerInterval.Add(new KeyValuePair<string, long>(label, statCounter.TotalRecursive));
  519. totalCacheHitPerInterval.Add(new KeyValuePair<string, long>(label, statCounter.TotalCached));
  520. totalBlockedPerInterval.Add(new KeyValuePair<string, long>(label, statCounter.TotalBlocked));
  521. totalDroppedPerInterval.Add(new KeyValuePair<string, long>(label, statCounter.TotalDropped));
  522. totalClientsPerInterval.Add(new KeyValuePair<string, long>(label, statCounter.TotalClients));
  523. }
  524. else
  525. {
  526. totalQueriesPerInterval.Add(new KeyValuePair<string, long>(label, 0));
  527. totalNoErrorPerInterval.Add(new KeyValuePair<string, long>(label, 0));
  528. totalServerFailurePerInterval.Add(new KeyValuePair<string, long>(label, 0));
  529. totalNxDomainPerInterval.Add(new KeyValuePair<string, long>(label, 0));
  530. totalRefusedPerInterval.Add(new KeyValuePair<string, long>(label, 0));
  531. totalAuthHitPerInterval.Add(new KeyValuePair<string, long>(label, 0));
  532. totalRecursionsPerInterval.Add(new KeyValuePair<string, long>(label, 0));
  533. totalCacheHitPerInterval.Add(new KeyValuePair<string, long>(label, 0));
  534. totalBlockedPerInterval.Add(new KeyValuePair<string, long>(label, 0));
  535. totalDroppedPerInterval.Add(new KeyValuePair<string, long>(label, 0));
  536. totalClientsPerInterval.Add(new KeyValuePair<string, long>(label, 0));
  537. }
  538. }
  539. Dictionary<string, List<KeyValuePair<string, long>>> data = new Dictionary<string, List<KeyValuePair<string, long>>>();
  540. {
  541. List<KeyValuePair<string, long>> stats = new List<KeyValuePair<string, long>>(10);
  542. stats.Add(new KeyValuePair<string, long>("totalQueries", totalStatCounter.TotalQueries));
  543. stats.Add(new KeyValuePair<string, long>("totalNoError", totalStatCounter.TotalNoError));
  544. stats.Add(new KeyValuePair<string, long>("totalServerFailure", totalStatCounter.TotalServerFailure));
  545. stats.Add(new KeyValuePair<string, long>("totalNxDomain", totalStatCounter.TotalNxDomain));
  546. stats.Add(new KeyValuePair<string, long>("totalRefused", totalStatCounter.TotalRefused));
  547. stats.Add(new KeyValuePair<string, long>("totalAuthoritative", totalStatCounter.TotalAuthoritative));
  548. stats.Add(new KeyValuePair<string, long>("totalRecursive", totalStatCounter.TotalRecursive));
  549. stats.Add(new KeyValuePair<string, long>("totalCached", totalStatCounter.TotalCached));
  550. stats.Add(new KeyValuePair<string, long>("totalBlocked", totalStatCounter.TotalBlocked));
  551. stats.Add(new KeyValuePair<string, long>("totalDropped", totalStatCounter.TotalDropped));
  552. stats.Add(new KeyValuePair<string, long>("totalClients", totalStatCounter.TotalClients));
  553. data.Add("stats", stats);
  554. }
  555. data.Add("totalQueriesPerInterval", totalQueriesPerInterval);
  556. data.Add("totalNoErrorPerInterval", totalNoErrorPerInterval);
  557. data.Add("totalServerFailurePerInterval", totalServerFailurePerInterval);
  558. data.Add("totalNxDomainPerInterval", totalNxDomainPerInterval);
  559. data.Add("totalRefusedPerInterval", totalRefusedPerInterval);
  560. data.Add("totalAuthHitPerInterval", totalAuthHitPerInterval);
  561. data.Add("totalRecursionsPerInterval", totalRecursionsPerInterval);
  562. data.Add("totalCacheHitPerInterval", totalCacheHitPerInterval);
  563. data.Add("totalBlockedPerInterval", totalBlockedPerInterval);
  564. data.Add("totalDroppedPerInterval", totalDroppedPerInterval);
  565. data.Add("totalClientsPerInterval", totalClientsPerInterval);
  566. data.Add("topDomains", totalStatCounter.GetTopDomains(10));
  567. data.Add("topBlockedDomains", totalStatCounter.GetTopBlockedDomains(10));
  568. data.Add("topClients", totalStatCounter.GetTopClients(10));
  569. data.Add("queryTypes", totalStatCounter.GetTopQueryTypes(10));
  570. data.Add("protocolTypes", totalStatCounter.GetTopProtocolTypes());
  571. return data;
  572. }
  573. public Dictionary<string, List<KeyValuePair<string, long>>> GetLastDayHourWiseStats(bool utcFormat)
  574. {
  575. return GetHourWiseStats(DateTime.UtcNow.AddHours(-24), 24, utcFormat);
  576. }
  577. public Dictionary<string, List<KeyValuePair<string, long>>> GetLastWeekDayWiseStats(bool utcFormat)
  578. {
  579. return GetDayWiseStats(DateTime.UtcNow.AddDays(-7).Date, 7, utcFormat);
  580. }
  581. public Dictionary<string, List<KeyValuePair<string, long>>> GetLastMonthDayWiseStats(bool utcFormat)
  582. {
  583. return GetDayWiseStats(DateTime.UtcNow.AddDays(-31).Date, 31, utcFormat);
  584. }
  585. public Dictionary<string, List<KeyValuePair<string, long>>> GetLastYearMonthWiseStats(bool utcFormat)
  586. {
  587. StatCounter totalStatCounter = new StatCounter();
  588. totalStatCounter.Lock();
  589. List<KeyValuePair<string, long>> totalQueriesPerInterval = new List<KeyValuePair<string, long>>();
  590. List<KeyValuePair<string, long>> totalNoErrorPerInterval = new List<KeyValuePair<string, long>>();
  591. List<KeyValuePair<string, long>> totalServerFailurePerInterval = new List<KeyValuePair<string, long>>();
  592. List<KeyValuePair<string, long>> totalNxDomainPerInterval = new List<KeyValuePair<string, long>>();
  593. List<KeyValuePair<string, long>> totalRefusedPerInterval = new List<KeyValuePair<string, long>>();
  594. List<KeyValuePair<string, long>> totalAuthHitPerInterval = new List<KeyValuePair<string, long>>();
  595. List<KeyValuePair<string, long>> totalRecursionsPerInterval = new List<KeyValuePair<string, long>>();
  596. List<KeyValuePair<string, long>> totalCacheHitPerInterval = new List<KeyValuePair<string, long>>();
  597. List<KeyValuePair<string, long>> totalBlockedPerInterval = new List<KeyValuePair<string, long>>();
  598. List<KeyValuePair<string, long>> totalDroppedPerInterval = new List<KeyValuePair<string, long>>();
  599. List<KeyValuePair<string, long>> totalClientsPerInterval = new List<KeyValuePair<string, long>>();
  600. DateTime lastYearDateTime = DateTime.UtcNow.AddMonths(-12);
  601. lastYearDateTime = new DateTime(lastYearDateTime.Year, lastYearDateTime.Month, 1, 0, 0, 0, DateTimeKind.Utc);
  602. for (int month = 0; month < 12; month++) //months
  603. {
  604. StatCounter monthlyStatCounter = new StatCounter();
  605. monthlyStatCounter.Lock();
  606. DateTime lastMonthDateTime = lastYearDateTime.AddMonths(month);
  607. string label;
  608. if (utcFormat)
  609. label = lastMonthDateTime.ToString("O");
  610. else
  611. label = lastMonthDateTime.ToLocalTime().ToString("MM/yyyy");
  612. int days = DateTime.DaysInMonth(lastMonthDateTime.Year, lastMonthDateTime.Month);
  613. for (int day = 0; day < days; day++) //days
  614. {
  615. StatCounter dailyStatCounter = LoadDailyStats(lastMonthDateTime.AddDays(day));
  616. monthlyStatCounter.Merge(dailyStatCounter, true);
  617. }
  618. totalStatCounter.Merge(monthlyStatCounter, true);
  619. totalQueriesPerInterval.Add(new KeyValuePair<string, long>(label, monthlyStatCounter.TotalQueries));
  620. totalNoErrorPerInterval.Add(new KeyValuePair<string, long>(label, monthlyStatCounter.TotalNoError));
  621. totalServerFailurePerInterval.Add(new KeyValuePair<string, long>(label, monthlyStatCounter.TotalServerFailure));
  622. totalNxDomainPerInterval.Add(new KeyValuePair<string, long>(label, monthlyStatCounter.TotalNxDomain));
  623. totalRefusedPerInterval.Add(new KeyValuePair<string, long>(label, monthlyStatCounter.TotalRefused));
  624. totalAuthHitPerInterval.Add(new KeyValuePair<string, long>(label, monthlyStatCounter.TotalAuthoritative));
  625. totalRecursionsPerInterval.Add(new KeyValuePair<string, long>(label, monthlyStatCounter.TotalRecursive));
  626. totalCacheHitPerInterval.Add(new KeyValuePair<string, long>(label, monthlyStatCounter.TotalCached));
  627. totalBlockedPerInterval.Add(new KeyValuePair<string, long>(label, monthlyStatCounter.TotalBlocked));
  628. totalDroppedPerInterval.Add(new KeyValuePair<string, long>(label, monthlyStatCounter.TotalDropped));
  629. totalClientsPerInterval.Add(new KeyValuePair<string, long>(label, monthlyStatCounter.TotalClients));
  630. }
  631. Dictionary<string, List<KeyValuePair<string, long>>> data = new Dictionary<string, List<KeyValuePair<string, long>>>();
  632. {
  633. List<KeyValuePair<string, long>> stats = new List<KeyValuePair<string, long>>(6);
  634. stats.Add(new KeyValuePair<string, long>("totalQueries", totalStatCounter.TotalQueries));
  635. stats.Add(new KeyValuePair<string, long>("totalNoError", totalStatCounter.TotalNoError));
  636. stats.Add(new KeyValuePair<string, long>("totalServerFailure", totalStatCounter.TotalServerFailure));
  637. stats.Add(new KeyValuePair<string, long>("totalNxDomain", totalStatCounter.TotalNxDomain));
  638. stats.Add(new KeyValuePair<string, long>("totalRefused", totalStatCounter.TotalRefused));
  639. stats.Add(new KeyValuePair<string, long>("totalAuthoritative", totalStatCounter.TotalAuthoritative));
  640. stats.Add(new KeyValuePair<string, long>("totalRecursive", totalStatCounter.TotalRecursive));
  641. stats.Add(new KeyValuePair<string, long>("totalCached", totalStatCounter.TotalCached));
  642. stats.Add(new KeyValuePair<string, long>("totalBlocked", totalStatCounter.TotalBlocked));
  643. stats.Add(new KeyValuePair<string, long>("totalDropped", totalStatCounter.TotalDropped));
  644. stats.Add(new KeyValuePair<string, long>("totalClients", totalStatCounter.TotalClients));
  645. data.Add("stats", stats);
  646. }
  647. data.Add("totalQueriesPerInterval", totalQueriesPerInterval);
  648. data.Add("totalNoErrorPerInterval", totalNoErrorPerInterval);
  649. data.Add("totalServerFailurePerInterval", totalServerFailurePerInterval);
  650. data.Add("totalNxDomainPerInterval", totalNxDomainPerInterval);
  651. data.Add("totalRefusedPerInterval", totalRefusedPerInterval);
  652. data.Add("totalAuthHitPerInterval", totalAuthHitPerInterval);
  653. data.Add("totalRecursionsPerInterval", totalRecursionsPerInterval);
  654. data.Add("totalCacheHitPerInterval", totalCacheHitPerInterval);
  655. data.Add("totalBlockedPerInterval", totalBlockedPerInterval);
  656. data.Add("totalDroppedPerInterval", totalDroppedPerInterval);
  657. data.Add("totalClientsPerInterval", totalClientsPerInterval);
  658. data.Add("topDomains", totalStatCounter.GetTopDomains(10));
  659. data.Add("topBlockedDomains", totalStatCounter.GetTopBlockedDomains(10));
  660. data.Add("topClients", totalStatCounter.GetTopClients(10));
  661. data.Add("queryTypes", totalStatCounter.GetTopQueryTypes(10));
  662. data.Add("protocolTypes", totalStatCounter.GetTopProtocolTypes());
  663. return data;
  664. }
  665. public Dictionary<string, List<KeyValuePair<string, long>>> GetHourWiseStats(DateTime startDate, DateTime endDate, bool utcFormat)
  666. {
  667. int hours = Convert.ToInt32((endDate - startDate).TotalHours) + 1;
  668. if (hours < 24)
  669. hours = 24;
  670. return GetHourWiseStats(startDate, hours, utcFormat);
  671. }
  672. public Dictionary<string, List<KeyValuePair<string, long>>> GetHourWiseStats(DateTime startDate, int hours, bool utcFormat)
  673. {
  674. StatCounter totalStatCounter = new StatCounter();
  675. totalStatCounter.Lock();
  676. List<KeyValuePair<string, long>> totalQueriesPerInterval = new List<KeyValuePair<string, long>>();
  677. List<KeyValuePair<string, long>> totalNoErrorPerInterval = new List<KeyValuePair<string, long>>();
  678. List<KeyValuePair<string, long>> totalServerFailurePerInterval = new List<KeyValuePair<string, long>>();
  679. List<KeyValuePair<string, long>> totalNxDomainPerInterval = new List<KeyValuePair<string, long>>();
  680. List<KeyValuePair<string, long>> totalRefusedPerInterval = new List<KeyValuePair<string, long>>();
  681. List<KeyValuePair<string, long>> totalAuthHitPerInterval = new List<KeyValuePair<string, long>>();
  682. List<KeyValuePair<string, long>> totalRecursionsPerInterval = new List<KeyValuePair<string, long>>();
  683. List<KeyValuePair<string, long>> totalCacheHitPerInterval = new List<KeyValuePair<string, long>>();
  684. List<KeyValuePair<string, long>> totalBlockedPerInterval = new List<KeyValuePair<string, long>>();
  685. List<KeyValuePair<string, long>> totalDroppedPerInterval = new List<KeyValuePair<string, long>>();
  686. List<KeyValuePair<string, long>> totalClientsPerInterval = new List<KeyValuePair<string, long>>();
  687. for (int hour = 0; hour < hours; hour++)
  688. {
  689. DateTime lastDateTime = startDate.AddHours(hour);
  690. string label;
  691. if (utcFormat)
  692. label = lastDateTime.AddHours(1).ToString("O");
  693. else
  694. label = lastDateTime.AddHours(1).ToLocalTime().ToString("MM/dd HH") + ":00";
  695. HourlyStats hourlyStats = LoadHourlyStats(lastDateTime);
  696. StatCounter hourlyStatCounter = hourlyStats.HourStat;
  697. totalStatCounter.Merge(hourlyStatCounter);
  698. totalQueriesPerInterval.Add(new KeyValuePair<string, long>(label, hourlyStatCounter.TotalQueries));
  699. totalNoErrorPerInterval.Add(new KeyValuePair<string, long>(label, hourlyStatCounter.TotalNoError));
  700. totalServerFailurePerInterval.Add(new KeyValuePair<string, long>(label, hourlyStatCounter.TotalServerFailure));
  701. totalNxDomainPerInterval.Add(new KeyValuePair<string, long>(label, hourlyStatCounter.TotalNxDomain));
  702. totalRefusedPerInterval.Add(new KeyValuePair<string, long>(label, hourlyStatCounter.TotalRefused));
  703. totalAuthHitPerInterval.Add(new KeyValuePair<string, long>(label, hourlyStatCounter.TotalAuthoritative));
  704. totalRecursionsPerInterval.Add(new KeyValuePair<string, long>(label, hourlyStatCounter.TotalRecursive));
  705. totalCacheHitPerInterval.Add(new KeyValuePair<string, long>(label, hourlyStatCounter.TotalCached));
  706. totalBlockedPerInterval.Add(new KeyValuePair<string, long>(label, hourlyStatCounter.TotalBlocked));
  707. totalDroppedPerInterval.Add(new KeyValuePair<string, long>(label, hourlyStatCounter.TotalDropped));
  708. totalClientsPerInterval.Add(new KeyValuePair<string, long>(label, hourlyStatCounter.TotalClients));
  709. }
  710. Dictionary<string, List<KeyValuePair<string, long>>> data = new Dictionary<string, List<KeyValuePair<string, long>>>();
  711. {
  712. List<KeyValuePair<string, long>> stats = new List<KeyValuePair<string, long>>(6);
  713. stats.Add(new KeyValuePair<string, long>("totalQueries", totalStatCounter.TotalQueries));
  714. stats.Add(new KeyValuePair<string, long>("totalNoError", totalStatCounter.TotalNoError));
  715. stats.Add(new KeyValuePair<string, long>("totalServerFailure", totalStatCounter.TotalServerFailure));
  716. stats.Add(new KeyValuePair<string, long>("totalNxDomain", totalStatCounter.TotalNxDomain));
  717. stats.Add(new KeyValuePair<string, long>("totalRefused", totalStatCounter.TotalRefused));
  718. stats.Add(new KeyValuePair<string, long>("totalAuthoritative", totalStatCounter.TotalAuthoritative));
  719. stats.Add(new KeyValuePair<string, long>("totalRecursive", totalStatCounter.TotalRecursive));
  720. stats.Add(new KeyValuePair<string, long>("totalCached", totalStatCounter.TotalCached));
  721. stats.Add(new KeyValuePair<string, long>("totalBlocked", totalStatCounter.TotalBlocked));
  722. stats.Add(new KeyValuePair<string, long>("totalDropped", totalStatCounter.TotalDropped));
  723. stats.Add(new KeyValuePair<string, long>("totalClients", totalStatCounter.TotalClients));
  724. data.Add("stats", stats);
  725. }
  726. data.Add("totalQueriesPerInterval", totalQueriesPerInterval);
  727. data.Add("totalNoErrorPerInterval", totalNoErrorPerInterval);
  728. data.Add("totalServerFailurePerInterval", totalServerFailurePerInterval);
  729. data.Add("totalNxDomainPerInterval", totalNxDomainPerInterval);
  730. data.Add("totalRefusedPerInterval", totalRefusedPerInterval);
  731. data.Add("totalAuthHitPerInterval", totalAuthHitPerInterval);
  732. data.Add("totalRecursionsPerInterval", totalRecursionsPerInterval);
  733. data.Add("totalCacheHitPerInterval", totalCacheHitPerInterval);
  734. data.Add("totalBlockedPerInterval", totalBlockedPerInterval);
  735. data.Add("totalDroppedPerInterval", totalDroppedPerInterval);
  736. data.Add("totalClientsPerInterval", totalClientsPerInterval);
  737. data.Add("topDomains", totalStatCounter.GetTopDomains(10));
  738. data.Add("topBlockedDomains", totalStatCounter.GetTopBlockedDomains(10));
  739. data.Add("topClients", totalStatCounter.GetTopClients(10));
  740. data.Add("queryTypes", totalStatCounter.GetTopQueryTypes(10));
  741. data.Add("protocolTypes", totalStatCounter.GetTopProtocolTypes());
  742. return data;
  743. }
  744. public Dictionary<string, List<KeyValuePair<string, long>>> GetDayWiseStats(DateTime startDate, DateTime endDate, bool utcFormat)
  745. {
  746. return GetDayWiseStats(startDate, Convert.ToInt32((endDate - startDate).TotalDays) + 1, utcFormat);
  747. }
  748. public Dictionary<string, List<KeyValuePair<string, long>>> GetDayWiseStats(DateTime startDate, int days, bool utcFormat)
  749. {
  750. StatCounter totalStatCounter = new StatCounter();
  751. totalStatCounter.Lock();
  752. List<KeyValuePair<string, long>> totalQueriesPerInterval = new List<KeyValuePair<string, long>>();
  753. List<KeyValuePair<string, long>> totalNoErrorPerInterval = new List<KeyValuePair<string, long>>();
  754. List<KeyValuePair<string, long>> totalServerFailurePerInterval = new List<KeyValuePair<string, long>>();
  755. List<KeyValuePair<string, long>> totalNxDomainPerInterval = new List<KeyValuePair<string, long>>();
  756. List<KeyValuePair<string, long>> totalRefusedPerInterval = new List<KeyValuePair<string, long>>();
  757. List<KeyValuePair<string, long>> totalAuthHitPerInterval = new List<KeyValuePair<string, long>>();
  758. List<KeyValuePair<string, long>> totalRecursionsPerInterval = new List<KeyValuePair<string, long>>();
  759. List<KeyValuePair<string, long>> totalCacheHitPerInterval = new List<KeyValuePair<string, long>>();
  760. List<KeyValuePair<string, long>> totalBlockedPerInterval = new List<KeyValuePair<string, long>>();
  761. List<KeyValuePair<string, long>> totalDroppedPerInterval = new List<KeyValuePair<string, long>>();
  762. List<KeyValuePair<string, long>> totalClientsPerInterval = new List<KeyValuePair<string, long>>();
  763. for (int day = 0; day < days; day++) //days
  764. {
  765. DateTime lastDayDateTime = startDate.AddDays(day);
  766. string label;
  767. if (utcFormat)
  768. label = lastDayDateTime.ToString("O");
  769. else
  770. label = lastDayDateTime.ToLocalTime().ToString("MM/dd");
  771. StatCounter dailyStatCounter = LoadDailyStats(lastDayDateTime);
  772. totalStatCounter.Merge(dailyStatCounter, true);
  773. totalQueriesPerInterval.Add(new KeyValuePair<string, long>(label, dailyStatCounter.TotalQueries));
  774. totalNoErrorPerInterval.Add(new KeyValuePair<string, long>(label, dailyStatCounter.TotalNoError));
  775. totalServerFailurePerInterval.Add(new KeyValuePair<string, long>(label, dailyStatCounter.TotalServerFailure));
  776. totalNxDomainPerInterval.Add(new KeyValuePair<string, long>(label, dailyStatCounter.TotalNxDomain));
  777. totalRefusedPerInterval.Add(new KeyValuePair<string, long>(label, dailyStatCounter.TotalRefused));
  778. totalAuthHitPerInterval.Add(new KeyValuePair<string, long>(label, dailyStatCounter.TotalAuthoritative));
  779. totalRecursionsPerInterval.Add(new KeyValuePair<string, long>(label, dailyStatCounter.TotalRecursive));
  780. totalCacheHitPerInterval.Add(new KeyValuePair<string, long>(label, dailyStatCounter.TotalCached));
  781. totalBlockedPerInterval.Add(new KeyValuePair<string, long>(label, dailyStatCounter.TotalBlocked));
  782. totalDroppedPerInterval.Add(new KeyValuePair<string, long>(label, dailyStatCounter.TotalDropped));
  783. totalClientsPerInterval.Add(new KeyValuePair<string, long>(label, dailyStatCounter.TotalClients));
  784. }
  785. Dictionary<string, List<KeyValuePair<string, long>>> data = new Dictionary<string, List<KeyValuePair<string, long>>>();
  786. {
  787. List<KeyValuePair<string, long>> stats = new List<KeyValuePair<string, long>>(6);
  788. stats.Add(new KeyValuePair<string, long>("totalQueries", totalStatCounter.TotalQueries));
  789. stats.Add(new KeyValuePair<string, long>("totalNoError", totalStatCounter.TotalNoError));
  790. stats.Add(new KeyValuePair<string, long>("totalServerFailure", totalStatCounter.TotalServerFailure));
  791. stats.Add(new KeyValuePair<string, long>("totalNxDomain", totalStatCounter.TotalNxDomain));
  792. stats.Add(new KeyValuePair<string, long>("totalRefused", totalStatCounter.TotalRefused));
  793. stats.Add(new KeyValuePair<string, long>("totalAuthoritative", totalStatCounter.TotalAuthoritative));
  794. stats.Add(new KeyValuePair<string, long>("totalRecursive", totalStatCounter.TotalRecursive));
  795. stats.Add(new KeyValuePair<string, long>("totalCached", totalStatCounter.TotalCached));
  796. stats.Add(new KeyValuePair<string, long>("totalBlocked", totalStatCounter.TotalBlocked));
  797. stats.Add(new KeyValuePair<string, long>("totalDropped", totalStatCounter.TotalDropped));
  798. stats.Add(new KeyValuePair<string, long>("totalClients", totalStatCounter.TotalClients));
  799. data.Add("stats", stats);
  800. }
  801. data.Add("totalQueriesPerInterval", totalQueriesPerInterval);
  802. data.Add("totalNoErrorPerInterval", totalNoErrorPerInterval);
  803. data.Add("totalServerFailurePerInterval", totalServerFailurePerInterval);
  804. data.Add("totalNxDomainPerInterval", totalNxDomainPerInterval);
  805. data.Add("totalRefusedPerInterval", totalRefusedPerInterval);
  806. data.Add("totalAuthHitPerInterval", totalAuthHitPerInterval);
  807. data.Add("totalRecursionsPerInterval", totalRecursionsPerInterval);
  808. data.Add("totalCacheHitPerInterval", totalCacheHitPerInterval);
  809. data.Add("totalBlockedPerInterval", totalBlockedPerInterval);
  810. data.Add("totalDroppedPerInterval", totalDroppedPerInterval);
  811. data.Add("totalClientsPerInterval", totalClientsPerInterval);
  812. data.Add("topDomains", totalStatCounter.GetTopDomains(10));
  813. data.Add("topBlockedDomains", totalStatCounter.GetTopBlockedDomains(10));
  814. data.Add("topClients", totalStatCounter.GetTopClients(10));
  815. data.Add("queryTypes", totalStatCounter.GetTopQueryTypes(10));
  816. data.Add("protocolTypes", totalStatCounter.GetTopProtocolTypes());
  817. return data;
  818. }
  819. public List<KeyValuePair<string, long>> GetLastHourTopStats(TopStatsType type, int limit)
  820. {
  821. StatCounter totalStatCounter = new StatCounter();
  822. totalStatCounter.Lock();
  823. DateTime lastHourDateTime = DateTime.UtcNow.AddMinutes(-60);
  824. lastHourDateTime = new DateTime(lastHourDateTime.Year, lastHourDateTime.Month, lastHourDateTime.Day, lastHourDateTime.Hour, lastHourDateTime.Minute, 0, DateTimeKind.Utc);
  825. for (int minute = 0; minute < 60; minute++)
  826. {
  827. DateTime lastDateTime = lastHourDateTime.AddMinutes(minute);
  828. StatCounter statCounter = _lastHourStatCountersCopy[lastDateTime.Minute];
  829. if ((statCounter != null) && statCounter.IsLocked)
  830. totalStatCounter.Merge(statCounter);
  831. }
  832. switch (type)
  833. {
  834. case TopStatsType.TopDomains:
  835. return totalStatCounter.GetTopDomains(limit);
  836. case TopStatsType.TopBlockedDomains:
  837. return totalStatCounter.GetTopBlockedDomains(limit);
  838. case TopStatsType.TopClients:
  839. return totalStatCounter.GetTopClients(limit);
  840. default:
  841. throw new NotSupportedException();
  842. }
  843. }
  844. public List<KeyValuePair<string, long>> GetLastDayTopStats(TopStatsType type, int limit)
  845. {
  846. return GetHourWiseTopStats(DateTime.UtcNow.AddHours(-24), 24, type, limit);
  847. }
  848. public List<KeyValuePair<string, long>> GetLastWeekTopStats(TopStatsType type, int limit)
  849. {
  850. return GetDayWiseTopStats(DateTime.UtcNow.AddDays(-7).Date, 7, type, limit);
  851. }
  852. public List<KeyValuePair<string, long>> GetLastMonthTopStats(TopStatsType type, int limit)
  853. {
  854. return GetDayWiseTopStats(DateTime.UtcNow.AddDays(-31).Date, 31, type, limit);
  855. }
  856. public List<KeyValuePair<string, long>> GetLastYearTopStats(TopStatsType type, int limit)
  857. {
  858. StatCounter totalStatCounter = new StatCounter();
  859. totalStatCounter.Lock();
  860. DateTime lastYearDateTime = DateTime.UtcNow.AddMonths(-12);
  861. lastYearDateTime = new DateTime(lastYearDateTime.Year, lastYearDateTime.Month, 1, 0, 0, 0, DateTimeKind.Utc);
  862. for (int month = 0; month < 12; month++) //months
  863. {
  864. StatCounter monthlyStatCounter = new StatCounter();
  865. monthlyStatCounter.Lock();
  866. DateTime lastMonthDateTime = lastYearDateTime.AddMonths(month);
  867. int days = DateTime.DaysInMonth(lastMonthDateTime.Year, lastMonthDateTime.Month);
  868. for (int day = 0; day < days; day++) //days
  869. {
  870. StatCounter dailyStatCounter = LoadDailyStats(lastMonthDateTime.AddDays(day));
  871. monthlyStatCounter.Merge(dailyStatCounter, true);
  872. }
  873. totalStatCounter.Merge(monthlyStatCounter, true);
  874. }
  875. switch (type)
  876. {
  877. case TopStatsType.TopDomains:
  878. return totalStatCounter.GetTopDomains(limit);
  879. case TopStatsType.TopBlockedDomains:
  880. return totalStatCounter.GetTopBlockedDomains(limit);
  881. case TopStatsType.TopClients:
  882. return totalStatCounter.GetTopClients(limit);
  883. default:
  884. throw new NotSupportedException();
  885. }
  886. }
  887. public List<KeyValuePair<string, long>> GetHourWiseTopStats(DateTime startDate, DateTime endDate, TopStatsType type, int limit)
  888. {
  889. int hours = Convert.ToInt32((endDate - startDate).TotalHours) + 1;
  890. if (hours < 24)
  891. hours = 24;
  892. return GetHourWiseTopStats(startDate, hours, type, limit);
  893. }
  894. public List<KeyValuePair<string, long>> GetHourWiseTopStats(DateTime startDate, int hours, TopStatsType type, int limit)
  895. {
  896. StatCounter totalStatCounter = new StatCounter();
  897. totalStatCounter.Lock();
  898. for (int hour = 0; hour < hours; hour++)
  899. {
  900. DateTime lastDateTime = startDate.AddHours(hour);
  901. HourlyStats hourlyStats = LoadHourlyStats(lastDateTime);
  902. StatCounter hourlyStatCounter = hourlyStats.HourStat;
  903. totalStatCounter.Merge(hourlyStatCounter);
  904. }
  905. switch (type)
  906. {
  907. case TopStatsType.TopDomains:
  908. return totalStatCounter.GetTopDomains(limit);
  909. case TopStatsType.TopBlockedDomains:
  910. return totalStatCounter.GetTopBlockedDomains(limit);
  911. case TopStatsType.TopClients:
  912. return totalStatCounter.GetTopClients(limit);
  913. default:
  914. throw new NotSupportedException();
  915. }
  916. }
  917. public List<KeyValuePair<string, long>> GetDayWiseTopStats(DateTime startDate, DateTime endDate, TopStatsType type, int limit)
  918. {
  919. return GetDayWiseTopStats(startDate, Convert.ToInt32((endDate - startDate).TotalDays) + 1, type, limit);
  920. }
  921. public List<KeyValuePair<string, long>> GetDayWiseTopStats(DateTime startDate, int days, TopStatsType type, int limit)
  922. {
  923. StatCounter totalStatCounter = new StatCounter();
  924. totalStatCounter.Lock();
  925. for (int day = 0; day < days; day++) //days
  926. {
  927. DateTime lastDayDateTime = startDate.AddDays(day);
  928. StatCounter dailyStatCounter = LoadDailyStats(lastDayDateTime);
  929. totalStatCounter.Merge(dailyStatCounter, true);
  930. }
  931. switch (type)
  932. {
  933. case TopStatsType.TopDomains:
  934. return totalStatCounter.GetTopDomains(limit);
  935. case TopStatsType.TopBlockedDomains:
  936. return totalStatCounter.GetTopBlockedDomains(limit);
  937. case TopStatsType.TopClients:
  938. return totalStatCounter.GetTopClients(limit);
  939. default:
  940. throw new NotSupportedException();
  941. }
  942. }
  943. public List<KeyValuePair<DnsQuestionRecord, long>> GetLastHourEligibleQueries(int minimumHitsPerHour)
  944. {
  945. StatCounter totalStatCounter = new StatCounter();
  946. totalStatCounter.Lock();
  947. DateTime lastHourDateTime = DateTime.UtcNow.AddMinutes(-60);
  948. lastHourDateTime = new DateTime(lastHourDateTime.Year, lastHourDateTime.Month, lastHourDateTime.Day, lastHourDateTime.Hour, lastHourDateTime.Minute, 0, DateTimeKind.Utc);
  949. for (int minute = 0; minute < 60; minute++)
  950. {
  951. DateTime lastDateTime = lastHourDateTime.AddMinutes(minute);
  952. StatCounter statCounter = _lastHourStatCountersCopy[lastDateTime.Minute];
  953. if ((statCounter != null) && statCounter.IsLocked)
  954. totalStatCounter.Merge(statCounter);
  955. }
  956. return totalStatCounter.GetEligibleQueries(minimumHitsPerHour);
  957. }
  958. public void GetLatestClientSubnetStats(int minutes, int ipv4PrefixLength, int ipv6PrefixLength, out IReadOnlyDictionary<IPAddress, long> clientSubnetStats, out IReadOnlyDictionary<IPAddress, long> errorClientSubnetStats)
  959. {
  960. StatCounter totalStatCounter = new StatCounter();
  961. totalStatCounter.Lock();
  962. DateTime lastHourDateTime = DateTime.UtcNow.AddMinutes(1 - minutes);
  963. lastHourDateTime = new DateTime(lastHourDateTime.Year, lastHourDateTime.Month, lastHourDateTime.Day, lastHourDateTime.Hour, lastHourDateTime.Minute, 0, DateTimeKind.Utc);
  964. for (int minute = 0; minute < minutes; minute++)
  965. {
  966. DateTime lastDateTime = lastHourDateTime.AddMinutes(minute);
  967. StatCounter statCounter = _lastHourStatCounters[lastDateTime.Minute];
  968. if (statCounter is not null)
  969. totalStatCounter.Merge(statCounter, false, true);
  970. }
  971. clientSubnetStats = totalStatCounter.GetClientSubnetStats(ipv4PrefixLength, ipv6PrefixLength);
  972. errorClientSubnetStats = totalStatCounter.GetErrorClientSubnetStats(ipv4PrefixLength, ipv6PrefixLength);
  973. }
  974. #endregion
  975. #region properties
  976. public bool EnableInMemoryStats
  977. {
  978. get { return _enableInMemoryStats; }
  979. set
  980. {
  981. if (_enableInMemoryStats != value)
  982. {
  983. _enableInMemoryStats = value;
  984. if (_enableInMemoryStats)
  985. {
  986. _hourlyStatsCache.Clear();
  987. _dailyStatsCache.Clear();
  988. }
  989. }
  990. }
  991. }
  992. public int MaxStatFileDays
  993. {
  994. get { return _maxStatFileDays; }
  995. set { _maxStatFileDays = value; }
  996. }
  997. #endregion
  998. class HourlyStats
  999. {
  1000. #region variables
  1001. readonly StatCounter _hourStat; //calculated value
  1002. StatCounter[] _minuteStats = new StatCounter[60];
  1003. #endregion
  1004. #region constructor
  1005. public HourlyStats()
  1006. {
  1007. _hourStat = new StatCounter();
  1008. _hourStat.Lock();
  1009. }
  1010. public HourlyStats(BinaryReader bR)
  1011. {
  1012. if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "HS") //format
  1013. throw new InvalidDataException("HourlyStats format is invalid.");
  1014. byte version = bR.ReadByte();
  1015. switch (version)
  1016. {
  1017. case 1:
  1018. _hourStat = new StatCounter();
  1019. _hourStat.Lock();
  1020. for (int i = 0; i < _minuteStats.Length; i++)
  1021. {
  1022. _minuteStats[i] = new StatCounter(bR);
  1023. _hourStat.Merge(_minuteStats[i]);
  1024. }
  1025. break;
  1026. default:
  1027. throw new InvalidDataException("HourlyStats version not supported.");
  1028. }
  1029. }
  1030. #endregion
  1031. #region public
  1032. public void UpdateStat(DateTime dateTime, StatCounter minuteStat)
  1033. {
  1034. if (!minuteStat.IsLocked)
  1035. throw new DnsServerException("StatCounter must be locked.");
  1036. _hourStat.Merge(minuteStat);
  1037. _minuteStats[dateTime.Minute] = minuteStat;
  1038. }
  1039. public void UnloadMinuteStats()
  1040. {
  1041. _minuteStats = null;
  1042. }
  1043. public void WriteTo(BinaryWriter bW)
  1044. {
  1045. bW.Write(Encoding.ASCII.GetBytes("HS")); //format
  1046. bW.Write((byte)1); //version
  1047. for (int i = 0; i < _minuteStats.Length; i++)
  1048. {
  1049. if (_minuteStats[i] == null)
  1050. {
  1051. _minuteStats[i] = new StatCounter();
  1052. _minuteStats[i].Lock();
  1053. }
  1054. _minuteStats[i].WriteTo(bW);
  1055. }
  1056. }
  1057. #endregion
  1058. #region properties
  1059. public StatCounter HourStat
  1060. { get { return _hourStat; } }
  1061. public StatCounter[] MinuteStats
  1062. {
  1063. get { return _minuteStats; }
  1064. }
  1065. #endregion
  1066. }
  1067. class StatCounter
  1068. {
  1069. #region variables
  1070. volatile bool _locked;
  1071. long _totalQueries;
  1072. long _totalNoError;
  1073. long _totalServerFailure;
  1074. long _totalNxDomain;
  1075. long _totalRefused;
  1076. long _totalAuthoritative;
  1077. long _totalRecursive;
  1078. long _totalCached;
  1079. long _totalBlocked;
  1080. long _totalDropped;
  1081. long _totalClients;
  1082. readonly ConcurrentDictionary<string, Counter> _queryDomains;
  1083. readonly ConcurrentDictionary<string, Counter> _queryBlockedDomains;
  1084. readonly ConcurrentDictionary<DnsResourceRecordType, Counter> _queryTypes;
  1085. readonly ConcurrentDictionary<DnsTransportProtocol, Counter> _protocolTypes;
  1086. readonly ConcurrentDictionary<IPAddress, Counter> _clientIpAddresses; //includes all queries
  1087. readonly ConcurrentDictionary<IPAddress, Counter> _errorIpAddresses; //includes REFUSED, FORMERR and SERVFAIL
  1088. readonly ConcurrentDictionary<DnsQuestionRecord, Counter> _queries;
  1089. bool _truncationFoundDuringMerge;
  1090. long _totalClientsDailyStatsSummation;
  1091. #endregion
  1092. #region constructor
  1093. public StatCounter()
  1094. {
  1095. _queryDomains = new ConcurrentDictionary<string, Counter>();
  1096. _queryBlockedDomains = new ConcurrentDictionary<string, Counter>();
  1097. _queryTypes = new ConcurrentDictionary<DnsResourceRecordType, Counter>();
  1098. _protocolTypes = new ConcurrentDictionary<DnsTransportProtocol, Counter>();
  1099. _clientIpAddresses = new ConcurrentDictionary<IPAddress, Counter>();
  1100. _errorIpAddresses = new ConcurrentDictionary<IPAddress, Counter>();
  1101. _queries = new ConcurrentDictionary<DnsQuestionRecord, Counter>();
  1102. }
  1103. public StatCounter(BinaryReader bR)
  1104. {
  1105. if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "SC") //format
  1106. throw new InvalidDataException("StatCounter format is invalid.");
  1107. byte version = bR.ReadByte();
  1108. switch (version)
  1109. {
  1110. case 1:
  1111. case 2:
  1112. case 3:
  1113. case 4:
  1114. case 5:
  1115. case 6:
  1116. _totalQueries = bR.ReadInt32();
  1117. _totalNoError = bR.ReadInt32();
  1118. _totalServerFailure = bR.ReadInt32();
  1119. _totalNxDomain = bR.ReadInt32();
  1120. _totalRefused = bR.ReadInt32();
  1121. if (version >= 3)
  1122. {
  1123. _totalAuthoritative = bR.ReadInt32();
  1124. _totalRecursive = bR.ReadInt32();
  1125. _totalCached = bR.ReadInt32();
  1126. _totalBlocked = bR.ReadInt32();
  1127. }
  1128. else
  1129. {
  1130. _totalBlocked = bR.ReadInt32();
  1131. if (version >= 2)
  1132. _totalCached = bR.ReadInt32();
  1133. }
  1134. if (version >= 6)
  1135. _totalClients = bR.ReadInt32();
  1136. {
  1137. int count = bR.ReadInt32();
  1138. _queryDomains = new ConcurrentDictionary<string, Counter>(1, count);
  1139. for (int i = 0; i < count; i++)
  1140. _queryDomains.TryAdd(bR.ReadShortString(), new Counter(bR.ReadInt32()));
  1141. }
  1142. {
  1143. int count = bR.ReadInt32();
  1144. _queryBlockedDomains = new ConcurrentDictionary<string, Counter>(1, count);
  1145. for (int i = 0; i < count; i++)
  1146. _queryBlockedDomains.TryAdd(bR.ReadShortString(), new Counter(bR.ReadInt32()));
  1147. }
  1148. {
  1149. int count = bR.ReadInt32();
  1150. _queryTypes = new ConcurrentDictionary<DnsResourceRecordType, Counter>(1, count);
  1151. for (int i = 0; i < count; i++)
  1152. _queryTypes.TryAdd((DnsResourceRecordType)bR.ReadUInt16(), new Counter(bR.ReadInt32()));
  1153. }
  1154. _protocolTypes = new ConcurrentDictionary<DnsTransportProtocol, Counter>(1, 0);
  1155. {
  1156. int count = bR.ReadInt32();
  1157. _clientIpAddresses = new ConcurrentDictionary<IPAddress, Counter>(1, count);
  1158. for (int i = 0; i < count; i++)
  1159. _clientIpAddresses.TryAdd(IPAddressExtensions.ReadFrom(bR), new Counter(bR.ReadInt32()));
  1160. if (version < 6)
  1161. _totalClients = count;
  1162. }
  1163. if (version >= 4)
  1164. {
  1165. int count = bR.ReadInt32();
  1166. _queries = new ConcurrentDictionary<DnsQuestionRecord, Counter>(1, count);
  1167. for (int i = 0; i < count; i++)
  1168. _queries.TryAdd(new DnsQuestionRecord(bR.BaseStream), new Counter(bR.ReadInt32()));
  1169. }
  1170. else
  1171. {
  1172. _queries = new ConcurrentDictionary<DnsQuestionRecord, Counter>(1, 0);
  1173. }
  1174. if (version >= 5)
  1175. {
  1176. int count = bR.ReadInt32();
  1177. _errorIpAddresses = new ConcurrentDictionary<IPAddress, Counter>(1, count);
  1178. for (int i = 0; i < count; i++)
  1179. _errorIpAddresses.TryAdd(IPAddressExtensions.ReadFrom(bR), new Counter(bR.ReadInt32()));
  1180. }
  1181. else
  1182. {
  1183. _errorIpAddresses = new ConcurrentDictionary<IPAddress, Counter>(1, 0);
  1184. }
  1185. break;
  1186. case 7:
  1187. case 8:
  1188. _totalQueries = bR.ReadInt64();
  1189. _totalNoError = bR.ReadInt64();
  1190. _totalServerFailure = bR.ReadInt64();
  1191. _totalNxDomain = bR.ReadInt64();
  1192. _totalRefused = bR.ReadInt64();
  1193. _totalAuthoritative = bR.ReadInt64();
  1194. _totalRecursive = bR.ReadInt64();
  1195. _totalCached = bR.ReadInt64();
  1196. _totalBlocked = bR.ReadInt64();
  1197. if (version >= 8)
  1198. _totalDropped = bR.ReadInt64();
  1199. _totalClients = bR.ReadInt64();
  1200. {
  1201. int count = bR.ReadInt32();
  1202. _queryDomains = new ConcurrentDictionary<string, Counter>(1, count);
  1203. for (int i = 0; i < count; i++)
  1204. _queryDomains.TryAdd(bR.ReadShortString(), new Counter(bR.ReadInt64()));
  1205. }
  1206. {
  1207. int count = bR.ReadInt32();
  1208. _queryBlockedDomains = new ConcurrentDictionary<string, Counter>(1, count);
  1209. for (int i = 0; i < count; i++)
  1210. _queryBlockedDomains.TryAdd(bR.ReadShortString(), new Counter(bR.ReadInt64()));
  1211. }
  1212. {
  1213. int count = bR.ReadInt32();
  1214. _queryTypes = new ConcurrentDictionary<DnsResourceRecordType, Counter>(1, count);
  1215. for (int i = 0; i < count; i++)
  1216. _queryTypes.TryAdd((DnsResourceRecordType)bR.ReadUInt16(), new Counter(bR.ReadInt64()));
  1217. }
  1218. if (version >= 8)
  1219. {
  1220. int count = bR.ReadInt32();
  1221. _protocolTypes = new ConcurrentDictionary<DnsTransportProtocol, Counter>(1, count);
  1222. for (int i = 0; i < count; i++)
  1223. _protocolTypes.TryAdd((DnsTransportProtocol)bR.ReadByte(), new Counter(bR.ReadInt64()));
  1224. }
  1225. else
  1226. {
  1227. _protocolTypes = new ConcurrentDictionary<DnsTransportProtocol, Counter>(1, 0);
  1228. }
  1229. {
  1230. int count = bR.ReadInt32();
  1231. _clientIpAddresses = new ConcurrentDictionary<IPAddress, Counter>(1, count);
  1232. for (int i = 0; i < count; i++)
  1233. _clientIpAddresses.TryAdd(IPAddressExtensions.ReadFrom(bR), new Counter(bR.ReadInt64()));
  1234. }
  1235. {
  1236. int count = bR.ReadInt32();
  1237. _queries = new ConcurrentDictionary<DnsQuestionRecord, Counter>(1, count);
  1238. for (int i = 0; i < count; i++)
  1239. _queries.TryAdd(new DnsQuestionRecord(bR.BaseStream), new Counter(bR.ReadInt64()));
  1240. }
  1241. {
  1242. int count = bR.ReadInt32();
  1243. _errorIpAddresses = new ConcurrentDictionary<IPAddress, Counter>(1, count);
  1244. for (int i = 0; i < count; i++)
  1245. _errorIpAddresses.TryAdd(IPAddressExtensions.ReadFrom(bR), new Counter(bR.ReadInt64()));
  1246. }
  1247. break;
  1248. default:
  1249. throw new InvalidDataException("StatCounter version not supported.");
  1250. }
  1251. _locked = true;
  1252. }
  1253. #endregion
  1254. #region private
  1255. private static List<KeyValuePair<string, long>> GetTopList(List<KeyValuePair<string, long>> list, int limit)
  1256. {
  1257. list.Sort(delegate (KeyValuePair<string, long> item1, KeyValuePair<string, long> item2)
  1258. {
  1259. return item2.Value.CompareTo(item1.Value);
  1260. });
  1261. if (list.Count > limit)
  1262. list.RemoveRange(limit, list.Count - limit);
  1263. return list;
  1264. }
  1265. private static Counter GetNewCounter<T>(T key)
  1266. {
  1267. return new Counter();
  1268. }
  1269. #endregion
  1270. #region public
  1271. public void Lock()
  1272. {
  1273. _locked = true;
  1274. }
  1275. public void Update(DnsQuestionRecord query, DnsResponseCode responseCode, DnsServerResponseType responseType, IPAddress clientIpAddress, DnsTransportProtocol protocol, bool rateLimited)
  1276. {
  1277. if (_locked)
  1278. return;
  1279. if (clientIpAddress.IsIPv4MappedToIPv6)
  1280. clientIpAddress = clientIpAddress.MapToIPv4();
  1281. _totalQueries++;
  1282. if (responseType == DnsServerResponseType.Dropped)
  1283. {
  1284. _totalDropped++;
  1285. if (rateLimited)
  1286. {
  1287. _clientIpAddresses.GetOrAdd(clientIpAddress, GetNewCounter).Increment();
  1288. _totalClients = _clientIpAddresses.Count;
  1289. }
  1290. }
  1291. else
  1292. {
  1293. switch (responseCode)
  1294. {
  1295. case DnsResponseCode.NoError:
  1296. if (query is not null)
  1297. {
  1298. switch (responseType)
  1299. {
  1300. case DnsServerResponseType.Blocked:
  1301. case DnsServerResponseType.UpstreamBlocked:
  1302. case DnsServerResponseType.CacheBlocked:
  1303. //skip blocked domains
  1304. break;
  1305. default:
  1306. _queryDomains.GetOrAdd(query.Name.ToLowerInvariant(), GetNewCounter).Increment();
  1307. _queries.GetOrAdd(query, GetNewCounter).Increment();
  1308. break;
  1309. }
  1310. }
  1311. _totalNoError++;
  1312. break;
  1313. case DnsResponseCode.ServerFailure:
  1314. _errorIpAddresses.GetOrAdd(clientIpAddress, GetNewCounter).Increment();
  1315. _totalServerFailure++;
  1316. break;
  1317. case DnsResponseCode.NxDomain:
  1318. _totalNxDomain++;
  1319. break;
  1320. case DnsResponseCode.Refused:
  1321. _errorIpAddresses.GetOrAdd(clientIpAddress, GetNewCounter).Increment();
  1322. _totalRefused++;
  1323. break;
  1324. case DnsResponseCode.FormatError:
  1325. _errorIpAddresses.GetOrAdd(clientIpAddress, GetNewCounter).Increment();
  1326. break;
  1327. }
  1328. switch (responseType)
  1329. {
  1330. case DnsServerResponseType.Authoritative:
  1331. _totalAuthoritative++;
  1332. break;
  1333. case DnsServerResponseType.Recursive:
  1334. _totalRecursive++;
  1335. break;
  1336. case DnsServerResponseType.Cached:
  1337. _totalCached++;
  1338. break;
  1339. case DnsServerResponseType.Blocked:
  1340. if (query is not null)
  1341. _queryBlockedDomains.GetOrAdd(query.Name.ToLowerInvariant(), GetNewCounter).Increment();
  1342. _totalBlocked++;
  1343. break;
  1344. case DnsServerResponseType.UpstreamBlocked:
  1345. _totalRecursive++;
  1346. if (query is not null)
  1347. _queryBlockedDomains.GetOrAdd(query.Name.ToLowerInvariant(), GetNewCounter).Increment();
  1348. _totalBlocked++;
  1349. break;
  1350. case DnsServerResponseType.CacheBlocked:
  1351. _totalCached++;
  1352. if (query is not null)
  1353. _queryBlockedDomains.GetOrAdd(query.Name.ToLowerInvariant(), GetNewCounter).Increment();
  1354. _totalBlocked++;
  1355. break;
  1356. }
  1357. if (query is not null)
  1358. _queryTypes.GetOrAdd(query.Type, GetNewCounter).Increment();
  1359. _clientIpAddresses.GetOrAdd(clientIpAddress, GetNewCounter).Increment();
  1360. _totalClients = _clientIpAddresses.Count;
  1361. }
  1362. _protocolTypes.GetOrAdd(protocol, GetNewCounter).Increment();
  1363. }
  1364. public void Merge(StatCounter statCounter, bool isDailyStatCounter = false, bool skipLock = false)
  1365. {
  1366. if (!skipLock && (!_locked || !statCounter._locked))
  1367. throw new DnsServerException("StatCounter must be locked.");
  1368. _totalQueries += statCounter._totalQueries;
  1369. _totalNoError += statCounter._totalNoError;
  1370. _totalServerFailure += statCounter._totalServerFailure;
  1371. _totalNxDomain += statCounter._totalNxDomain;
  1372. _totalRefused += statCounter._totalRefused;
  1373. _totalAuthoritative += statCounter._totalAuthoritative;
  1374. _totalRecursive += statCounter._totalRecursive;
  1375. _totalCached += statCounter._totalCached;
  1376. _totalBlocked += statCounter._totalBlocked;
  1377. _totalDropped += statCounter._totalDropped;
  1378. foreach (KeyValuePair<string, Counter> queryDomain in statCounter._queryDomains)
  1379. _queryDomains.GetOrAdd(queryDomain.Key, GetNewCounter).Merge(queryDomain.Value);
  1380. foreach (KeyValuePair<string, Counter> queryBlockedDomain in statCounter._queryBlockedDomains)
  1381. _queryBlockedDomains.GetOrAdd(queryBlockedDomain.Key, GetNewCounter).Merge(queryBlockedDomain.Value);
  1382. foreach (KeyValuePair<DnsResourceRecordType, Counter> queryType in statCounter._queryTypes)
  1383. _queryTypes.GetOrAdd(queryType.Key, GetNewCounter).Merge(queryType.Value);
  1384. foreach (KeyValuePair<DnsTransportProtocol, Counter> protocolType in statCounter._protocolTypes)
  1385. _protocolTypes.GetOrAdd(protocolType.Key, GetNewCounter).Merge(protocolType.Value);
  1386. foreach (KeyValuePair<IPAddress, Counter> clientIpAddress in statCounter._clientIpAddresses)
  1387. _clientIpAddresses.GetOrAdd(clientIpAddress.Key, GetNewCounter).Merge(clientIpAddress.Value);
  1388. foreach (KeyValuePair<IPAddress, Counter> refusedIpAddress in statCounter._errorIpAddresses)
  1389. _errorIpAddresses.GetOrAdd(refusedIpAddress.Key, GetNewCounter).Merge(refusedIpAddress.Value);
  1390. foreach (KeyValuePair<DnsQuestionRecord, Counter> query in statCounter._queries)
  1391. _queries.GetOrAdd(query.Key, GetNewCounter).Merge(query.Value);
  1392. _totalClients = _clientIpAddresses.Count;
  1393. _totalClientsDailyStatsSummation += statCounter._totalClients;
  1394. if (isDailyStatCounter && (statCounter._totalClients > statCounter._clientIpAddresses.Count))
  1395. _truncationFoundDuringMerge = true;
  1396. }
  1397. public bool Truncate(int limit)
  1398. {
  1399. bool truncated = false;
  1400. if (_queryDomains.Count > limit)
  1401. {
  1402. List<KeyValuePair<string, Counter>> topDomains = new List<KeyValuePair<string, Counter>>(_queryDomains);
  1403. _queryDomains.Clear();
  1404. topDomains.Sort(delegate (KeyValuePair<string, Counter> item1, KeyValuePair<string, Counter> item2)
  1405. {
  1406. return item2.Value.Count.CompareTo(item1.Value.Count);
  1407. });
  1408. if (topDomains.Count > limit)
  1409. topDomains.RemoveRange(limit, topDomains.Count - limit);
  1410. foreach (KeyValuePair<string, Counter> item in topDomains)
  1411. _queryDomains[item.Key] = item.Value;
  1412. truncated = true;
  1413. }
  1414. if (_queryBlockedDomains.Count > limit)
  1415. {
  1416. List<KeyValuePair<string, Counter>> topBlockedDomains = new List<KeyValuePair<string, Counter>>(_queryBlockedDomains);
  1417. _queryBlockedDomains.Clear();
  1418. topBlockedDomains.Sort(delegate (KeyValuePair<string, Counter> item1, KeyValuePair<string, Counter> item2)
  1419. {
  1420. return item2.Value.Count.CompareTo(item1.Value.Count);
  1421. });
  1422. if (topBlockedDomains.Count > limit)
  1423. topBlockedDomains.RemoveRange(limit, topBlockedDomains.Count - limit);
  1424. foreach (KeyValuePair<string, Counter> item in topBlockedDomains)
  1425. _queryBlockedDomains[item.Key] = item.Value;
  1426. truncated = true;
  1427. }
  1428. if (_queryTypes.Count > limit)
  1429. {
  1430. List<KeyValuePair<DnsResourceRecordType, Counter>> queryTypes = new List<KeyValuePair<DnsResourceRecordType, Counter>>(_queryTypes);
  1431. _queryTypes.Clear();
  1432. queryTypes.Sort(delegate (KeyValuePair<DnsResourceRecordType, Counter> item1, KeyValuePair<DnsResourceRecordType, Counter> item2)
  1433. {
  1434. return item2.Value.Count.CompareTo(item1.Value.Count);
  1435. });
  1436. if (queryTypes.Count > limit)
  1437. {
  1438. long othersCount = 0;
  1439. for (int i = limit; i < queryTypes.Count; i++)
  1440. othersCount += queryTypes[i].Value.Count;
  1441. queryTypes.RemoveRange(limit - 1, queryTypes.Count - (limit - 1));
  1442. queryTypes.Add(new KeyValuePair<DnsResourceRecordType, Counter>(DnsResourceRecordType.Unknown, new Counter(othersCount)));
  1443. }
  1444. foreach (KeyValuePair<DnsResourceRecordType, Counter> item in queryTypes)
  1445. _queryTypes[item.Key] = item.Value;
  1446. truncated = true;
  1447. }
  1448. if (_clientIpAddresses.Count > limit)
  1449. {
  1450. List<KeyValuePair<IPAddress, Counter>> topClients = new List<KeyValuePair<IPAddress, Counter>>(_clientIpAddresses);
  1451. _clientIpAddresses.Clear();
  1452. topClients.Sort(delegate (KeyValuePair<IPAddress, Counter> item1, KeyValuePair<IPAddress, Counter> item2)
  1453. {
  1454. return item2.Value.Count.CompareTo(item1.Value.Count);
  1455. });
  1456. if (topClients.Count > limit)
  1457. topClients.RemoveRange(limit, topClients.Count - limit);
  1458. foreach (KeyValuePair<IPAddress, Counter> item in topClients)
  1459. _clientIpAddresses[item.Key] = item.Value;
  1460. truncated = true;
  1461. }
  1462. if (_errorIpAddresses.Count > limit)
  1463. {
  1464. List<KeyValuePair<IPAddress, Counter>> topErrorClients = new List<KeyValuePair<IPAddress, Counter>>(_errorIpAddresses);
  1465. _errorIpAddresses.Clear();
  1466. topErrorClients.Sort(delegate (KeyValuePair<IPAddress, Counter> item1, KeyValuePair<IPAddress, Counter> item2)
  1467. {
  1468. return item2.Value.Count.CompareTo(item1.Value.Count);
  1469. });
  1470. if (topErrorClients.Count > limit)
  1471. topErrorClients.RemoveRange(limit, topErrorClients.Count - limit);
  1472. foreach (KeyValuePair<IPAddress, Counter> item in topErrorClients)
  1473. _errorIpAddresses[item.Key] = item.Value;
  1474. truncated = true;
  1475. }
  1476. if (_queries.Count > limit)
  1477. {
  1478. //only last hour queries data is required for cache auto prefetching
  1479. _queries.Clear();
  1480. truncated = true;
  1481. }
  1482. return truncated;
  1483. }
  1484. public void WriteTo(BinaryWriter bW)
  1485. {
  1486. if (!_locked)
  1487. throw new DnsServerException("StatCounter must be locked.");
  1488. bW.Write(Encoding.ASCII.GetBytes("SC")); //format
  1489. bW.Write((byte)8); //version
  1490. bW.Write(_totalQueries);
  1491. bW.Write(_totalNoError);
  1492. bW.Write(_totalServerFailure);
  1493. bW.Write(_totalNxDomain);
  1494. bW.Write(_totalRefused);
  1495. bW.Write(_totalAuthoritative);
  1496. bW.Write(_totalRecursive);
  1497. bW.Write(_totalCached);
  1498. bW.Write(_totalBlocked);
  1499. bW.Write(_totalDropped);
  1500. bW.Write(_totalClients);
  1501. {
  1502. bW.Write(_queryDomains.Count);
  1503. foreach (KeyValuePair<string, Counter> queryDomain in _queryDomains)
  1504. {
  1505. bW.WriteShortString(queryDomain.Key);
  1506. bW.Write(queryDomain.Value.Count);
  1507. }
  1508. }
  1509. {
  1510. bW.Write(_queryBlockedDomains.Count);
  1511. foreach (KeyValuePair<string, Counter> queryBlockedDomain in _queryBlockedDomains)
  1512. {
  1513. bW.WriteShortString(queryBlockedDomain.Key);
  1514. bW.Write(queryBlockedDomain.Value.Count);
  1515. }
  1516. }
  1517. {
  1518. bW.Write(_queryTypes.Count);
  1519. foreach (KeyValuePair<DnsResourceRecordType, Counter> queryType in _queryTypes)
  1520. {
  1521. bW.Write((ushort)queryType.Key);
  1522. bW.Write(queryType.Value.Count);
  1523. }
  1524. }
  1525. {
  1526. bW.Write(_protocolTypes.Count);
  1527. foreach (KeyValuePair<DnsTransportProtocol, Counter> protocolType in _protocolTypes)
  1528. {
  1529. bW.Write((byte)protocolType.Key);
  1530. bW.Write(protocolType.Value.Count);
  1531. }
  1532. }
  1533. {
  1534. bW.Write(_clientIpAddresses.Count);
  1535. foreach (KeyValuePair<IPAddress, Counter> clientIpAddress in _clientIpAddresses)
  1536. {
  1537. clientIpAddress.Key.WriteTo(bW);
  1538. bW.Write(clientIpAddress.Value.Count);
  1539. }
  1540. }
  1541. {
  1542. bW.Write(_queries.Count);
  1543. foreach (KeyValuePair<DnsQuestionRecord, Counter> query in _queries)
  1544. {
  1545. query.Key.WriteTo(bW.BaseStream, null);
  1546. bW.Write(query.Value.Count);
  1547. }
  1548. }
  1549. {
  1550. bW.Write(_errorIpAddresses.Count);
  1551. foreach (KeyValuePair<IPAddress, Counter> refusedIpAddress in _errorIpAddresses)
  1552. {
  1553. refusedIpAddress.Key.WriteTo(bW);
  1554. bW.Write(refusedIpAddress.Value.Count);
  1555. }
  1556. }
  1557. }
  1558. public List<KeyValuePair<string, long>> GetTopDomains(int limit)
  1559. {
  1560. List<KeyValuePair<string, long>> topDomains = new List<KeyValuePair<string, long>>(_queryDomains.Count);
  1561. foreach (KeyValuePair<string, Counter> item in _queryDomains)
  1562. topDomains.Add(new KeyValuePair<string, long>(item.Key, item.Value.Count));
  1563. return GetTopList(topDomains, limit);
  1564. }
  1565. public List<KeyValuePair<string, long>> GetTopBlockedDomains(int limit)
  1566. {
  1567. List<KeyValuePair<string, long>> topBlockedDomains = new List<KeyValuePair<string, long>>(_queryBlockedDomains.Count);
  1568. foreach (KeyValuePair<string, Counter> item in _queryBlockedDomains)
  1569. topBlockedDomains.Add(new KeyValuePair<string, long>(item.Key, item.Value.Count));
  1570. return GetTopList(topBlockedDomains, limit);
  1571. }
  1572. public List<KeyValuePair<string, long>> GetTopClients(int limit)
  1573. {
  1574. List<KeyValuePair<string, long>> topClients = new List<KeyValuePair<string, long>>(_clientIpAddresses.Count);
  1575. foreach (KeyValuePair<IPAddress, Counter> item in _clientIpAddresses)
  1576. topClients.Add(new KeyValuePair<string, long>(item.Key.ToString(), item.Value.Count));
  1577. return GetTopList(topClients, limit);
  1578. }
  1579. public List<KeyValuePair<string, long>> GetTopQueryTypes(int limit)
  1580. {
  1581. List<KeyValuePair<string, long>> queryTypes = new List<KeyValuePair<string, long>>(_queryTypes.Count);
  1582. foreach (KeyValuePair<DnsResourceRecordType, Counter> item in _queryTypes)
  1583. queryTypes.Add(new KeyValuePair<string, long>(item.Key.ToString(), item.Value.Count));
  1584. queryTypes.Sort(delegate (KeyValuePair<string, long> item1, KeyValuePair<string, long> item2)
  1585. {
  1586. return item2.Value.CompareTo(item1.Value);
  1587. });
  1588. if (queryTypes.Count > limit)
  1589. {
  1590. long othersCount = 0;
  1591. for (int i = limit; i < queryTypes.Count; i++)
  1592. othersCount += queryTypes[i].Value;
  1593. queryTypes.RemoveRange((limit - 1), queryTypes.Count - (limit - 1));
  1594. queryTypes.Add(new KeyValuePair<string, long>("Others", othersCount));
  1595. }
  1596. return queryTypes;
  1597. }
  1598. public List<KeyValuePair<string, long>> GetTopProtocolTypes()
  1599. {
  1600. List<KeyValuePair<string, long>> protocolTypes = new List<KeyValuePair<string, long>>(_protocolTypes.Count);
  1601. foreach (KeyValuePair<DnsTransportProtocol, Counter> protocolType in _protocolTypes)
  1602. protocolTypes.Add(new KeyValuePair<string, long>(protocolType.Key.ToString(), protocolType.Value.Count));
  1603. protocolTypes.Sort(delegate (KeyValuePair<string, long> item1, KeyValuePair<string, long> item2)
  1604. {
  1605. return item2.Value.CompareTo(item1.Value);
  1606. });
  1607. return protocolTypes;
  1608. }
  1609. public List<KeyValuePair<DnsQuestionRecord, long>> GetEligibleQueries(int minimumHits)
  1610. {
  1611. List<KeyValuePair<DnsQuestionRecord, long>> eligibleQueries = new List<KeyValuePair<DnsQuestionRecord, long>>(Convert.ToInt32(_queries.Count * 0.1));
  1612. foreach (KeyValuePair<DnsQuestionRecord, Counter> item in _queries)
  1613. {
  1614. if (item.Value.Count >= minimumHits)
  1615. eligibleQueries.Add(new KeyValuePair<DnsQuestionRecord, long>(item.Key, item.Value.Count));
  1616. }
  1617. return eligibleQueries;
  1618. }
  1619. public Dictionary<IPAddress, long> GetClientSubnetStats(int ipv4PrefixLength, int ipv6PrefixLength)
  1620. {
  1621. Dictionary<IPAddress, long> clientSubnetStats = new Dictionary<IPAddress, long>(_clientIpAddresses.Count);
  1622. foreach (KeyValuePair<IPAddress, Counter> item in _clientIpAddresses)
  1623. {
  1624. IPAddress clientSubnet;
  1625. switch (item.Key.AddressFamily)
  1626. {
  1627. case AddressFamily.InterNetwork:
  1628. clientSubnet = item.Key.GetNetworkAddress(ipv4PrefixLength);
  1629. break;
  1630. case AddressFamily.InterNetworkV6:
  1631. clientSubnet = item.Key.GetNetworkAddress(ipv6PrefixLength);
  1632. break;
  1633. default:
  1634. throw new NotSupportedException("AddressFamily not supported.");
  1635. }
  1636. if (clientSubnetStats.TryGetValue(clientSubnet, out long existingValue))
  1637. clientSubnetStats[clientSubnet] = existingValue + item.Value.Count;
  1638. else
  1639. clientSubnetStats.Add(clientSubnet, item.Value.Count);
  1640. }
  1641. return clientSubnetStats;
  1642. }
  1643. public Dictionary<IPAddress, long> GetErrorClientSubnetStats(int ipv4PrefixLength, int ipv6PrefixLength)
  1644. {
  1645. Dictionary<IPAddress, long> errorClientSubnetStats = new Dictionary<IPAddress, long>(_errorIpAddresses.Count);
  1646. foreach (KeyValuePair<IPAddress, Counter> item in _errorIpAddresses)
  1647. {
  1648. IPAddress clientSubnet;
  1649. switch (item.Key.AddressFamily)
  1650. {
  1651. case AddressFamily.InterNetwork:
  1652. clientSubnet = item.Key.GetNetworkAddress(ipv4PrefixLength);
  1653. break;
  1654. case AddressFamily.InterNetworkV6:
  1655. clientSubnet = item.Key.GetNetworkAddress(ipv6PrefixLength);
  1656. break;
  1657. default:
  1658. throw new NotSupportedException("AddressFamily not supported.");
  1659. }
  1660. if (errorClientSubnetStats.TryGetValue(clientSubnet, out long existingValue))
  1661. errorClientSubnetStats[clientSubnet] = existingValue + item.Value.Count;
  1662. else
  1663. errorClientSubnetStats.Add(clientSubnet, item.Value.Count);
  1664. }
  1665. return errorClientSubnetStats;
  1666. }
  1667. #endregion
  1668. #region properties
  1669. public bool IsLocked
  1670. { get { return _locked; } }
  1671. public long TotalQueries
  1672. { get { return _totalQueries; } }
  1673. public long TotalNoError
  1674. { get { return _totalNoError; } }
  1675. public long TotalServerFailure
  1676. { get { return _totalServerFailure; } }
  1677. public long TotalNxDomain
  1678. { get { return _totalNxDomain; } }
  1679. public long TotalRefused
  1680. { get { return _totalRefused; } }
  1681. public long TotalAuthoritative
  1682. { get { return _totalAuthoritative; } }
  1683. public long TotalRecursive
  1684. { get { return _totalRecursive; } }
  1685. public long TotalCached
  1686. { get { return _totalCached; } }
  1687. public long TotalBlocked
  1688. { get { return _totalBlocked; } }
  1689. public long TotalDropped
  1690. { get { return _totalDropped; } }
  1691. public long TotalClients
  1692. {
  1693. get
  1694. {
  1695. if (_truncationFoundDuringMerge)
  1696. return _totalClientsDailyStatsSummation;
  1697. return _totalClients;
  1698. }
  1699. }
  1700. #endregion
  1701. class Counter
  1702. {
  1703. #region variables
  1704. long _count;
  1705. #endregion
  1706. #region constructor
  1707. public Counter()
  1708. { }
  1709. public Counter(long count)
  1710. {
  1711. _count = count;
  1712. }
  1713. #endregion
  1714. #region public
  1715. public void Increment()
  1716. {
  1717. _count++;
  1718. }
  1719. public void Merge(Counter counter)
  1720. {
  1721. _count += counter._count;
  1722. }
  1723. #endregion
  1724. #region properties
  1725. public long Count
  1726. { get { return _count; } }
  1727. #endregion
  1728. }
  1729. }
  1730. class StatsQueueItem
  1731. {
  1732. #region variables
  1733. public readonly DateTime _timestamp;
  1734. public readonly DnsDatagram _request;
  1735. public readonly IPEndPoint _remoteEP;
  1736. public readonly DnsTransportProtocol _protocol;
  1737. public readonly DnsDatagram _response;
  1738. public readonly bool _rateLimited;
  1739. #endregion
  1740. #region constructor
  1741. public StatsQueueItem(DnsDatagram request, IPEndPoint remoteEP, DnsTransportProtocol protocol, DnsDatagram response, bool rateLimited)
  1742. {
  1743. _timestamp = DateTime.UtcNow;
  1744. _request = request;
  1745. _remoteEP = remoteEP;
  1746. _protocol = protocol;
  1747. _response = response;
  1748. _rateLimited = rateLimited;
  1749. }
  1750. #endregion
  1751. }
  1752. }
  1753. }