mirror of
https://github.com/fergalmoran/DnsServer.git
synced 2025-12-22 09:29:50 +00:00
138 lines
5.6 KiB
C#
138 lines
5.6 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 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; private set; }
|
|
public string ClientIp { get; private set; }
|
|
public DnsTransportProtocol Protocol { get; private set; }
|
|
public DnsServerResponseType ResponseType { get; private set; }
|
|
public double? ResponseRtt { get; private set; }
|
|
public DnsResponseCode ResponseCode { get; private set; }
|
|
public DnsQuestion? Question { get; private set; }
|
|
public List<DnsResourceRecord> Answers { get; private 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();
|
|
Protocol = protocol;
|
|
ResponseType = response.Tag == null ? DnsServerResponseType.Recursive : (DnsServerResponseType)response.Tag;
|
|
|
|
if ((ResponseType == DnsServerResponseType.Recursive) && (response.Metadata is not null))
|
|
ResponseRtt = response.Metadata.RoundTripTime;
|
|
|
|
ResponseCode = response.RCODE;
|
|
|
|
// Extract request information
|
|
if (request.Question.Count > 0)
|
|
{
|
|
DnsQuestionRecord query = request.Question[0];
|
|
|
|
Question = new DnsQuestion
|
|
{
|
|
QuestionName = query.Name,
|
|
QuestionType = query.Type,
|
|
QuestionClass = query.Class,
|
|
};
|
|
}
|
|
|
|
// Convert answer section into a simple string summary (comma-separated for multiple answers)
|
|
Answers = new List<DnsResourceRecord>(response.Answer.Count);
|
|
if (response.Answer.Count > 0)
|
|
{
|
|
Answers.AddRange(response.Answer.Select(record => new DnsResourceRecord
|
|
{
|
|
Name = record.Name,
|
|
RecordType = record.Type,
|
|
RecordClass = record.Class,
|
|
RecordTtl = record.TTL,
|
|
RecordData = record.RDATA.ToString(),
|
|
DnssecStatus = record.DnssecStatus,
|
|
}));
|
|
}
|
|
}
|
|
|
|
public class DnsQuestion
|
|
{
|
|
public string QuestionName { get; set; }
|
|
public DnsResourceRecordType QuestionType { get; set; }
|
|
public DnsClass QuestionClass { get; set; }
|
|
}
|
|
|
|
public class DnsResourceRecord
|
|
{
|
|
public string Name { get; set; }
|
|
public DnsResourceRecordType RecordType { get; set; }
|
|
public DnsClass RecordClass { get; set; }
|
|
public uint RecordTtl { get; set; }
|
|
public string RecordData { 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)
|
|
{
|
|
string? 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
|
|
};
|
|
}
|
|
}
|
|
} |