mirror of
https://github.com/fergalmoran/DnsServer.git
synced 2025-12-22 09:29:50 +00:00
215 lines
6.1 KiB
C#
215 lines
6.1 KiB
C#
/*
|
|
Technitium DNS Server
|
|
Copyright (C) 2025 Shreyas Zare (shreyas@technitium.com)
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
using DnsServerCore.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;
|
|
using TechnitiumLibrary.Net.Dns;
|
|
|
|
namespace LogExporter
|
|
{
|
|
public sealed class App : IDnsApplication, IDnsQueryLogger
|
|
{
|
|
#region variables
|
|
|
|
IDnsServer? _dnsServer;
|
|
BufferManagementConfig? _config;
|
|
|
|
readonly ExportManager _exportManager = new ExportManager();
|
|
|
|
bool _enableLogging;
|
|
|
|
readonly ConcurrentQueue<LogEntry> _queuedLogs = new ConcurrentQueue<LogEntry>();
|
|
readonly Timer _queueTimer;
|
|
const int QUEUE_TIMER_INTERVAL = 10000;
|
|
const int BULK_INSERT_COUNT = 1000;
|
|
|
|
bool _disposed;
|
|
|
|
#endregion
|
|
|
|
#region constructor
|
|
|
|
public App()
|
|
{
|
|
_queueTimer = new Timer(HandleExportLogCallback);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(disposing: true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
private void Dispose(bool disposing)
|
|
{
|
|
if (!_disposed)
|
|
{
|
|
if (disposing)
|
|
{
|
|
_queueTimer?.Dispose();
|
|
|
|
ExportLogsAsync().Sync(); //flush any pending logs
|
|
|
|
_exportManager.Dispose();
|
|
}
|
|
|
|
_disposed = true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region public
|
|
|
|
public Task InitializeAsync(IDnsServer dnsServer, string config)
|
|
{
|
|
_dnsServer = dnsServer;
|
|
_config = BufferManagementConfig.Deserialize(config);
|
|
|
|
if (_config is null)
|
|
throw new DnsClientException("Invalid application configuration.");
|
|
|
|
if (_config.FileTarget!.Enabled)
|
|
{
|
|
_exportManager.RemoveStrategy(typeof(FileExportStrategy));
|
|
_exportManager.AddStrategy(new FileExportStrategy(_config.FileTarget!.Path));
|
|
}
|
|
else
|
|
{
|
|
_exportManager.RemoveStrategy(typeof(FileExportStrategy));
|
|
}
|
|
|
|
if (_config.HttpTarget!.Enabled)
|
|
{
|
|
_exportManager.RemoveStrategy(typeof(HttpExportStrategy));
|
|
_exportManager.AddStrategy(new HttpExportStrategy(_config.HttpTarget.Endpoint, _config.HttpTarget.Headers));
|
|
}
|
|
else
|
|
{
|
|
_exportManager.RemoveStrategy(typeof(HttpExportStrategy));
|
|
}
|
|
|
|
if (_config.SyslogTarget!.Enabled)
|
|
{
|
|
_exportManager.RemoveStrategy(typeof(SyslogExportStrategy));
|
|
_exportManager.AddStrategy(new SyslogExportStrategy(_config.SyslogTarget.Address, _config.SyslogTarget.Port, _config.SyslogTarget.Protocol));
|
|
}
|
|
else
|
|
{
|
|
_exportManager.RemoveStrategy(typeof(SyslogExportStrategy));
|
|
}
|
|
|
|
_enableLogging = _exportManager.HasStrategy();
|
|
|
|
if (_enableLogging)
|
|
_queueTimer.Change(QUEUE_TIMER_INTERVAL, Timeout.Infinite);
|
|
else
|
|
_queueTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task InsertLogAsync(DateTime timestamp, DnsDatagram request, IPEndPoint remoteEP, DnsTransportProtocol protocol, DnsDatagram response)
|
|
{
|
|
if (_enableLogging)
|
|
{
|
|
if (_queuedLogs.Count < _config!.MaxQueueSize)
|
|
_queuedLogs.Enqueue(new LogEntry(timestamp, remoteEP, protocol, request, response));
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region private
|
|
|
|
private async Task ExportLogsAsync()
|
|
{
|
|
try
|
|
{
|
|
List<LogEntry> logs = new List<LogEntry>(BULK_INSERT_COUNT);
|
|
|
|
while (true)
|
|
{
|
|
while (logs.Count < BULK_INSERT_COUNT && _queuedLogs.TryDequeue(out LogEntry? log))
|
|
{
|
|
logs.Add(log);
|
|
}
|
|
|
|
if (logs.Count < 1)
|
|
break;
|
|
|
|
await _exportManager.ImplementStrategyAsync(logs);
|
|
|
|
logs.Clear();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_dnsServer?.WriteLog(ex);
|
|
}
|
|
}
|
|
|
|
private async void HandleExportLogCallback(object? state)
|
|
{
|
|
try
|
|
{
|
|
// Process logs within the timer interval, then let the timer reschedule
|
|
await ExportLogsAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_dnsServer?.WriteLog(ex);
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
_queueTimer?.Change(QUEUE_TIMER_INTERVAL, Timeout.Infinite);
|
|
}
|
|
catch (ObjectDisposedException)
|
|
{ }
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region properties
|
|
|
|
public string Description
|
|
{
|
|
get { return "Allows exporting query logs to third party sinks. It supports exporting to File, HTTP endpoint, and Syslog (UDP, TCP, TLS, and Local protocols)."; }
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|