mirror of
https://github.com/fergalmoran/DnsServer.git
synced 2025-12-22 09:29:50 +00:00
209
Apps/LogExporterApp/App.cs
Normal file
209
Apps/LogExporterApp/App.cs
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
93
Apps/LogExporterApp/BufferManagementConfig.cs
Normal file
93
Apps/LogExporterApp/BufferManagementConfig.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
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 System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace LogExporter
|
||||||
|
{
|
||||||
|
public class BufferManagementConfig
|
||||||
|
{
|
||||||
|
[JsonPropertyName("maxLogEntries")]
|
||||||
|
public int? MaxLogEntries { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("file")]
|
||||||
|
public FileTarget? FileTarget { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("http")]
|
||||||
|
public HttpTarget? HttpTarget { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("syslog")]
|
||||||
|
public SyslogTarget? SyslogTarget { get; set; }
|
||||||
|
|
||||||
|
// Load configuration from JSON
|
||||||
|
public static BufferManagementConfig? Deserialize(string json)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize<BufferManagementConfig>(json, DnsConfigSerializerOptions.Default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TargetBase
|
||||||
|
{
|
||||||
|
[JsonPropertyName("enabled")]
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SyslogTarget : TargetBase
|
||||||
|
{
|
||||||
|
[JsonPropertyName("address")]
|
||||||
|
public string Address { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("port")]
|
||||||
|
public int? Port { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("protocol")]
|
||||||
|
public string? Protocol { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FileTarget : TargetBase
|
||||||
|
{
|
||||||
|
[JsonPropertyName("path")]
|
||||||
|
public string Path { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HttpTarget : TargetBase
|
||||||
|
{
|
||||||
|
[JsonPropertyName("endpoint")]
|
||||||
|
public string Endpoint { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("headers")]
|
||||||
|
public Dictionary<string, string>? Headers { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup reusable options with a single instance
|
||||||
|
public static class DnsConfigSerializerOptions
|
||||||
|
{
|
||||||
|
public static readonly JsonSerializerOptions Default = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // Convert properties to camelCase
|
||||||
|
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // For safe encoding
|
||||||
|
NumberHandling = JsonNumberHandling.Strict,
|
||||||
|
AllowTrailingCommas = true, // Allow trailing commas in JSON
|
||||||
|
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, // Convert dictionary keys to camelCase
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull // Ignore null values
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
128
Apps/LogExporterApp/LogEntry.cs
Normal file
128
Apps/LogExporterApp/LogEntry.cs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using TechnitiumLibrary.Net.Dns;
|
||||||
|
using TechnitiumLibrary.Net.Dns.ResourceRecords;
|
||||||
|
|
||||||
|
namespace LogExporter
|
||||||
|
{
|
||||||
|
public class LogEntry
|
||||||
|
{
|
||||||
|
public DateTime Timestamp { get; set; }
|
||||||
|
public string ClientIp { get; set; }
|
||||||
|
public int ClientPort { get; set; }
|
||||||
|
public bool DnssecOk { get; set; }
|
||||||
|
public DnsTransportProtocol Protocol { get; set; }
|
||||||
|
public DnsResponseCode ResponseCode { get; set; }
|
||||||
|
public List<Question> Questions { get; set; }
|
||||||
|
public List<Answer> Answers { get; set; }
|
||||||
|
public object? RequestTag { get; set; }
|
||||||
|
public object? ResponseTag { get; set; }
|
||||||
|
|
||||||
|
public LogEntry(DateTime timestamp, IPEndPoint remoteEP, DnsTransportProtocol protocol, DnsDatagram request, DnsDatagram response)
|
||||||
|
{
|
||||||
|
// Assign timestamp and ensure it's in UTC
|
||||||
|
Timestamp = timestamp.Kind == DateTimeKind.Utc ? timestamp : timestamp.ToUniversalTime();
|
||||||
|
|
||||||
|
// Extract client information
|
||||||
|
ClientIp = remoteEP.Address.ToString();
|
||||||
|
ClientPort = remoteEP.Port;
|
||||||
|
DnssecOk = request.DnssecOk;
|
||||||
|
Protocol = protocol;
|
||||||
|
ResponseCode = response.RCODE;
|
||||||
|
|
||||||
|
// Extract request information
|
||||||
|
Questions = new List<Question>(request.Question.Count);
|
||||||
|
if (request.Question?.Count > 0)
|
||||||
|
{
|
||||||
|
Questions.AddRange(request.Question.Select(questionRecord => new Question
|
||||||
|
{
|
||||||
|
QuestionName = questionRecord.Name,
|
||||||
|
QuestionType = questionRecord.Type,
|
||||||
|
QuestionClass = questionRecord.Class,
|
||||||
|
Size = questionRecord.UncompressedLength,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert answer section into a simple string summary (comma-separated for multiple answers)
|
||||||
|
Answers = new List<Answer>(response.Answer.Count);
|
||||||
|
if (response.Answer?.Count > 0)
|
||||||
|
{
|
||||||
|
Answers.AddRange(response.Answer.Select(record => new Answer
|
||||||
|
{
|
||||||
|
RecordType = record.Type,
|
||||||
|
RecordData = record.RDATA.ToString(),
|
||||||
|
RecordClass = record.Class,
|
||||||
|
RecordTtl = record.TTL,
|
||||||
|
Size = record.UncompressedLength,
|
||||||
|
DnssecStatus = record.DnssecStatus,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Tag != null)
|
||||||
|
{
|
||||||
|
RequestTag = request.Tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.Tag != null)
|
||||||
|
{
|
||||||
|
ResponseTag = response.Tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Question
|
||||||
|
{
|
||||||
|
public string QuestionName { get; set; }
|
||||||
|
public DnsResourceRecordType? QuestionType { get; set; }
|
||||||
|
public DnsClass? QuestionClass { get; set; }
|
||||||
|
public int Size { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Answer
|
||||||
|
{
|
||||||
|
public DnsResourceRecordType RecordType { get; set; }
|
||||||
|
public string RecordData { get; set; }
|
||||||
|
public DnsClass RecordClass { get; set; }
|
||||||
|
public uint RecordTtl { get; set; }
|
||||||
|
public int Size { get; set; }
|
||||||
|
public DnssecStatus DnssecStatus { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(this, DnsLogSerializerOptions.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom DateTime converter to handle UTC serialization in ISO 8601 format
|
||||||
|
public class JsonDateTimeConverter : JsonConverter<DateTime>
|
||||||
|
{
|
||||||
|
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
var dts = reader.GetString();
|
||||||
|
return dts == null ? DateTime.MinValue : DateTime.Parse(dts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup reusable options with a single instance
|
||||||
|
public static class DnsLogSerializerOptions
|
||||||
|
{
|
||||||
|
public static readonly JsonSerializerOptions Default = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = false, // Newline delimited logs should not be multiline
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // Convert properties to camelCase
|
||||||
|
Converters = { new JsonStringEnumConverter(), new JsonDateTimeConverter() }, // Handle enums and DateTime conversion
|
||||||
|
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // For safe encoding
|
||||||
|
NumberHandling = JsonNumberHandling.Strict,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull // Ignore null values
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
Apps/LogExporterApp/LogExporterApp.csproj
Normal file
53
Apps/LogExporterApp/LogExporterApp.csproj
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
<Version>1.0</Version>
|
||||||
|
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||||
|
<Company>Technitium</Company>
|
||||||
|
<Product>Technitium DNS Server</Product>
|
||||||
|
<Authors>Zafer Balkan</Authors>
|
||||||
|
<AssemblyName>LogExporterApp</AssemblyName>
|
||||||
|
<RootNamespace>LogExporter</RootNamespace>
|
||||||
|
<PackageProjectUrl>https://technitium.com/dns/</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://github.com/TechnitiumSoftware/DnsServer</RepositoryUrl>
|
||||||
|
<Description>The app allows exporting logs to a third party sink using an internal buffer.</Description>
|
||||||
|
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Http" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.PeriodicBatching" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.SyslogMessages" Version="4.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\DnsServerCore.ApplicationCommon\DnsServerCore.ApplicationCommon.csproj">
|
||||||
|
<Private>false</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="TechnitiumLibrary.Net">
|
||||||
|
<HintPath>..\..\..\TechnitiumLibrary\bin\TechnitiumLibrary.Net.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="TechnitiumLibrary">
|
||||||
|
<HintPath>..\..\..\TechnitiumLibrary\bin\TechnitiumLibrary.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="dnsApp.config">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
65
Apps/LogExporterApp/Strategy/ExportManager.cs
Normal file
65
Apps/LogExporterApp/Strategy/ExportManager.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LogExporter.Strategy
|
||||||
|
{
|
||||||
|
public class ExportManager
|
||||||
|
{
|
||||||
|
#region variables
|
||||||
|
|
||||||
|
private readonly Dictionary<Type, IExportStrategy> _exportStrategies;
|
||||||
|
|
||||||
|
#endregion variables
|
||||||
|
|
||||||
|
#region constructor
|
||||||
|
|
||||||
|
public ExportManager()
|
||||||
|
{
|
||||||
|
_exportStrategies = new Dictionary<Type, IExportStrategy>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion constructor
|
||||||
|
|
||||||
|
#region public
|
||||||
|
|
||||||
|
public void AddOrReplaceStrategy(IExportStrategy strategy)
|
||||||
|
{
|
||||||
|
_exportStrategies[strategy.GetType()] = strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasStrategy()
|
||||||
|
{
|
||||||
|
return _exportStrategies.Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ImplementStrategy(List<LogEntry> logs)
|
||||||
|
{
|
||||||
|
foreach (var strategy in _exportStrategies.Values)
|
||||||
|
{
|
||||||
|
await strategy.ExportAsync(logs).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion public
|
||||||
|
}
|
||||||
|
}
|
||||||
84
Apps/LogExporterApp/Strategy/FileExportStrategy.cs
Normal file
84
Apps/LogExporterApp/Strategy/FileExportStrategy.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
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 Serilog;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LogExporter.Strategy
|
||||||
|
{
|
||||||
|
public class FileExportStrategy : IExportStrategy
|
||||||
|
{
|
||||||
|
#region variables
|
||||||
|
|
||||||
|
private readonly Serilog.Core.Logger _sender;
|
||||||
|
|
||||||
|
private bool disposedValue;
|
||||||
|
|
||||||
|
#endregion variables
|
||||||
|
|
||||||
|
#region constructor
|
||||||
|
|
||||||
|
public FileExportStrategy(string filePath)
|
||||||
|
{
|
||||||
|
_sender = new LoggerConfiguration().WriteTo.File(filePath, outputTemplate: "{Message:lj}{NewLine}{Exception}").CreateLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion constructor
|
||||||
|
|
||||||
|
#region public
|
||||||
|
|
||||||
|
public Task ExportAsync(List<LogEntry> logs)
|
||||||
|
{
|
||||||
|
return Task.Run(() =>
|
||||||
|
{
|
||||||
|
foreach (LogEntry logEntry in logs)
|
||||||
|
{
|
||||||
|
_sender.Information(logEntry.ToString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion public
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
System.GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_sender.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IDisposable
|
||||||
|
}
|
||||||
|
}
|
||||||
132
Apps/LogExporterApp/Strategy/HttpExportStrategy.cs
Normal file
132
Apps/LogExporterApp/Strategy/HttpExportStrategy.cs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
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 Microsoft.Extensions.Configuration;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Sinks.Http;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LogExporter.Strategy
|
||||||
|
{
|
||||||
|
public class HttpExportStrategy : IExportStrategy
|
||||||
|
{
|
||||||
|
#region variables
|
||||||
|
|
||||||
|
private readonly Serilog.Core.Logger _sender;
|
||||||
|
|
||||||
|
private bool disposedValue;
|
||||||
|
|
||||||
|
#endregion variables
|
||||||
|
|
||||||
|
#region constructor
|
||||||
|
|
||||||
|
public HttpExportStrategy(string endpoint, Dictionary<string, string>? headers = null)
|
||||||
|
{
|
||||||
|
IConfigurationRoot? configuration = null;
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
configuration = new ConfigurationBuilder()
|
||||||
|
.AddInMemoryCollection(headers)
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sender = new LoggerConfiguration().WriteTo.Http(endpoint, null, httpClient: new CustomHttpClient(), configuration: configuration).Enrich.FromLogContext().CreateLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion constructor
|
||||||
|
|
||||||
|
#region public
|
||||||
|
|
||||||
|
public Task ExportAsync(List<LogEntry> logs)
|
||||||
|
{
|
||||||
|
return Task.Run(() =>
|
||||||
|
{
|
||||||
|
foreach (LogEntry logEntry in logs)
|
||||||
|
{
|
||||||
|
_sender.Information(logEntry.ToString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion public
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_sender.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IDisposable
|
||||||
|
|
||||||
|
#region Classes
|
||||||
|
|
||||||
|
public class CustomHttpClient : IHttpClient
|
||||||
|
{
|
||||||
|
private readonly HttpClient httpClient;
|
||||||
|
|
||||||
|
public CustomHttpClient() => httpClient = new HttpClient();
|
||||||
|
|
||||||
|
public void Configure(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
foreach (var pair in configuration.GetChildren())
|
||||||
|
{
|
||||||
|
httpClient.DefaultRequestHeaders.Add(pair.Key, pair.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
httpClient?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<HttpResponseMessage> PostAsync(string requestUri, Stream contentStream, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
using var content = new StreamContent(contentStream);
|
||||||
|
content.Headers.Add("Content-Type", "application/json");
|
||||||
|
|
||||||
|
return await httpClient
|
||||||
|
.PostAsync(requestUri, content, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Classes
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Apps/LogExporterApp/Strategy/IExportStrategy.cs
Normal file
33
Apps/LogExporterApp/Strategy/IExportStrategy.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LogExporter.Strategy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Strategu interface to decide the sinks for exporting the logs.
|
||||||
|
/// </summary>
|
||||||
|
public interface IExportStrategy: IDisposable
|
||||||
|
{
|
||||||
|
Task ExportAsync(List<LogEntry> logs);
|
||||||
|
}
|
||||||
|
}
|
||||||
203
Apps/LogExporterApp/Strategy/SyslogExportStrategy.cs
Normal file
203
Apps/LogExporterApp/Strategy/SyslogExportStrategy.cs
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
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 Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
using Serilog.Parsing;
|
||||||
|
using Serilog.Sinks.Syslog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LogExporter.Strategy
|
||||||
|
{
|
||||||
|
public class SyslogExportStrategy : IExportStrategy
|
||||||
|
{
|
||||||
|
#region variables
|
||||||
|
|
||||||
|
private const string _appName = "Technitium DNS Server";
|
||||||
|
|
||||||
|
private const string _sdId = "meta";
|
||||||
|
|
||||||
|
private const string DEFAUL_PROTOCOL = "udp";
|
||||||
|
|
||||||
|
private const int DEFAULT_PORT = 514;
|
||||||
|
|
||||||
|
private readonly Facility _facility = Facility.Local6;
|
||||||
|
|
||||||
|
private readonly Rfc5424Formatter _formatter;
|
||||||
|
|
||||||
|
private readonly Serilog.Core.Logger _sender;
|
||||||
|
|
||||||
|
private bool disposedValue;
|
||||||
|
|
||||||
|
#endregion variables
|
||||||
|
|
||||||
|
#region constructor
|
||||||
|
|
||||||
|
public SyslogExportStrategy(string address, int? port, string? protocol)
|
||||||
|
{
|
||||||
|
port ??= DEFAULT_PORT;
|
||||||
|
protocol ??= DEFAUL_PROTOCOL;
|
||||||
|
|
||||||
|
var conf = new LoggerConfiguration();
|
||||||
|
|
||||||
|
_sender = protocol.ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"tls" => conf.WriteTo.TcpSyslog(address, port.Value, _appName, FramingType.OCTET_COUNTING, SyslogFormat.RFC5424, _facility, useTls: true).Enrich.FromLogContext().CreateLogger(),
|
||||||
|
"tcp" => conf.WriteTo.TcpSyslog(address, port.Value, _appName, FramingType.OCTET_COUNTING, SyslogFormat.RFC5424, _facility, useTls: false).Enrich.FromLogContext().CreateLogger(),
|
||||||
|
"udp" => conf.WriteTo.UdpSyslog(address, port.Value, _appName, SyslogFormat.RFC5424, _facility).Enrich.FromLogContext().CreateLogger(),
|
||||||
|
"local" => conf.WriteTo.LocalSyslog(_appName, _facility).Enrich.FromLogContext().CreateLogger(),
|
||||||
|
_ => throw new Exception("Invalid protocol specified"),
|
||||||
|
};
|
||||||
|
|
||||||
|
_formatter = new Rfc5424Formatter(_facility, _appName, null, _sdId, Environment.MachineName);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion constructor
|
||||||
|
|
||||||
|
#region public
|
||||||
|
|
||||||
|
public Task ExportAsync(List<LogEntry> logs)
|
||||||
|
{
|
||||||
|
return Task.Run(() =>
|
||||||
|
{
|
||||||
|
foreach (var log in logs)
|
||||||
|
{
|
||||||
|
_sender.Information((string?)_formatter.FormatMessage((LogEvent?)Convert(log)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endregion public
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_sender.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IDisposable
|
||||||
|
|
||||||
|
#region private
|
||||||
|
|
||||||
|
private LogEvent Convert(LogEntry log)
|
||||||
|
{
|
||||||
|
// Initialize properties with base log details
|
||||||
|
var properties = new List<LogEventProperty>
|
||||||
|
{
|
||||||
|
new LogEventProperty("timestamp", new ScalarValue(log.Timestamp.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ"))),
|
||||||
|
new LogEventProperty("clientIp", new ScalarValue(log.ClientIp)),
|
||||||
|
new LogEventProperty("clientPort", new ScalarValue(log.ClientPort.ToString())),
|
||||||
|
new LogEventProperty("dnssecOk", new ScalarValue(log.DnssecOk.ToString())),
|
||||||
|
new LogEventProperty("protocol", new ScalarValue(log.Protocol.ToString())),
|
||||||
|
new LogEventProperty("rCode", new ScalarValue(log.ResponseCode.ToString()))
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add each question as properties
|
||||||
|
if (log.Questions?.Count > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < log.Questions.Count; i++)
|
||||||
|
{
|
||||||
|
var question = log.Questions[i];
|
||||||
|
properties.Add(new LogEventProperty($"qName_{i}", new ScalarValue(question.QuestionName)));
|
||||||
|
properties.Add(new LogEventProperty($"qType_{i}", new ScalarValue(question.QuestionType?.ToString() ?? "unknown")));
|
||||||
|
properties.Add(new LogEventProperty($"qClass_{i}", new ScalarValue(question.QuestionClass?.ToString() ?? "unknown")));
|
||||||
|
properties.Add(new LogEventProperty($"qSize_{i}", new ScalarValue(question.Size.ToString())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate questions summary
|
||||||
|
var questionSummary = string.Join("; ", log.Questions.Select((q, i) =>
|
||||||
|
$"QNAME_{i}: {q.QuestionName}, QTYPE: {q.QuestionType?.ToString() ?? "unknown"}, QCLASS: {q.QuestionClass?.ToString() ?? "unknown"}"));
|
||||||
|
properties.Add(new LogEventProperty("questionsSummary", new ScalarValue(questionSummary)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
properties.Add(new LogEventProperty("questionsSummary", new ScalarValue(string.Empty)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add each answer as properties
|
||||||
|
if (log.Answers?.Count > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < log.Answers.Count; i++)
|
||||||
|
{
|
||||||
|
var answer = log.Answers[i];
|
||||||
|
properties.Add(new LogEventProperty($"aType_{i}", new ScalarValue(answer.RecordType.ToString())));
|
||||||
|
properties.Add(new LogEventProperty($"aData_{i}", new ScalarValue(answer.RecordData)));
|
||||||
|
properties.Add(new LogEventProperty($"aClass_{i}", new ScalarValue(answer.RecordClass.ToString())));
|
||||||
|
properties.Add(new LogEventProperty($"aTtl_{i}", new ScalarValue(answer.RecordTtl.ToString())));
|
||||||
|
properties.Add(new LogEventProperty($"aSize_{i}", new ScalarValue(answer.Size.ToString())));
|
||||||
|
properties.Add(new LogEventProperty($"aDnssecStatus_{i}", new ScalarValue(answer.DnssecStatus.ToString())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate answers summary
|
||||||
|
var answerSummary = string.Join(", ", log.Answers.Select(a => a.RecordData));
|
||||||
|
properties.Add(new LogEventProperty("answersSummary", new ScalarValue(answerSummary)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
properties.Add(new LogEventProperty("answersSummary", new ScalarValue(string.Empty)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add request and response tags if present
|
||||||
|
if (log.RequestTag != null)
|
||||||
|
{
|
||||||
|
properties.Add(new LogEventProperty("requestTag", new ScalarValue(log.RequestTag.ToString())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log.ResponseTag != null)
|
||||||
|
{
|
||||||
|
properties.Add(new LogEventProperty("responseTag", new ScalarValue(log.ResponseTag.ToString())));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the message template to match the original summary format
|
||||||
|
const string templateText = "{questionsSummary}; RCODE: {rCode}; ANSWER: [{answersSummary}]";
|
||||||
|
|
||||||
|
// Parse the template
|
||||||
|
var template = new MessageTemplateParser().Parse(templateText);
|
||||||
|
|
||||||
|
// Create the LogEvent and return it
|
||||||
|
return new LogEvent(
|
||||||
|
timestamp: log.Timestamp,
|
||||||
|
level: LogEventLevel.Information,
|
||||||
|
exception: null,
|
||||||
|
messageTemplate: template,
|
||||||
|
properties: properties
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion private
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Apps/LogExporterApp/dnsApp.config
Normal file
20
Apps/LogExporterApp/dnsApp.config
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"maxLogEntries": 1000,
|
||||||
|
"file": {
|
||||||
|
"path": "./dns_logs.json",
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"http": {
|
||||||
|
"endpoint": "http://localhost:5000/logs",
|
||||||
|
"headers": {
|
||||||
|
"Authorization": "Bearer abc123"
|
||||||
|
},
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"syslog": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"port": 514,
|
||||||
|
"protocol": "UDP",
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DropRequestsApp", "Apps\Dro
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QueryLogsSqliteApp", "Apps\QueryLogsSqliteApp\QueryLogsSqliteApp.csproj", "{186DEF23-863E-4954-BE16-5E5FCA75ECA2}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QueryLogsSqliteApp", "Apps\QueryLogsSqliteApp\QueryLogsSqliteApp.csproj", "{186DEF23-863E-4954-BE16-5E5FCA75ECA2}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogExporterApp", "Apps\LogExporterApp\LogExporterApp.csproj", "{6F9BCCA9-6422-484B-A065-EF8AF9DA74B5}"
|
||||||
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdvancedBlockingApp", "Apps\AdvancedBlockingApp\AdvancedBlockingApp.csproj", "{A4C31093-CA65-42D4-928A-11907076C0DE}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdvancedBlockingApp", "Apps\AdvancedBlockingApp\AdvancedBlockingApp.csproj", "{A4C31093-CA65-42D4-928A-11907076C0DE}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NxDomainApp", "Apps\NxDomainApp\NxDomainApp.csproj", "{BB0010FC-20E9-4397-BF9B-C9955D9AD339}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NxDomainApp", "Apps\NxDomainApp\NxDomainApp.csproj", "{BB0010FC-20E9-4397-BF9B-C9955D9AD339}"
|
||||||
@@ -61,6 +63,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DnsRebindingProtectionApp",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FilterAaaaApp", "Apps\FilterAaaaApp\FilterAaaaApp.csproj", "{0A9B7F39-80DA-4084-AD47-8707576927ED}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FilterAaaaApp", "Apps\FilterAaaaApp\FilterAaaaApp.csproj", "{0A9B7F39-80DA-4084-AD47-8707576927ED}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3746EF13-91C5-4858-9DC2-D3C2504BD135}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.editorconfig = .editorconfig
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -119,6 +126,10 @@ Global
|
|||||||
{186DEF23-863E-4954-BE16-5E5FCA75ECA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{186DEF23-863E-4954-BE16-5E5FCA75ECA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{186DEF23-863E-4954-BE16-5E5FCA75ECA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{186DEF23-863E-4954-BE16-5E5FCA75ECA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{186DEF23-863E-4954-BE16-5E5FCA75ECA2}.Release|Any CPU.Build.0 = Release|Any CPU
|
{186DEF23-863E-4954-BE16-5E5FCA75ECA2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6F9BCCA9-6422-484B-A065-EF8AF9DA74B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6F9BCCA9-6422-484B-A065-EF8AF9DA74B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6F9BCCA9-6422-484B-A065-EF8AF9DA74B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6F9BCCA9-6422-484B-A065-EF8AF9DA74B5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{A4C31093-CA65-42D4-928A-11907076C0DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{A4C31093-CA65-42D4-928A-11907076C0DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{A4C31093-CA65-42D4-928A-11907076C0DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{A4C31093-CA65-42D4-928A-11907076C0DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{A4C31093-CA65-42D4-928A-11907076C0DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{A4C31093-CA65-42D4-928A-11907076C0DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -192,6 +203,7 @@ Global
|
|||||||
{099D27AF-3AEB-495A-A5D0-46DA59CC9213} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
{099D27AF-3AEB-495A-A5D0-46DA59CC9213} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
||||||
{738079D1-FA5A-40CD-8A27-D831919EE209} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
{738079D1-FA5A-40CD-8A27-D831919EE209} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
||||||
{186DEF23-863E-4954-BE16-5E5FCA75ECA2} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
{186DEF23-863E-4954-BE16-5E5FCA75ECA2} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
||||||
|
{6F9BCCA9-6422-484B-A065-EF8AF9DA74B5} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
||||||
{A4C31093-CA65-42D4-928A-11907076C0DE} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
{A4C31093-CA65-42D4-928A-11907076C0DE} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
||||||
{BB0010FC-20E9-4397-BF9B-C9955D9AD339} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
{BB0010FC-20E9-4397-BF9B-C9955D9AD339} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
||||||
{45C6F9AD-57D6-4D6D-9498-10B5C828E47E} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
{45C6F9AD-57D6-4D6D-9498-10B5C828E47E} = {938BF8EF-74B9-4FE0-B46F-11EBB7A4B3D2}
|
||||||
|
|||||||
Reference in New Issue
Block a user