Codingstyle nullable
This commit is contained in:
@@ -1,62 +1,96 @@
|
||||
namespace Swan.Net.Dns
|
||||
{
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// DnsClient public interfaces.
|
||||
/// </summary>
|
||||
internal partial class DnsClient
|
||||
{
|
||||
public interface IDnsMessage
|
||||
{
|
||||
IList<DnsQuestion> Questions { get; }
|
||||
|
||||
int Size { get; }
|
||||
byte[] ToArray();
|
||||
}
|
||||
|
||||
public interface IDnsMessageEntry
|
||||
{
|
||||
DnsDomain Name { get; }
|
||||
DnsRecordType Type { get; }
|
||||
DnsRecordClass Class { get; }
|
||||
|
||||
int Size { get; }
|
||||
byte[] ToArray();
|
||||
}
|
||||
|
||||
public interface IDnsResourceRecord : IDnsMessageEntry
|
||||
{
|
||||
TimeSpan TimeToLive { get; }
|
||||
int DataLength { get; }
|
||||
byte[] Data { get; }
|
||||
}
|
||||
|
||||
public interface IDnsRequest : IDnsMessage
|
||||
{
|
||||
int Id { get; set; }
|
||||
DnsOperationCode OperationCode { get; set; }
|
||||
bool RecursionDesired { get; set; }
|
||||
}
|
||||
|
||||
public interface IDnsResponse : IDnsMessage
|
||||
{
|
||||
int Id { get; set; }
|
||||
IList<IDnsResourceRecord> AnswerRecords { get; }
|
||||
IList<IDnsResourceRecord> AuthorityRecords { get; }
|
||||
IList<IDnsResourceRecord> AdditionalRecords { get; }
|
||||
bool IsRecursionAvailable { get; set; }
|
||||
bool IsAuthorativeServer { get; set; }
|
||||
bool IsTruncated { get; set; }
|
||||
DnsOperationCode OperationCode { get; set; }
|
||||
DnsResponseCode ResponseCode { get; set; }
|
||||
}
|
||||
|
||||
public interface IDnsRequestResolver
|
||||
{
|
||||
Task<DnsClientResponse> Request(DnsClientRequest request);
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Swan.Net.Dns {
|
||||
/// <summary>
|
||||
/// DnsClient public interfaces.
|
||||
/// </summary>
|
||||
internal partial class DnsClient {
|
||||
public interface IDnsMessage {
|
||||
IList<DnsQuestion> Questions {
|
||||
get;
|
||||
}
|
||||
|
||||
Int32 Size {
|
||||
get;
|
||||
}
|
||||
Byte[] ToArray();
|
||||
}
|
||||
|
||||
public interface IDnsMessageEntry {
|
||||
DnsDomain Name {
|
||||
get;
|
||||
}
|
||||
DnsRecordType Type {
|
||||
get;
|
||||
}
|
||||
DnsRecordClass Class {
|
||||
get;
|
||||
}
|
||||
|
||||
Int32 Size {
|
||||
get;
|
||||
}
|
||||
Byte[] ToArray();
|
||||
}
|
||||
|
||||
public interface IDnsResourceRecord : IDnsMessageEntry {
|
||||
TimeSpan TimeToLive {
|
||||
get;
|
||||
}
|
||||
Int32 DataLength {
|
||||
get;
|
||||
}
|
||||
Byte[] Data {
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDnsRequest : IDnsMessage {
|
||||
Int32 Id {
|
||||
get; set;
|
||||
}
|
||||
DnsOperationCode OperationCode {
|
||||
get; set;
|
||||
}
|
||||
Boolean RecursionDesired {
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDnsResponse : IDnsMessage {
|
||||
Int32 Id {
|
||||
get; set;
|
||||
}
|
||||
IList<IDnsResourceRecord> AnswerRecords {
|
||||
get;
|
||||
}
|
||||
IList<IDnsResourceRecord> AuthorityRecords {
|
||||
get;
|
||||
}
|
||||
IList<IDnsResourceRecord> AdditionalRecords {
|
||||
get;
|
||||
}
|
||||
Boolean IsRecursionAvailable {
|
||||
get; set;
|
||||
}
|
||||
Boolean IsAuthorativeServer {
|
||||
get; set;
|
||||
}
|
||||
Boolean IsTruncated {
|
||||
get; set;
|
||||
}
|
||||
DnsOperationCode OperationCode {
|
||||
get; set;
|
||||
}
|
||||
DnsResponseCode ResponseCode {
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDnsRequestResolver {
|
||||
Task<DnsClientResponse> Request(DnsClientRequest request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+557
-680
File diff suppressed because it is too large
Load Diff
@@ -1,419 +1,344 @@
|
||||
namespace Swan.Net.Dns
|
||||
{
|
||||
using Formatters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// <summary>
|
||||
/// DnsClient public methods.
|
||||
/// </summary>
|
||||
internal partial class DnsClient
|
||||
{
|
||||
public abstract class DnsResourceRecordBase : IDnsResourceRecord
|
||||
{
|
||||
private readonly IDnsResourceRecord _record;
|
||||
|
||||
protected DnsResourceRecordBase(IDnsResourceRecord record)
|
||||
{
|
||||
_record = record;
|
||||
}
|
||||
|
||||
public DnsDomain Name => _record.Name;
|
||||
|
||||
public DnsRecordType Type => _record.Type;
|
||||
|
||||
public DnsRecordClass Class => _record.Class;
|
||||
|
||||
public TimeSpan TimeToLive => _record.TimeToLive;
|
||||
|
||||
public int DataLength => _record.DataLength;
|
||||
|
||||
public byte[] Data => _record.Data;
|
||||
|
||||
public int Size => _record.Size;
|
||||
|
||||
protected virtual string[] IncludedProperties
|
||||
=> new[] {nameof(Name), nameof(Type), nameof(Class), nameof(TimeToLive), nameof(DataLength)};
|
||||
|
||||
public byte[] ToArray() => _record.ToArray();
|
||||
|
||||
public override string ToString()
|
||||
=> Json.SerializeOnly(this, true, IncludedProperties);
|
||||
}
|
||||
|
||||
public class DnsResourceRecord : IDnsResourceRecord
|
||||
{
|
||||
public DnsResourceRecord(
|
||||
DnsDomain domain,
|
||||
byte[] data,
|
||||
DnsRecordType type,
|
||||
DnsRecordClass klass = DnsRecordClass.IN,
|
||||
TimeSpan ttl = default)
|
||||
{
|
||||
Name = domain;
|
||||
Type = type;
|
||||
Class = klass;
|
||||
TimeToLive = ttl;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public DnsDomain Name { get; }
|
||||
|
||||
public DnsRecordType Type { get; }
|
||||
|
||||
public DnsRecordClass Class { get; }
|
||||
|
||||
public TimeSpan TimeToLive { get; }
|
||||
|
||||
public int DataLength => Data.Length;
|
||||
|
||||
public byte[] Data { get; }
|
||||
|
||||
public int Size => Name.Size + Tail.SIZE + Data.Length;
|
||||
|
||||
public static DnsResourceRecord FromArray(byte[] message, int offset, out int endOffset)
|
||||
{
|
||||
var domain = DnsDomain.FromArray(message, offset, out offset);
|
||||
var tail = message.ToStruct<Tail>(offset, Tail.SIZE);
|
||||
|
||||
var data = new byte[tail.DataLength];
|
||||
|
||||
offset += Tail.SIZE;
|
||||
Array.Copy(message, offset, data, 0, data.Length);
|
||||
|
||||
endOffset = offset + data.Length;
|
||||
|
||||
return new DnsResourceRecord(domain, data, tail.Type, tail.Class, tail.TimeToLive);
|
||||
}
|
||||
|
||||
public byte[] ToArray() =>
|
||||
new MemoryStream(Size)
|
||||
.Append(Name.ToArray())
|
||||
.Append(new Tail()
|
||||
{
|
||||
Type = Type,
|
||||
Class = Class,
|
||||
TimeToLive = TimeToLive,
|
||||
DataLength = Data.Length,
|
||||
}.ToBytes())
|
||||
.Append(Data)
|
||||
.ToArray();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Json.SerializeOnly(
|
||||
this,
|
||||
true,
|
||||
nameof(Name),
|
||||
nameof(Type),
|
||||
nameof(Class),
|
||||
nameof(TimeToLive),
|
||||
nameof(DataLength));
|
||||
}
|
||||
|
||||
[StructEndianness(Endianness.Big)]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
private struct Tail
|
||||
{
|
||||
public const int SIZE = 10;
|
||||
|
||||
private ushort type;
|
||||
private ushort klass;
|
||||
private uint ttl;
|
||||
private ushort dataLength;
|
||||
|
||||
public DnsRecordType Type
|
||||
{
|
||||
get => (DnsRecordType) type;
|
||||
set => type = (ushort) value;
|
||||
}
|
||||
|
||||
public DnsRecordClass Class
|
||||
{
|
||||
get => (DnsRecordClass) klass;
|
||||
set => klass = (ushort) value;
|
||||
}
|
||||
|
||||
public TimeSpan TimeToLive
|
||||
{
|
||||
get => TimeSpan.FromSeconds(ttl);
|
||||
set => ttl = (uint) value.TotalSeconds;
|
||||
}
|
||||
|
||||
public int DataLength
|
||||
{
|
||||
get => dataLength;
|
||||
set => dataLength = (ushort) value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DnsPointerResourceRecord : DnsResourceRecordBase
|
||||
{
|
||||
public DnsPointerResourceRecord(IDnsResourceRecord record, byte[] message, int dataOffset)
|
||||
: base(record)
|
||||
{
|
||||
PointerDomainName = DnsDomain.FromArray(message, dataOffset);
|
||||
}
|
||||
|
||||
public DnsDomain PointerDomainName { get; }
|
||||
|
||||
protected override string[] IncludedProperties
|
||||
{
|
||||
get
|
||||
{
|
||||
var temp = new List<string>(base.IncludedProperties) {nameof(PointerDomainName)};
|
||||
return temp.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DnsIPAddressResourceRecord : DnsResourceRecordBase
|
||||
{
|
||||
public DnsIPAddressResourceRecord(IDnsResourceRecord record)
|
||||
: base(record)
|
||||
{
|
||||
IPAddress = new IPAddress(Data);
|
||||
}
|
||||
|
||||
public IPAddress IPAddress { get; }
|
||||
|
||||
protected override string[] IncludedProperties
|
||||
=> new List<string>(base.IncludedProperties) {nameof(IPAddress)}.ToArray();
|
||||
}
|
||||
|
||||
public class DnsNameServerResourceRecord : DnsResourceRecordBase
|
||||
{
|
||||
public DnsNameServerResourceRecord(IDnsResourceRecord record, byte[] message, int dataOffset)
|
||||
: base(record)
|
||||
{
|
||||
NSDomainName = DnsDomain.FromArray(message, dataOffset);
|
||||
}
|
||||
|
||||
public DnsDomain NSDomainName { get; }
|
||||
|
||||
protected override string[] IncludedProperties
|
||||
=> new List<string>(base.IncludedProperties) {nameof(NSDomainName)}.ToArray();
|
||||
}
|
||||
|
||||
public class DnsCanonicalNameResourceRecord : DnsResourceRecordBase
|
||||
{
|
||||
public DnsCanonicalNameResourceRecord(IDnsResourceRecord record, byte[] message, int dataOffset)
|
||||
: base(record)
|
||||
{
|
||||
CanonicalDomainName = DnsDomain.FromArray(message, dataOffset);
|
||||
}
|
||||
|
||||
public DnsDomain CanonicalDomainName { get; }
|
||||
|
||||
protected override string[] IncludedProperties
|
||||
=> new List<string>(base.IncludedProperties) {nameof(CanonicalDomainName)}.ToArray();
|
||||
}
|
||||
|
||||
public class DnsMailExchangeResourceRecord : DnsResourceRecordBase
|
||||
{
|
||||
private const int PreferenceSize = 2;
|
||||
|
||||
public DnsMailExchangeResourceRecord(
|
||||
IDnsResourceRecord record,
|
||||
byte[] message,
|
||||
int dataOffset)
|
||||
: base(record)
|
||||
{
|
||||
var preference = new byte[PreferenceSize];
|
||||
Array.Copy(message, dataOffset, preference, 0, preference.Length);
|
||||
|
||||
if (BitConverter.IsLittleEndian)
|
||||
{
|
||||
Array.Reverse(preference);
|
||||
}
|
||||
|
||||
dataOffset += PreferenceSize;
|
||||
|
||||
Preference = BitConverter.ToUInt16(preference, 0);
|
||||
ExchangeDomainName = DnsDomain.FromArray(message, dataOffset);
|
||||
}
|
||||
|
||||
public int Preference { get; }
|
||||
|
||||
public DnsDomain ExchangeDomainName { get; }
|
||||
|
||||
protected override string[] IncludedProperties => new List<string>(base.IncludedProperties)
|
||||
{
|
||||
nameof(Preference),
|
||||
nameof(ExchangeDomainName),
|
||||
}.ToArray();
|
||||
}
|
||||
|
||||
public class DnsStartOfAuthorityResourceRecord : DnsResourceRecordBase
|
||||
{
|
||||
public DnsStartOfAuthorityResourceRecord(IDnsResourceRecord record, byte[] message, int dataOffset)
|
||||
: base(record)
|
||||
{
|
||||
MasterDomainName = DnsDomain.FromArray(message, dataOffset, out dataOffset);
|
||||
ResponsibleDomainName = DnsDomain.FromArray(message, dataOffset, out dataOffset);
|
||||
|
||||
var tail = message.ToStruct<Options>(dataOffset, Options.SIZE);
|
||||
|
||||
SerialNumber = tail.SerialNumber;
|
||||
RefreshInterval = tail.RefreshInterval;
|
||||
RetryInterval = tail.RetryInterval;
|
||||
ExpireInterval = tail.ExpireInterval;
|
||||
MinimumTimeToLive = tail.MinimumTimeToLive;
|
||||
}
|
||||
|
||||
public DnsStartOfAuthorityResourceRecord(
|
||||
DnsDomain domain,
|
||||
DnsDomain master,
|
||||
DnsDomain responsible,
|
||||
long serial,
|
||||
TimeSpan refresh,
|
||||
TimeSpan retry,
|
||||
TimeSpan expire,
|
||||
TimeSpan minTtl,
|
||||
TimeSpan ttl = default)
|
||||
: base(Create(domain, master, responsible, serial, refresh, retry, expire, minTtl, ttl))
|
||||
{
|
||||
MasterDomainName = master;
|
||||
ResponsibleDomainName = responsible;
|
||||
|
||||
SerialNumber = serial;
|
||||
RefreshInterval = refresh;
|
||||
RetryInterval = retry;
|
||||
ExpireInterval = expire;
|
||||
MinimumTimeToLive = minTtl;
|
||||
}
|
||||
|
||||
public DnsDomain MasterDomainName { get; }
|
||||
|
||||
public DnsDomain ResponsibleDomainName { get; }
|
||||
|
||||
public long SerialNumber { get; }
|
||||
|
||||
public TimeSpan RefreshInterval { get; }
|
||||
|
||||
public TimeSpan RetryInterval { get; }
|
||||
|
||||
public TimeSpan ExpireInterval { get; }
|
||||
|
||||
public TimeSpan MinimumTimeToLive { get; }
|
||||
|
||||
protected override string[] IncludedProperties => new List<string>(base.IncludedProperties)
|
||||
{
|
||||
nameof(MasterDomainName),
|
||||
nameof(ResponsibleDomainName),
|
||||
nameof(SerialNumber),
|
||||
}.ToArray();
|
||||
|
||||
private static IDnsResourceRecord Create(
|
||||
DnsDomain domain,
|
||||
DnsDomain master,
|
||||
DnsDomain responsible,
|
||||
long serial,
|
||||
TimeSpan refresh,
|
||||
TimeSpan retry,
|
||||
TimeSpan expire,
|
||||
TimeSpan minTtl,
|
||||
TimeSpan ttl)
|
||||
{
|
||||
var data = new MemoryStream(Options.SIZE + master.Size + responsible.Size);
|
||||
var tail = new Options
|
||||
{
|
||||
SerialNumber = serial,
|
||||
RefreshInterval = refresh,
|
||||
RetryInterval = retry,
|
||||
ExpireInterval = expire,
|
||||
MinimumTimeToLive = minTtl,
|
||||
};
|
||||
|
||||
data.Append(master.ToArray()).Append(responsible.ToArray()).Append(tail.ToBytes());
|
||||
|
||||
return new DnsResourceRecord(domain, data.ToArray(), DnsRecordType.SOA, DnsRecordClass.IN, ttl);
|
||||
}
|
||||
|
||||
[StructEndianness(Endianness.Big)]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct Options
|
||||
{
|
||||
public const int SIZE = 20;
|
||||
|
||||
private uint serialNumber;
|
||||
private uint refreshInterval;
|
||||
private uint retryInterval;
|
||||
private uint expireInterval;
|
||||
private uint ttl;
|
||||
|
||||
public long SerialNumber
|
||||
{
|
||||
get => serialNumber;
|
||||
set => serialNumber = (uint) value;
|
||||
}
|
||||
|
||||
public TimeSpan RefreshInterval
|
||||
{
|
||||
get => TimeSpan.FromSeconds(refreshInterval);
|
||||
set => refreshInterval = (uint) value.TotalSeconds;
|
||||
}
|
||||
|
||||
public TimeSpan RetryInterval
|
||||
{
|
||||
get => TimeSpan.FromSeconds(retryInterval);
|
||||
set => retryInterval = (uint) value.TotalSeconds;
|
||||
}
|
||||
|
||||
public TimeSpan ExpireInterval
|
||||
{
|
||||
get => TimeSpan.FromSeconds(expireInterval);
|
||||
set => expireInterval = (uint) value.TotalSeconds;
|
||||
}
|
||||
|
||||
public TimeSpan MinimumTimeToLive
|
||||
{
|
||||
get => TimeSpan.FromSeconds(ttl);
|
||||
set => ttl = (uint) value.TotalSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class DnsResourceRecordFactory
|
||||
{
|
||||
public static IList<IDnsResourceRecord> GetAllFromArray(
|
||||
byte[] message,
|
||||
int offset,
|
||||
int count,
|
||||
out int endOffset)
|
||||
{
|
||||
var result = new List<IDnsResourceRecord>(count);
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
result.Add(GetFromArray(message, offset, out offset));
|
||||
}
|
||||
|
||||
endOffset = offset;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IDnsResourceRecord GetFromArray(byte[] message, int offset, out int endOffset)
|
||||
{
|
||||
var record = DnsResourceRecord.FromArray(message, offset, out endOffset);
|
||||
var dataOffset = endOffset - record.DataLength;
|
||||
|
||||
return record.Type switch
|
||||
{
|
||||
DnsRecordType.A => (IDnsResourceRecord) new DnsIPAddressResourceRecord(record),
|
||||
DnsRecordType.AAAA => new DnsIPAddressResourceRecord(record),
|
||||
DnsRecordType.NS => new DnsNameServerResourceRecord(record, message, dataOffset),
|
||||
DnsRecordType.CNAME => new DnsCanonicalNameResourceRecord(record, message, dataOffset),
|
||||
DnsRecordType.SOA => new DnsStartOfAuthorityResourceRecord(record, message, dataOffset),
|
||||
DnsRecordType.PTR => new DnsPointerResourceRecord(record, message, dataOffset),
|
||||
DnsRecordType.MX => new DnsMailExchangeResourceRecord(record, message, dataOffset),
|
||||
_ => record
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
using Swan.Formatters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Swan.Net.Dns {
|
||||
/// <summary>
|
||||
/// DnsClient public methods.
|
||||
/// </summary>
|
||||
internal partial class DnsClient {
|
||||
public abstract class DnsResourceRecordBase : IDnsResourceRecord {
|
||||
private readonly IDnsResourceRecord _record;
|
||||
|
||||
protected DnsResourceRecordBase(IDnsResourceRecord record) => this._record = record;
|
||||
|
||||
public DnsDomain Name => this._record.Name;
|
||||
|
||||
public DnsRecordType Type => this._record.Type;
|
||||
|
||||
public DnsRecordClass Class => this._record.Class;
|
||||
|
||||
public TimeSpan TimeToLive => this._record.TimeToLive;
|
||||
|
||||
public Int32 DataLength => this._record.DataLength;
|
||||
|
||||
public Byte[] Data => this._record.Data;
|
||||
|
||||
public Int32 Size => this._record.Size;
|
||||
|
||||
protected virtual String[] IncludedProperties => new[] { nameof(this.Name), nameof(this.Type), nameof(this.Class), nameof(this.TimeToLive), nameof(this.DataLength) };
|
||||
|
||||
public Byte[] ToArray() => this._record.ToArray();
|
||||
|
||||
public override String ToString() => Json.SerializeOnly(this, true, this.IncludedProperties);
|
||||
}
|
||||
|
||||
public class DnsResourceRecord : IDnsResourceRecord {
|
||||
public DnsResourceRecord(DnsDomain domain, Byte[] data, DnsRecordType type, DnsRecordClass klass = DnsRecordClass.IN, TimeSpan ttl = default) {
|
||||
this.Name = domain;
|
||||
this.Type = type;
|
||||
this.Class = klass;
|
||||
this.TimeToLive = ttl;
|
||||
this.Data = data;
|
||||
}
|
||||
|
||||
public DnsDomain Name {
|
||||
get;
|
||||
}
|
||||
|
||||
public DnsRecordType Type {
|
||||
get;
|
||||
}
|
||||
|
||||
public DnsRecordClass Class {
|
||||
get;
|
||||
}
|
||||
|
||||
public TimeSpan TimeToLive {
|
||||
get;
|
||||
}
|
||||
|
||||
public Int32 DataLength => this.Data.Length;
|
||||
|
||||
public Byte[] Data {
|
||||
get;
|
||||
}
|
||||
|
||||
public Int32 Size => this.Name.Size + Tail.SIZE + this.Data.Length;
|
||||
|
||||
public static DnsResourceRecord FromArray(Byte[] message, Int32 offset, out Int32 endOffset) {
|
||||
DnsDomain domain = DnsDomain.FromArray(message, offset, out offset);
|
||||
Tail tail = message.ToStruct<Tail>(offset, Tail.SIZE);
|
||||
|
||||
Byte[] data = new Byte[tail.DataLength];
|
||||
|
||||
offset += Tail.SIZE;
|
||||
Array.Copy(message, offset, data, 0, data.Length);
|
||||
|
||||
endOffset = offset + data.Length;
|
||||
|
||||
return new DnsResourceRecord(domain, data, tail.Type, tail.Class, tail.TimeToLive);
|
||||
}
|
||||
|
||||
public Byte[] ToArray() => new MemoryStream(this.Size).Append(this.Name.ToArray()).Append(new Tail() { Type = Type, Class = Class, TimeToLive = TimeToLive, DataLength = this.Data.Length, }.ToBytes()).Append(this.Data).ToArray();
|
||||
|
||||
public override String ToString() => Json.SerializeOnly(this, true, nameof(this.Name), nameof(this.Type), nameof(this.Class), nameof(this.TimeToLive), nameof(this.DataLength));
|
||||
|
||||
[StructEndianness(Endianness.Big)]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
private struct Tail {
|
||||
public const Int32 SIZE = 10;
|
||||
|
||||
private UInt16 type;
|
||||
private UInt16 klass;
|
||||
private UInt32 ttl;
|
||||
private UInt16 dataLength;
|
||||
|
||||
public DnsRecordType Type {
|
||||
get => (DnsRecordType)this.type;
|
||||
set => this.type = (UInt16)value;
|
||||
}
|
||||
|
||||
public DnsRecordClass Class {
|
||||
get => (DnsRecordClass)this.klass;
|
||||
set => this.klass = (UInt16)value;
|
||||
}
|
||||
|
||||
public TimeSpan TimeToLive {
|
||||
get => TimeSpan.FromSeconds(this.ttl);
|
||||
set => this.ttl = (UInt32)value.TotalSeconds;
|
||||
}
|
||||
|
||||
public Int32 DataLength {
|
||||
get => this.dataLength;
|
||||
set => this.dataLength = (UInt16)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DnsPointerResourceRecord : DnsResourceRecordBase {
|
||||
public DnsPointerResourceRecord(IDnsResourceRecord record, Byte[] message, Int32 dataOffset) : base(record) => this.PointerDomainName = DnsDomain.FromArray(message, dataOffset);
|
||||
|
||||
public DnsDomain PointerDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
protected override String[] IncludedProperties {
|
||||
get {
|
||||
List<String> temp = new List<String>(base.IncludedProperties) { nameof(this.PointerDomainName) };
|
||||
return temp.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DnsIPAddressResourceRecord : DnsResourceRecordBase {
|
||||
public DnsIPAddressResourceRecord(IDnsResourceRecord record) : base(record) => this.IPAddress = new IPAddress(this.Data);
|
||||
|
||||
public IPAddress IPAddress {
|
||||
get;
|
||||
}
|
||||
|
||||
protected override String[] IncludedProperties => new List<String>(base.IncludedProperties) { nameof(this.IPAddress) }.ToArray();
|
||||
}
|
||||
|
||||
public class DnsNameServerResourceRecord : DnsResourceRecordBase {
|
||||
public DnsNameServerResourceRecord(IDnsResourceRecord record, Byte[] message, Int32 dataOffset) : base(record) => this.NSDomainName = DnsDomain.FromArray(message, dataOffset);
|
||||
|
||||
public DnsDomain NSDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
protected override String[] IncludedProperties => new List<String>(base.IncludedProperties) { nameof(this.NSDomainName) }.ToArray();
|
||||
}
|
||||
|
||||
public class DnsCanonicalNameResourceRecord : DnsResourceRecordBase {
|
||||
public DnsCanonicalNameResourceRecord(IDnsResourceRecord record, Byte[] message, Int32 dataOffset) : base(record) => this.CanonicalDomainName = DnsDomain.FromArray(message, dataOffset);
|
||||
|
||||
public DnsDomain CanonicalDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
protected override String[] IncludedProperties => new List<String>(base.IncludedProperties) { nameof(this.CanonicalDomainName) }.ToArray();
|
||||
}
|
||||
|
||||
public class DnsMailExchangeResourceRecord : DnsResourceRecordBase {
|
||||
private const Int32 PreferenceSize = 2;
|
||||
|
||||
public DnsMailExchangeResourceRecord(IDnsResourceRecord record, Byte[] message, Int32 dataOffset)
|
||||
: base(record) {
|
||||
Byte[] preference = new Byte[PreferenceSize];
|
||||
Array.Copy(message, dataOffset, preference, 0, preference.Length);
|
||||
|
||||
if(BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(preference);
|
||||
}
|
||||
|
||||
dataOffset += PreferenceSize;
|
||||
|
||||
this.Preference = BitConverter.ToUInt16(preference, 0);
|
||||
this.ExchangeDomainName = DnsDomain.FromArray(message, dataOffset);
|
||||
}
|
||||
|
||||
public Int32 Preference {
|
||||
get;
|
||||
}
|
||||
|
||||
public DnsDomain ExchangeDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
protected override String[] IncludedProperties => new List<String>(base.IncludedProperties)
|
||||
{
|
||||
nameof(this.Preference),
|
||||
nameof(this.ExchangeDomainName),
|
||||
}.ToArray();
|
||||
}
|
||||
|
||||
public class DnsStartOfAuthorityResourceRecord : DnsResourceRecordBase {
|
||||
public DnsStartOfAuthorityResourceRecord(IDnsResourceRecord record, Byte[] message, Int32 dataOffset) : base(record) {
|
||||
this.MasterDomainName = DnsDomain.FromArray(message, dataOffset, out dataOffset);
|
||||
this.ResponsibleDomainName = DnsDomain.FromArray(message, dataOffset, out dataOffset);
|
||||
|
||||
Options tail = message.ToStruct<Options>(dataOffset, Options.SIZE);
|
||||
|
||||
this.SerialNumber = tail.SerialNumber;
|
||||
this.RefreshInterval = tail.RefreshInterval;
|
||||
this.RetryInterval = tail.RetryInterval;
|
||||
this.ExpireInterval = tail.ExpireInterval;
|
||||
this.MinimumTimeToLive = tail.MinimumTimeToLive;
|
||||
}
|
||||
|
||||
public DnsStartOfAuthorityResourceRecord(DnsDomain domain, DnsDomain master, DnsDomain responsible, Int64 serial, TimeSpan refresh, TimeSpan retry, TimeSpan expire, TimeSpan minTtl, TimeSpan ttl = default)
|
||||
: base(Create(domain, master, responsible, serial, refresh, retry, expire, minTtl, ttl)) {
|
||||
this.MasterDomainName = master;
|
||||
this.ResponsibleDomainName = responsible;
|
||||
|
||||
this.SerialNumber = serial;
|
||||
this.RefreshInterval = refresh;
|
||||
this.RetryInterval = retry;
|
||||
this.ExpireInterval = expire;
|
||||
this.MinimumTimeToLive = minTtl;
|
||||
}
|
||||
|
||||
public DnsDomain MasterDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
public DnsDomain ResponsibleDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
public Int64 SerialNumber {
|
||||
get;
|
||||
}
|
||||
|
||||
public TimeSpan RefreshInterval {
|
||||
get;
|
||||
}
|
||||
|
||||
public TimeSpan RetryInterval {
|
||||
get;
|
||||
}
|
||||
|
||||
public TimeSpan ExpireInterval {
|
||||
get;
|
||||
}
|
||||
|
||||
public TimeSpan MinimumTimeToLive {
|
||||
get;
|
||||
}
|
||||
|
||||
protected override String[] IncludedProperties => new List<String>(base.IncludedProperties)
|
||||
{
|
||||
nameof(this.MasterDomainName),
|
||||
nameof(this.ResponsibleDomainName),
|
||||
nameof(this.SerialNumber),
|
||||
}.ToArray();
|
||||
|
||||
private static IDnsResourceRecord Create(DnsDomain domain, DnsDomain master, DnsDomain responsible, Int64 serial, TimeSpan refresh, TimeSpan retry, TimeSpan expire, TimeSpan minTtl, TimeSpan ttl) {
|
||||
MemoryStream data = new MemoryStream(Options.SIZE + master.Size + responsible.Size);
|
||||
Options tail = new Options {
|
||||
SerialNumber = serial,
|
||||
RefreshInterval = refresh,
|
||||
RetryInterval = retry,
|
||||
ExpireInterval = expire,
|
||||
MinimumTimeToLive = minTtl,
|
||||
};
|
||||
|
||||
_ = data.Append(master.ToArray()).Append(responsible.ToArray()).Append(tail.ToBytes());
|
||||
|
||||
return new DnsResourceRecord(domain, data.ToArray(), DnsRecordType.SOA, DnsRecordClass.IN, ttl);
|
||||
}
|
||||
|
||||
[StructEndianness(Endianness.Big)]
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
public struct Options {
|
||||
public const Int32 SIZE = 20;
|
||||
|
||||
private UInt32 serialNumber;
|
||||
private UInt32 refreshInterval;
|
||||
private UInt32 retryInterval;
|
||||
private UInt32 expireInterval;
|
||||
private UInt32 ttl;
|
||||
|
||||
public Int64 SerialNumber {
|
||||
get => this.serialNumber;
|
||||
set => this.serialNumber = (UInt32)value;
|
||||
}
|
||||
|
||||
public TimeSpan RefreshInterval {
|
||||
get => TimeSpan.FromSeconds(this.refreshInterval);
|
||||
set => this.refreshInterval = (UInt32)value.TotalSeconds;
|
||||
}
|
||||
|
||||
public TimeSpan RetryInterval {
|
||||
get => TimeSpan.FromSeconds(this.retryInterval);
|
||||
set => this.retryInterval = (UInt32)value.TotalSeconds;
|
||||
}
|
||||
|
||||
public TimeSpan ExpireInterval {
|
||||
get => TimeSpan.FromSeconds(this.expireInterval);
|
||||
set => this.expireInterval = (UInt32)value.TotalSeconds;
|
||||
}
|
||||
|
||||
public TimeSpan MinimumTimeToLive {
|
||||
get => TimeSpan.FromSeconds(this.ttl);
|
||||
set => this.ttl = (UInt32)value.TotalSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class DnsResourceRecordFactory {
|
||||
public static IList<IDnsResourceRecord> GetAllFromArray(Byte[] message, Int32 offset, Int32 count, out Int32 endOffset) {
|
||||
List<IDnsResourceRecord> result = new List<IDnsResourceRecord>(count);
|
||||
|
||||
for(Int32 i = 0; i < count; i++) {
|
||||
result.Add(GetFromArray(message, offset, out offset));
|
||||
}
|
||||
|
||||
endOffset = offset;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IDnsResourceRecord GetFromArray(Byte[] message, Int32 offset, out Int32 endOffset) {
|
||||
DnsResourceRecord record = DnsResourceRecord.FromArray(message, offset, out endOffset);
|
||||
Int32 dataOffset = endOffset - record.DataLength;
|
||||
|
||||
return record.Type switch
|
||||
{
|
||||
DnsRecordType.A => (new DnsIPAddressResourceRecord(record)),
|
||||
DnsRecordType.AAAA => new DnsIPAddressResourceRecord(record),
|
||||
DnsRecordType.NS => new DnsNameServerResourceRecord(record, message, dataOffset),
|
||||
DnsRecordType.CNAME => new DnsCanonicalNameResourceRecord(record, message, dataOffset),
|
||||
DnsRecordType.SOA => new DnsStartOfAuthorityResourceRecord(record, message, dataOffset),
|
||||
DnsRecordType.PTR => new DnsPointerResourceRecord(record, message, dataOffset),
|
||||
DnsRecordType.MX => new DnsMailExchangeResourceRecord(record, message, dataOffset),
|
||||
_ => record
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+173
-214
@@ -1,215 +1,174 @@
|
||||
namespace Swan.Net.Dns
|
||||
{
|
||||
using Formatters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// DnsClient Response inner class.
|
||||
/// </summary>
|
||||
internal partial class DnsClient
|
||||
{
|
||||
public class DnsClientResponse : IDnsResponse
|
||||
{
|
||||
private readonly DnsResponse _response;
|
||||
private readonly byte[] _message;
|
||||
|
||||
internal DnsClientResponse(DnsClientRequest request, DnsResponse response, byte[] message)
|
||||
{
|
||||
Request = request;
|
||||
|
||||
_message = message;
|
||||
_response = response;
|
||||
}
|
||||
|
||||
public DnsClientRequest Request { get; }
|
||||
|
||||
public int Id
|
||||
{
|
||||
get { return _response.Id; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public IList<IDnsResourceRecord> AnswerRecords => _response.AnswerRecords;
|
||||
|
||||
public IList<IDnsResourceRecord> AuthorityRecords =>
|
||||
new ReadOnlyCollection<IDnsResourceRecord>(_response.AuthorityRecords);
|
||||
|
||||
public IList<IDnsResourceRecord> AdditionalRecords =>
|
||||
new ReadOnlyCollection<IDnsResourceRecord>(_response.AdditionalRecords);
|
||||
|
||||
public bool IsRecursionAvailable
|
||||
{
|
||||
get { return _response.IsRecursionAvailable; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public bool IsAuthorativeServer
|
||||
{
|
||||
get { return _response.IsAuthorativeServer; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public bool IsTruncated
|
||||
{
|
||||
get { return _response.IsTruncated; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public DnsOperationCode OperationCode
|
||||
{
|
||||
get { return _response.OperationCode; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public DnsResponseCode ResponseCode
|
||||
{
|
||||
get { return _response.ResponseCode; }
|
||||
set { }
|
||||
}
|
||||
|
||||
public IList<DnsQuestion> Questions => new ReadOnlyCollection<DnsQuestion>(_response.Questions);
|
||||
|
||||
public int Size => _message.Length;
|
||||
|
||||
public byte[] ToArray() => _message;
|
||||
|
||||
public override string ToString() => _response.ToString();
|
||||
}
|
||||
|
||||
public class DnsResponse : IDnsResponse
|
||||
{
|
||||
private DnsHeader _header;
|
||||
|
||||
public DnsResponse(
|
||||
DnsHeader header,
|
||||
IList<DnsQuestion> questions,
|
||||
IList<IDnsResourceRecord> answers,
|
||||
IList<IDnsResourceRecord> authority,
|
||||
IList<IDnsResourceRecord> additional)
|
||||
{
|
||||
_header = header;
|
||||
Questions = questions;
|
||||
AnswerRecords = answers;
|
||||
AuthorityRecords = authority;
|
||||
AdditionalRecords = additional;
|
||||
}
|
||||
|
||||
public IList<DnsQuestion> Questions { get; }
|
||||
|
||||
public IList<IDnsResourceRecord> AnswerRecords { get; }
|
||||
|
||||
public IList<IDnsResourceRecord> AuthorityRecords { get; }
|
||||
|
||||
public IList<IDnsResourceRecord> AdditionalRecords { get; }
|
||||
|
||||
public int Id
|
||||
{
|
||||
get => _header.Id;
|
||||
set => _header.Id = value;
|
||||
}
|
||||
|
||||
public bool IsRecursionAvailable
|
||||
{
|
||||
get => _header.RecursionAvailable;
|
||||
set => _header.RecursionAvailable = value;
|
||||
}
|
||||
|
||||
public bool IsAuthorativeServer
|
||||
{
|
||||
get => _header.AuthorativeServer;
|
||||
set => _header.AuthorativeServer = value;
|
||||
}
|
||||
|
||||
public bool IsTruncated
|
||||
{
|
||||
get => _header.Truncated;
|
||||
set => _header.Truncated = value;
|
||||
}
|
||||
|
||||
public DnsOperationCode OperationCode
|
||||
{
|
||||
get => _header.OperationCode;
|
||||
set => _header.OperationCode = value;
|
||||
}
|
||||
|
||||
public DnsResponseCode ResponseCode
|
||||
{
|
||||
get => _header.ResponseCode;
|
||||
set => _header.ResponseCode = value;
|
||||
}
|
||||
|
||||
public int Size
|
||||
=> _header.Size +
|
||||
Questions.Sum(q => q.Size) +
|
||||
AnswerRecords.Sum(a => a.Size) +
|
||||
AuthorityRecords.Sum(a => a.Size) +
|
||||
AdditionalRecords.Sum(a => a.Size);
|
||||
|
||||
public static DnsResponse FromArray(byte[] message)
|
||||
{
|
||||
var header = DnsHeader.FromArray(message);
|
||||
var offset = header.Size;
|
||||
|
||||
if (!header.Response || header.QuestionCount == 0)
|
||||
{
|
||||
throw new ArgumentException("Invalid response message");
|
||||
}
|
||||
|
||||
if (header.Truncated)
|
||||
{
|
||||
return new DnsResponse(header,
|
||||
DnsQuestion.GetAllFromArray(message, offset, header.QuestionCount),
|
||||
new List<IDnsResourceRecord>(),
|
||||
new List<IDnsResourceRecord>(),
|
||||
new List<IDnsResourceRecord>());
|
||||
}
|
||||
|
||||
return new DnsResponse(header,
|
||||
DnsQuestion.GetAllFromArray(message, offset, header.QuestionCount, out offset),
|
||||
DnsResourceRecordFactory.GetAllFromArray(message, offset, header.AnswerRecordCount, out offset),
|
||||
DnsResourceRecordFactory.GetAllFromArray(message, offset, header.AuthorityRecordCount, out offset),
|
||||
DnsResourceRecordFactory.GetAllFromArray(message, offset, header.AdditionalRecordCount, out offset));
|
||||
}
|
||||
|
||||
public byte[] ToArray()
|
||||
{
|
||||
UpdateHeader();
|
||||
var result = new MemoryStream(Size);
|
||||
|
||||
result
|
||||
.Append(_header.ToArray())
|
||||
.Append(Questions.Select(q => q.ToArray()))
|
||||
.Append(AnswerRecords.Select(a => a.ToArray()))
|
||||
.Append(AuthorityRecords.Select(a => a.ToArray()))
|
||||
.Append(AdditionalRecords.Select(a => a.ToArray()));
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
UpdateHeader();
|
||||
|
||||
return Json.SerializeOnly(
|
||||
this,
|
||||
true,
|
||||
nameof(Questions),
|
||||
nameof(AnswerRecords),
|
||||
nameof(AuthorityRecords),
|
||||
nameof(AdditionalRecords));
|
||||
}
|
||||
|
||||
private void UpdateHeader()
|
||||
{
|
||||
_header.QuestionCount = Questions.Count;
|
||||
_header.AnswerRecordCount = AnswerRecords.Count;
|
||||
_header.AuthorityRecordCount = AuthorityRecords.Count;
|
||||
_header.AdditionalRecordCount = AdditionalRecords.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
using Swan.Formatters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Swan.Net.Dns {
|
||||
/// <summary>
|
||||
/// DnsClient Response inner class.
|
||||
/// </summary>
|
||||
internal partial class DnsClient {
|
||||
public class DnsClientResponse : IDnsResponse {
|
||||
private readonly DnsResponse _response;
|
||||
private readonly Byte[] _message;
|
||||
|
||||
internal DnsClientResponse(DnsClientRequest request, DnsResponse response, Byte[] message) {
|
||||
this.Request = request;
|
||||
|
||||
this._message = message;
|
||||
this._response = response;
|
||||
}
|
||||
|
||||
public DnsClientRequest Request {
|
||||
get;
|
||||
}
|
||||
|
||||
public Int32 Id {
|
||||
get => this._response.Id;
|
||||
set {
|
||||
}
|
||||
}
|
||||
|
||||
public IList<IDnsResourceRecord> AnswerRecords => this._response.AnswerRecords;
|
||||
|
||||
public IList<IDnsResourceRecord> AuthorityRecords => new ReadOnlyCollection<IDnsResourceRecord>(this._response.AuthorityRecords);
|
||||
|
||||
public IList<IDnsResourceRecord> AdditionalRecords => new ReadOnlyCollection<IDnsResourceRecord>(this._response.AdditionalRecords);
|
||||
|
||||
public Boolean IsRecursionAvailable {
|
||||
get => this._response.IsRecursionAvailable;
|
||||
set {
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean IsAuthorativeServer {
|
||||
get => this._response.IsAuthorativeServer;
|
||||
set {
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean IsTruncated {
|
||||
get => this._response.IsTruncated;
|
||||
set {
|
||||
}
|
||||
}
|
||||
|
||||
public DnsOperationCode OperationCode {
|
||||
get => this._response.OperationCode;
|
||||
set {
|
||||
}
|
||||
}
|
||||
|
||||
public DnsResponseCode ResponseCode {
|
||||
get => this._response.ResponseCode;
|
||||
set {
|
||||
}
|
||||
}
|
||||
|
||||
public IList<DnsQuestion> Questions => new ReadOnlyCollection<DnsQuestion>(this._response.Questions);
|
||||
|
||||
public Int32 Size => this._message.Length;
|
||||
|
||||
public Byte[] ToArray() => this._message;
|
||||
|
||||
public override String ToString() => this._response.ToString();
|
||||
}
|
||||
|
||||
public class DnsResponse : IDnsResponse {
|
||||
private DnsHeader _header;
|
||||
|
||||
public DnsResponse(DnsHeader header, IList<DnsQuestion> questions, IList<IDnsResourceRecord> answers, IList<IDnsResourceRecord> authority, IList<IDnsResourceRecord> additional) {
|
||||
this._header = header;
|
||||
this.Questions = questions;
|
||||
this.AnswerRecords = answers;
|
||||
this.AuthorityRecords = authority;
|
||||
this.AdditionalRecords = additional;
|
||||
}
|
||||
|
||||
public IList<DnsQuestion> Questions {
|
||||
get;
|
||||
}
|
||||
|
||||
public IList<IDnsResourceRecord> AnswerRecords {
|
||||
get;
|
||||
}
|
||||
|
||||
public IList<IDnsResourceRecord> AuthorityRecords {
|
||||
get;
|
||||
}
|
||||
|
||||
public IList<IDnsResourceRecord> AdditionalRecords {
|
||||
get;
|
||||
}
|
||||
|
||||
public Int32 Id {
|
||||
get => this._header.Id;
|
||||
set => this._header.Id = value;
|
||||
}
|
||||
|
||||
public Boolean IsRecursionAvailable {
|
||||
get => this._header.RecursionAvailable;
|
||||
set => this._header.RecursionAvailable = value;
|
||||
}
|
||||
|
||||
public Boolean IsAuthorativeServer {
|
||||
get => this._header.AuthorativeServer;
|
||||
set => this._header.AuthorativeServer = value;
|
||||
}
|
||||
|
||||
public Boolean IsTruncated {
|
||||
get => this._header.Truncated;
|
||||
set => this._header.Truncated = value;
|
||||
}
|
||||
|
||||
public DnsOperationCode OperationCode {
|
||||
get => this._header.OperationCode;
|
||||
set => this._header.OperationCode = value;
|
||||
}
|
||||
|
||||
public DnsResponseCode ResponseCode {
|
||||
get => this._header.ResponseCode;
|
||||
set => this._header.ResponseCode = value;
|
||||
}
|
||||
|
||||
public Int32 Size => this._header.Size + this.Questions.Sum(q => q.Size) + this.AnswerRecords.Sum(a => a.Size) + this.AuthorityRecords.Sum(a => a.Size) + this.AdditionalRecords.Sum(a => a.Size);
|
||||
|
||||
public static DnsResponse FromArray(Byte[] message) {
|
||||
DnsHeader header = DnsHeader.FromArray(message);
|
||||
Int32 offset = header.Size;
|
||||
|
||||
if(!header.Response || header.QuestionCount == 0) {
|
||||
throw new ArgumentException("Invalid response message");
|
||||
}
|
||||
|
||||
return header.Truncated
|
||||
? new DnsResponse(header, DnsQuestion.GetAllFromArray(message, offset, header.QuestionCount), new List<IDnsResourceRecord>(), new List<IDnsResourceRecord>(), new List<IDnsResourceRecord>())
|
||||
: new DnsResponse(header, DnsQuestion.GetAllFromArray(message, offset, header.QuestionCount, out offset), DnsResourceRecordFactory.GetAllFromArray(message, offset, header.AnswerRecordCount, out offset), DnsResourceRecordFactory.GetAllFromArray(message, offset, header.AuthorityRecordCount, out offset), DnsResourceRecordFactory.GetAllFromArray(message, offset, header.AdditionalRecordCount, out _));
|
||||
}
|
||||
|
||||
public Byte[] ToArray() {
|
||||
this.UpdateHeader();
|
||||
MemoryStream result = new MemoryStream(this.Size);
|
||||
|
||||
_ = result.Append(this._header.ToArray()).Append(this.Questions.Select(q => q.ToArray())).Append(this.AnswerRecords.Select(a => a.ToArray())).Append(this.AuthorityRecords.Select(a => a.ToArray())).Append(this.AdditionalRecords.Select(a => a.ToArray()));
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public override String ToString() {
|
||||
this.UpdateHeader();
|
||||
|
||||
return Json.SerializeOnly(this, true, nameof(this.Questions), nameof(this.AnswerRecords), nameof(this.AuthorityRecords), nameof(this.AdditionalRecords));
|
||||
}
|
||||
|
||||
private void UpdateHeader() {
|
||||
this._header.QuestionCount = this.Questions.Count;
|
||||
this._header.AnswerRecordCount = this.AnswerRecords.Count;
|
||||
this._header.AuthorityRecordCount = this.AuthorityRecords.Count;
|
||||
this._header.AdditionalRecordCount = this.AdditionalRecords.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+64
-78
@@ -1,79 +1,65 @@
|
||||
namespace Swan.Net.Dns
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// DnsClient public methods.
|
||||
/// </summary>
|
||||
internal partial class DnsClient
|
||||
{
|
||||
private readonly IPEndPoint _dns;
|
||||
private readonly IDnsRequestResolver _resolver;
|
||||
|
||||
public DnsClient(IPEndPoint dns, IDnsRequestResolver? resolver = null)
|
||||
{
|
||||
_dns = dns;
|
||||
_resolver = resolver ?? new DnsUdpRequestResolver(new DnsTcpRequestResolver());
|
||||
}
|
||||
|
||||
public DnsClient(IPAddress ip, int port = Network.DnsDefaultPort, IDnsRequestResolver? resolver = null)
|
||||
: this(new IPEndPoint(ip, port), resolver)
|
||||
{
|
||||
}
|
||||
|
||||
public DnsClientRequest Create(IDnsRequest? request = null)
|
||||
=> new DnsClientRequest(_dns, request, _resolver);
|
||||
|
||||
public async Task<IList<IPAddress>> Lookup(string domain, DnsRecordType type = DnsRecordType.A)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(domain))
|
||||
throw new ArgumentNullException(nameof(domain));
|
||||
|
||||
if (type != DnsRecordType.A && type != DnsRecordType.AAAA)
|
||||
{
|
||||
throw new ArgumentException("Invalid record type " + type);
|
||||
}
|
||||
|
||||
var response = await Resolve(domain, type).ConfigureAwait(false);
|
||||
var ips = response.AnswerRecords
|
||||
.Where(r => r.Type == type)
|
||||
.Cast<DnsIPAddressResourceRecord>()
|
||||
.Select(r => r.IPAddress)
|
||||
.ToList();
|
||||
|
||||
return ips.Count == 0 ? throw new DnsQueryException(response, "No matching records") : ips;
|
||||
}
|
||||
|
||||
public async Task<string> Reverse(IPAddress ip)
|
||||
{
|
||||
if (ip == null)
|
||||
throw new ArgumentNullException(nameof(ip));
|
||||
|
||||
var response = await Resolve(DnsDomain.PointerName(ip), DnsRecordType.PTR);
|
||||
var ptr = response.AnswerRecords.FirstOrDefault(r => r.Type == DnsRecordType.PTR);
|
||||
|
||||
return ptr == null
|
||||
? throw new DnsQueryException(response, "No matching records")
|
||||
: ((DnsPointerResourceRecord) ptr).PointerDomainName.ToString();
|
||||
}
|
||||
|
||||
public Task<DnsClientResponse> Resolve(string domain, DnsRecordType type) =>
|
||||
Resolve(new DnsDomain(domain), type);
|
||||
|
||||
public Task<DnsClientResponse> Resolve(DnsDomain domain, DnsRecordType type)
|
||||
{
|
||||
var request = Create();
|
||||
var question = new DnsQuestion(domain, type);
|
||||
|
||||
request.Questions.Add(question);
|
||||
request.OperationCode = DnsOperationCode.Query;
|
||||
request.RecursionDesired = true;
|
||||
|
||||
return request.Resolve();
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#nullable enable
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Swan.Net.Dns {
|
||||
/// <summary>
|
||||
/// DnsClient public methods.
|
||||
/// </summary>
|
||||
internal partial class DnsClient {
|
||||
private readonly IPEndPoint _dns;
|
||||
private readonly IDnsRequestResolver _resolver;
|
||||
|
||||
public DnsClient(IPEndPoint dns, IDnsRequestResolver? resolver = null) {
|
||||
this._dns = dns;
|
||||
this._resolver = resolver ?? new DnsUdpRequestResolver(new DnsTcpRequestResolver());
|
||||
}
|
||||
|
||||
public DnsClient(IPAddress ip, Int32 port = Network.DnsDefaultPort, IDnsRequestResolver? resolver = null) : this(new IPEndPoint(ip, port), resolver) {
|
||||
}
|
||||
|
||||
public DnsClientRequest Create(IDnsRequest? request = null) => new DnsClientRequest(this._dns, request, this._resolver);
|
||||
|
||||
public async Task<IList<IPAddress>> Lookup(String domain, DnsRecordType type = DnsRecordType.A) {
|
||||
if(String.IsNullOrWhiteSpace(domain)) {
|
||||
throw new ArgumentNullException(nameof(domain));
|
||||
}
|
||||
|
||||
if(type != DnsRecordType.A && type != DnsRecordType.AAAA) {
|
||||
throw new ArgumentException("Invalid record type " + type);
|
||||
}
|
||||
|
||||
DnsClientResponse response = await this.Resolve(domain, type).ConfigureAwait(false);
|
||||
List<IPAddress> ips = response.AnswerRecords.Where(r => r.Type == type).Cast<DnsIPAddressResourceRecord>().Select(r => r.IPAddress).ToList();
|
||||
|
||||
return ips.Count == 0 ? throw new DnsQueryException(response, "No matching records") : ips;
|
||||
}
|
||||
|
||||
public async Task<String> Reverse(IPAddress ip) {
|
||||
if(ip == null) {
|
||||
throw new ArgumentNullException(nameof(ip));
|
||||
}
|
||||
|
||||
DnsClientResponse response = await this.Resolve(DnsDomain.PointerName(ip), DnsRecordType.PTR);
|
||||
IDnsResourceRecord ptr = response.AnswerRecords.FirstOrDefault(r => r.Type == DnsRecordType.PTR);
|
||||
|
||||
return ptr == null ? throw new DnsQueryException(response, "No matching records") : ((DnsPointerResourceRecord)ptr).PointerDomainName.ToString();
|
||||
}
|
||||
|
||||
public Task<DnsClientResponse> Resolve(String domain, DnsRecordType type) => this.Resolve(new DnsDomain(domain), type);
|
||||
|
||||
public Task<DnsClientResponse> Resolve(DnsDomain domain, DnsRecordType type) {
|
||||
DnsClientRequest request = this.Create();
|
||||
DnsQuestion question = new DnsQuestion(domain, type);
|
||||
|
||||
request.Questions.Add(question);
|
||||
request.OperationCode = DnsOperationCode.Query;
|
||||
request.RecursionDesired = true;
|
||||
|
||||
return request.Resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,28 @@
|
||||
namespace Swan.Net.Dns
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// An exception thrown when the DNS query fails.
|
||||
/// </summary>
|
||||
/// <seealso cref="Exception" />
|
||||
[Serializable]
|
||||
public class DnsQueryException : Exception
|
||||
{
|
||||
internal DnsQueryException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
internal DnsQueryException(string message, Exception e)
|
||||
: base(message, e)
|
||||
{
|
||||
}
|
||||
|
||||
internal DnsQueryException(DnsClient.IDnsResponse response)
|
||||
: this(response, Format(response))
|
||||
{
|
||||
}
|
||||
|
||||
internal DnsQueryException(DnsClient.IDnsResponse response, string message)
|
||||
: base(message)
|
||||
{
|
||||
Response = response;
|
||||
}
|
||||
|
||||
internal DnsClient.IDnsResponse? Response { get; }
|
||||
|
||||
private static string Format(DnsClient.IDnsResponse response) => $"Invalid response received with code {response.ResponseCode}";
|
||||
}
|
||||
#nullable enable
|
||||
using System;
|
||||
|
||||
namespace Swan.Net.Dns {
|
||||
/// <summary>
|
||||
/// An exception thrown when the DNS query fails.
|
||||
/// </summary>
|
||||
/// <seealso cref="Exception" />
|
||||
[Serializable]
|
||||
public class DnsQueryException : Exception {
|
||||
internal DnsQueryException(String message) : base(message) {
|
||||
}
|
||||
|
||||
internal DnsQueryException(String message, Exception e) : base(message, e) {
|
||||
}
|
||||
|
||||
internal DnsQueryException(DnsClient.IDnsResponse response) : this(response, Format(response)) {
|
||||
}
|
||||
|
||||
internal DnsQueryException(DnsClient.IDnsResponse response, String message) : base(message) => this.Response = response;
|
||||
|
||||
internal DnsClient.IDnsResponse? Response {
|
||||
get;
|
||||
}
|
||||
|
||||
private static String Format(DnsClient.IDnsResponse response) => $"Invalid response received with code {response.ResponseCode}";
|
||||
}
|
||||
}
|
||||
|
||||
+127
-120
@@ -1,123 +1,130 @@
|
||||
namespace Swan.Net.Dns
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Swan.Net.Dns {
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a response from a DNS server.
|
||||
/// </summary>
|
||||
public class DnsQueryResult {
|
||||
private readonly List<DnsRecord> _mAnswerRecords = new List<DnsRecord>();
|
||||
private readonly List<DnsRecord> _mAdditionalRecords = new List<DnsRecord>();
|
||||
private readonly List<DnsRecord> _mAuthorityRecords = new List<DnsRecord>();
|
||||
|
||||
/// <summary>
|
||||
/// Represents a response from a DNS server.
|
||||
/// Initializes a new instance of the <see cref="DnsQueryResult"/> class.
|
||||
/// </summary>
|
||||
public class DnsQueryResult
|
||||
{
|
||||
private readonly List<DnsRecord> _mAnswerRecords = new List<DnsRecord>();
|
||||
private readonly List<DnsRecord> _mAdditionalRecords = new List<DnsRecord>();
|
||||
private readonly List<DnsRecord> _mAuthorityRecords = new List<DnsRecord>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DnsQueryResult"/> class.
|
||||
/// </summary>
|
||||
/// <param name="response">The response.</param>
|
||||
internal DnsQueryResult(DnsClient.IDnsResponse response)
|
||||
: this()
|
||||
{
|
||||
Id = response.Id;
|
||||
IsAuthoritativeServer = response.IsAuthorativeServer;
|
||||
IsRecursionAvailable = response.IsRecursionAvailable;
|
||||
IsTruncated = response.IsTruncated;
|
||||
OperationCode = response.OperationCode;
|
||||
ResponseCode = response.ResponseCode;
|
||||
|
||||
if (response.AnswerRecords != null)
|
||||
{
|
||||
foreach (var record in response.AnswerRecords)
|
||||
AnswerRecords.Add(new DnsRecord(record));
|
||||
}
|
||||
|
||||
if (response.AuthorityRecords != null)
|
||||
{
|
||||
foreach (var record in response.AuthorityRecords)
|
||||
AuthorityRecords.Add(new DnsRecord(record));
|
||||
}
|
||||
|
||||
if (response.AdditionalRecords != null)
|
||||
{
|
||||
foreach (var record in response.AdditionalRecords)
|
||||
AdditionalRecords.Add(new DnsRecord(record));
|
||||
}
|
||||
}
|
||||
|
||||
private DnsQueryResult()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The identifier.
|
||||
/// </value>
|
||||
public int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is authoritative server.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is authoritative server; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsAuthoritativeServer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is truncated.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is truncated; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsTruncated { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is recursion available.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is recursion available; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsRecursionAvailable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the operation code.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The operation code.
|
||||
/// </value>
|
||||
public DnsOperationCode OperationCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response code.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The response code.
|
||||
/// </value>
|
||||
public DnsResponseCode ResponseCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the answer records.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The answer records.
|
||||
/// </value>
|
||||
public IList<DnsRecord> AnswerRecords => _mAnswerRecords;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the additional records.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The additional records.
|
||||
/// </value>
|
||||
public IList<DnsRecord> AdditionalRecords => _mAdditionalRecords;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authority records.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The authority records.
|
||||
/// </value>
|
||||
public IList<DnsRecord> AuthorityRecords => _mAuthorityRecords;
|
||||
}
|
||||
/// <param name="response">The response.</param>
|
||||
internal DnsQueryResult(DnsClient.IDnsResponse response) : this() {
|
||||
this.Id = response.Id;
|
||||
this.IsAuthoritativeServer = response.IsAuthorativeServer;
|
||||
this.IsRecursionAvailable = response.IsRecursionAvailable;
|
||||
this.IsTruncated = response.IsTruncated;
|
||||
this.OperationCode = response.OperationCode;
|
||||
this.ResponseCode = response.ResponseCode;
|
||||
|
||||
if(response.AnswerRecords != null) {
|
||||
foreach(DnsClient.IDnsResourceRecord record in response.AnswerRecords) {
|
||||
this.AnswerRecords.Add(new DnsRecord(record));
|
||||
}
|
||||
}
|
||||
|
||||
if(response.AuthorityRecords != null) {
|
||||
foreach(DnsClient.IDnsResourceRecord record in response.AuthorityRecords) {
|
||||
this.AuthorityRecords.Add(new DnsRecord(record));
|
||||
}
|
||||
}
|
||||
|
||||
if(response.AdditionalRecords != null) {
|
||||
foreach(DnsClient.IDnsResourceRecord record in response.AdditionalRecords) {
|
||||
this.AdditionalRecords.Add(new DnsRecord(record));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DnsQueryResult() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The identifier.
|
||||
/// </value>
|
||||
public System.Int32 Id {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is authoritative server.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is authoritative server; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public System.Boolean IsAuthoritativeServer {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is truncated.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is truncated; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public System.Boolean IsTruncated {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is recursion available.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is recursion available; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public System.Boolean IsRecursionAvailable {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the operation code.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The operation code.
|
||||
/// </value>
|
||||
public DnsOperationCode OperationCode {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response code.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The response code.
|
||||
/// </value>
|
||||
public DnsResponseCode ResponseCode {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the answer records.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The answer records.
|
||||
/// </value>
|
||||
public IList<DnsRecord> AnswerRecords => this._mAnswerRecords;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the additional records.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The additional records.
|
||||
/// </value>
|
||||
public IList<DnsRecord> AdditionalRecords => this._mAdditionalRecords;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authority records.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The authority records.
|
||||
/// </value>
|
||||
public IList<DnsRecord> AuthorityRecords => this._mAuthorityRecords;
|
||||
}
|
||||
}
|
||||
|
||||
+236
-205
@@ -1,208 +1,239 @@
|
||||
namespace Swan.Net.Dns
|
||||
{
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace Swan.Net.Dns {
|
||||
/// <summary>
|
||||
/// Represents a DNS record entry.
|
||||
/// </summary>
|
||||
public class DnsRecord {
|
||||
/// <summary>
|
||||
/// Represents a DNS record entry.
|
||||
/// Initializes a new instance of the <see cref="DnsRecord"/> class.
|
||||
/// </summary>
|
||||
public class DnsRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DnsRecord"/> class.
|
||||
/// </summary>
|
||||
/// <param name="record">The record.</param>
|
||||
internal DnsRecord(DnsClient.IDnsResourceRecord record)
|
||||
: this()
|
||||
{
|
||||
Name = record.Name.ToString();
|
||||
Type = record.Type;
|
||||
Class = record.Class;
|
||||
TimeToLive = record.TimeToLive;
|
||||
Data = record.Data;
|
||||
|
||||
// PTR
|
||||
PointerDomainName = (record as DnsClient.DnsPointerResourceRecord)?.PointerDomainName?.ToString();
|
||||
|
||||
// A
|
||||
IPAddress = (record as DnsClient.DnsIPAddressResourceRecord)?.IPAddress;
|
||||
|
||||
// NS
|
||||
NameServerDomainName = (record as DnsClient.DnsNameServerResourceRecord)?.NSDomainName?.ToString();
|
||||
|
||||
// CNAME
|
||||
CanonicalDomainName = (record as DnsClient.DnsCanonicalNameResourceRecord)?.CanonicalDomainName.ToString();
|
||||
|
||||
// MX
|
||||
MailExchangerDomainName = (record as DnsClient.DnsMailExchangeResourceRecord)?.ExchangeDomainName.ToString();
|
||||
MailExchangerPreference = (record as DnsClient.DnsMailExchangeResourceRecord)?.Preference;
|
||||
|
||||
// SOA
|
||||
SoaMasterDomainName = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.MasterDomainName.ToString();
|
||||
SoaResponsibleDomainName = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.ResponsibleDomainName.ToString();
|
||||
SoaSerialNumber = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.SerialNumber;
|
||||
SoaRefreshInterval = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.RefreshInterval;
|
||||
SoaRetryInterval = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.RetryInterval;
|
||||
SoaExpireInterval = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.ExpireInterval;
|
||||
SoaMinimumTimeToLive = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.MinimumTimeToLive;
|
||||
}
|
||||
|
||||
private DnsRecord()
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name.
|
||||
/// </value>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type.
|
||||
/// </value>
|
||||
public DnsRecordType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the class.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The class.
|
||||
/// </value>
|
||||
public DnsRecordClass Class { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time to live.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The time to live.
|
||||
/// </value>
|
||||
public TimeSpan TimeToLive { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw data of the record.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The data.
|
||||
/// </value>
|
||||
public byte[] Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data text bytes in ASCII encoding.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The data text.
|
||||
/// </value>
|
||||
public string DataText => Data == null ? string.Empty : Encoding.ASCII.GetString(Data);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the pointer domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the pointer domain.
|
||||
/// </value>
|
||||
public string PointerDomainName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ip address.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The ip address.
|
||||
/// </value>
|
||||
public IPAddress IPAddress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the name server domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the name server domain.
|
||||
/// </value>
|
||||
public string NameServerDomainName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the canonical domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the canonical domain.
|
||||
/// </value>
|
||||
public string CanonicalDomainName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mail exchanger preference.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mail exchanger preference.
|
||||
/// </value>
|
||||
public int? MailExchangerPreference { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the mail exchanger domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the mail exchanger domain.
|
||||
/// </value>
|
||||
public string MailExchangerDomainName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the soa master domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the soa master domain.
|
||||
/// </value>
|
||||
public string SoaMasterDomainName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the soa responsible domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the soa responsible domain.
|
||||
/// </value>
|
||||
public string SoaResponsibleDomainName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soa serial number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The soa serial number.
|
||||
/// </value>
|
||||
public long? SoaSerialNumber { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soa refresh interval.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The soa refresh interval.
|
||||
/// </value>
|
||||
public TimeSpan? SoaRefreshInterval { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soa retry interval.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The soa retry interval.
|
||||
/// </value>
|
||||
public TimeSpan? SoaRetryInterval { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soa expire interval.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The soa expire interval.
|
||||
/// </value>
|
||||
public TimeSpan? SoaExpireInterval { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soa minimum time to live.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The soa minimum time to live.
|
||||
/// </value>
|
||||
public TimeSpan? SoaMinimumTimeToLive { get; }
|
||||
}
|
||||
/// <param name="record">The record.</param>
|
||||
internal DnsRecord(DnsClient.IDnsResourceRecord record) : this() {
|
||||
this.Name = record.Name.ToString();
|
||||
this.Type = record.Type;
|
||||
this.Class = record.Class;
|
||||
this.TimeToLive = record.TimeToLive;
|
||||
this.Data = record.Data;
|
||||
|
||||
// PTR
|
||||
this.PointerDomainName = (record as DnsClient.DnsPointerResourceRecord)?.PointerDomainName?.ToString();
|
||||
|
||||
// A
|
||||
this.IPAddress = (record as DnsClient.DnsIPAddressResourceRecord)?.IPAddress;
|
||||
|
||||
// NS
|
||||
this.NameServerDomainName = (record as DnsClient.DnsNameServerResourceRecord)?.NSDomainName?.ToString();
|
||||
|
||||
// CNAME
|
||||
this.CanonicalDomainName = (record as DnsClient.DnsCanonicalNameResourceRecord)?.CanonicalDomainName.ToString();
|
||||
|
||||
// MX
|
||||
this.MailExchangerDomainName = (record as DnsClient.DnsMailExchangeResourceRecord)?.ExchangeDomainName.ToString();
|
||||
this.MailExchangerPreference = (record as DnsClient.DnsMailExchangeResourceRecord)?.Preference;
|
||||
|
||||
// SOA
|
||||
this.SoaMasterDomainName = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.MasterDomainName.ToString();
|
||||
this.SoaResponsibleDomainName = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.ResponsibleDomainName.ToString();
|
||||
this.SoaSerialNumber = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.SerialNumber;
|
||||
this.SoaRefreshInterval = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.RefreshInterval;
|
||||
this.SoaRetryInterval = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.RetryInterval;
|
||||
this.SoaExpireInterval = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.ExpireInterval;
|
||||
this.SoaMinimumTimeToLive = (record as DnsClient.DnsStartOfAuthorityResourceRecord)?.MinimumTimeToLive;
|
||||
}
|
||||
|
||||
private DnsRecord() {
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name.
|
||||
/// </value>
|
||||
public String Name {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type.
|
||||
/// </value>
|
||||
public DnsRecordType Type {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the class.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The class.
|
||||
/// </value>
|
||||
public DnsRecordClass Class {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time to live.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The time to live.
|
||||
/// </value>
|
||||
public TimeSpan TimeToLive {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw data of the record.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The data.
|
||||
/// </value>
|
||||
public Byte[] Data {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data text bytes in ASCII encoding.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The data text.
|
||||
/// </value>
|
||||
public String DataText => this.Data == null ? String.Empty : Encoding.ASCII.GetString(this.Data);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the pointer domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the pointer domain.
|
||||
/// </value>
|
||||
public String PointerDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ip address.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The ip address.
|
||||
/// </value>
|
||||
public IPAddress IPAddress {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the name server domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the name server domain.
|
||||
/// </value>
|
||||
public String NameServerDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the canonical domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the canonical domain.
|
||||
/// </value>
|
||||
public String CanonicalDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mail exchanger preference.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The mail exchanger preference.
|
||||
/// </value>
|
||||
public Int32? MailExchangerPreference {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the mail exchanger domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the mail exchanger domain.
|
||||
/// </value>
|
||||
public String MailExchangerDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the soa master domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the soa master domain.
|
||||
/// </value>
|
||||
public String SoaMasterDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the soa responsible domain.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name of the soa responsible domain.
|
||||
/// </value>
|
||||
public String SoaResponsibleDomainName {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soa serial number.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The soa serial number.
|
||||
/// </value>
|
||||
public Int64? SoaSerialNumber {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soa refresh interval.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The soa refresh interval.
|
||||
/// </value>
|
||||
public TimeSpan? SoaRefreshInterval {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soa retry interval.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The soa retry interval.
|
||||
/// </value>
|
||||
public TimeSpan? SoaRetryInterval {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soa expire interval.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The soa expire interval.
|
||||
/// </value>
|
||||
public TimeSpan? SoaExpireInterval {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soa minimum time to live.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The soa minimum time to live.
|
||||
/// </value>
|
||||
public TimeSpan? SoaMinimumTimeToLive {
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+161
-166
@@ -1,172 +1,167 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
namespace Swan.Net.Dns
|
||||
{
|
||||
namespace Swan.Net.Dns {
|
||||
/// <summary>
|
||||
/// Enumerates the different DNS record types.
|
||||
/// </summary>
|
||||
public enum DnsRecordType {
|
||||
/// <summary>
|
||||
/// Enumerates the different DNS record types.
|
||||
/// </summary>
|
||||
public enum DnsRecordType
|
||||
{
|
||||
/// <summary>
|
||||
/// A records
|
||||
/// </summary>
|
||||
A = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Nameserver records
|
||||
/// </summary>
|
||||
NS = 2,
|
||||
|
||||
/// <summary>
|
||||
/// CNAME records
|
||||
/// </summary>
|
||||
CNAME = 5,
|
||||
|
||||
/// <summary>
|
||||
/// SOA records
|
||||
/// </summary>
|
||||
SOA = 6,
|
||||
|
||||
/// <summary>
|
||||
/// WKS records
|
||||
/// </summary>
|
||||
WKS = 11,
|
||||
|
||||
/// <summary>
|
||||
/// PTR records
|
||||
/// </summary>
|
||||
PTR = 12,
|
||||
|
||||
/// <summary>
|
||||
/// MX records
|
||||
/// </summary>
|
||||
MX = 15,
|
||||
|
||||
/// <summary>
|
||||
/// TXT records
|
||||
/// </summary>
|
||||
TXT = 16,
|
||||
|
||||
/// <summary>
|
||||
/// A records fot IPv6
|
||||
/// </summary>
|
||||
AAAA = 28,
|
||||
|
||||
/// <summary>
|
||||
/// SRV records
|
||||
/// </summary>
|
||||
SRV = 33,
|
||||
|
||||
/// <summary>
|
||||
/// ANY records
|
||||
/// </summary>
|
||||
ANY = 255,
|
||||
}
|
||||
|
||||
/// A records
|
||||
/// </summary>
|
||||
A = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different DNS record classes.
|
||||
/// </summary>
|
||||
public enum DnsRecordClass
|
||||
{
|
||||
/// <summary>
|
||||
/// IN records
|
||||
/// </summary>
|
||||
IN = 1,
|
||||
|
||||
/// <summary>
|
||||
/// ANY records
|
||||
/// </summary>
|
||||
ANY = 255,
|
||||
}
|
||||
|
||||
/// Nameserver records
|
||||
/// </summary>
|
||||
NS = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different DNS operation codes.
|
||||
/// </summary>
|
||||
public enum DnsOperationCode
|
||||
{
|
||||
/// <summary>
|
||||
/// Query operation
|
||||
/// </summary>
|
||||
Query = 0,
|
||||
|
||||
/// <summary>
|
||||
/// IQuery operation
|
||||
/// </summary>
|
||||
IQuery,
|
||||
|
||||
/// <summary>
|
||||
/// Status operation
|
||||
/// </summary>
|
||||
Status,
|
||||
|
||||
/// <summary>
|
||||
/// Notify operation
|
||||
/// </summary>
|
||||
Notify = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Update operation
|
||||
/// </summary>
|
||||
Update,
|
||||
}
|
||||
|
||||
/// CNAME records
|
||||
/// </summary>
|
||||
CNAME = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different DNS query response codes.
|
||||
/// </summary>
|
||||
public enum DnsResponseCode
|
||||
{
|
||||
/// <summary>
|
||||
/// No error
|
||||
/// </summary>
|
||||
NoError = 0,
|
||||
|
||||
/// <summary>
|
||||
/// No error
|
||||
/// </summary>
|
||||
FormatError,
|
||||
|
||||
/// <summary>
|
||||
/// Format error
|
||||
/// </summary>
|
||||
ServerFailure,
|
||||
|
||||
/// <summary>
|
||||
/// Server failure error
|
||||
/// </summary>
|
||||
NameError,
|
||||
|
||||
/// <summary>
|
||||
/// Name error
|
||||
/// </summary>
|
||||
NotImplemented,
|
||||
|
||||
/// <summary>
|
||||
/// Not implemented error
|
||||
/// </summary>
|
||||
Refused,
|
||||
|
||||
/// <summary>
|
||||
/// Refused error
|
||||
/// </summary>
|
||||
YXDomain,
|
||||
|
||||
/// <summary>
|
||||
/// YXRR error
|
||||
/// </summary>
|
||||
YXRRSet,
|
||||
|
||||
/// <summary>
|
||||
/// NXRR Set error
|
||||
/// </summary>
|
||||
NXRRSet,
|
||||
|
||||
/// <summary>
|
||||
/// Not authorized error
|
||||
/// </summary>
|
||||
NotAuth,
|
||||
|
||||
/// <summary>
|
||||
/// Not zone error
|
||||
/// </summary>
|
||||
NotZone,
|
||||
}
|
||||
/// SOA records
|
||||
/// </summary>
|
||||
SOA = 6,
|
||||
|
||||
/// <summary>
|
||||
/// WKS records
|
||||
/// </summary>
|
||||
WKS = 11,
|
||||
|
||||
/// <summary>
|
||||
/// PTR records
|
||||
/// </summary>
|
||||
PTR = 12,
|
||||
|
||||
/// <summary>
|
||||
/// MX records
|
||||
/// </summary>
|
||||
MX = 15,
|
||||
|
||||
/// <summary>
|
||||
/// TXT records
|
||||
/// </summary>
|
||||
TXT = 16,
|
||||
|
||||
/// <summary>
|
||||
/// A records fot IPv6
|
||||
/// </summary>
|
||||
AAAA = 28,
|
||||
|
||||
/// <summary>
|
||||
/// SRV records
|
||||
/// </summary>
|
||||
SRV = 33,
|
||||
|
||||
/// <summary>
|
||||
/// ANY records
|
||||
/// </summary>
|
||||
ANY = 255,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different DNS record classes.
|
||||
/// </summary>
|
||||
public enum DnsRecordClass {
|
||||
/// <summary>
|
||||
/// IN records
|
||||
/// </summary>
|
||||
IN = 1,
|
||||
|
||||
/// <summary>
|
||||
/// ANY records
|
||||
/// </summary>
|
||||
ANY = 255,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different DNS operation codes.
|
||||
/// </summary>
|
||||
public enum DnsOperationCode {
|
||||
/// <summary>
|
||||
/// Query operation
|
||||
/// </summary>
|
||||
Query = 0,
|
||||
|
||||
/// <summary>
|
||||
/// IQuery operation
|
||||
/// </summary>
|
||||
IQuery,
|
||||
|
||||
/// <summary>
|
||||
/// Status operation
|
||||
/// </summary>
|
||||
Status,
|
||||
|
||||
/// <summary>
|
||||
/// Notify operation
|
||||
/// </summary>
|
||||
Notify = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Update operation
|
||||
/// </summary>
|
||||
Update,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the different DNS query response codes.
|
||||
/// </summary>
|
||||
public enum DnsResponseCode {
|
||||
/// <summary>
|
||||
/// No error
|
||||
/// </summary>
|
||||
NoError = 0,
|
||||
|
||||
/// <summary>
|
||||
/// No error
|
||||
/// </summary>
|
||||
FormatError,
|
||||
|
||||
/// <summary>
|
||||
/// Format error
|
||||
/// </summary>
|
||||
ServerFailure,
|
||||
|
||||
/// <summary>
|
||||
/// Server failure error
|
||||
/// </summary>
|
||||
NameError,
|
||||
|
||||
/// <summary>
|
||||
/// Name error
|
||||
/// </summary>
|
||||
NotImplemented,
|
||||
|
||||
/// <summary>
|
||||
/// Not implemented error
|
||||
/// </summary>
|
||||
Refused,
|
||||
|
||||
/// <summary>
|
||||
/// Refused error
|
||||
/// </summary>
|
||||
YXDomain,
|
||||
|
||||
/// <summary>
|
||||
/// YXRR error
|
||||
/// </summary>
|
||||
YXRRSet,
|
||||
|
||||
/// <summary>
|
||||
/// NXRR Set error
|
||||
/// </summary>
|
||||
NXRRSet,
|
||||
|
||||
/// <summary>
|
||||
/// Not authorized error
|
||||
/// </summary>
|
||||
NotAuth,
|
||||
|
||||
/// <summary>
|
||||
/// Not zone error
|
||||
/// </summary>
|
||||
NotZone,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user