Skip to content

Commit 8bbe7d1

Browse files
authored
Merge pull request #1421 from zbalkan/logexporter
Added optional EDNS logging for detailed logging capability
2 parents 029c8a5 + 4f06bdd commit 8bbe7d1

9 files changed

Lines changed: 101 additions & 58 deletions

File tree

Apps/LogExporterApp/App.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Technitium DNS Server
33
Copyright (C) 2025 Shreyas Zare (shreyas@technitium.com)
4+
Copyright (C) 2025 Zafer Balkan (zafer@zaferbalkan.com)
45
56
This program is free software: you can redistribute it and/or modify
67
it under the terms of the GNU General Public License as published by
@@ -35,7 +36,7 @@ public sealed class App : IDnsApplication, IDnsQueryLogger
3536
#region variables
3637

3738
IDnsServer? _dnsServer;
38-
BufferManagementConfig? _config;
39+
AppConfig? _config;
3940

4041
readonly ExportManager _exportManager = new ExportManager();
4142

@@ -91,7 +92,7 @@ private void Dispose(bool disposing)
9192
public Task InitializeAsync(IDnsServer dnsServer, string config)
9293
{
9394
_dnsServer = dnsServer;
94-
_config = BufferManagementConfig.Deserialize(config);
95+
_config = AppConfig.Deserialize(config);
9596

9697
if (_config is null)
9798
throw new DnsClientException("Invalid application configuration.");
@@ -141,7 +142,7 @@ public Task InsertLogAsync(DateTime timestamp, DnsDatagram request, IPEndPoint r
141142
if (_enableLogging)
142143
{
143144
if (_queuedLogs.Count < _config!.MaxQueueSize)
144-
_queuedLogs.Enqueue(new LogEntry(timestamp, remoteEP, protocol, request, response));
145+
_queuedLogs.Enqueue(new LogEntry(timestamp, remoteEP, protocol, request, response, _config.EnableEdnsLogging));
145146
}
146147

147148
return Task.CompletedTask;

Apps/LogExporterApp/BufferManagementConfig.cs renamed to Apps/LogExporterApp/AppConfig.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Technitium DNS Server
33
Copyright (C) 2025 Shreyas Zare (shreyas@technitium.com)
4+
Copyright (C) 2025 Zafer Balkan (zafer@zaferbalkan.com)
45
56
This program is free software: you can redistribute it and/or modify
67
it under the terms of the GNU General Public License as published by
@@ -23,11 +24,13 @@ You should have received a copy of the GNU General Public License
2324

2425
namespace LogExporter
2526
{
26-
public class BufferManagementConfig
27+
public class AppConfig
2728
{
2829
[JsonPropertyName("maxQueueSize")]
29-
public int MaxQueueSize
30-
{ get; set; }
30+
public int MaxQueueSize { get; set; }
31+
32+
[JsonPropertyName("enableEdnsLogging ")]
33+
public bool EnableEdnsLogging { get; set; }
3134

3235
[JsonPropertyName("file")]
3336
public FileTarget? FileTarget { get; set; }
@@ -39,9 +42,9 @@ public int MaxQueueSize
3942
public SyslogTarget? SyslogTarget { get; set; }
4043

4144
// Load configuration from JSON
42-
public static BufferManagementConfig? Deserialize(string json)
45+
public static AppConfig? Deserialize(string json)
4346
{
44-
return JsonSerializer.Deserialize<BufferManagementConfig>(json, DnsConfigSerializerOptions.Default);
47+
return JsonSerializer.Deserialize<AppConfig>(json, DnsConfigSerializerOptions.Default);
4548
}
4649
}
4750

Apps/LogExporterApp/LogEntry.cs

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Technitium DNS Server
33
Copyright (C) 2025 Shreyas Zare (shreyas@technitium.com)
4+
Copyright (C) 2025 Zafer Balkan (zafer@zaferbalkan.com)
45
56
This program is free software: you can redistribute it and/or modify
67
it under the terms of the GNU General Public License as published by
@@ -31,16 +32,7 @@ namespace LogExporter
3132
{
3233
public class LogEntry
3334
{
34-
public DateTime Timestamp { get; private set; }
35-
public string ClientIp { get; private set; }
36-
public DnsTransportProtocol Protocol { get; private set; }
37-
public DnsServerResponseType ResponseType { get; private set; }
38-
public double? ResponseRtt { get; private set; }
39-
public DnsResponseCode ResponseCode { get; private set; }
40-
public DnsQuestion? Question { get; private set; }
41-
public List<DnsResourceRecord> Answers { get; private set; }
42-
43-
public LogEntry(DateTime timestamp, IPEndPoint remoteEP, DnsTransportProtocol protocol, DnsDatagram request, DnsDatagram response)
35+
public LogEntry(DateTime timestamp, IPEndPoint remoteEP, DnsTransportProtocol protocol, DnsDatagram request, DnsDatagram response, bool ednsLogging = false)
4436
{
4537
// Assign timestamp and ensure it's in UTC
4638
Timestamp = timestamp.Kind == DateTimeKind.Utc ? timestamp : timestamp.ToUniversalTime();
@@ -82,31 +74,74 @@ public LogEntry(DateTime timestamp, IPEndPoint remoteEP, DnsTransportProtocol pr
8274
DnssecStatus = record.DnssecStatus,
8375
}));
8476
}
77+
78+
EDNS = new List<EDNSLog>();
79+
if (!ednsLogging || response.EDNS is null)
80+
{
81+
return;
82+
}
83+
foreach (TechnitiumLibrary.Net.Dns.EDnsOptions.EDnsOption extendedErrorLog in response.EDNS.Options.Where(o => o.Code == TechnitiumLibrary.Net.Dns.EDnsOptions.EDnsOptionCode.EXTENDED_DNS_ERROR))
84+
{
85+
string[] extractedData = extendedErrorLog.Data.ToString().Replace("[", string.Empty).Replace("]", string.Empty).Split(":", StringSplitOptions.TrimEntries);
86+
87+
EDNS.Add(new EDNSLog
88+
{
89+
ErrType = extractedData[0],
90+
Message = extractedData[1]
91+
});
92+
}
93+
}
94+
95+
public List<DnsResourceRecord> Answers { get; private set; }
96+
public string ClientIp { get; private set; }
97+
public List<EDNSLog> EDNS { get; private set; }
98+
public DnsTransportProtocol Protocol { get; private set; }
99+
public DnsQuestion? Question { get; private set; }
100+
public DnsResponseCode ResponseCode { get; private set; }
101+
public double? ResponseRtt { get; private set; }
102+
public DnsServerResponseType ResponseType { get; private set; }
103+
public DateTime Timestamp { get; private set; }
104+
public override string ToString()
105+
{
106+
return JsonSerializer.Serialize(this, DnsLogSerializerOptions.Default);
107+
}
108+
109+
public static class DnsLogSerializerOptions
110+
{
111+
public static readonly JsonSerializerOptions Default = new JsonSerializerOptions
112+
{
113+
WriteIndented = false,
114+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
115+
Converters = { new JsonStringEnumConverter(), new JsonDateTimeConverter() },
116+
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
117+
NumberHandling = JsonNumberHandling.Strict,
118+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
119+
};
85120
}
86121

87122
public class DnsQuestion
88123
{
124+
public DnsClass QuestionClass { get; set; }
89125
public required string QuestionName { get; set; }
90126
public DnsResourceRecordType QuestionType { get; set; }
91-
public DnsClass QuestionClass { get; set; }
92127
}
93128

94129
public class DnsResourceRecord
95130
{
131+
public DnssecStatus DnssecStatus { get; set; }
96132
public required string Name { get; set; }
97-
public DnsResourceRecordType RecordType { get; set; }
98133
public DnsClass RecordClass { get; set; }
99-
public uint RecordTtl { get; set; }
100134
public required string RecordData { get; set; }
101-
public DnssecStatus DnssecStatus { get; set; }
135+
public uint RecordTtl { get; set; }
136+
public DnsResourceRecordType RecordType { get; set; }
102137
}
103138

104-
public override string ToString()
139+
public class EDNSLog
105140
{
106-
return JsonSerializer.Serialize(this, DnsLogSerializerOptions.Default);
141+
public string ErrType { get; set; }
142+
public string Message { get; set; }
107143
}
108144

109-
// Custom DateTime converter to handle UTC serialization in ISO 8601 format
110145
public class JsonDateTimeConverter : JsonConverter<DateTime>
111146
{
112147
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
@@ -120,19 +155,5 @@ public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializer
120155
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ"));
121156
}
122157
}
123-
124-
// Setup reusable options with a single instance
125-
public static class DnsLogSerializerOptions
126-
{
127-
public static readonly JsonSerializerOptions Default = new JsonSerializerOptions
128-
{
129-
WriteIndented = false, // Newline delimited logs should not be multiline
130-
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // Convert properties to camelCase
131-
Converters = { new JsonStringEnumConverter(), new JsonDateTimeConverter() }, // Handle enums and DateTime conversion
132-
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // For safe encoding
133-
NumberHandling = JsonNumberHandling.Strict,
134-
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull // Ignore null values
135-
};
136-
}
137158
}
138159
}

Apps/LogExporterApp/Strategy/ExportManager.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Technitium DNS Server
33
Copyright (C) 2025 Shreyas Zare (shreyas@technitium.com)
4+
Copyright (C) 2025 Zafer Balkan (zafer@zaferbalkan.com)
45
56
This program is free software: you can redistribute it and/or modify
67
it under the terms of the GNU General Public License as published by

Apps/LogExporterApp/Strategy/FileExportStrategy.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Technitium DNS Server
33
Copyright (C) 2025 Shreyas Zare (shreyas@technitium.com)
4+
Copyright (C) 2025 Zafer Balkan (zafer@zaferbalkan.com)
45
56
This program is free software: you can redistribute it and/or modify
67
it under the terms of the GNU General Public License as published by

Apps/LogExporterApp/Strategy/HttpExportStrategy.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Technitium DNS Server
33
Copyright (C) 2025 Shreyas Zare (shreyas@technitium.com)
4+
Copyright (C) 2025 Zafer Balkan (zafer@zaferbalkan.com)
45
56
This program is free software: you can redistribute it and/or modify
67
it under the terms of the GNU General Public License as published by

Apps/LogExporterApp/Strategy/IExportStrategy.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Technitium DNS Server
33
Copyright (C) 2025 Shreyas Zare (shreyas@technitium.com)
4+
Copyright (C) 2025 Zafer Balkan (zafer@zaferbalkan.com)
45
56
This program is free software: you can redistribute it and/or modify
67
it under the terms of the GNU General Public License as published by

Apps/LogExporterApp/Strategy/SyslogExportStrategy.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
Technitium DNS Server
33
Copyright (C) 2025 Shreyas Zare (shreyas@technitium.com)
4+
Copyright (C) 2025 Zafer Balkan (zafer@zaferbalkan.com)
45
56
This program is free software: you can redistribute it and/or modify
67
it under the terms of the GNU General Public License as published by
@@ -118,7 +119,7 @@ private static LogEvent Convert(LogEntry log)
118119
properties.Add(new LogEventProperty("qType", new ScalarValue(question.QuestionType.ToString())));
119120
properties.Add(new LogEventProperty("qClass", new ScalarValue(question.QuestionClass.ToString())));
120121

121-
string questionSummary = $"QNAME: {question.QuestionName}, QTYPE: {question.QuestionType.ToString()}, QCLASS: {question.QuestionClass.ToString()}";
122+
string questionSummary = $"QNAME: {question.QuestionName}, QTYPE: {question.QuestionType}, QCLASS: {question.QuestionClass}";
122123
properties.Add(new LogEventProperty("questionsSummary", new ScalarValue(questionSummary)));
123124
}
124125
else
@@ -150,6 +151,18 @@ private static LogEvent Convert(LogEntry log)
150151
properties.Add(new LogEventProperty("answersSummary", new ScalarValue(string.Empty)));
151152
}
152153

154+
// Add EDNS logs
155+
if (log.EDNS.Count > 0)
156+
{
157+
for (int i = 0; i < log.EDNS.Count; i++)
158+
{
159+
var ednsLog = log.EDNS[i];
160+
properties.Add(new LogEventProperty($"ednsErrType_{i}", new ScalarValue(ednsLog.ErrType)));
161+
properties.Add(new LogEventProperty($"ednsMessage_{i}", new ScalarValue(ednsLog.Message)));
162+
163+
}
164+
}
165+
153166
// Define the message template to match the original summary format
154167
const string templateText = "{questionsSummary}; RCODE: {rCode}; ANSWER: [{answersSummary}]";
155168

Apps/LogExporterApp/dnsApp.config

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
{
2-
"maxQueueSize": 1000000,
3-
"file": {
4-
"path": "./dns_logs.json",
5-
"enabled": false
6-
},
7-
"http": {
8-
"endpoint": "http://localhost:5000/logs",
9-
"headers": {
10-
"Authorization": "Bearer abc123"
11-
},
12-
"enabled": false
13-
},
14-
"syslog": {
15-
"address": "127.0.0.1",
16-
"port": 514,
17-
"protocol": "UDP",
18-
"enabled": false
19-
}
2+
"maxQueueSize": 1000000,
3+
"ebableEdnsLogging": false,
4+
"file": {
5+
"path": "./dns_logs.json",
6+
"enabled": false
7+
},
8+
"http": {
9+
"endpoint": "http://localhost:5000/logs",
10+
"headers": {
11+
"Authorization": "Bearer abc123"
12+
},
13+
"enabled": false
14+
},
15+
"syslog": {
16+
"address": "127.0.0.1",
17+
"port": 514,
18+
"protocol": "UDP",
19+
"enabled": false
20+
}
2021
}

0 commit comments

Comments
 (0)