Folder structure reconstruction
This commit is contained in:
135
MailServer/DNS/Protocol/DnsDatagramReader.cs
Normal file
135
MailServer/DNS/Protocol/DnsDatagramReader.cs
Normal file
@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace DnsClient.Protocol
|
||||
{
|
||||
public class DnsDatagramReader
|
||||
{
|
||||
private readonly byte[] _data;
|
||||
private int _index;
|
||||
|
||||
public int Index
|
||||
{
|
||||
get
|
||||
{
|
||||
return _index;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > _data.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
}
|
||||
|
||||
_index = value;
|
||||
}
|
||||
}
|
||||
|
||||
public DnsDatagramReader(byte[] data, int startIndex = 0)
|
||||
{
|
||||
_data = data;
|
||||
Index = startIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
https://tools.ietf.org/html/rfc1035#section-3.3:
|
||||
<character-string> is a single
|
||||
length octet followed by that number of characters. <character-string>
|
||||
is treated as binary information, and can be up to 256 characters in
|
||||
length (including the length octet).
|
||||
* */
|
||||
/// <summary>
|
||||
/// Reads the single length octet and the following characters as ASCII text.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string ReadString()
|
||||
{
|
||||
var length = ReadByte();
|
||||
|
||||
var result = Encoding.ASCII.GetString(_data, _index, length);
|
||||
_index += length;
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
if (_index >= _data.Length)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Cannot read byte.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return _data[_index++];
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ReadBytes(int length)
|
||||
{
|
||||
if (_data.Length < _index + length)
|
||||
{
|
||||
throw new IndexOutOfRangeException($"Cannot read that many bytes: '{length}'.");
|
||||
}
|
||||
|
||||
var result = new byte[length];
|
||||
Array.Copy(_data, _index, result, 0, length);
|
||||
_index += length;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an IP address from the next 4 bytes.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IPAddress"/>.</returns>
|
||||
/// <exception cref="IndexOutOfRangeException">If there are no 4 bytes to read.</exception>
|
||||
public IPAddress ReadIPAddress()
|
||||
{
|
||||
if (_data.Length < _index + 4)
|
||||
{
|
||||
throw new IndexOutOfRangeException("IPAddress expected exactly 4 bytes.");
|
||||
}
|
||||
|
||||
return new IPAddress(ReadBytes(4));
|
||||
}
|
||||
|
||||
public IPAddress ReadIPv6Address()
|
||||
{
|
||||
var address = new IPAddress(ReadBytes(8 * 2));
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
public DnsName ReadName()
|
||||
{
|
||||
return DnsName.FromBytes(_data, ref _index);
|
||||
}
|
||||
|
||||
public ushort ReadUInt16()
|
||||
{
|
||||
if (_data.Length < Index + 2)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Cannot read more data.");
|
||||
}
|
||||
|
||||
var result = BitConverter.ToUInt16(_data, _index);
|
||||
_index += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
public ushort ReadUInt16Reverse()
|
||||
{
|
||||
if (_data.Length < Index + 2)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Cannot read more data.");
|
||||
}
|
||||
|
||||
byte a = _data[_index++], b = _data[_index++];
|
||||
return (ushort)(a << 8 | b);
|
||||
}
|
||||
|
||||
public uint ReadUInt32Reverse()
|
||||
{
|
||||
return (uint)(ReadUInt16Reverse() << 16 | ReadUInt16Reverse());
|
||||
}
|
||||
}
|
||||
}
|
113
MailServer/DNS/Protocol/DnsDatagramWriter.cs
Normal file
113
MailServer/DNS/Protocol/DnsDatagramWriter.cs
Normal file
@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace DnsClient.Protocol
|
||||
{
|
||||
public class DnsDatagramWriter
|
||||
{
|
||||
private byte[] _buffer;
|
||||
|
||||
public byte[] Data => _buffer;
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public DnsDatagramWriter(int length)
|
||||
{
|
||||
_buffer = new byte[length];
|
||||
}
|
||||
|
||||
private DnsDatagramWriter(byte[] data, int newLength)
|
||||
{
|
||||
if (data.Length > newLength)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(newLength));
|
||||
}
|
||||
|
||||
_buffer = new byte[newLength];
|
||||
Array.Copy(data, _buffer, data.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new writer instance with a new length.
|
||||
/// </summary>
|
||||
/// <param name="byLength">The amount of bytes the current buffer should be extended by.</param>
|
||||
/// <returns>A new writer.</returns>
|
||||
public void Extend(int byLength)
|
||||
{
|
||||
if (byLength <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(byLength));
|
||||
}
|
||||
|
||||
var fullLength = byLength + _buffer.Length;
|
||||
var newBuffer = new byte[fullLength];
|
||||
Array.Copy(_buffer, 0, newBuffer, 0, _buffer.Length);
|
||||
//return new DnsDatagramWriter(newBuffer, fullLength) { Offset = Offset };
|
||||
_buffer = newBuffer;
|
||||
}
|
||||
|
||||
public void SetBytes(byte[] data, int length) => SetBytes(data, 0, Index, length);
|
||||
|
||||
public void SetInt(int value) => SetInt(value, Index);
|
||||
|
||||
public void SetInt16(short value) => SetInt16(value, Index);
|
||||
|
||||
public void SetInt16Network(short value) => SetInt16Network(value, Index);
|
||||
|
||||
public void SetUInt32Network(uint value)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((int)value));
|
||||
SetBytes(bytes, Index, bytes.Length);
|
||||
}
|
||||
|
||||
public void SetIntNetwork(int value) => SetIntNetwork(value, Index);
|
||||
|
||||
public void SetUInt16(ushort value) => SetInt16((short)value, Index);
|
||||
|
||||
public void SetUInt16Network(ushort value) => SetInt16Network((short)value, Index);
|
||||
|
||||
private void SetBytes(byte[] data, int destOffset, int length)
|
||||
{
|
||||
SetBytes(data, 0, destOffset, length);
|
||||
}
|
||||
|
||||
private void SetBytes(byte[] data, int dataOffset, int destOffset, int length)
|
||||
{
|
||||
if (length + dataOffset > data.Length || length + dataOffset > _buffer.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
}
|
||||
if (destOffset + dataOffset + length > _buffer.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(destOffset));
|
||||
}
|
||||
|
||||
Array.ConstrainedCopy(data, dataOffset, _buffer, destOffset, length);
|
||||
Index = destOffset + length;
|
||||
}
|
||||
|
||||
private void SetInt(int value, int offset)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(value);
|
||||
SetBytes(bytes, offset, bytes.Length);
|
||||
}
|
||||
|
||||
private void SetInt16(short value, int offset)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(value);
|
||||
SetBytes(bytes, offset, bytes.Length);
|
||||
}
|
||||
|
||||
private void SetInt16Network(short value, int offset)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value));
|
||||
SetBytes(bytes, offset, bytes.Length);
|
||||
}
|
||||
|
||||
private void SetIntNetwork(int value, int offset)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value));
|
||||
SetBytes(bytes, offset, bytes.Length);
|
||||
}
|
||||
}
|
||||
}
|
195
MailServer/DNS/Protocol/DnsRecordFactory.cs
Normal file
195
MailServer/DNS/Protocol/DnsRecordFactory.cs
Normal file
@ -0,0 +1,195 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
34
MailServer/DNS/Protocol/DnsRequestMessage.cs
Normal file
34
MailServer/DNS/Protocol/DnsRequestMessage.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace DnsClient.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a simple request message which can be send through <see cref="DnsMessageHandler"/>.
|
||||
/// </summary>
|
||||
public class DnsRequestMessage
|
||||
{
|
||||
public DnsRequestHeader Header { get; }
|
||||
|
||||
public DnsQuestion Question { get; }
|
||||
|
||||
public DnsRequestMessage(DnsRequestHeader header, DnsQuestion question)
|
||||
{
|
||||
if (header == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(header));
|
||||
}
|
||||
if (question == null )
|
||||
{
|
||||
throw new ArgumentNullException(nameof(question));
|
||||
}
|
||||
if (header.QuestionCount != 1)
|
||||
{
|
||||
throw new InvalidOperationException("Header question count and number of questions do not match.");
|
||||
}
|
||||
|
||||
Header = header;
|
||||
Question = question;
|
||||
}
|
||||
}
|
||||
}
|
84
MailServer/DNS/Protocol/DnsResourceRecord.cs
Normal file
84
MailServer/DNS/Protocol/DnsResourceRecord.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System;
|
||||
|
||||
namespace DnsClient.Protocol
|
||||
{
|
||||
public abstract class DnsResourceRecord : ResourceRecordInfo
|
||||
{
|
||||
public DnsResourceRecord(ResourceRecordInfo info)
|
||||
: base(info.QueryName, info.RecordType, info.RecordClass, info.TimeToLive, info.RawDataLength)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return ToString(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as <c>ToString</c> but offsets the <see cref="ResourceRecordInfo.QueryName"/>
|
||||
/// by <paramref name="offset"/>.
|
||||
/// Set the offset to -32 for example to make it print nicely in consols.
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>A string representing this instance.</returns>
|
||||
public virtual string ToString(int offset = 0)
|
||||
{
|
||||
return string.Format("{0," + offset + "}{1} \t{2} \t{3} \t{4}",
|
||||
QueryName,
|
||||
TimeToLive,
|
||||
RecordClass,
|
||||
RecordType,
|
||||
RecordToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the actual record's value only and not the full object representation.
|
||||
/// <see cref="ToString(int)"/> uses this to compose the full string value of this instance.
|
||||
/// </summary>
|
||||
/// <returns>A string representing this record.</returns>
|
||||
public abstract string RecordToString();
|
||||
}
|
||||
|
||||
public class ResourceRecordInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The query name.
|
||||
/// </summary>
|
||||
public string QueryName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies type of resource record.
|
||||
/// </summary>
|
||||
public ResourceRecordType RecordType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies type class of resource record, mostly IN but can be CS, CH or HS .
|
||||
/// </summary>
|
||||
public QueryClass RecordClass { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The TTL value for the record set by the server.
|
||||
/// </summary>
|
||||
public uint TimeToLive { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bytes for this resource record stored in RDATA
|
||||
/// </summary>
|
||||
public int RawDataLength { get; }
|
||||
|
||||
public ResourceRecordInfo(string queryName, ResourceRecordType recordType, QueryClass recordClass, uint ttl, int length)
|
||||
{
|
||||
if (queryName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(queryName));
|
||||
}
|
||||
|
||||
QueryName = queryName;
|
||||
RecordType = recordType;
|
||||
RecordClass = recordClass;
|
||||
TimeToLive = ttl;
|
||||
RawDataLength = length;
|
||||
}
|
||||
}
|
||||
}
|
76
MailServer/DNS/Protocol/DnsResponseMessage.cs
Normal file
76
MailServer/DNS/Protocol/DnsResponseMessage.cs
Normal file
@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DnsClient.Protocol
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple response message which gets returned by the <see cref="LookupClient"/>.
|
||||
/// </summary>
|
||||
public class DnsResponseMessage
|
||||
{
|
||||
private readonly IList<DnsResourceRecord> _additionals = new List<DnsResourceRecord>();
|
||||
private readonly IList<DnsResourceRecord> _answers = new List<DnsResourceRecord>();
|
||||
private readonly IList<DnsResourceRecord> _authorities = new List<DnsResourceRecord>();
|
||||
private readonly DnsResponseHeader _header;
|
||||
private readonly IList<DnsQuestion> _questions = new List<DnsQuestion>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the readonly representation of this message which can be returned.
|
||||
/// </summary>
|
||||
public DnsQueryResponse AsReadonly
|
||||
=> new DnsQueryResponse(_header, _questions.ToArray(), _answers.ToArray(), _additionals.ToArray(), _authorities.ToArray());
|
||||
|
||||
public DnsResponseHeader Header => _header;
|
||||
|
||||
public DnsResponseMessage(DnsResponseHeader header)
|
||||
{
|
||||
if (header == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(header));
|
||||
}
|
||||
|
||||
_header = header;
|
||||
}
|
||||
|
||||
public void AddAdditional(DnsResourceRecord record)
|
||||
{
|
||||
if (record == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(record));
|
||||
}
|
||||
|
||||
_additionals.Add(record);
|
||||
}
|
||||
|
||||
public void AddAnswer(DnsResourceRecord record)
|
||||
{
|
||||
if (record == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(record));
|
||||
}
|
||||
|
||||
_answers.Add(record);
|
||||
}
|
||||
|
||||
public void AddAuthority(DnsResourceRecord record)
|
||||
{
|
||||
if (record == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(record));
|
||||
}
|
||||
|
||||
_authorities.Add(record);
|
||||
}
|
||||
|
||||
public void AddQuestion(DnsQuestion question)
|
||||
{
|
||||
if (question == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(question));
|
||||
}
|
||||
|
||||
_questions.Add(question);
|
||||
}
|
||||
}
|
||||
}
|
45
MailServer/DNS/Protocol/Record/ARecord.cs
Normal file
45
MailServer/DNS/Protocol/Record/ARecord.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace DnsClient.Protocol.Record
|
||||
{
|
||||
/*
|
||||
3.4.1. A RDATA format
|
||||
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| ADDRESS |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
|
||||
where:
|
||||
|
||||
ADDRESS A 32 bit Internet address.
|
||||
|
||||
Hosts that have multiple Internet addresses will have multiple A
|
||||
records.
|
||||
*
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// A DNS resource record represending an IP address.
|
||||
/// Hosts that have multiple Internet addresses will have multiple A records.
|
||||
/// </summary>
|
||||
public class ARecord : DnsResourceRecord
|
||||
{
|
||||
public IPAddress Address { get; }
|
||||
|
||||
public ARecord(ResourceRecordInfo info, IPAddress address) : base(info)
|
||||
{
|
||||
if (address == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(address));
|
||||
}
|
||||
|
||||
Address = address;
|
||||
}
|
||||
|
||||
public override string RecordToString()
|
||||
{
|
||||
return Address.ToString();
|
||||
}
|
||||
}
|
||||
}
|
35
MailServer/DNS/Protocol/Record/AaaaRecord.cs
Normal file
35
MailServer/DNS/Protocol/Record/AaaaRecord.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace DnsClient.Protocol.Record
|
||||
{
|
||||
/* https://tools.ietf.org/html/rfc3596#section-2.2
|
||||
2.2 AAAA data format
|
||||
|
||||
A 128 bit IPv6 address is encoded in the data portion of an AAAA
|
||||
resource record in network byte order (high-order byte first).
|
||||
*/
|
||||
/// <summary>
|
||||
/// A 128 bit IPv6 address is encoded in the data portion of an AAAA
|
||||
/// resource record in network byte order(high-order byte first).
|
||||
/// </summary>
|
||||
public class AAAARecord : DnsResourceRecord
|
||||
{
|
||||
public IPAddress Address { get; }
|
||||
|
||||
public AAAARecord(ResourceRecordInfo info, IPAddress address) : base(info)
|
||||
{
|
||||
if (address == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(address));
|
||||
}
|
||||
|
||||
Address = address;
|
||||
}
|
||||
|
||||
public override string RecordToString()
|
||||
{
|
||||
return Address.ToString();
|
||||
}
|
||||
}
|
||||
}
|
93
MailServer/DNS/Protocol/Record/CaaRecord.cs
Normal file
93
MailServer/DNS/Protocol/Record/CaaRecord.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DnsClient.Protocol.Record
|
||||
{
|
||||
/* RFC 6844 (https://tools.ietf.org/html/rfc6844#section-5.1)
|
||||
A CAA RR contains a single property entry consisting of a tag-value
|
||||
pair. Each tag represents a property of the CAA record. The value
|
||||
of a CAA property is that specified in the corresponding value field.
|
||||
|
||||
A domain name MAY have multiple CAA RRs associated with it and a
|
||||
given property MAY be specified more than once.
|
||||
|
||||
The CAA data field contains one property entry. A property entry
|
||||
consists of the following data fields:
|
||||
|
||||
+0-1-2-3-4-5-6-7-|0-1-2-3-4-5-6-7-|
|
||||
| Flags | Tag Length = n |
|
||||
+----------------+----------------+...+---------------+
|
||||
| Tag char 0 | Tag char 1 |...| Tag char n-1 |
|
||||
+----------------+----------------+...+---------------+
|
||||
+----------------+----------------+.....+----------------+
|
||||
| Value byte 0 | Value byte 1 |.....| Value byte m-1 |
|
||||
+----------------+----------------+.....+----------------+
|
||||
|
||||
Where n is the length specified in the Tag length field and m is the
|
||||
remaining octets in the Value field (m = d - n - 2) where d is the
|
||||
length of the RDATA section.
|
||||
|
||||
The data fields are defined as follows:
|
||||
|
||||
Flags: One octet containing the following fields:
|
||||
|
||||
Bit 0, Issuer Critical Flag: If the value is set to '1', the
|
||||
critical flag is asserted and the property MUST be understood
|
||||
if the CAA record is to be correctly processed by a certificate
|
||||
issuer.
|
||||
|
||||
A Certification Authority MUST NOT issue certificates for any
|
||||
Domain that contains a CAA critical property for an unknown or
|
||||
unsupported property tag that for which the issuer critical
|
||||
flag is set.
|
||||
|
||||
Note that according to the conventions set out in [RFC1035], bit 0
|
||||
is the Most Significant Bit and bit 7 is the Least Significant
|
||||
Bit. Thus, the Flags value 1 means that bit 7 is set while a value
|
||||
of 128 means that bit 0 is set according to this convention.
|
||||
|
||||
All other bit positions are reserved for future use.
|
||||
|
||||
To ensure compatibility with future extensions to CAA, DNS records
|
||||
compliant with this version of the CAA specification MUST clear
|
||||
(set to "0") all reserved flags bits. Applications that interpret
|
||||
CAA records MUST ignore the value of all reserved flag bits.
|
||||
|
||||
Tag Length: A single octet containing an unsigned integer specifying
|
||||
the tag length in octets. The tag length MUST be at least 1 and
|
||||
SHOULD be no more than 15.
|
||||
|
||||
Tag: The property identifier, a sequence of US-ASCII characters.
|
||||
|
||||
Tag values MAY contain US-ASCII characters 'a' through 'z', 'A'
|
||||
through 'Z', and the numbers 0 through 9. Tag values SHOULD NOT
|
||||
contain any other characters. Matching of tag values is case
|
||||
insensitive.
|
||||
|
||||
Tag values submitted for registration by IANA MUST NOT contain any
|
||||
characters other than the (lowercase) US-ASCII characters 'a'
|
||||
through 'z' and the numbers 0 through 9.
|
||||
|
||||
Value: A sequence of octets representing the property value.
|
||||
Property values are encoded as binary values and MAY employ sub-
|
||||
formats.
|
||||
|
||||
The length of the value field is specified implicitly as the
|
||||
remaining length of the enclosing Resource Record data field.
|
||||
* */
|
||||
/// <summary>
|
||||
/// Record type 257
|
||||
/// The Certification Authority Authorization (CAA) DNS Resource Record
|
||||
/// allows a DNS domain name holder to specify one or more Certification
|
||||
/// Authorities(CAs) authorized to issue certificates for that domain.
|
||||
/// CAA Resource Records allow a public Certification Authority to
|
||||
/// implement additional controls to reduce the risk of unintended
|
||||
/// certificate mis-issue.This document defines the syntax of the CAA
|
||||
/// record and rules for processing CAA records by certificate issuers.
|
||||
/// </summary>
|
||||
public class CaaRecord
|
||||
{
|
||||
}
|
||||
}
|
14
MailServer/DNS/Protocol/Record/EmptyRecord.cs
Normal file
14
MailServer/DNS/Protocol/Record/EmptyRecord.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace DnsClient.Protocol.Record
|
||||
{
|
||||
public class EmptyRecord : DnsResourceRecord
|
||||
{
|
||||
public EmptyRecord(ResourceRecordInfo info) : base(info)
|
||||
{
|
||||
}
|
||||
|
||||
public override string RecordToString()
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
65
MailServer/DNS/Protocol/Record/MXRecord.cs
Normal file
65
MailServer/DNS/Protocol/Record/MXRecord.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
|
||||
namespace DnsClient.Protocol.Record
|
||||
{
|
||||
/*
|
||||
3.3.9. MX RDATA format
|
||||
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| PREFERENCE |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
/ EXCHANGE /
|
||||
/ /
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
|
||||
where:
|
||||
|
||||
PREFERENCE A 16 bit integer which specifies the preference given to
|
||||
this RR among others at the same owner. Lower values
|
||||
are preferred.
|
||||
|
||||
EXCHANGE A <domain-name> which specifies a host willing to act as
|
||||
a mail exchange for the owner name.
|
||||
|
||||
MX records cause type A additional section processing for the host
|
||||
specified by EXCHANGE. The use of MX RRs is explained in detail in
|
||||
[RFC-974].
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// MX records cause type A additional section processing for the host
|
||||
/// specified by EXCHANGE.The use of MX RRs is explained in detail in
|
||||
/// [RFC-974].
|
||||
/// </summary>
|
||||
public class MxRecord : DnsResourceRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a 16 bit integer which specifies the preference given to
|
||||
/// this RR among others at the same owner.
|
||||
/// Lower values are preferred.
|
||||
/// </summary>
|
||||
public ushort Preference { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A <domain-name> which specifies a host willing to act as a mail exchange.
|
||||
/// </summary>
|
||||
public string Exchange { get; }
|
||||
|
||||
public MxRecord(ResourceRecordInfo info, ushort preference, string domainName)
|
||||
: base(info)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(domainName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(domainName));
|
||||
}
|
||||
|
||||
Preference = preference;
|
||||
Exchange = domainName;
|
||||
}
|
||||
|
||||
public override string RecordToString()
|
||||
{
|
||||
return string.Format("{0} {1}", Preference, Exchange);
|
||||
}
|
||||
}
|
||||
}
|
51
MailServer/DNS/Protocol/Record/NsRecord.cs
Normal file
51
MailServer/DNS/Protocol/Record/NsRecord.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
|
||||
namespace DnsClient.Protocol.Record
|
||||
{
|
||||
/*
|
||||
https://tools.ietf.org/html/rfc1035#section-3.3.11:
|
||||
NS RDATA format
|
||||
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
/ NSDNAME /
|
||||
/ /
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
|
||||
where:
|
||||
|
||||
NSDNAME A <domain-name> which specifies a host which should be
|
||||
authoritative for the specified class and domain.
|
||||
|
||||
NS records cause both the usual additional section processing to locate
|
||||
a type A record, and, when used in a referral, a special search of the
|
||||
zone in which they reside for glue information.
|
||||
|
||||
The NS RR states that the named host should be expected to have a zone
|
||||
starting at owner name of the specified class. Note that the class may
|
||||
not indicate the protocol family which should be used to communicate
|
||||
with the host, although it is typically a strong hint. For example,
|
||||
hosts which are name servers for either Internet (IN) or Hesiod (HS)
|
||||
class information are normally queried using IN class protocols.
|
||||
*/
|
||||
|
||||
public class NsRecord : DnsResourceRecord
|
||||
{
|
||||
public string NSDName { get; }
|
||||
|
||||
internal NsRecord(ResourceRecordInfo info, string name)
|
||||
: base(info)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
NSDName = name;
|
||||
}
|
||||
|
||||
public override string RecordToString()
|
||||
{
|
||||
return NSDName;
|
||||
}
|
||||
}
|
||||
}
|
44
MailServer/DNS/Protocol/Record/PtrRecord.cs
Normal file
44
MailServer/DNS/Protocol/Record/PtrRecord.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace DnsClient.Protocol.Record
|
||||
{
|
||||
/* RFC 1035 (https://tools.ietf.org/html/rfc1035#section-3.3.12)
|
||||
3.3.12. PTR RDATA format
|
||||
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
/ PTRDNAME /
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
|
||||
where:
|
||||
|
||||
PTRDNAME A <domain-name> which points to some location in the
|
||||
domain name space.
|
||||
|
||||
PTR records cause no additional section processing. These RRs are used
|
||||
in special domains to point to some other location in the domain space.
|
||||
These records are simple data, and don't imply any special processing
|
||||
similar to that performed by CNAME, which identifies aliases. See the
|
||||
description of the IN-ADDR.ARPA domain for an example.
|
||||
*/
|
||||
public class PtrRecord : DnsResourceRecord
|
||||
{
|
||||
public string PtrDomainName { get; }
|
||||
|
||||
internal PtrRecord(ResourceRecordInfo info, string ptrDName)
|
||||
: base(info)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ptrDName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ptrDName));
|
||||
}
|
||||
|
||||
PtrDomainName = ptrDName;
|
||||
}
|
||||
|
||||
public override string RecordToString()
|
||||
{
|
||||
return PtrDomainName;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using DnsClient.Protocol;
|
||||
using DnsClient.Protocol.Record;
|
||||
|
||||
namespace DnsClient
|
||||
{
|
||||
public static class RecordCollectionExtension
|
||||
{
|
||||
public static IEnumerable<AAAARecord> AaaaRecords(this IEnumerable<DnsResourceRecord> records)
|
||||
{
|
||||
return records.OfType<AAAARecord>();
|
||||
}
|
||||
|
||||
public static IEnumerable<ARecord> ARecords(this IEnumerable<DnsResourceRecord> records)
|
||||
{
|
||||
return records.OfType<ARecord>();
|
||||
}
|
||||
|
||||
public static IEnumerable<CaaRecord> CaaRecords(this IEnumerable<DnsResourceRecord> records)
|
||||
{
|
||||
return records.OfType<CaaRecord>();
|
||||
}
|
||||
|
||||
public static IEnumerable<NsRecord> NsRecords(this IEnumerable<DnsResourceRecord> records)
|
||||
{
|
||||
return records.OfType<NsRecord>();
|
||||
}
|
||||
|
||||
public static IEnumerable<DnsResourceRecord> OfRecordType(this IEnumerable<DnsResourceRecord> records, ResourceRecordType type)
|
||||
{
|
||||
return records.Where(p => p.RecordType == type);
|
||||
}
|
||||
|
||||
public static IEnumerable<PtrRecord> PtrRecords(this IEnumerable<DnsResourceRecord> records)
|
||||
{
|
||||
return records.OfType<PtrRecord>();
|
||||
}
|
||||
|
||||
public static IEnumerable<SoaRecord> SoaRecords(this IEnumerable<DnsResourceRecord> records)
|
||||
{
|
||||
return records.OfType<SoaRecord>();
|
||||
}
|
||||
|
||||
public static IEnumerable<SrvRecord> SrvRecords(this IEnumerable<DnsResourceRecord> records)
|
||||
{
|
||||
return records.OfType<SrvRecord>();
|
||||
}
|
||||
|
||||
public static IEnumerable<TxtRecord> TxtRecords(this IEnumerable<DnsResourceRecord> records)
|
||||
{
|
||||
return records.OfType<TxtRecord>();
|
||||
}
|
||||
}
|
||||
}
|
111
MailServer/DNS/Protocol/Record/SoaRecord.cs
Normal file
111
MailServer/DNS/Protocol/Record/SoaRecord.cs
Normal file
@ -0,0 +1,111 @@
|
||||
namespace DnsClient.Protocol.Record
|
||||
{
|
||||
/*
|
||||
3.3.13. SOA RDATA format
|
||||
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
/ MNAME /
|
||||
/ /
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
/ RNAME /
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| SERIAL |
|
||||
| |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| REFRESH |
|
||||
| |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| RETRY |
|
||||
| |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| EXPIRE |
|
||||
| |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
| MINIMUM |
|
||||
| |
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
|
||||
where:
|
||||
|
||||
MNAME The <domain-name> of the name server that was the
|
||||
original or primary source of data for this zone.
|
||||
|
||||
RNAME A <domain-name> which specifies the mailbox of the
|
||||
person responsible for this zone.
|
||||
|
||||
SERIAL The unsigned 32 bit version number of the original copy
|
||||
of the zone. Zone transfers preserve this value. This
|
||||
value wraps and should be compared using sequence space
|
||||
arithmetic.
|
||||
|
||||
REFRESH A 32 bit time interval before the zone should be
|
||||
refreshed.
|
||||
|
||||
RETRY A 32 bit time interval that should elapse before a
|
||||
failed refresh should be retried.
|
||||
|
||||
EXPIRE A 32 bit time value that specifies the upper limit on
|
||||
the time interval that can elapse before the zone is no
|
||||
longer authoritative.
|
||||
|
||||
MINIMUM The unsigned 32 bit minimum TTL field that should be
|
||||
exported with any RR from this zone.
|
||||
|
||||
SOA records cause no additional section processing.
|
||||
|
||||
All times are in units of seconds.
|
||||
|
||||
Most of these fields are pertinent only for name server maintenance
|
||||
operations. However, MINIMUM is used in all query operations that
|
||||
retrieve RRs from a zone. Whenever a RR is sent in a response to a
|
||||
query, the TTL field is set to the maximum of the TTL field from the RR
|
||||
and the MINIMUM field in the appropriate SOA. Thus MINIMUM is a lower
|
||||
bound on the TTL field for all RRs in a zone. Note that this use of
|
||||
MINIMUM should occur when the RRs are copied into the response and not
|
||||
when the zone is loaded from a master file or via a zone transfer. The
|
||||
reason for this provison is to allow future dynamic update facilities to
|
||||
change the SOA RR with known semantics.
|
||||
*/
|
||||
|
||||
public class SoaRecord : DnsResourceRecord
|
||||
{
|
||||
public uint Expire { get; }
|
||||
|
||||
public uint Minimum { get; }
|
||||
|
||||
public string MName { get; }
|
||||
|
||||
public uint Refresh { get; }
|
||||
|
||||
public uint Retry { get; }
|
||||
|
||||
public string RName { get; }
|
||||
|
||||
public uint Serial { get; }
|
||||
|
||||
public SoaRecord(ResourceRecordInfo info, string mName, string rName, uint serial, uint refresh, uint retry, uint expire, uint minimum)
|
||||
: base(info)
|
||||
{
|
||||
MName = mName;
|
||||
RName = rName;
|
||||
Serial = serial;
|
||||
Refresh = refresh;
|
||||
Retry = retry;
|
||||
Expire = expire;
|
||||
Minimum = minimum;
|
||||
}
|
||||
|
||||
public override string RecordToString()
|
||||
{
|
||||
return string.Format(
|
||||
"{0} {1} {2} {3} {4} {5} {6}",
|
||||
MName,
|
||||
RName,
|
||||
Serial,
|
||||
Refresh,
|
||||
Retry,
|
||||
Expire,
|
||||
Minimum);
|
||||
}
|
||||
}
|
||||
}
|
147
MailServer/DNS/Protocol/Record/SrvRecord.cs
Normal file
147
MailServer/DNS/Protocol/Record/SrvRecord.cs
Normal file
@ -0,0 +1,147 @@
|
||||
namespace DnsClient.Protocol.Record
|
||||
{
|
||||
/* RFC 2782 (https://tools.ietf.org/html/rfc2782)
|
||||
|
||||
The format of the SRV RR
|
||||
|
||||
Here is the format of the SRV RR, whose DNS type code is 33:
|
||||
|
||||
_Service._Proto.Name TTL Class SRV Priority Weight Port Target
|
||||
|
||||
(There is an example near the end of this document.)
|
||||
|
||||
Service
|
||||
The symbolic name of the desired service, as defined in Assigned
|
||||
Numbers [STD 2] or locally. An underscore (_) is prepended to
|
||||
the service identifier to avoid collisions with DNS labels that
|
||||
occur in nature.
|
||||
Some widely used services, notably POP, don't have a single
|
||||
universal name. If Assigned Numbers names the service
|
||||
indicated, that name is the only name which is legal for SRV
|
||||
lookups. The Service is case insensitive.
|
||||
|
||||
Proto
|
||||
The symbolic name of the desired protocol, with an underscore
|
||||
(_) prepended to prevent collisions with DNS labels that occur
|
||||
in nature. _TCP and _UDP are at present the most useful values
|
||||
for this field, though any name defined by Assigned Numbers or
|
||||
locally may be used (as for Service). The Proto is case
|
||||
insensitive.
|
||||
|
||||
Name
|
||||
The domain this RR refers to. The SRV RR is unique in that the
|
||||
name one searches for is not this name; the example near the end
|
||||
shows this clearly.
|
||||
|
||||
TTL
|
||||
Standard DNS meaning [RFC 1035].
|
||||
|
||||
Class
|
||||
Standard DNS meaning [RFC 1035]. SRV records occur in the IN
|
||||
Class.
|
||||
|
||||
Priority
|
||||
The priority of this target host. A client MUST attempt to
|
||||
contact the target host with the lowest-numbered priority it can
|
||||
reach; target hosts with the same priority SHOULD be tried in an
|
||||
order defined by the weight field. The range is 0-65535. This
|
||||
is a 16 bit unsigned integer in network byte order.
|
||||
|
||||
Weight
|
||||
A server selection mechanism. The weight field specifies a
|
||||
relative weight for entries with the same priority. Larger
|
||||
weights SHOULD be given a proportionately higher probability of
|
||||
being selected. The range of this number is 0-65535. This is a
|
||||
16 bit unsigned integer in network byte order. Domain
|
||||
administrators SHOULD use Weight 0 when there isn't any server
|
||||
selection to do, to make the RR easier to read for humans (less
|
||||
noisy). In the presence of records containing weights greater
|
||||
than 0, records with weight 0 should have a very small chance of
|
||||
being selected.
|
||||
|
||||
In the absence of a protocol whose specification calls for the
|
||||
use of other weighting information, a client arranges the SRV
|
||||
RRs of the same Priority in the order in which target hosts,
|
||||
|
||||
specified by the SRV RRs, will be contacted. The following
|
||||
algorithm SHOULD be used to order the SRV RRs of the same
|
||||
priority:
|
||||
|
||||
To select a target to be contacted next, arrange all SRV RRs
|
||||
(that have not been ordered yet) in any order, except that all
|
||||
those with weight 0 are placed at the beginning of the list.
|
||||
|
||||
Compute the sum of the weights of those RRs, and with each RR
|
||||
associate the running sum in the selected order. Then choose a
|
||||
uniform random number between 0 and the sum computed
|
||||
(inclusive), and select the RR whose running sum value is the
|
||||
first in the selected order which is greater than or equal to
|
||||
the random number selected. The target host specified in the
|
||||
selected SRV RR is the next one to be contacted by the client.
|
||||
Remove this SRV RR from the set of the unordered SRV RRs and
|
||||
apply the described algorithm to the unordered SRV RRs to select
|
||||
the next target host. Continue the ordering process until there
|
||||
are no unordered SRV RRs. This process is repeated for each
|
||||
Priority.
|
||||
|
||||
Port
|
||||
The port on this target host of this service. The range is 0-
|
||||
65535. This is a 16 bit unsigned integer in network byte order.
|
||||
This is often as specified in Assigned Numbers but need not be.
|
||||
|
||||
Target
|
||||
The domain name of the target host. There MUST be one or more
|
||||
address records for this name, the name MUST NOT be an alias (in
|
||||
the sense of RFC 1034 or RFC 2181). Implementors are urged, but
|
||||
not required, to return the address record(s) in the Additional
|
||||
Data section. Unless and until permitted by future standards
|
||||
action, name compression is not to be used for this field.
|
||||
|
||||
A Target of "." means that the service is decidedly not
|
||||
available at this domain.
|
||||
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// The SRV RR allows administrators to use several servers for a single
|
||||
/// domain, to move services from host to host with little fuss, and to
|
||||
/// designate some hosts as primary servers for a service and others as
|
||||
/// backups.
|
||||
///
|
||||
/// Clients ask for a specific service/protocol for a specific domain
|
||||
/// (the word domain is used here in the strict RFC 1034 sense), and get
|
||||
/// back the names of any available servers.
|
||||
///
|
||||
/// Note that where this document refers to "address records", it means A
|
||||
/// RR's, AAAA RR's, or their most modern equivalent.
|
||||
/// </summary>
|
||||
public class SrvRecord : DnsResourceRecord
|
||||
{
|
||||
public ushort Port { get; }
|
||||
|
||||
public ushort Priority { get; }
|
||||
|
||||
public string Target { get; }
|
||||
|
||||
public ushort Weight { get; }
|
||||
|
||||
public SrvRecord(ResourceRecordInfo info, ushort priority, ushort weigth, ushort port, string target)
|
||||
: base(info)
|
||||
{
|
||||
Priority = priority;
|
||||
Weight = weigth;
|
||||
Port = port;
|
||||
Target = target;
|
||||
}
|
||||
|
||||
public override string RecordToString()
|
||||
{
|
||||
return string.Format(
|
||||
"{0} {1} {2} {3}",
|
||||
Priority,
|
||||
Weight,
|
||||
Port,
|
||||
Target);
|
||||
}
|
||||
}
|
||||
}
|
57
MailServer/DNS/Protocol/Record/TxtRecord.cs
Normal file
57
MailServer/DNS/Protocol/Record/TxtRecord.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DnsClient.Protocol.Record
|
||||
{
|
||||
/*
|
||||
* RFC 1464 https://tools.ietf.org/html/rfc1464
|
||||
|
||||
https://tools.ietf.org/html/rfc1035#section-3.3:
|
||||
<character-string> is a single
|
||||
length octet followed by that number of characters. <character-string>
|
||||
is treated as binary information, and can be up to 256 characters in
|
||||
length (including the length octet).
|
||||
|
||||
https://tools.ietf.org/html/rfc1035#section-3.3.14:
|
||||
TXT RDATA format
|
||||
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
/ TXT-DATA /
|
||||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
|
||||
where:
|
||||
|
||||
TXT-DATA One or more <character-string>s.
|
||||
|
||||
TXT RRs are used to hold descriptive text. The semantics of the text
|
||||
depends on the domain where it is found.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// TXT RRs are used to hold descriptive text. The semantics of the text
|
||||
/// depends on the domain where it is found.
|
||||
/// </summary>
|
||||
public class TxtRecord : DnsResourceRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of TXT values of this TXT RR.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<string> Text { get; }
|
||||
|
||||
public TxtRecord(ResourceRecordInfo info, string[] values)
|
||||
: base(info)
|
||||
{
|
||||
if (values == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
}
|
||||
|
||||
Text = values;
|
||||
}
|
||||
|
||||
public override string RecordToString()
|
||||
{
|
||||
return string.Join(" ", Text).Trim();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user