dotnet-core_mail-server/MailServer/DNS/Protocol/DnsRecordFactory.cs

195 lines
7.0 KiB
C#

using System;
using System.Collections.Generic;
using DnsClient.Protocol.Record;
namespace DnsClient.Protocol
{
public class DnsRecordFactory
{
public static IDictionary<ResourceRecordType, Func<DnsDatagramReader, ResourceRecordInfo, DnsResourceRecord>> s_recordFactory =
new Dictionary<ResourceRecordType, Func<DnsDatagramReader, ResourceRecordInfo, DnsResourceRecord>>();
private readonly DnsDatagramReader _reader;
public DnsRecordFactory(DnsDatagramReader reader)
{
if (reader == null)
{
throw new ArgumentNullException(nameof(reader));
}
_reader = reader;
}
/*
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| |
/ /
/ NAME /
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TYPE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| CLASS |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| TTL |
| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| RDLENGTH |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/ RDATA /
/ /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* */
public ResourceRecordInfo ReadRecordInfo()
{
return new ResourceRecordInfo(
_reader.ReadName().ToString(), // name
(ResourceRecordType)_reader.ReadUInt16Reverse(),// type
(QueryClass)_reader.ReadUInt16Reverse(), // class
_reader.ReadUInt32Reverse(), // ttl - 32bit!!
_reader.ReadUInt16Reverse()); // RDLength
//reader.ReadBytes(reader.ReadUInt16Reverse())); // rdata
}
public DnsResourceRecord GetRecord(ResourceRecordInfo info)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
var oldIndex = _reader.Index;
DnsResourceRecord result;
if (s_recordFactory.ContainsKey(info.RecordType))
{
result = s_recordFactory[info.RecordType](_reader, info);
}
else
{
switch (info.RecordType)
{
case ResourceRecordType.A:
result = ResolveARecord(info);
break;
case ResourceRecordType.NS:
result = ResolveNsRecord(info);
break;
case ResourceRecordType.SOA:
result = ResolveSoaRecord(info);
break;
case ResourceRecordType.PTR:
result = ResolvePtrRecord(info);
break;
case ResourceRecordType.MX:
result = ResolveMXRecord(info);
break;
case ResourceRecordType.TXT:
result = ResolveTXTRecord(info);
break;
case ResourceRecordType.AAAA:
result = ResolveAAAARecord(info);
break;
case ResourceRecordType.SRV:
result = ResolveSrvRecord(info);
break;
default:
// update reader index because we don't read full data for the empty record
_reader.Index += info.RawDataLength;
result = new EmptyRecord(info);
break;
}
}
// sanity check
if (_reader.Index != oldIndex + info.RawDataLength)
{
throw new InvalidOperationException("Record reader index out of sync.");
}
return result;
}
private PtrRecord ResolvePtrRecord(ResourceRecordInfo info)
{
return new PtrRecord(info, _reader.ReadName().ToString());
}
private AAAARecord ResolveAAAARecord(ResourceRecordInfo info)
{
var address = _reader.ReadIPv6Address();
return new AAAARecord(info, address);
}
// default resolver implementation for an A Record
private ARecord ResolveARecord(ResourceRecordInfo info)
{
if (info.RawDataLength != 4)
{
throw new IndexOutOfRangeException($"Reading wrong length for an IP address. Expected 4 found {info.RawDataLength}.");
}
return new ARecord(info, _reader.ReadIPAddress());
}
private MxRecord ResolveMXRecord(ResourceRecordInfo info)
{
var preference = _reader.ReadUInt16Reverse();
var domain = _reader.ReadName();
return new MxRecord(info, preference, domain.ToString());
}
private NsRecord ResolveNsRecord(ResourceRecordInfo info)
{
var name = _reader.ReadName();
return new NsRecord(info, name.ToString());
}
private SoaRecord ResolveSoaRecord(ResourceRecordInfo info)
{
var mName = _reader.ReadName();
var rName = _reader.ReadName();
var serial = _reader.ReadUInt32Reverse();
var refresh = _reader.ReadUInt32Reverse();
var retry = _reader.ReadUInt32Reverse();
var expire = _reader.ReadUInt32Reverse();
var minimum = _reader.ReadUInt32Reverse();
return new SoaRecord(info, mName.ToString(), rName.ToString(), serial, refresh, retry, expire, minimum);
}
private SrvRecord ResolveSrvRecord(ResourceRecordInfo info)
{
var priority = _reader.ReadUInt16Reverse();
var weight = _reader.ReadUInt16Reverse();
var port = _reader.ReadUInt16Reverse();
var target = _reader.ReadName();
return new SrvRecord(info, priority, weight, port, target.ToString());
}
private TxtRecord ResolveTXTRecord(ResourceRecordInfo info)
{
int pos = _reader.Index;
var values = new List<string>();
while ((_reader.Index - pos) < info.RawDataLength)
{
values.Add(_reader.ReadString());
}
return new TxtRecord(info, values.ToArray());
}
}
}