mirror of
https://github.com/fergalmoran/DnsServer.git
synced 2025-12-22 09:29:50 +00:00
209 lines
6.3 KiB
C#
209 lines
6.3 KiB
C#
/*
|
|
Technitium DNS Server
|
|
Copyright (C) 2024 Shreyas Zare (shreyas@technitium.com)
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
using DnsServerCore.ApplicationCommon;
|
|
using LogExporter.Strategy;
|
|
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using TechnitiumLibrary.Net.Dns;
|
|
using TechnitiumLibrary.Net.Dns.ResourceRecords;
|
|
|
|
namespace LogExporter
|
|
{
|
|
public sealed class App : IDnsApplication, IDnsQueryLogger
|
|
{
|
|
#region variables
|
|
|
|
private const int BULK_INSERT_COUNT = 1000;
|
|
|
|
private const int DEFAULT_QUEUE_CAPACITY = 1000;
|
|
|
|
private const int QUEUE_TIMER_INTERVAL = 10000;
|
|
|
|
private readonly IReadOnlyList<DnsLogEntry> _emptyList = [];
|
|
|
|
private readonly ExportManager _exportManager = new ExportManager();
|
|
|
|
private BufferManagementConfig? _config;
|
|
|
|
private IDnsServer _dnsServer;
|
|
|
|
private BlockingCollection<LogEntry> _logBuffer;
|
|
|
|
private Timer _queueTimer;
|
|
|
|
private bool disposedValue;
|
|
|
|
#endregion variables
|
|
|
|
#region constructor
|
|
|
|
public App()
|
|
{
|
|
}
|
|
|
|
#endregion constructor
|
|
|
|
#region IDisposable
|
|
|
|
public void Dispose()
|
|
{
|
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
|
Dispose(disposing: true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private void Dispose(bool disposing)
|
|
{
|
|
if (!disposedValue)
|
|
{
|
|
if (disposing)
|
|
{
|
|
_queueTimer?.Dispose();
|
|
|
|
ExportLogs(); //flush any pending logs
|
|
|
|
_logBuffer.Dispose();
|
|
}
|
|
|
|
disposedValue = true;
|
|
}
|
|
}
|
|
|
|
#endregion IDisposable
|
|
|
|
#region public
|
|
|
|
public Task InitializeAsync(IDnsServer dnsServer, string config)
|
|
{
|
|
_dnsServer = dnsServer;
|
|
_config = BufferManagementConfig.Deserialize(config);
|
|
|
|
if (_config == null)
|
|
{
|
|
throw new DnsClientException("Invalid application configuration.");
|
|
}
|
|
|
|
if (_config.MaxLogEntries != null)
|
|
{
|
|
_logBuffer = new BlockingCollection<LogEntry>(_config.MaxLogEntries.Value);
|
|
}
|
|
else
|
|
{
|
|
_logBuffer = new BlockingCollection<LogEntry>(DEFAULT_QUEUE_CAPACITY);
|
|
}
|
|
|
|
RegisterExportTargets();
|
|
if (_exportManager.HasStrategy())
|
|
{
|
|
_queueTimer = new Timer(HandleExportLogCallback, state: null, QUEUE_TIMER_INTERVAL, Timeout.Infinite);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task InsertLogAsync(DateTime timestamp, DnsDatagram request, IPEndPoint remoteEP, DnsTransportProtocol protocol, DnsDatagram response)
|
|
{
|
|
_logBuffer.Add(new LogEntry(timestamp, remoteEP, protocol, request, response));
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task<DnsLogPage> QueryLogsAsync(long pageNumber, int entriesPerPage, bool descendingOrder, DateTime? start, DateTime? end, IPAddress clientIpAddress, DnsTransportProtocol? protocol, DnsServerResponseType? responseType, DnsResponseCode? rcode, string qname, DnsResourceRecordType? qtype, DnsClass? qclass)
|
|
{
|
|
return Task.FromResult(new DnsLogPage(0, 0, 0, _emptyList));
|
|
}
|
|
|
|
#endregion public
|
|
|
|
#region private
|
|
|
|
private void ExportLogs()
|
|
{
|
|
var logs = new List<LogEntry>(BULK_INSERT_COUNT);
|
|
|
|
// Process logs within the timer interval, then let the timer reschedule
|
|
while (logs.Count <= BULK_INSERT_COUNT && _logBuffer.TryTake(out var log))
|
|
{
|
|
logs.Add(log);
|
|
}
|
|
|
|
// If we have any logs to process, export them
|
|
if (logs.Count > 0)
|
|
{
|
|
_exportManager.ImplementStrategy(logs);
|
|
}
|
|
}
|
|
|
|
private void HandleExportLogCallback(object? state)
|
|
{
|
|
try
|
|
{
|
|
ExportLogs();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_dnsServer?.WriteLog(ex);
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
_queueTimer.Change(QUEUE_TIMER_INTERVAL, Timeout.Infinite);
|
|
}
|
|
catch (ObjectDisposedException)
|
|
{ }
|
|
}
|
|
}
|
|
|
|
private void RegisterExportTargets()
|
|
{
|
|
// Helper function to register an export strategy if the target is enabled
|
|
void RegisterIfEnabled<TTarget, TStrategy>(TTarget target, Func<TTarget, TStrategy> strategyFactory)
|
|
where TTarget : TargetBase
|
|
where TStrategy : IExportStrategy
|
|
{
|
|
if (target?.Enabled == true)
|
|
{
|
|
var strategy = strategyFactory(target);
|
|
_exportManager.AddOrReplaceStrategy(strategy);
|
|
}
|
|
}
|
|
|
|
// Register the different strategies using the helper
|
|
RegisterIfEnabled(_config!.FileTarget!, target => new FileExportStrategy(target.Path));
|
|
RegisterIfEnabled(_config!.HttpTarget!, target => new HttpExportStrategy(target.Endpoint, target.Headers));
|
|
RegisterIfEnabled(_config!.SyslogTarget!, target => new SyslogExportStrategy(target.Address, target.Port, target.Protocol));
|
|
}
|
|
|
|
#endregion private
|
|
|
|
#region properties
|
|
|
|
public string Description
|
|
{
|
|
get { return "The app allows exporting logs to a third party sink using an internal buffer."; }
|
|
}
|
|
|
|
#endregion properties
|
|
}
|
|
} |