Neukonstruktion

This commit is contained in:
Fabian Stamm
2016-11-26 12:11:27 +01:00
parent 1af64ace95
commit aab06c8ea8
106 changed files with 4576 additions and 704 deletions

24
MailServer/MailServer.sln Normal file
View File

@ -0,0 +1,24 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.25920.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MailServer", "MailServer\MailServer.csproj", "{ABB6A3E6-38B6-4D02-AC9C-91FA69CF03BE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D43013F8-933F-4ADC-8943-08E91662A070}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ABB6A3E6-38B6-4D02-AC9C-91FA69CF03BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ABB6A3E6-38B6-4D02-AC9C-91FA69CF03BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ABB6A3E6-38B6-4D02-AC9C-91FA69CF03BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ABB6A3E6-38B6-4D02-AC9C-91FA69CF03BE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,199 @@
////using System;
////using System.Collections.Generic;
////namespace DnsClient
////{
//// /*
//// * Reference RFC6895#section-2.3
//// *
//// RCODE Name Description Reference
//// Decimal
//// Hexadecimal
//// 0 NoError No Error [RFC1035]
//// 1 FormErr Format Error [RFC1035]
//// 2 ServFail Server Failure [RFC1035]
//// 3 NXDomain Non-Existent Domain [RFC1035]
//// 4 NotImp Not Implemented [RFC1035]
//// 5 Refused Query Refused [RFC1035]
//// 6 YXDomain Name Exists when it should not [RFC2136]
//// 7 YXRRSet RR Set Exists when it should not [RFC2136]
//// 8 NXRRSet RR Set that should exist does not [RFC2136]
//// 9 NotAuth Server Not Authoritative for zone [RFC2136]
//// 9 NotAuth Not Authorized [RFC2845]
//// 10 NotZone Name not contained in zone [RFC2136]
//// 11 - 15
//// 0xB - 0xF Unassigned
//// 16 BADVERS Bad OPT Version [RFC6891]
//// 16 BADSIG TSIG Signature Failure [RFC2845]
//// 17 BADKEY Key not recognized [RFC2845]
//// 18 BADTIME Signature out of time window [RFC2845]
//// 19 BADMODE Bad TKEY Mode [RFC2930]
//// 20 BADNAME Duplicate key name [RFC2930]
//// 21 BADALG Algorithm not supported [RFC2930]
//// 22 BADTRUNC Bad Truncation [RFC4635]
//// 23 - 3,840
//// 0x0017 - 0x0F00 Unassigned
//// 3,841 - 4,095
//// 0x0F01 - 0x0FFF Reserved for Private Use
//// 4,096 - 65,534
//// 0x1000 - 0xFFFE Unassigned
//// 65,535
//// 0xFFFF Reserved; can only be allocated by Standards
//// Action.
//// */
//// public enum DnsErrorCode : ushort
//// {
//// NoError = 0,
//// FormErr = 1,
//// ServFail = 2,
//// NXDomain = 3,
//// NotImp = 4,
//// Refused = 5,
//// YXDomain = 6,
//// YXRRSet = 7,
//// NXRRSet = 8,
//// NotAuth = 9,
//// NotZone = 10,
//// BADVERS = 16, // or BADSIG
//// BADKEY = 17,
//// BADTIME = 18,
//// BADMODE = 19,
//// BADNAME = 20,
//// BADALG = 21,
//// BADTRUNC = 22,
//// BADCOOKIE = 23,
//// Unassigned = 666
//// }
//// public static class DnsErrorCodeText
//// {
//// public const string BADALG = "Algorithm not supported";
//// public const string BADCOOKIE = "Bad/missing Server Cookie";
//// public const string BADKEY = "Key not recognized";
//// public const string BADMODE = "Bad TKEY Mode";
//// public const string BADNAME = "Duplicate key name";
//// public const string BADSIG = "TSIG Signature Failure";
//// public const string BADTIME = "Signature out of time window";
//// public const string BADTRUNC = "Bad Truncation";
//// public const string BADVERS = "Bad OPT Version";
//// public const string FormErr = "Format Error";
//// public const string NoError = "No Error";
//// public const string NotAuth = "Server Not Authoritative for zone or Not Authorized";
//// public const string NotImp = "Not Implemented";
//// public const string NotZone = "Name not contained in zone";
//// public const string NXDomain = "Non-Existent Domain";
//// public const string NXRRSet = "RR Set that should exist does not";
//// public const string Refused = "Query Refused";
//// public const string ServFail = "Server Failure";
//// public const string Unassigned = "Unknown Error";
//// public const string YXDomain = "Name Exists when it should not";
//// public const string YXRRSet = "RR Set Exists when it should not";
//// private static readonly Dictionary<DnsErrorCode, string> errors = new Dictionary<DnsErrorCode, string>()
//// {
//// { DnsErrorCode.NoError, DnsErrorCodeText.NoError },
//// { DnsErrorCode.FormErr, DnsErrorCodeText.FormErr },
//// { DnsErrorCode.ServFail, DnsErrorCodeText.ServFail },
//// { DnsErrorCode.NXDomain, DnsErrorCodeText.NXDomain },
//// { DnsErrorCode.NotImp, DnsErrorCodeText.NotImp },
//// { DnsErrorCode.Refused, DnsErrorCodeText.Refused },
//// { DnsErrorCode.YXDomain, DnsErrorCodeText.YXDomain },
//// { DnsErrorCode.YXRRSet, DnsErrorCodeText.YXRRSet },
//// { DnsErrorCode.NXRRSet, DnsErrorCodeText.NXRRSet },
//// { DnsErrorCode.NotAuth, DnsErrorCodeText.NotAuth },
//// { DnsErrorCode.NotZone, DnsErrorCodeText.NotZone },
//// { DnsErrorCode.BADVERS, DnsErrorCodeText.BADVERS },
//// { DnsErrorCode.BADKEY, DnsErrorCodeText.BADKEY },
//// { DnsErrorCode.BADTIME, DnsErrorCodeText.BADTIME },
//// { DnsErrorCode.BADMODE, DnsErrorCodeText.BADMODE },
//// { DnsErrorCode.BADNAME, DnsErrorCodeText.BADNAME },
//// { DnsErrorCode.BADALG, DnsErrorCodeText.BADALG },
//// { DnsErrorCode.BADTRUNC, DnsErrorCodeText.BADTRUNC },
//// { DnsErrorCode.BADCOOKIE, DnsErrorCodeText.BADCOOKIE },
//// };
//// public static string GetErrorText(DnsErrorCode code)
//// {
//// if (!errors.ContainsKey(code))
//// {
//// return Unassigned;
//// }
//// return errors[code];
//// }
//// }
//// public class DnsErrorException : Exception
//// {
//// public DnsErrorCode Code { get; }
//// public string DnsError { get; }
//// /// <summary>
//// /// Creates an instance of <see cref="DnsErrorException"/> with <see cref="DnsErrorCode.Unassigned"/>.
//// /// </summary>
//// public DnsErrorException() : base(DnsErrorCodeText.Unassigned)
//// {
//// Code = DnsErrorCode.Unassigned;
//// DnsError = DnsErrorCodeText.GetErrorText(Code);
//// }
//// /// <summary>
//// /// Creates an instance of <see cref="DnsErrorException"/> with <see cref="DnsErrorCode.Unassigned"/>
//// /// and a custom message.
//// /// </summary>
//// public DnsErrorException(string message) : base(message)
//// {
//// Code = DnsErrorCode.Unassigned;
//// DnsError = DnsErrorCodeText.GetErrorText(Code);
//// }
//// /// <summary>
//// /// Creates an instance of <see cref="DnsErrorException"/> with
//// /// the standard error text for this <paramref name="code"/>.
//// /// </summary>
//// public DnsErrorException(DnsErrorCode code) : base(DnsErrorCodeText.GetErrorText(code))
//// {
//// Code = code;
//// DnsError = DnsErrorCodeText.GetErrorText(Code);
//// }
//// /// <summary>
//// /// Creates an instance of <see cref="DnsErrorException"/> with <see cref="DnsErrorCode.Unassigned"/>
//// /// and a custom message.
//// /// </summary>
//// public DnsErrorException(string message, Exception innerException) : base(message, innerException)
//// {
//// Code = DnsErrorCode.Unassigned;
//// DnsError = DnsErrorCodeText.GetErrorText(Code);
//// }
//// /// <summary>
//// /// Creates an instance of <see cref="DnsErrorException"/> with a custom message
//// /// and the given <paramref name="code"/>.
//// /// </summary>
//// public DnsErrorException(DnsErrorCode code, string message) : base(message)
//// {
//// Code = code;
//// DnsError = DnsErrorCodeText.GetErrorText(Code);
//// }
//// /// <summary>
//// /// Creates an instance of <see cref="DnsErrorException"/> with a custom message
//// /// and the given <paramref name="code"/>.
//// /// </summary>
//// public DnsErrorException(DnsErrorCode code, string message, Exception innerException) : base(message, innerException)
//// {
//// Code = code;
//// DnsError = DnsErrorCodeText.GetErrorText(Code);
//// }
//// }
////}

View File

@ -0,0 +1,47 @@
using System;
namespace DnsClient
{
/* Reference: https://tools.ietf.org/html/rfc6895#section-2
* Response header fields
*
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ID |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
==> |QR| OpCode |AA|TC|RD|RA| Z|AD|CD| RCODE | <==
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| QDCOUNT/ZOCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ANCOUNT/PRCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| NSCOUNT/UPCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ARCOUNT |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* */
/// <summary>
/// The flags of the header's second 16bit value
/// </summary>
public enum DnsHeaderFlag : ushort
{
IsCheckingDisabled = 0x0010,
IsAuthenticData = 0x0020,
FutureUse = 0x0040, // Z bit seems to be ignored now, see https://tools.ietf.org/html/rfc6895#section-2.1
RecursionAvailable = 0x0080,
RecursionDesired = 0x0100,
ResultTruncated = 0x0200,
HasAuthorityAnswer = 0x0400,
HasQuery = 0x8000,
}
public static class DnsHeader
{
public static readonly ushort OPCODE_MASK = 0x7800;
public static readonly ushort OPCODE_SHIFT = 11;
public static readonly ushort RCODE_MASK = 0x000F;
}
}

View File

@ -0,0 +1,101 @@
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using DnsClient.Protocol;
namespace DnsClient
{
public abstract class DnsMessageHandler : IDisposable
{
private bool _disposedValue = false;
public abstract Task<DnsResponseMessage> QueryAsync(IPEndPoint server, DnsRequestMessage request, CancellationToken cancellationToken);
public abstract bool IsTransientException<T>(T exception) where T : Exception;
public virtual byte[] GetRequestData(DnsRequestMessage request)
{
var question = request.Question;
var questionData = question.QueryName.AsBytes();
// 4 more bytes for the type and class
var writer = new DnsDatagramWriter(DnsRequestHeader.HeaderLength + questionData.Length + 4);
writer.SetInt16Network((short)request.Header.Id);
writer.SetUInt16Network(request.Header.RawFlags);
writer.SetInt16Network((short)request.Header.QuestionCount);
// jump to end of header, we didn't write all fields
writer.Index = DnsRequestHeader.HeaderLength;
writer.SetBytes(questionData, questionData.Length);
writer.SetUInt16Network((ushort)question.QuestionType);
writer.SetUInt16Network((ushort)question.QuestionClass);
return writer.Data;
}
public virtual DnsResponseMessage GetResponseMessage(byte[] responseData)
{
var reader = new DnsDatagramReader(responseData);
var factory = new DnsRecordFactory(reader);
var id = reader.ReadUInt16Reverse();
var flags = reader.ReadUInt16Reverse();
var questionCount = reader.ReadUInt16Reverse();
var answerCount = reader.ReadUInt16Reverse();
var nameServerCount = reader.ReadUInt16Reverse();
var additionalCount = reader.ReadUInt16Reverse();
var header = new DnsResponseHeader(id, flags, questionCount, answerCount, additionalCount, nameServerCount);
var response = new DnsResponseMessage(header);
for (int questionIndex = 0; questionIndex < questionCount; questionIndex++)
{
var question = new DnsQuestion(reader.ReadName(), (QueryType)reader.ReadUInt16Reverse(), (QueryClass)reader.ReadUInt16Reverse());
response.AddQuestion(question);
}
for (int answerIndex = 0; answerIndex < answerCount; answerIndex++)
{
var info = factory.ReadRecordInfo();
var record = factory.GetRecord(info);
response.AddAnswer(record);
}
for (int serverIndex = 0; serverIndex < nameServerCount; serverIndex++)
{
var info = factory.ReadRecordInfo();
var record = factory.GetRecord(info);
response.AddAuthority(record);
}
for (int additionalIndex = 0; additionalIndex < additionalCount; additionalIndex++)
{
var info = factory.ReadRecordInfo();
var record = factory.GetRecord(info);
response.AddAdditional(record);
}
return response;
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
}
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
}

View File

@ -0,0 +1,350 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DnsClient
{
public class DnsName : IComparable
{
private const byte ReferenceByte = 0xc0;
private List<string> _labels = new List<string>();
private short _octets = 1;
public bool HasRootLabel => (_labels.Count > 0 && Get(0).Equals(""));
public bool IsEmpty => Size == 0;
public bool IsHostName => !_labels.Any(p => !IsHostNameLabel(p));
public int Octets => _octets;
public int Size => _labels.Where(p => p != "").Count();
/// <summary>
/// Creates an empty <see cref="DnsName"/> instance.
/// </summary>
public DnsName()
{
Add(0, "");
}
/// <summary>
/// Initializes a new instance of <see cref="DnsName"/> by parsing the <paramref name="name"/>.
/// </summary>
/// <param name="name">The input name.</param>
public DnsName(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (name.Length > 0)
{
Parse(name);
}
if (!HasRootLabel) Add(0, "");
}
public static DnsName FromBytes(byte[] data, ref int offset)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (offset > data.Length - 1)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
var result = new DnsName();
// read the length byte for the label, then get the content from offset+1 to length
// proceed till we reach zero length byte.
byte length;
while ((length = data[offset++]) != 0)
{
// respect the reference bit and lookup the name at the given position
// the reader will advance only for the 2 bytes read.
if ((length & ReferenceByte) != 0)
{
var subset = (length & 0x3f) << 8 | data[offset++];
var subName = FromBytes(data, ref subset);
result.Concat(subName);
return result;
}
if (offset + length > data.Length - 1)
{
throw new ArgumentOutOfRangeException(
nameof(data),
$"Found invalid label position {offset - 1} or length {length} in the source data.");
}
var label = Encoding.ASCII.GetString(data, offset, length);
result.Add(1, label);
offset += length;
}
return result;
}
public void Add(int pos, string label)
{
if (label == null)
{
throw new ArgumentNullException(nameof(label));
}
if (pos < 0 || pos > _labels.Count)
{
throw new ArgumentOutOfRangeException(nameof(pos));
}
// Check for empty labels: may have only one, and only at end.
int len = label.Length;
if ((pos > 0 && len == 0) ||
(pos == 0 && HasRootLabel))
{
throw new InvalidOperationException("Empty label must be the last label in a domain name");
}
// Total length must not be larger than 255 characters (including the ending zero).
if (len > 0)
{
if (_octets + len + 1 >= 256)
{
throw new InvalidOperationException("Name too long");
}
_octets += (short)(len + 1);
}
int i = _labels.Count - pos;
VerifyLabel(label);
_labels.Insert(i, label);
}
public byte[] AsBytes()
{
var bytes = new byte[_octets];
var offset = 0;
for (int i = _labels.Count - 1; i >= 0; i--)
{
var label = Get(i);
// should never cause issues as each label's length is limited to 64 chars.
var len = checked((byte)label.Length);
// set the label length byte
bytes[offset++] = len;
// set the label's content
var labelBytes = Encoding.ASCII.GetBytes(label);
Array.ConstrainedCopy(labelBytes, 0, bytes, offset, len);
offset += len;
}
return bytes;
}
public int CompareTo(object obj)
{
if (obj == null)
{
return 1;
}
return ToString().CompareTo(obj.ToString());
}
public void Concat(DnsName other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
foreach (var label in other._labels.Where(p => !string.IsNullOrWhiteSpace(p)))
{
Add(1, label);
}
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
var otherName = obj as DnsName;
if (otherName == null)
{
return false;
}
return CompareTo(otherName) == 0;
}
public string Get(int pos)
{
if (pos < 0 || pos > _labels.Count)
{
throw new ArgumentOutOfRangeException(nameof(pos));
}
return _labels[_labels.Count - pos - 1];
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
public override string ToString()
{
var buf = new StringBuilder();
foreach (var label in _labels)
{
if (buf.Length > 0 || label.Length == 0)
{
buf.Append('.');
}
Escaped(buf, label);
}
var name = buf.ToString();
return name;
}
private static bool IsHostNameChar(char c)
{
return (c == '-' ||
c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z' ||
c >= '0' && c <= '9');
}
private static bool IsHostNameLabel(string label)
{
for (int i = 0; i < label.Length; i++)
{
char c = label.ElementAt(i);
if (!IsHostNameChar(c))
{
return false;
}
}
return !(label.StartsWith("-") || label.EndsWith("-"));
}
private static void VerifyLabel(string label)
{
// http://www.freesoft.org/CIE/RFC/1035/9.htm
// dns name limits are 63octets per label
// (63 letters).(63 letters).(63 letters).(62 letters)
if (label.Length > 63)
{
throw new InvalidOperationException("Label exceeds 63 octets: " + label);
}
// Check for two-byte characters.
for (int i = 0; i < label.Length; i++)
{
char c = label.ElementAt(i);
if ((c & 0xFF00) != 0)
{
throw new InvalidOperationException("Label has two-byte char: " + label);
}
}
}
private void Escaped(StringBuilder buf, string label)
{
for (int i = 0; i < label.Length; i++)
{
char c = label.ElementAt(i);
if (c == '.' || c == '\\')
{
buf.Append('\\');
}
buf.Append(c);
}
}
private char GetEscaped(string domainName, int pos)
{
try
{
// assert (name.charAt(pos) == '\\');
char c1 = domainName.ElementAt(++pos);
if (IsDigit(c1))
{
// sequence is `\DDD'
char c2 = domainName.ElementAt(++pos);
char c3 = domainName.ElementAt(++pos);
if (IsDigit(c2) && IsDigit(c3))
{
return (char)((c1 - '0') * 100 + (c2 - '0') * 10 + (c3 - '0'));
}
else
{
throw new ArgumentException("Invalid escape sequence.", nameof(domainName));
}
}
else
{
return c1;
}
}
catch (ArgumentOutOfRangeException)
{
throw new ArgumentException("Invalid escape sequence.", nameof(domainName));
}
}
private bool IsDigit(char c)
{
return (c >= '0' && c <= '9');
}
private void Parse(string domainName)
{
var label = new StringBuilder();
for (int index = 0; index < domainName.Length; index++)
{
var c = domainName[index];
if (c == '\\')
{
c = GetEscaped(domainName, index++);
if (IsDigit(domainName[index]))
{
index += 2;
}
label.Append(c);
}
else if (c != '.')
{
label.Append(c);
}
else
{
Add(0, label.ToString());
label.Clear();
}
}
if (label.Length > 0)
{
Add(0, label.ToString());
}
}
}
}

View File

@ -0,0 +1,66 @@
using System;
namespace DnsClient
{
/*
*
* Reference: [RFC6895][RFC1035]
0 Query [RFC1035]
1 IQuery (Inverse Query, OBSOLETE) [RFC3425]
2 Status [RFC1035]
3 Unassigned
4 Notify [RFC1996]
5 Update [RFC2136]
6-15 Unassigned
* */
/// <summary>
/// RFCs 1035, 1996, 2136, 3425.
/// Specifies kind of query in this message.
/// This value is set by the originator of a query and copied into the response.
/// </summary>
public enum DnsOpCode : short
{
/// <summary>
/// RFC 1035.
/// A standard query.
/// </summary>
Query,
/// <summary>
/// RFC 3425.
/// An inverse query.
/// </summary>
[Obsolete]
IQuery,
/// <summary>
/// RFC 1035.
/// A server status request.
/// </summary>
Status,
Unassinged3,
/// <summary>
/// RFC 1996.
/// </summary>
Notify,
/// <summary>
/// RFC 2136.
/// </summary>
Update,
Unassinged6,
Unassinged7,
Unassinged8,
Unassinged9,
Unassinged10,
Unassinged11,
Unassinged12,
Unassinged13,
Unassinged14,
Unassinged15,
}
}

View File

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using DnsClient.Protocol;
using DnsClient.Protocol.Record;
namespace DnsClient
{
/// <summary>
/// Immutable version of the <see cref="DnsResponseMessage"/>.
/// </summary>
public class DnsQueryResponse
{
private int? _hashCode;
/// <summary>
/// Gets a list of additional records.
/// </summary>
public IReadOnlyCollection<DnsResourceRecord> Additionals { get; }
/// <summary>
/// Gets a list of all answers, addtional and authority records.
/// </summary>
public IReadOnlyCollection<DnsResourceRecord> AllRecords
{
get
{
return Answers.Concat(Additionals).Concat(Authorities).ToArray();
}
}
/// <summary>
/// Gets a list of answer records.
/// </summary>
public IReadOnlyCollection<DnsResourceRecord> Answers { get; }
/// <summary>
/// Gets a list of authority records.
/// </summary>
public IReadOnlyCollection<DnsResourceRecord> Authorities { get; }
/// <summary>
/// Returns a string value representing the error response code in case an error occured, otherwise empty.
/// </summary>
public string ErrorMessage => HasError ? DnsResponseCodeText.GetErrorText(Header.ResponseCode) : string.Empty;
/// <summary>
/// A flag indicating if the header contains a response codde other than <see cref="DnsResponseCode.NoError"/>.
/// </summary>
public bool HasError => Header?.ResponseCode != DnsResponseCode.NoError;
/// <summary>
/// Gets the header of the response.
/// </summary>
public DnsResponseHeader Header { get; }
/// <summary>
/// Gets the list of questions.
/// </summary>
public IReadOnlyCollection<DnsQuestion> Questions { get; }
/// <summary>
/// Creates a new instace of <see cref="DnsQueryResponse"/>.
/// </summary>
/// <see cref="DnsResponseMessage"/>
public DnsQueryResponse(
DnsResponseHeader header,
IReadOnlyCollection<DnsQuestion> questions,
IReadOnlyCollection<DnsResourceRecord> answers,
IReadOnlyCollection<DnsResourceRecord> additionals,
IReadOnlyCollection<DnsResourceRecord> authorities)
{
if (header == null) throw new ArgumentNullException(nameof(header));
if (questions == null) throw new ArgumentNullException(nameof(questions));
if (answers == null) throw new ArgumentNullException(nameof(answers));
if (additionals == null) throw new ArgumentNullException(nameof(additionals));
if (authorities == null) throw new ArgumentNullException(nameof(authorities));
Header = header;
Questions = questions;
Answers = answers;
Additionals = additionals;
Authorities = authorities;
}
/// <inheritdoc />
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
var response = obj as DnsQueryResponse;
if (response == null)
{
return false;
}
return
Header.ToString().Equals(response.Header.ToString())
&& string.Join("", Questions).Equals(string.Join("", response.Questions))
&& string.Join("", AllRecords).Equals(string.Join("", response.AllRecords));
}
/// <inheritdoc />
public override int GetHashCode()
{
if (!_hashCode.HasValue)
{
_hashCode = (Header.ToString() + string.Join("", Questions) + string.Join("", AllRecords)).GetHashCode();
}
return _hashCode.Value;
}
}
}

View File

@ -0,0 +1,40 @@
using System;
namespace DnsClient
{
public class DnsQuestion
{
public DnsName QueryName { get; }
public QueryClass QuestionClass { get; }
public QueryType QuestionType { get; }
public DnsQuestion(string queryName, QueryType questionType, QueryClass questionClass)
: this(new DnsName(queryName), questionType, questionClass)
{
}
public DnsQuestion(DnsName queryName, QueryType questionType, QueryClass questionClass)
{
if (queryName == null)
{
throw new ArgumentNullException(nameof(queryName));
}
QueryName = queryName;
QuestionType = questionType;
QuestionClass = questionClass;
}
public override string ToString()
{
return ToString(0);
}
public string ToString(int offset = -32)
{
return string.Format("{0,"+offset+"} \t{1} \t{2}", QueryName, QuestionClass, QuestionType);
}
}
}

View File

@ -0,0 +1,75 @@
namespace DnsClient
{
public class DnsRequestHeader
{
public const int HeaderLength = 12;
private ushort _flags = 0;
public ushort RawFlags => _flags;
public DnsHeaderFlag HeaderFlags
{
get
{
return (DnsHeaderFlag)_flags;
}
set
{
_flags &= (ushort)~(DnsHeaderFlag.IsCheckingDisabled);
_flags &= (ushort)~(DnsHeaderFlag.IsAuthenticData);
_flags &= (ushort)~(DnsHeaderFlag.FutureUse);
_flags &= (ushort)~(DnsHeaderFlag.HasQuery);
_flags &= (ushort)~(DnsHeaderFlag.HasAuthorityAnswer);
_flags &= (ushort)~(DnsHeaderFlag.ResultTruncated);
_flags &= (ushort)~(DnsHeaderFlag.RecursionDesired);
_flags &= (ushort)~(DnsHeaderFlag.RecursionAvailable);
_flags |= (ushort)value;
}
}
public int Id { get; set; }
public DnsOpCode OpCode
{
get
{
return (DnsOpCode)((DnsHeader.OPCODE_MASK & _flags) >> DnsHeader.OPCODE_SHIFT);
}
set
{
_flags &= (ushort)~(DnsHeader.OPCODE_MASK);
_flags |= (ushort)(((ushort)value << DnsHeader.OPCODE_SHIFT) & DnsHeader.OPCODE_MASK);
}
}
public int QuestionCount { get; set; }
public bool UseRecursion
{
get { return (HeaderFlags | DnsHeaderFlag.RecursionDesired) != 0; }
set
{
HeaderFlags |= DnsHeaderFlag.RecursionDesired;
}
}
public DnsRequestHeader(int id, int questionCount, DnsOpCode queryKind)
: this(id, questionCount, true, queryKind)
{
}
public DnsRequestHeader(int id, int questionCount, bool useRecursion, DnsOpCode queryKind)
{
Id = id;
QuestionCount = questionCount;
OpCode = queryKind;
UseRecursion = useRecursion;
}
public override string ToString()
{
return $"{Id} - Qs: {QuestionCount} Recursion: {UseRecursion} OpCode: {OpCode}";
}
}
}

View File

@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
namespace DnsClient
{
/*
* Reference RFC6895#section-2.3
*/
// <summary>
/// RFCs 1035, 2136, 2671, 2845, 2930, 4635.
/// </summary>
public enum DnsResponseCode : ushort
{
/// <summary>
/// RFC 1035.
/// No error condition
/// </summary>
NoError = 0,
/// <summary>
/// RFC 1035.
/// Format error. The name server was unable to interpret the query.
/// </summary>
FormatError = 1,
/// <summary>
/// RFC 1035.
/// Server failure. The name server was unable to process this query due to a problem with the name server.
/// </summary>
ServerFailure = 2,
/// <summary>
/// RFC 1035.
/// Name Error. Meaningful only for responses from an authoritative name server,
/// this code signifies that the domain name referenced in the query does not exist.
/// </summary>
NotExistentDomain = 3,
/// <summary>
/// RFC 1035.
/// Not Implemented. The name server does not support the requested kind of query.
/// </summary>
NotImplemented = 4,
/// <summary>
/// RFC 1035.
/// Refused. The name server refuses to perform the specified operation for policy reasons.
/// For example, a name server may not wish to provide the information to the particular requester,
/// or a name server may not wish to perform a particular operation (e.g., zone transfer) for particular data.
/// </summary>
Refused = 5,
/// <summary>
/// RFC 2136.
/// Name Exists when it should not.
/// </summary>
ExistingDomain = 6,
/// <summary>
/// RFC 2136.
/// Resource record set exists when it should not.
/// </summary>
ExistingResourceRecordSet = 7,
/// <summary>
/// RFC 2136.
/// Resource record set that should exist but does not.
/// </summary>
MissingResourceRecordSet = 8,
/// <summary>
/// RFC 2136 / RFC2845
/// Server Not Authoritative for zone / Not Authorized.
/// </summary>
NotAuthorized = 9,
/// <summary>
/// RFC 2136.
/// Name not contained in zone.
/// </summary>
NotZone = 10,
/// <summary>
/// RFCs 2671 / 2845.
/// Bad OPT Version or TSIG Signature Failure.
/// </summary>
BadVersionOrBadSignature = 16,
/// <summary>
/// RFC 2845.
/// Key not recognized.
/// </summary>
BadKey = 17,
/// <summary>
/// RFC 2845.
/// Signature out of time window.
/// </summary>
BadTime = 18,
/// <summary>
/// RFC 2930.
/// Bad TKEY Mode.
/// </summary>
BadMode = 19,
/// <summary>
/// RFC 2930.
/// Duplicate key name.
/// </summary>
BadName = 20,
/// <summary>
/// RFC 2930.
/// Algorithm not supported.
/// </summary>
BadAlgorithm = 21,
/// <summary>
/// RFC 4635.
/// BADTRUNC - Bad Truncation.
/// </summary>
BadTruncation = 22,
/// <summary>
/// RFC 7873
/// Bad/missing Server Cookie
/// </summary>
BadCookie = 23,
/// <summary>
/// Unknown error.
/// </summary>
Unassigned = 666
}
public static class DnsResponseCodeText
{
internal const string BADALG = "Algorithm not supported";
internal const string BADCOOKIE = "Bad/missing Server Cookie";
internal const string BADKEY = "Key not recognized";
internal const string BADMODE = "Bad TKEY Mode";
internal const string BADNAME = "Duplicate key name";
internal const string BADSIG = "TSIG Signature Failure";
internal const string BADTIME = "Signature out of time window";
internal const string BADTRUNC = "Bad Truncation";
internal const string BADVERS = "Bad OPT Version";
internal const string FormErr = "Format Error";
internal const string NoError = "No Error";
internal const string NotAuth = "Server Not Authoritative for zone or Not Authorized";
internal const string NotImp = "Not Implemented";
internal const string NotZone = "Name not contained in zone";
internal const string NXDomain = "Non-Existent Domain";
internal const string NXRRSet = "RR Set that should exist does not";
internal const string Refused = "Query Refused";
internal const string ServFail = "Server Failure";
internal const string Unassigned = "Unknown Error";
internal const string YXDomain = "Name Exists when it should not";
internal const string YXRRSet = "RR Set Exists when it should not";
private static readonly Dictionary<DnsResponseCode, string> errors = new Dictionary<DnsResponseCode, string>()
{
{ DnsResponseCode.NoError, DnsResponseCodeText.NoError },
{ DnsResponseCode.FormatError, DnsResponseCodeText.FormErr },
{ DnsResponseCode.ServerFailure, DnsResponseCodeText.ServFail },
{ DnsResponseCode.NotExistentDomain, DnsResponseCodeText.NXDomain },
{ DnsResponseCode.NotImplemented, DnsResponseCodeText.NotImp },
{ DnsResponseCode.Refused, DnsResponseCodeText.Refused },
{ DnsResponseCode.ExistingDomain, DnsResponseCodeText.YXDomain },
{ DnsResponseCode.ExistingResourceRecordSet, DnsResponseCodeText.YXRRSet },
{ DnsResponseCode.MissingResourceRecordSet, DnsResponseCodeText.NXRRSet },
{ DnsResponseCode.NotAuthorized, DnsResponseCodeText.NotAuth },
{ DnsResponseCode.NotZone, DnsResponseCodeText.NotZone },
{ DnsResponseCode.BadVersionOrBadSignature, DnsResponseCodeText.BADVERS },
{ DnsResponseCode.BadKey, DnsResponseCodeText.BADKEY },
{ DnsResponseCode.BadTime, DnsResponseCodeText.BADTIME },
{ DnsResponseCode.BadMode, DnsResponseCodeText.BADMODE },
{ DnsResponseCode.BadName, DnsResponseCodeText.BADNAME },
{ DnsResponseCode.BadAlgorithm, DnsResponseCodeText.BADALG },
{ DnsResponseCode.BadTruncation, DnsResponseCodeText.BADTRUNC },
{ DnsResponseCode.BadCookie, DnsResponseCodeText.BADCOOKIE },
};
public static string GetErrorText(DnsResponseCode code)
{
if (!errors.ContainsKey(code))
{
return Unassigned;
}
return errors[code];
}
}
public class DnsResponseException : Exception
{
public DnsResponseCode Code { get; }
public string DnsError { get; }
/// <summary>
/// Creates an instance of <see cref="DnsResponseException"/> with <see cref="DnsResponseCode.Unassigned"/>.
/// </summary>
public DnsResponseException() : base(DnsResponseCodeText.Unassigned)
{
Code = DnsResponseCode.Unassigned;
DnsError = DnsResponseCodeText.GetErrorText(Code);
}
/// <summary>
/// Creates an instance of <see cref="DnsResponseException"/> with <see cref="DnsResponseCode.Unassigned"/>
/// and a custom message.
/// </summary>
public DnsResponseException(string message) : base(message)
{
Code = DnsResponseCode.Unassigned;
DnsError = DnsResponseCodeText.GetErrorText(Code);
}
/// <summary>
/// Creates an instance of <see cref="DnsResponseException"/> with
/// the standard error text for this <paramref name="code"/>.
/// </summary>
public DnsResponseException(DnsResponseCode code) : base(DnsResponseCodeText.GetErrorText(code))
{
Code = code;
DnsError = DnsResponseCodeText.GetErrorText(Code);
}
/// <summary>
/// Creates an instance of <see cref="DnsResponseException"/> with <see cref="DnsResponseCode.Unassigned"/>
/// and a custom message.
/// </summary>
public DnsResponseException(string message, Exception innerException) : base(message, innerException)
{
Code = DnsResponseCode.Unassigned;
DnsError = DnsResponseCodeText.GetErrorText(Code);
}
/// <summary>
/// Creates an instance of <see cref="DnsResponseException"/> with a custom message
/// and the given <paramref name="code"/>.
/// </summary>
public DnsResponseException(DnsResponseCode code, string message) : base(message)
{
Code = code;
DnsError = DnsResponseCodeText.GetErrorText(Code);
}
/// <summary>
/// Creates an instance of <see cref="DnsResponseException"/> with a custom message
/// and the given <paramref name="code"/>.
/// </summary>
public DnsResponseException(DnsResponseCode code, string message, Exception innerException) : base(message, innerException)
{
Code = code;
DnsError = DnsResponseCodeText.GetErrorText(Code);
}
}
}

View File

@ -0,0 +1,57 @@
namespace DnsClient
{
public class DnsResponseHeader
{
private readonly ushort _flags = 0;
public int AdditionalCount { get; }
public int AnswerCount { get; }
public bool FutureUse => HasFlag(DnsHeaderFlag.FutureUse);
public bool HasAuthorityAnswer => HasFlag(DnsHeaderFlag.HasAuthorityAnswer);
public DnsHeaderFlag HeaderFlags => (DnsHeaderFlag)_flags;
public int Id { get; }
public bool IsAuthenticData => HasFlag(DnsHeaderFlag.IsAuthenticData);
public bool IsCheckingDisabled => HasFlag(DnsHeaderFlag.IsCheckingDisabled);
public bool HasQuery => HasFlag(DnsHeaderFlag.HasQuery);
public int NameServerCount { get; }
public DnsOpCode OPCode => (DnsOpCode)((DnsHeader.OPCODE_MASK & _flags) >> DnsHeader.OPCODE_SHIFT);
public int QuestionCount { get; }
public bool RecursionAvailable => HasFlag(DnsHeaderFlag.RecursionAvailable);
public DnsResponseCode ResponseCode => (DnsResponseCode)(_flags & DnsHeader.RCODE_MASK);
////ResponseCode {set
////{
//// _flags &= (ushort)~(DnsHeader.RCODE_MASK);
//// _flags |= (ushort)((ushort)value & DnsHeader.RCODE_MASK);
////}}
public bool ResultTruncated => HasFlag(DnsHeaderFlag.ResultTruncated);
public bool RecursionDesired => HasFlag(DnsHeaderFlag.RecursionDesired);
public DnsResponseHeader(int id, ushort flags, int questionCount, int answerCount, int additionalCount, int serverCount)
{
Id = id;
_flags = flags;
QuestionCount = questionCount;
AnswerCount = answerCount;
AdditionalCount = additionalCount;
NameServerCount = serverCount;
}
private bool HasFlag(DnsHeaderFlag flag) => (HeaderFlags & flag) != 0;
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using DnsClient.Protocol;
namespace DnsClient
{
public class DnsUdpMessageHandler : DnsMessageHandler, IDisposable
{
private readonly UdpClient _client = new UdpClient();
private bool _disposedValue = false;
public override bool IsTransientException<T>(T exception)
{
Debug.WriteLine("Check transient {0}.", exception);
if (exception is SocketException) return true;
return false;
}
public override async Task<DnsResponseMessage> QueryAsync(
IPEndPoint server,
DnsRequestMessage request,
CancellationToken cancellationToken)
{
var sw = Stopwatch.StartNew();
//using (var udpClient = new UdpClient())
//{
var data = GetRequestData(request);
await _client.SendAsync(data, data.Length, server);
var result = await _client.ReceiveAsync();
var response = GetResponseMessage(result.Buffer);
if (request.Header.Id != response.Header.Id)
{
throw new DnsResponseException("Header id missmatch.");
}
return response;
//}
}
protected override void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_client.Dispose();
}
_disposedValue = true;
}
base.Dispose(disposing);
}
}
}

View File

@ -0,0 +1,368 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DnsClient.Protocol;
namespace DnsClient
{
public class LookupClient : IDisposable
{
private static readonly TimeSpan s_defaultTimeout = TimeSpan.FromSeconds(5);
private static readonly TimeSpan s_infiniteTimeout = System.Threading.Timeout.InfiniteTimeSpan;
private static readonly TimeSpan s_maxTimeout = TimeSpan.FromMilliseconds(int.MaxValue);
private static ushort _uniqueId = 0;
private readonly ResponseCache _cache = new ResponseCache(true);
private readonly object _endpointLock = new object();
private readonly DnsMessageHandler _messageHandler;
private Queue<EndPointInfo> _endpoints;
private TimeSpan _timeout = s_defaultTimeout;
private bool _disposedValue = false;
/// <summary>
/// Gets the list of configured name servers.
/// </summary>
public IReadOnlyCollection<IPEndPoint> NameServers { get; }
/// <summary>
/// Gets or set a flag indicating if recursion should be enabled for DNS queries.
/// </summary>
public bool Recursion { get; set; } = true;
/// <summary>
/// Gets or sets number of tries to connect to one name server before trying the next one or throwing an exception.
/// </summary>
public int Retries { get; set; } = 5;
/// <summary>
/// Gets or sets a flag indicating if the <see cref="LookupClient"/> should throw an <see cref="DnsResponseException"/>
/// if the returned result contains an error flag other than <see cref="DnsResponseCode.NoError"/>.
/// (The default behavior is <c>False</c>).
/// </summary>
public bool ThrowDnsErrors { get; set; } = false;
/// <summary>
/// Gets or sets timeout in milliseconds.
/// Timeout must be greater than zero and less than <see cref="int.MaxValue"/>.
/// </summary>
public TimeSpan Timeout
{
get { return _timeout; }
set
{
if ((value <= TimeSpan.Zero || value > s_maxTimeout) && value != s_infiniteTimeout)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
_timeout = value;
}
}
/// <summary>
/// Gets or sets a flag indicating if the <see cref="LookupClient"/> should use caching or not.
/// The TTL of cached results is defined by each resource record individually.
/// </summary>
public bool UseCache
{
get
{
return _cache.Enabled;
}
set
{
_cache.Enabled = value;
}
}
/// <summary>
/// Gets or sets a <see cref="TimeSpan"/> which can override the TTL of a resource record in case the
/// TTL of the record is lower than this minimum value.
/// This is useful in cases where the server retruns a zero TTL and the record should be cached for a
/// very short duration anyways.
///
/// This setting gets igonred in case <see cref="UseCache"/> is set to <c>False</c>.
/// </summary>
public TimeSpan? MimimumCacheTimeout
{
get
{
return _cache.MinimumTimout;
}
set
{
_cache.MinimumTimout = value;
}
}
public LookupClient()
: this(NameServer.ResolveNameServers().ToArray())
{
}
public LookupClient(params IPEndPoint[] nameServers)
: this(new DnsUdpMessageHandler(), nameServers)
{
}
public LookupClient(params IPAddress[] nameServers)
: this(
new DnsUdpMessageHandler(),
nameServers.Select(p => new IPEndPoint(p, NameServer.DefaultPort)).ToArray())
{
}
public LookupClient(DnsMessageHandler messageHandler, ICollection<IPEndPoint> nameServers)
{
if (messageHandler == null)
{
throw new ArgumentNullException(nameof(messageHandler));
}
if (nameServers == null || nameServers.Count == 0)
{
throw new ArgumentException("At least one name server must be configured.", nameof(nameServers));
}
NameServers = nameServers.ToArray();
_endpoints = new Queue<EndPointInfo>();
foreach (var server in NameServers)
{
_endpoints.Enqueue(new EndPointInfo(server));
}
_messageHandler = messageHandler;
}
/// <summary>
/// Translates the IPV4 or IPV6 address into an arpa address.
/// </summary>
/// <param name="ip">IP address to get the arpa address form</param>
/// <returns>The mirrored IPV4 or IPV6 arpa address</returns>
public static string GetArpaName(IPAddress ip)
{
var bytes = ip.GetAddressBytes();
Array.Reverse(bytes);
// check IP6
if (ip.AddressFamily == AddressFamily.InterNetworkV6)
{
// reveresed bytes need to be split into 4 bit parts and separated by '.'
var newBytes = bytes
.SelectMany(b => new[] { (b >> 0) & 0xf, (b >> 4) & 0xf })
.Aggregate(new StringBuilder(), (s, b) => s.Append(b.ToString("x")).Append(".")) + "ip6.arpa.";
return newBytes;
}
else if (ip.AddressFamily == AddressFamily.InterNetwork)
{
// else IP4
return string.Join(".", bytes) + ".in-addr.arpa.";
}
throw new InvalidOperationException("Not a valid IP4 or IP6 address.");
}
public Task<DnsQueryResponse> QueryAsync(string query, QueryType queryType)
=> QueryAsync(query, queryType, CancellationToken.None);
public Task<DnsQueryResponse> QueryAsync(string query, QueryType queryType, CancellationToken cancellationToken)
=> QueryAsync(query, queryType, QueryClass.IN, cancellationToken);
public Task<DnsQueryResponse> QueryAsync(string query, QueryType queryType, QueryClass queryClass)
=> QueryAsync(query, queryType, queryClass, CancellationToken.None);
public Task<DnsQueryResponse> QueryAsync(string query, QueryType queryType, QueryClass queryClass, CancellationToken cancellationToken)
=> QueryAsync(new DnsQuestion(query, queryType, queryClass), cancellationToken);
////public Task<DnsQueryResponse> QueryAsync(params DnsQuestion[] questions)
//// => QueryAsync(CancellationToken.None, questions);
private async Task<DnsQueryResponse> QueryAsync(DnsQuestion question, CancellationToken cancellationToken)
{
if (question == null)
{
throw new ArgumentNullException(nameof(question));
}
var head = new DnsRequestHeader(GetNextUniqueId(), 1, Recursion, DnsOpCode.Query);
var request = new DnsRequestMessage(head, question);
var cacheKey = ResponseCache.GetCacheKey(question);
var result = await _cache.GetOrAdd(cacheKey, async () => await ResolveQueryAsync(request, cancellationToken));
return result;
}
public Task<DnsQueryResponse> QueryReverseAsync(IPAddress ipAddress)
=> QueryReverseAsync(ipAddress, CancellationToken.None);
public Task<DnsQueryResponse> QueryReverseAsync(IPAddress ipAddress, CancellationToken cancellationToken)
{
if (ipAddress == null)
{
throw new ArgumentNullException(nameof(ipAddress));
}
var arpa = GetArpaName(ipAddress);
return QueryAsync(arpa, QueryType.PTR, QueryClass.IN, cancellationToken);
}
private static ushort GetNextUniqueId()
{
if (_uniqueId == ushort.MaxValue || _uniqueId == 0)
{
_uniqueId = (ushort)(new Random()).Next(ushort.MaxValue / 2);
}
return _uniqueId++;
}
// TODO: TCP fallback on truncates
// TODO: most popular DNS servers do not support mulitple queries in one packet, therefore, split it into multiple requests?
//private async Task<DnsQueryResponse> QueryAsync(DnsRequestMessage request, CancellationToken cancellationToken)
//{
//}
private async Task<DnsQueryResponse> ResolveQueryAsync(DnsRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
for (int index = 0; index < NameServers.Count; index++)
{
EndPointInfo serverInfo = null;
lock (_endpointLock)
{
while (_endpoints.Count > 0 && serverInfo == null)
{
serverInfo = _endpoints.Dequeue();
if (serverInfo.IsDisabled)
{
serverInfo = null;
}
else
{
// put it back and then use it..
_endpoints.Enqueue(serverInfo);
}
}
if (serverInfo == null)
{
// let's be optimistic and eable them again, maybe they wher offline one for a while
_endpoints.ToList().ForEach(p => p.IsDisabled = false);
continue;
}
}
var tries = 0;
do
{
tries++;
try
{
DnsResponseMessage response;
var resultTask = _messageHandler.QueryAsync(serverInfo.Endpoint, request, cancellationToken);
if (Timeout != s_infiniteTimeout)
{
response = await resultTask.TimeoutAfter(Timeout);
}
response = await resultTask;
var result = response.AsReadonly;
if (ThrowDnsErrors && result.Header.ResponseCode != DnsResponseCode.NoError)
{
throw new DnsResponseException(result.Header.ResponseCode);
}
return result;
}
catch (DnsResponseException)
{
// occurs only if the option to throw dns exceptions is enabled on the lookup client. (see above).
// lets not mess with the stack
throw;
}
catch (TimeoutException)
{
// do nothing... transient if timeoutAfter timed out
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.AddressFamilyNotSupported)
{
// this socket error might indicate the server endpoint is actually bad and should be ignored in future queries.
serverInfo.IsDisabled = true;
Debug.WriteLine($"Disabling name server {serverInfo.Endpoint}.");
break;
}
catch (Exception ex) when (_messageHandler.IsTransientException(ex))
{
}
catch (Exception ex)
{
var agg = ex as AggregateException;
if (agg != null)
{
agg.Handle(e =>
{
if (e is TimeoutException) return true;
if (_messageHandler.IsTransientException(e)) return true;
return false;
});
throw new DnsResponseException("Unhandled exception", agg.InnerException);
}
throw new DnsResponseException("Unhandled exception", ex);
}
finally
{
// do cleanup stuff or logging?
}
} while (tries <= Retries && !cancellationToken.IsCancellationRequested);
}
throw new DnsResponseException($"No connection could be established to any of the following name servers: {string.Join(", ", NameServers)}.");
}
private class EndPointInfo
{
public IPEndPoint Endpoint { get; }
public bool IsDisabled { get; set; }
public EndPointInfo(IPEndPoint endpoint)
{
Endpoint = endpoint;
IsDisabled = false;
}
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_messageHandler.Dispose();
}
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
namespace DnsClient
{
public class NameServer
{
/// <summary>
/// The default DNS server port.
/// </summary>
public const int DefaultPort = 53;
/// <summary>
/// Gets a list of name servers by iterating over the available network interfaces.
/// </summary>
/// <returns>The list of name servers.</returns>
public static ICollection<IPEndPoint> ResolveNameServers()
{
var result = new HashSet<IPEndPoint>();
var adapters = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface networkInterface in
adapters
.Where(p => p.OperationalStatus == OperationalStatus.Up
&& p.NetworkInterfaceType != NetworkInterfaceType.Loopback))
{
foreach (IPAddress dnsAddress in networkInterface
.GetIPProperties()
.DnsAddresses
.Where(i =>
i.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork
|| i.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6))
{
result.Add(new IPEndPoint(dnsAddress, DefaultPort));
}
}
return result.ToArray();
}
}
}

View 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());
}
}
}

View 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);
}
}
}

View 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());
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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();
}
}
}

View 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();
}
}
}

View 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
{
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View File

@ -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>();
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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();
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Linq;
namespace DnsClient
{
/* RFC 1035 (https://tools.ietf.org/html/rfc1035#section-3.2.4)
* 3.2.4. CLASS values
*
CLASS fields appear in resource records. The following CLASS mnemonics
and values are defined:
IN 1 the Internet
CS 2 the CSNET class (Obsolete - used only for examples in
some obsolete RFCs)
CH 3 the CHAOS class
HS 4 Hesiod [Dyer 87]
*/
/// <summary>
/// CLASS fields appear in resource records.
/// </summary>
public enum QueryClass : short
{
/// <summary>
/// The Internet.
/// </summary>
IN = 1,
/// <summary>
/// The CSNET class (Obsolete - used only for examples in some obsolete RFCs).
/// </summary>
CS = 2,
/// <summary>
/// The CHAOS class.
/// </summary>
CH = 3,
/// <summary>
/// Hesiod [Dyer 87].
/// </summary>
HS = 4
}
}

View File

@ -0,0 +1,116 @@
using System;
using System.Linq;
namespace DnsClient
{
/*
* RFC 1035 (https://tools.ietf.org/html/rfc1035#section-3.2.3)
* */
/// <summary>
/// QTYPE fields appear in the question part of a query. QTYPES are a superset of TYPEs, hence all TYPEs are valid QTYPEs.
/// </summary>
public enum QueryType : short
{
/// <summary>
/// A host address [RFC1035].
/// </summary>
A = 1,
/// <summary>
/// An authoritative name server [RFC1035].
/// </summary>
NS = 2,
/// <summary>
/// A mail destination (OBSOLETE - use MX) [RFC1035].
/// </summary>
MD = 3,
/// <summary>
/// A mail forwarder (OBSOLETE - use MX) [RFC1035].
/// </summary>
MF = 4,
/// <summary>
/// The canonical name for an alias [RFC1035].
/// </summary>
CNAME = 5,
/// <summary>
/// Marks the start of a zone of authority [RFC1035].
/// </summary>
SOA = 6,
/// <summary>
/// A mailbox domain name (EXPERIMENTAL) [RFC1035]. TODO:impl
/// </summary>
MB = 7,
/// <summary>
/// A mail group member (EXPERIMENTAL) [RFC1035]. TODO:impl
/// </summary>
MG = 8,
/// <summary>
/// A mail rename domain name (EXPERIMENTAL) [RFC1035]. TODO:impl
/// </summary>
MR = 9,
/// <summary>
/// A null RR (EXPERIMENTAL) [RFC1035]. TODO:impl
/// </summary>
NULL = 10,
/// <summary>
/// A well known service description [RFC1035] TODO:impl
/// </summary>
WKS = 11,
/// <summary>
/// A domain name pointer [RFC1035]
/// </summary>
PTR = 12,
/// <summary>
/// Host information [RFC1035] TODO:impl
/// </summary>
HINFO = 13,
/// <summary>
/// Mailbox or mail list information [RFC1035] TODO:impl
/// </summary>
MINFO = 14,
/// <summary>
/// Mail exchange [RFC1035]
/// </summary>
MX = 15,
/// <summary>
/// Text strings [RFC1035]
/// </summary>
TXT = 16,
/// <summary>
/// A IPV6 host address, [RFC3596]
/// </summary>
AAAA = 28,
/// <summary>
/// Location of services [RFC2782]
/// </summary>
SRV = 33,
/// <summary>
/// RRSIG rfc3755. TODO:impl
/// </summary>
RRSIG = 46,
/// <summary>
/// Generic any query *.
/// </summary>
ANY = 255,
CAA = 257,
}
}

View File

@ -0,0 +1,114 @@
using System;
using System.Linq;
namespace DnsClient
{
/*
* RFC 1035 (https://tools.ietf.org/html/rfc1035#section-3.2.2)
* */
/// <summary>
/// TYPE fields are used in resource records. Note that these types are a subset of QTYPEs.
/// </summary>
public enum ResourceRecordType : short
{
/// <summary>
/// A host address [RFC1035].
/// </summary>
A = 1,
/// <summary>
/// An authoritative name server [RFC1035].
/// </summary>
NS = 2,
/// <summary>
/// A mail destination (OBSOLETE - use MX) [RFC1035].
/// </summary>
MD = 3,
/// <summary>
/// A mail forwarder (OBSOLETE - use MX) [RFC1035].
/// </summary>
MF = 4,
/// <summary>
/// The canonical name for an alias [RFC1035].
/// </summary>
CNAME = 5,
/// <summary>
/// Marks the start of a zone of authority [RFC1035].
/// </summary>
SOA = 6,
/// <summary>
/// A mailbox domain name (EXPERIMENTAL) [RFC1035].
/// </summary>
MB = 7,
/// <summary>
/// A mail group member (EXPERIMENTAL) [RFC1035].
/// </summary>
MG = 8,
/// <summary>
/// A mail rename domain name (EXPERIMENTAL) [RFC1035].
/// </summary>
MR = 9,
/// <summary>
/// A null RR (EXPERIMENTAL) [RFC1035].
/// </summary>
NULL = 10,
/// <summary>
/// A well known service description [RFC1035]
/// </summary>
WKS = 11,
/// <summary>
/// A domain name pointer [RFC1035]
/// </summary>
PTR = 12,
/// <summary>
/// Host information [RFC1035]
/// </summary>
HINFO = 13,
/// <summary>
/// Mailbox or mail list information [RFC1035]
/// </summary>
MINFO = 14,
/// <summary>
/// Mail exchange [RFC1035]
/// </summary>
MX = 15,
/// <summary>
/// Text strings [RFC1035]
/// </summary>
TXT = 16,
/// <summary>
/// A IPV6 host address, [RFC3596]
/// </summary>
AAAA = 28,
/// <summary>
/// Location of services [RFC2782]
/// </summary>
SRV = 33,
/// <summary>
/// RRSIG rfc3755. TODO:impl
/// </summary>
RRSIG = 46,
/// <summary>
/// TODO:impl
/// </summary>
CAA = 257,
}
}

View File

@ -0,0 +1,154 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace DnsClient
{
internal class ResponseCache
{
private static readonly long CleanupInterval = (long)TimeSpan.FromMinutes(10).TotalMilliseconds;
private ConcurrentDictionary<string, ResponseEntry> _cache = new ConcurrentDictionary<string, ResponseEntry>();
private object _cleanupLock = new object();
private bool _cleanupRunning = false;
private int _lastCleanup = 0;
public int Count => _cache.Count;
public bool Enabled { get; set; }
public TimeSpan? MinimumTimout { get; set; }
public ResponseCache(bool enabled = true, TimeSpan? minimumTimout = null)
{
Enabled = enabled;
MinimumTimout = minimumTimout;
}
public static string GetCacheKey(DnsQuestion question)
{
if (question == null)
{
throw new ArgumentNullException(nameof(question));
}
return question.QueryName + ":" + question.QuestionClass + ":" + question.QuestionType;
}
public async Task<DnsQueryResponse> GetOrAdd(string key, Func<Task<DnsQueryResponse>> create)
{
if (create == null)
{
throw new ArgumentNullException(nameof(create));
}
if (!Enabled)
{
return await create();
}
ResponseEntry entry;
if (_cache.TryGetValue(key, out entry))
{
if (entry.IsExpired)
{
_cache.TryRemove(key, out entry);
}
else
{
return entry.Response;
}
}
var record = await create();
// only cache in case the result is valid and does need caching
if (record != null)
{
var newEntry = CreatedEntry(record);
// respecting minimum expiration value which gets evaluated in CreateEntry
if (newEntry.TTL > 0)
{
_cache.TryAdd(key, newEntry);
StartCleanup();
}
}
return record;
}
private static void DoCleanup(ResponseCache cache)
{
cache._cleanupRunning = true;
foreach (var entry in cache._cache)
{
if (entry.Value.IsExpired)
{
ResponseEntry o;
cache._cache.TryRemove(entry.Key, out o);
}
}
cache._cleanupRunning = false;
}
private ResponseEntry CreatedEntry(DnsQueryResponse response)
{
var entry = new ResponseEntry(response);
// minimum timeout
if (MinimumTimout.HasValue && entry.TTL < MinimumTimout.Value.TotalMilliseconds)
{
entry.TTL = (long)MinimumTimout.Value.TotalMilliseconds;
}
return entry;
}
private void StartCleanup()
{
var currentTicks = Environment.TickCount;
if (!_cleanupRunning && _lastCleanup + CleanupInterval < currentTicks)
{
lock (_cleanupLock)
{
if (!_cleanupRunning && _lastCleanup + CleanupInterval < currentTicks)
{
_lastCleanup = currentTicks;
Task.Factory.StartNew(
state => DoCleanup((ResponseCache)state),
this,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
}
}
}
}
private class ResponseEntry
{
public bool IsExpired => Environment.TickCount > TicksCreated + TTL;
public DnsQueryResponse Response { get; set; }
public int TicksCreated { get; }
public long TTL { get; set; }
public ResponseEntry(DnsQueryResponse response)
{
var minTtl = response.AllRecords.Min(p => p?.TimeToLive);
Response = response;
TTL = response.HasError || !minTtl.HasValue ? 0 : (int)minTtl.Value * 1000; // ttl is in second, we calculate in millis
TicksCreated = Environment.TickCount;
}
}
}
}

View File

@ -0,0 +1,35 @@
namespace System.Threading.Tasks
{
internal static class TaskExtensions
{
public static async Task TimeoutAfter(this Task task, TimeSpan timeout)
{
var cts = new CancellationTokenSource();
if (task == await Task.WhenAny(task, Task.Delay((int)timeout.TotalMilliseconds, cts.Token)))
{
cts.Cancel();
await task;
}
else
{
throw new TimeoutException();
}
}
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
{
var cts = new CancellationTokenSource();
if (task == await Task.WhenAny(task, Task.Delay((int)timeout.TotalMilliseconds, cts.Token)))
{
cts.Cancel();
return await task;
}
else
{
throw new TimeoutException();
}
}
}
}

View File

@ -0,0 +1,52 @@
{
"version": "1.0.0-*",
"authors": [ "MichaCo" ],
"packOptions": {
"licenseUrl": "https://github.com/MichaCo/DnsClient.NET/blob/master/LICENSE",
"projectUrl": "https://github.com/MichaCo/DnsClient.NET",
"repository": {
"type": "git",
"url": "https://github.com/MichaCo/DnsClient.NET"
},
"summary": "A simple DNS client written in C#.",
"tags": [ "DNS", "Name Server", "CSharp", ".NET", ".NET Core" ]
},
"buildOptions": {
"allowUnsafe": false,
"languageVersion": "csharp6",
"warningsAsErrors": false,
"xmlDoc": true,
"keyFile": "../../Tools/key.snk"
},
"copyright": "Copyright (c) 2016 MichaConrad",
"configurations": {
"Debug": {
"buildOptions": {
"define": [ "DEBUG" ]
}
},
"Release": {
"buildOptions": {
"define": [ "RELEASE" ],
"optimize": true
}
}
},
"dependencies": {
},
"frameworks": {
"netstandard1.3": {
"dependencies": {
"NETStandard.Library": "1.6.0",
"System.Net.NameResolution": "4.0.0",
"System.Net.NetworkInformation": "4.3.0",
"System.Runtime": "4.3.0"
},
"buildOptions": { "define": [ "XPLAT" ] }
},
"net45": {
"frameworkAssemblies": {
}
}
}
}

View File

@ -0,0 +1,115 @@
using System;
using System.Diagnostics;
using System.Text;
namespace DnDns
{
internal static class DnsHelpers
{
private const long Epoch = 621355968000000000;
internal static byte[] CanonicaliseDnsName(string name, bool lowerCase)
{
if (!name.EndsWith("."))
{
name += ".";
}
if (name == ".")
{
return new byte[1];
}
StringBuilder sb = new StringBuilder();
sb.Append('\0');
for (int i = 0, j = 0; i < name.Length; i++, j++)
{
if (lowerCase)
{
sb.Append(char.ToLower(name[i]));
}
else
{
sb.Append(name[i]);
}
if (name[i] == '.')
{
sb[i - j] = (char) (j & 0xff);
j = -1;
}
}
sb[sb.Length - 1] = '\0';
return Encoding.ASCII.GetBytes(sb.ToString());
}
internal static String DumpArrayToString(byte[] bytes)
{
StringBuilder builder = new StringBuilder();
builder.Append("[");
foreach (byte b in bytes)
{
builder.Append(" ");
builder.Append((sbyte)b);
builder.Append(" ");
}
builder.Append("]");
return builder.ToString();
}
/// <summary>
/// Converts a instance of a <see cref="DateTime"/> class to a 48 bit format time since epoch.
/// Epoch is defined as 1-Jan-70 UTC.
/// </summary>
/// <param name="dateTimeToConvert">The <see cref="DateTime"/> instance to convert to DNS format.</param>
/// <param name="timeHigh">The upper 16 bits of time.</param>
/// <param name="timeLow">The lower 32 bits of the time object.</param>
internal static void ConvertToDnsTime(DateTime dateTimeToConvert, out int timeHigh, out long timeLow)
{
long secondsFromEpoch = (dateTimeToConvert.ToUniversalTime().Ticks - Epoch) / 10000000;
timeHigh = (int)(secondsFromEpoch >> 32);
timeLow = (secondsFromEpoch & 0xFFFFFFFFL);
Console.WriteLine(String.Format("Date: {0}", dateTimeToConvert));
Console.WriteLine(String.Format("secondsFromEpoch: {0}", secondsFromEpoch));
Console.WriteLine(String.Format("timeHigh: {0}", timeHigh));
Console.WriteLine(String.Format("timeLow: {0}", timeLow));
}
/// <summary>
/// Convert from DNS 48 but time format to a <see cref="DateTime"/> instance.
/// </summary>
/// <param name="timeHigh">The upper 16 bits of time.</param>
/// <param name="timeLow">The lower 32 bits of the time object.</param>
/// <returns>The converted date time</returns>
internal static DateTime ConvertFromDnsTime(long timeLow, long timeHigh)
{
long time = (timeHigh << 32) + timeLow;
time = time*10000000;
time += Epoch;
return new DateTime(time);
}
/// <summary>
/// Convert from DNS 48 but time format to a <see cref="DateTime"/> instance.
/// </summary>
/// <param name="dnsTime">The upper 48 bits of time.</param>
/// <returns>The converted date time</returns>
internal static DateTime ConvertFromDnsTime(long dnsTime)
{
dnsTime = dnsTime * 10000000;
dnsTime += Epoch;
return new DateTime(dnsTime);
}
}
}

View File

@ -0,0 +1,94 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
namespace DnDns.Enums
{
/// <summary>
/// RFC 1035:
///
/// 3.2.4. CLASS values
///
/// CLASS fields appear in resource records. The following CLASS mnemonics
/// and values are defined:
///
/// IN 1 the Internet
///
/// CS 2 the CSNET class (Obsolete - used only for examples in
/// some obsolete RFCs)
///
/// CH 3 the CHAOS class
///
/// HS 4 Hesiod [Dyer 87]
///
/// 3.2.5. QCLASS values
///
/// QCLASS fields appear in the question section of a query. QCLASS values
/// are a superset of CLASS values; every CLASS is a valid QCLASS. In
/// addition to CLASS values, the following QCLASSes are defined:
///
/// * 255 any class
/// </summary>
public enum NsClass : byte
{
/// <summary>
/// Cookie?? - NOT IMPLEMENTED
/// </summary>
INVALID = 0,
/// <summary>
/// // Internet (inet), RFC 1035
/// </summary>
INET = 1,
/// <summary>
/// MIT Chaos-net, RFC 1035 - NOT IMPLEMENTED
/// </summary>
CHAOS = 3,
/// <summary>
/// MIT Hesiod, RFC 1035 - NOT IMPLEMENTED
/// </summary>
HS = 4,
/// <summary>
/// RFC 2136 - None
/// prereq sections in update requests -- NOT IMPLEMENTED
/// </summary>
NONE = 254,
/// <summary>
/// Any QCLASS only, Wildcard match, RFC 1035 - IMPLEMENTED for INET only
/// </summary>
ANY = 255
}
}

View File

@ -0,0 +1,298 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
namespace DnDns.Enums
{
// DNS HEADER: http://www.faqs.org/rfcs/rfc1035.html
// 1 1 1 1 1 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | Query Identifier |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// |QR| Opcode |AA|TC|RD|RA| Z|AD|CD| RCODE | <-- The Enums below are combined to create this 16 bit (2 byte) field
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | QDCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ANCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | NSCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ARCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// <summary>
/// FlagMasks are used as a bitmask to isolate bits 16 through 31 of the DNS header to convert
/// them to their appropriate Enum types
/// </summary>
internal enum FlagMasks : ushort
{
QueryResponseMask = 0x8000,
OpCodeMask = 0x7800,
NsFlagMask = 0x07F0,
RCodeMask = 0x000F
}
/// <summary>
/// |QR| - Starts at bit 16 of DNS Header, size: 1 bit
///
/// RFC 1035:
/// A one bit field that specifies whether this message is a
/// query (0), or a response (1).
///
/// </summary>
[Flags()]
public enum QueryResponse : ushort
{
/// <summary>
/// // QR Query or Response [RFC1035] ( 0 = Query )
/// </summary>
Query = 0x0,
/// <summary>
/// // QR Query or Response [RFC1035] ( 1 = Response )
/// </summary>
Response = 0x8000
}
/// <summary>
/// | OpCode | - 4 bits of Dns header, Bit 17 - 20, see RFC 1035
///
/// RFC 1035:
///
/// A four bit field that specifies kind of query in this
/// message. This value is set by the originator of a query
/// and copied into the response.
///
/// The values are:
///
/// 0 a standard query (QUERY)
///
/// 1 an inverse query (IQUERY)
///
/// 2 a server status request (STATUS)
///
/// 3-15 reserved for future
/// </summary>
[Flags()]
public enum OpCode : ushort
{
/// <summary>
/// Standard query
/// [RFC1035] (QUERY)
/// </summary>
QUERY = 0x0000,
/// <summary>
/// Inverse query
/// [RFC1035] (IQUERY)
/// </summary>
IQUERY = 0x0800,
/// <summary>
/// Server status request
/// [RFC1035] (STATUS)
/// </summary>
STATUS = 0x1000,
}
/// <summary>
/// |AA|TC|RD|RA| Z|AD|CD| - 8 bits (1 byte) flag fields
///
/// reference: http://www.networksorcery.com/enp/protocol/dns.htm
/// </summary>
[Flags()]
public enum NsFlags : ushort
{
/// <summary>
/// AA - Authorative Answer [RFC1035] ( 0 = Not authoritative, 1 = Is authoritative )
/// Authoritative Answer - this bit is valid in responses,
/// and specifies that the responding name server is an
/// authority for the domain name in question section.
///
/// Note that the contents of the answer section may have
/// multiple owner names because of aliases. The AA bit
/// corresponds to the name which matches the query name, or
/// the first owner name in the answer section.
/// </summary>
AA = 0x0400,
/// <summary>
/// TC - Truncated Response [RFC1035] ( 0 = Not truncated, 1 = Message truncated )
///
/// TrunCation - specifies that this message was truncated
/// due to length greater than that permitted on the
/// transmission channel.
/// </summary>
TC = 0x0200,
/// <summary>
/// RD - Recursion Desired [RFC1035] ( 0 = Recursion not desired, 1 = Recursion desired )
///
/// Recursion Desired - this bit may be set in a query and
/// is copied into the response. If RD is set, it directs
/// the name server to pursue the query recursively.
/// Recursive query support is optional.
/// </summary>
RD = 0x0100,
/// <summary>
/// RA - Recursion Allowed [RFC1035] ( 0 = Recursive query support not available, 1 = Recursive query support available )
///
/// Recursion Available - this be is set or cleared in a
/// response, and denotes whether recursive query support is
/// available in the name server.
/// </summary>
RA = 0x0080,
/// <summary>
/// AD - Authentic Data [RFC4035] ( Authenticated data. 1 bit ) [NOT IMPLEMENTED]
///
/// Indicates in a response that all data included in the answer and authority
/// sections of the response have been authenticated by the server according to
/// the policies of that server. It should be set only if all data in the response
/// has been cryptographically verified or otherwise meets the server's local security
/// policy.
/// </summary>
AD = 0x0020,
/// <summary>
/// CD - Checking Disabled [RFC4035] ( Checking Disabled. 1 bit ) [NOT IMPLEMENTED]
/// </summary>
CD = 0x0010
}
/// <summary>
/// | RCODE | - 4 bits error codes
///
/// Response code - this 4 bit field is set as part of
/// responses. The values have the following interpretation:
///
/// Fields 6-15 Reserved for future use.
///
/// reference: http://www.networksorcery.com/enp/protocol/dns.htm
/// </summary>
[Flags()]
public enum RCode : ushort
{
/// <summary>
/// No error condition
/// </summary>
NoError = 0,
/// <summary>
/// Format error - The name server was unable to
/// interpret the query.
/// </summary>
FormatError = 1,
/// <summary>
/// Server failure - The name server was unable to process
/// this query due to a problem with the name server.
/// </summary>
ServerFailure = 2,
/// <summary>
/// Name Error - Meaningful only for responses from an
/// authoritative name server, this code signifies that
/// the domain name referenced in the query does not
/// exist.
/// </summary>
NameError = 3,
/// <summary>
/// Not Implemented - The name server does not support
/// the requested kind of query.
/// </summary>
NotImplemented = 4,
/// <summary>
/// Refused - The name server refuses to perform the
/// specified operation for policy reasons. For example,
/// a name server may not wish to provide the information
/// to the particular requester, or a name server may not
/// wish to perform a particular operation (e.g., zone
/// transfer) for particular data.
/// </summary>
Refused = 5,
/// <summary>
/// RFC 2136
/// Name Exists when it should not.
/// </summary>
YXDomain = 6,
/// <summary>
/// RFC 2136
/// RR Set Exists when it should not.
/// </summary>
YXRRSet = 7,
/// <summary>
/// RFC 2136
/// RR Set that should exist does not.
/// </summary>
NXRRSet = 8,
/// <summary>
/// RFC 2136
/// Server Not Authoritative for zone.
/// </summary>
NotAuth = 9,
/// <summary>
/// RFC 2136
/// Name not contained in zone.
/// </summary>
NotZone = 10,
/// <summary>
/// RFC 2671
/// RFC 2845
///
/// BADVERS Bad OPT Version.
/// BADSIG TSIG Signature Failure.
/// </summary>
BADVERS_BADSIG = 16,
/// <summary>
/// RFC 2845
/// Key not recognized.
/// </summary>
BADKEY = 17,
/// <summary>
/// RFC 2845
/// Signature out of time window.
/// </summary>
BADTIME = 18,
/// <summary>
/// RFC 2930
/// Bad TKEY Mode.
/// </summary>
BADMODE = 19,
/// <summary>
/// RFC 2930
/// Duplicate key name.
/// </summary>
BADNAME = 20,
/// <summary>
/// RFC 2930
/// Algorithm not supported.
/// </summary>
BADALG = 21
}
}

View File

@ -0,0 +1,305 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
namespace DnDns.Enums
{
/// <summary>
/// Currently defined type values for resources and queries.
///
/// RFC 1034
///
/// 3.2.2. TYPE values
///
/// TYPE fields are used in resource records. Note that these types are a
/// subset of QTYPEs.
///
/// TYPE value and meaning
///
/// A 1 a host address, Implemented
///
/// NS 2 an authoritative name server, Implemented
///
/// MD 3 a mail destination (Obsolete - use MX), NOT Implemented
///
/// MF 4 a mail forwarder (Obsolete - use MX), NOT Implemented
///
/// CNAME 5 the canonical name for an alias, Implemented
///
/// SOA 6 marks the start of a zone of authority, Implemented
///
/// MB 7 a mailbox domain name (EXPERIMENTAL), Implemented
///
/// MG 8 a mail group member (EXPERIMENTAL), Implemented
///
/// MR 9 a mail rename domain name (EXPERIMENTAL), Implemented
///
/// NULL 10 a null RR (EXPERIMENTAL), NOT IMPLEMENTED
///
/// WKS 11 a well known service description
///
/// PTR 12 a domain name pointer
///
/// HINFO 13 host information
///
/// MINFO 14 mailbox or mail list information
///
/// MX 15 mail exchange
///
/// TXT 16 text strings
///
/// 3.2.3. QTYPE values
///
/// QTYPE fields appear in the question part of a query. QTYPES are a
/// superset of TYPEs, hence all TYPEs are valid QTYPEs. In addition, the
/// following QTYPEs are defined:
///
/// AXFR 252 A request for a transfer of an entire zone
///
/// MAILB 253 A request for mailbox-related records (MB, MG or MR)
///
/// MAILA 254 A request for mail agent RRs (Obsolete - see MX)
///
/// * 255 A request for all records
///
/// </summary>
public enum NsType : uint
{
/// <summary>
/// Invalid
/// </summary>
INVALID = 0,
/// <summary>
/// Host address
/// </summary>
A = 1,
/// <summary>
/// Authoritative server
/// </summary>
NS = 2,
/// <summary>
/// Mail destination - NOT IMPLEMENTED
/// </summary>
MD = 3,
/// <summary>
/// Mail forwarder, NOT IMPLEMENTED
/// </summary>
MF = 4,
/// <summary>
/// Canonical name
/// </summary>
CNAME = 5,
/// <summary>
/// Start of authority zone
/// </summary>
SOA = 6,
// Mailbox domain name
MB = 7,
/// <summary>
/// Mail group member
/// </summary>
MG = 8,
/// <summary>
/// Mail rename name
/// </summary>
MR = 9,
/// <summary>
/// Null resource record
/// </summary>
NULL = 10,
/// <summary>
/// Well known service
/// </summary>
WKS = 11,
/// <summary>
/// Domain name pointer
/// </summary>
PTR = 12,
/// <summary>
/// Host information
/// </summary>
HINFO = 13,
/// <summary>
/// Mailbox information
/// </summary>
MINFO = 14,
/// <summary>
/// Mail routing information
/// </summary>
MX = 15,
/// <summary>
/// Text strings, RFC 1464
/// </summary>
TXT = 16,
/// <summary>
/// Responsible person, RFC 1183, Implemented
/// </summary>
RP = 17,
/// <summary>
/// AFS cell database, RFC 1183, Implemented
/// </summary>
AFSDB = 18,
/// <summary>
/// X_25 calling address, RFC 1183, Implemented
/// </summary>
X25 = 19,
/// <summary>
/// ISDN calling address, RFC 1183, Implemented
/// </summary>
ISDN = 20,
/// <summary>
/// Router, RFC 1183, Implemented
/// </summary>
RT = 21,
/// <summary>
/// NSAP address, RFC 1706
/// </summary>
NSAP = 22,
/// <summary>
/// Reverse NSAP lookup - deprecated by PTR ?
/// </summary>
NSAP_PTR = 23,
/// <summary>
/// Security signature, RFC 2535
/// </summary>
SIG = 24,
/// <summary>
/// Security key, RFC 2535
/// </summary>
KEY = 25,
/// <summary>
/// X.400 mail mapping, RFC ?
/// </summary>
PX = 26,
/// <summary>
/// Geographical position - withdrawn, RFC 1712
/// </summary>
GPOS = 27,
/// <summary>
/// Ip6 Address, RFC 1886 -- Implemented
/// </summary>
AAAA = 28,
/// <summary>
/// Location Information, RFC 1876, Implemented
/// </summary>
LOC = 29,
/// <summary>
/// Next domain (security), RFC 2065
/// </summary>
NXT = 30,
/// <summary>
/// Endpoint identifier,RFC ?
/// </summary>
EID = 31,
/// <summary>
/// Nimrod Locator, RFC ?
/// </summary>
NIMLOC = 32,
/// <summary>
/// Server Record, RFC 2052, Implemented
/// </summary>
SRV = 33,
/// <summary>
/// ATM Address, RFC ?, Implemented
/// </summary>
ATMA = 34,
/// <summary>
/// Naming Authority PoinTeR, RFC 2915
/// </summary>
MAPTR = 35,
/// <summary>
/// Key Exchange, RFC 2230
/// </summary>
KX = 36,
/// <summary>
/// Certification record, RFC 2538
/// </summary>
CERT = 37,
/// <summary>
/// IPv6 address (deprecates AAAA), RFC 3226
/// </summary>
A6 = 38,
/// <summary>
/// Non-terminal DNAME (for IPv6), RFC 2874
/// </summary>
DNAME = 39,
/// <summary>
/// Kitchen sink (experimentatl), RFC ?
/// </summary>
SINK = 40,
/// <summary>
/// EDNS0 option (meta-RR), RFC 2671
/// </summary>
OPT = 41,
/// <summary>
/// Transaction key, RFC 2930
/// </summary>
TKEY = 249,
/// <summary>
/// Transaction signature, RFC 2845
/// </summary>
TSIG = 250,
/// <summary>
/// Incremental zone transfer, RFC 1995
/// </summary>
IXFR = 251,
/// <summary>
/// Transfer zone of authority, RFC 1035
/// </summary>
AXFR = 252,
/// <summary>
/// Transfer mailbox records, RFC 1035
/// </summary>
MAILB = 253,
/// <summary>
/// Transfer mail agent records, RFC 1035
/// </summary>
MAILA = 254,
/// <summary>
/// All of the above, RFC 1035
/// </summary>
ANY = 255,
/// <summary>
/// DNSSEC Trust Authorities
/// </summary>
DNSSECTrustAuthorities = 32768,
/// <summary>
/// DNSSEC Lookaside Validation, RFC4431
/// </summary>
DNSSECLookasideValidation = 32769
}
}

View File

@ -0,0 +1,51 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
namespace DnDns.Enums
{
/// <summary>
/// Defines Well Known TCP Ports for Services
/// </summary>
public enum TcpServices : short
{
/// <summary>
/// Domain Name Server Port
/// </summary>
Domain = 53
}
}

View File

@ -0,0 +1,51 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
namespace DnDns.Enums
{
/// <summary>
/// Defines Well Known UDP Ports for Services
/// </summary>
public enum UdpServices : short
{
/// <summary>
/// Domain Name Server Protocol Port
/// </summary>
Domain = 53
}
}

View File

@ -0,0 +1,287 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using DnDns.Enums;
using DnDns.Records;
namespace DnDns.Query
{
/// <summary>
/// DnsQueryBase maintains the common state of DNS Queries (both responses and requests)
/// </summary>
public abstract class DnsQueryBase
{
#region Fields
// RFC 1034
//
// 4.1.1. Header section format
//
// 1 1 1 1 1 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ID |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | QDCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ANCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | NSCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ARCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// <summary>
/// ID - A 16 bit identifier. This identifier is copied
/// the corresponding reply and can be used by the requester
/// to match up replies to outstanding queries.
/// </summary>
protected ushort _transactionId;
/// <summary>
/// _flags will store a combination of the enums that make up the 16 bits after the
/// TransactionID in the DNS protocol header
/// </summary>
protected ushort _flags;
/// <summary>
/// A one bit field that specifies whether this message is a
/// query (0), or a response (1).
/// </summary>
protected QueryResponse _queryResponse;
/// <summary>
/// OPCODE - A four bit field that specifies kind of query in this
/// message. This value is set by the originator of a query
/// and copied into the response.
/// </summary>
protected OpCode _opCode;
/// <summary>
/// - A combination of flag fields in the DNS header (|AA|TC|RD|RA|)
/// </summary>
protected NsFlags _nsFlags;
/// <summary>
/// Response code - this 4 bit field is set as part of
/// responses only.
/// </summary>
protected RCode _rCode;
/// <summary>
/// QDCOUNT - an unsigned 16 bit integer specifying the number of
/// entries in the question section.
/// </summary>
protected ushort _questions;
/// <summary>
/// ANCOUNT - an unsigned 16 bit integer specifying the number of
/// resource records in the answer section.
/// </summary>
protected ushort _answerRRs;
/// <summary>
/// NSCOUNT - an unsigned 16 bit integer specifying the number of name
/// server resource records in the authority records
/// section.
/// </summary>
protected ushort _authorityRRs;
// RFC 1034
// 4.1.2. Question section format
// 1 1 1 1 1 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | |
// / QNAME /
// / /
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | QTYPE |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | QCLASS |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// <summary>
/// QNAME - a domain name represented as a sequence of labels, where
/// each label consists of a length octet followed by that
/// number of octets. The domain name terminates with the
/// zero length octet for the null label of the root. Note
/// that this field may be an odd number of octets; no
/// padding is used
/// </summary>
protected string _name;
/// <summary>
/// QTYPE - a two octet code which specifies the type of the query.
/// The values for this field include all codes valid for a
/// TYPE field, together with some more general codes which
/// can match more than one type of RR.
/// </summary>
protected NsType _nsType;
/// <summary>
/// QCLASS - a two octet code that specifies the class of the query.
/// For example, the QCLASS field is IN for the Internet.
/// </summary>
protected NsClass _nsClass;
/// <summary>
/// The additional records for the DNS Query
/// </summary>
protected List<IDnsRecord> _additionalRecords = new List<IDnsRecord>();
#endregion Fields
#region Properties
/// ID - A 16 bit identifier. This identifier is copied
/// the corresponding reply and can be used by the requester
/// to match up replies to outstanding queries.
/// </summary>
public ushort TransactionID
{
get { return _transactionId; }
}
/// <summary>
/// A one bit field that specifies whether this message is a
/// query (0), or a response (1).
/// </summary>
public QueryResponse QueryResponse
{
get { return _queryResponse; }
}
/// <summary>
/// OPCODE - A four bit field that specifies kind of query in this
/// message. This value is set by the originator of a query
/// and copied into the response.
/// </summary>
public OpCode OpCode
{
get { return _opCode; }
}
/// <summary>
/// NsFlags - A combination of flag fields in the DNS header (|AA|TC|RD|RA|)
/// </summary>
public NsFlags NsFlags
{
get { return _nsFlags; }
}
/// <summary>
/// Response code - this 4 bit field is set as part of
/// responses only.
/// </summary>
public RCode RCode
{
get { return _rCode; }
}
/// <summary>
/// QDCOUNT - an unsigned 16 bit integer specifying the number of
/// entries in the question section.
/// </summary>
public ushort Questions
{
get { return _questions; }
}
/// <summary>
/// ANCOUNT - an unsigned 16 bit integer specifying the number of
/// resource records in the answer section.
/// </summary>
public ushort AnswerRRs
{
get { return _answerRRs; }
}
/// <summary>
/// NSCOUNT - an unsigned 16 bit integer specifying the number of name
/// server resource records in the authority records
/// section.
/// </summary>
public ushort AuthorityRRs
{
get { return _authorityRRs; }
}
/// <summary>
/// ARCOUNT - an unsigned 16 bit integer specifying the number of
/// resource records in the additional records section.
/// </summary>
public ushort AdditionalRRs
{
get { return (ushort) _additionalRecords.Count; }
}
/// <summary>
/// QNAME - a domain name represented as a sequence of labels, where
/// each label consists of a length octet followed by that
/// number of octets. The domain name terminates with the
/// zero length octet for the null label of the root. Note
/// that this field may be an odd number of octets; no
/// padding is used
/// </summary>
public string Name
{
get { return _name; }
set { _name = value; }
}
/// <summary>
/// QTYPE - a two octet code which specifies the type of the query.
/// The values for this field include all codes valid for a
/// TYPE field, together with some more general codes which
/// can match more than one type of RR.
/// </summary>
public NsType NsType
{
get { return _nsType; }
set { _nsType = value; }
}
/// <summary>
/// QCLASS - a two octet code that specifies the class of the query.
/// For example, the QCLASS field is IN for the Internet.
/// </summary>
public NsClass NsClass
{
get { return _nsClass; }
set { _nsClass = value; }
}
public List<IDnsRecord> AdditionalRRecords
{
get { return _additionalRecords; }
}
#endregion
}
}

View File

@ -0,0 +1,385 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Text;
using DnDns.Enums;
using DnDns.Records;
using DnDns.Security;
using System.Threading.Tasks;
namespace DnDns.Query
{
/// <summary>
/// Summary description for DnsQueryRequest.
/// </summary>
public class DnsQueryRequest : DnsQueryBase
{
private static Random r = new Random();
private int _bytesSent = 0;
private int _socketTimeout = 5000;
/// <summary>
/// The number of bytes sent to query the DNS Server.
/// </summary>
public int BytesSent
{
get { return _bytesSent; }
}
/// <summary>
/// Gets or sets the amount of time in milliseconds that a DnsQueryRequest will wait to receive data once a read operation is initiated.
/// Defauts to 5 seconds (5000 ms)
/// </summary>
public int Timeout
{
get { return _socketTimeout; }
set { _socketTimeout = value; }
}
#region Constructors
public DnsQueryRequest()
{
// Construct the class with some defaults
_transactionId = (ushort) r.Next();
_flags = 0;
_queryResponse = QueryResponse.Query;
this._opCode = OpCode.QUERY;
// Recursion Desired
this._nsFlags = NsFlags.RD;
this._questions = 1;
}
#endregion Constructors
private byte[] BuildQuery(string host)
{
string newHost;
int newLocation = 0;
int oldLocation = 0;
MemoryStream ms = new MemoryStream();
host = host.Trim();
// decide how to build this query based on type
switch (_nsType)
{
case NsType.PTR:
IPAddress queryIP = IPAddress.Parse(host);
// pointer should be translated as follows
// 209.115.22.3 -> 3.22.115.209.in-addr.arpa
char[] ipDelim = new char[] {'.'};
string[] s = host.Split(ipDelim,4);
newHost = String.Format("{0}.{1}.{2}.{3}.in-addr.arpa", s[3], s[2], s[1], s[0]);
break;
default:
newHost = host;
break;
}
// Package up the host
while(oldLocation < newHost.Length)
{
newLocation = newHost.IndexOf(".", oldLocation);
if (newLocation == -1) newLocation = newHost.Length;
byte subDomainLength = (byte)(newLocation - oldLocation);
char[] sub = newHost.Substring(oldLocation, subDomainLength).ToCharArray();
ms.WriteByte(subDomainLength);
ms.Write(Encoding.ASCII.GetBytes(sub, 0, sub.Length), 0, sub.Length);
oldLocation = newLocation + 1;
}
// Terminate the domain name w/ a 0x00.
ms.WriteByte(0x00);
return ms.ToArray();
}
/// <summary>
///
/// </summary>
/// <param name="host"></param>
/// <param name="queryType"></param>
/// <param name="queryClass"></param>
/// <param name="protocol"></param>
/// <returns></returns>
public async Task<DnsQueryResponse> ResolveAsync(string host, NsType queryType, NsClass queryClass, ProtocolType protocol)
{
return await ResolveAsync(host, queryType, queryClass, protocol, null);
}
public async Task<DnsQueryResponse> ResolveAsync(string host, NsType queryType, NsClass queryClass, ProtocolType protocol, TsigMessageSecurityProvider provider)
{
string dnsServer = string.Empty;
dnsServer = "8.8.8.8";
/*// Test for Unix/Linux OS
if (Tools.IsPlatformLinuxUnix())
{
dnsServer = Tools.DiscoverUnixDnsServerAddress();
}
else
{
IPAddressCollection dnsServerCollection = Tools.DiscoverDnsServerAddresses();
if (dnsServerCollection.Count == 0)
throw new Exception("Couldn't detect local DNS Server.");
dnsServer = dnsServerCollection[0].ToString();
}
if (String.IsNullOrEmpty(dnsServer))
throw new Exception("Couldn't detect local DNS Server.");
*/
return await ResolveAsync(dnsServer, host, queryType, queryClass, protocol, provider);
}
/// <summary>
///
/// </summary>
/// <param name="dnsServer"></param>
/// <param name="host"></param>
/// <param name="queryType"></param>
/// <param name="queryClass"></param>
/// <param name="protocol"></param>
/// <param name="messageSecurityProvider">The instance of the message security provider to use to secure the DNS request.</param>
/// <returns>A <see cref="T:DnDns.Net.Dns.DnsQueryResponse"></see> instance that contains the Dns Answer for the request query.</returns>
/// <PermissionSet>
/// <IPermission class="System.Net.DnsPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" />
/// </PermissionSet>
public async Task<DnsQueryResponse> ResolveAsync(string dnsServer, string host, NsType queryType, NsClass queryClass, ProtocolType protocol, IMessageSecurityProvider messageSecurityProvider)
{
byte[] bDnsQuery = this.BuildDnsRequest(host, queryType, queryClass, protocol, messageSecurityProvider);
// Connect to DNS server and get the record for the current server.
IPHostEntry ipe = await Dns.GetHostEntryAsync(dnsServer);
IPAddress ipa = ipe.AddressList[0];
IPEndPoint ipep = new IPEndPoint(ipa, (int)UdpServices.Domain);
byte[] recvBytes = null;
switch (protocol)
{
case ProtocolType.Tcp:
{
recvBytes = ResolveTcp(bDnsQuery, ipep);
break;
}
case ProtocolType.Udp:
{
recvBytes = ResolveUdp(bDnsQuery, ipep);
break;
}
default:
{
throw new InvalidOperationException("Invalid Protocol: " + protocol);
}
}
//Console.Assert(recvBytes != null, "Failed to retrieve data from the remote DNS server.");
if(recvBytes == null)
{
Logging.AddLogMessage(Logging.LoggingType.ERROR, "Failed to retrieve data from the remote DNS server.");
}
DnsQueryResponse dnsQR = new DnsQueryResponse();
dnsQR.ParseResponse(recvBytes, protocol);
return dnsQR;
}
private byte[] ResolveUdp(byte[] bDnsQuery, IPEndPoint ipep)
{
// UDP messages, data size = 512 octets or less
//UdpClient udpClient = new UdpClient();
Socket udp = new Socket(SocketType.Dgram, ProtocolType.Udp);
byte[] recvBytes = null;
try
{
udp.ReceiveTimeout = _socketTimeout;
//udpClient.Client.ReceiveTimeout = _socketTimeout;
//udpClient.ConnectAsync(ipep);
udp.Connect(ipep);
//udpClient.Send(bDnsQuery, bDnsQuery.Length);
udp.Send(bDnsQuery);
//recvBytes = udpClient.Receive(ref ipep);
recvBytes = new byte[udp.ReceiveBufferSize];
udp.Receive(recvBytes);
}
finally
{
//udpClient.Close();
}
return recvBytes;
}
private static byte[] ResolveTcp(byte[] bDnsQuery, IPEndPoint ipep)
{
Socket tcp = new Socket(SocketType.Stream, ProtocolType.Tcp);
//TcpClient tcpClient = new TcpClient();
byte[] recvBytes = null;
try
{
tcp.Connect(ipep);
//tcpClient.ConnectAsync(ipep);
//NetworkStream ns = tcp.Receive
//NetworkStream netStream = tcpClient.GetStream();
//BinaryReader netReader = new System.IO.BinaryReader(netStream);
//netStream.Write(bDnsQuery, 0, bDnsQuery.Length);
tcp.Send(bDnsQuery);
// wait until data is avail
//while (!netStream.DataAvailable) ;
while (tcp.Available < 1) ;
//if (tcpClient.Connected && netStream.DataAvailable)
if (tcp.Connected && tcp.Available > 1)
{
// Read first two bytes to find out the length of the response
byte[] bLen = new byte[2];
// NOTE: The order of the next two lines matter. Do not reorder
// Array indexes are also intentionally reversed
tcp.Receive(bLen, bLen.Length, SocketFlags.None);
//bLen[1] = (byte)netStream.ReadByte();
//bLen[0] = (byte)netStream.ReadByte();
UInt16 length = BitConverter.ToUInt16(bLen, 0);
recvBytes = new byte[length];
tcp.Receive(recvBytes, length, SocketFlags.None);
//netStream.Read(recvBytes, 0, length);
}
} catch (Exception e) {
Logging.AddException(e);
}
return recvBytes;
}
private byte[] BuildDnsRequest(string host, NsType queryType, NsClass queryClass, ProtocolType protocol, IMessageSecurityProvider messageSecurityProvider)
{
// Combind the NsFlags with our constant flags
ushort flags = (ushort)((ushort)_queryResponse | (ushort)_opCode | (ushort)_nsFlags);
this._flags = flags;
//NOTE: This limits the librarys ablity to issue multiple queries per request.
this._nsType = queryType;
this._nsClass = queryClass;
this._name = host;
if(messageSecurityProvider != null)
{
messageSecurityProvider.SecureMessage(this);
}
byte[] bDnsQuery = GetMessageBytes();
// Add two byte prefix that contains the packet length per RFC 1035 section 4.2.2
if (protocol == ProtocolType.Tcp)
{
// 4.2.2. TCP usageMessages sent over TCP connections use server port 53 (decimal).
// The message is prefixed with a two byte length field which gives the message
// length, excluding the two byte length field. This length field allows the
// low-level processing to assemble a complete message before beginning to parse
// it.
int len = bDnsQuery.Length;
Array.Resize<byte>(ref bDnsQuery, len + 2);
Array.Copy(bDnsQuery, 0, bDnsQuery, 2, len);
bDnsQuery[0] = (byte)((len >> 8) & 0xFF);
bDnsQuery[1] = (byte)((len & 0xFF));
}
return bDnsQuery;
}
internal byte[] GetMessageBytes()
{
MemoryStream memoryStream = new MemoryStream();
byte[] data = new byte[2];
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder(_transactionId) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder(_flags) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder(_questions) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder(_answerRRs) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder(_authorityRRs) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder(_additionalRecords.Count) >> 16));
memoryStream.Write(data, 0, data.Length);
data = DnsHelpers.CanonicaliseDnsName(_name, false);
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder((ushort)_nsType) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder((ushort)_nsClass) >> 16));
memoryStream.Write(data, 0, data.Length);
foreach (IDnsRecord dnsRecord in AdditionalRRecords)
{
data = dnsRecord.GetMessageBytes();
memoryStream.Write(data, 0, data.Length);
}
Logging.AddLogMessage(Logging.LoggingType.INFO, String.Format("The message bytes: {0}", DnsHelpers.DumpArrayToString(memoryStream.ToArray())));
return memoryStream.ToArray();
}
}
}

View File

@ -0,0 +1,168 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using DnDns.Records;
using DnDns.Enums;
namespace DnDns.Query
{
/// <summary>
/// Summary description for DnsQueryResponse.
/// </summary>
public class DnsQueryResponse : DnsQueryBase
{
#region Fields
private DnsQueryRequest _queryRequest = new DnsQueryRequest();
private IDnsRecord[] _answers;
private IDnsRecord[] _authoritiveNameServers;
private int _bytesReceived = 0;
#endregion Fields
#region properties
public DnsQueryRequest QueryRequest
{
get { return _queryRequest; }
}
public IDnsRecord[] Answers
{
get { return _answers; }
}
public IDnsRecord[] AuthoritiveNameServers
{
get { return _authoritiveNameServers; }
}
public int BytesReceived
{
get { return _bytesReceived; }
}
#endregion
/// <summary>
///
/// </summary>
public DnsQueryResponse()
{
}
private DnsQueryRequest ParseQuery(ref MemoryStream ms)
{
DnsQueryRequest queryRequest = new DnsQueryRequest();
// Read name
queryRequest.Name = DnsRecordBase.ParseName(ref ms);
return queryRequest;
}
internal void ParseResponse(byte[] recvBytes, ProtocolType protocol)
{
MemoryStream memoryStream = new MemoryStream(recvBytes);
byte[] flagBytes = new byte[2];
byte[] transactionId = new byte[2];
byte[] questions = new byte[2];
byte[] answerRRs = new byte[2];
byte[] authorityRRs = new byte[2];
byte[] additionalRRCountBytes = new byte[2];
byte[] nsType = new byte[2];
byte[] nsClass = new byte[2];
this._bytesReceived = recvBytes.Length;
// Parse DNS Response
memoryStream.Read(transactionId, 0, 2);
memoryStream.Read(flagBytes, 0, 2);
memoryStream.Read(questions, 0, 2);
memoryStream.Read(answerRRs, 0, 2);
memoryStream.Read(authorityRRs, 0, 2);
memoryStream.Read(additionalRRCountBytes, 0, 2);
// Parse Header
_transactionId = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(transactionId, 0));
_flags = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(flagBytes, 0));
_queryResponse = (QueryResponse)(_flags & (ushort)FlagMasks.QueryResponseMask);
_opCode = (OpCode)(_flags & (ushort)FlagMasks.OpCodeMask);
_nsFlags = (NsFlags)(_flags & (ushort)FlagMasks.NsFlagMask);
_rCode = (RCode)(_flags & (ushort)FlagMasks.RCodeMask);
// Parse Questions Section
_questions = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(questions, 0));
_answerRRs = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(answerRRs, 0));
_authorityRRs = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(authorityRRs, 0));
ushort additionalRRCount = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(additionalRRCountBytes, 0));
_additionalRecords = new List<IDnsRecord>();
_answers = new DnsRecordBase[_answerRRs];
_authoritiveNameServers = new DnsRecordBase[_authorityRRs];
// Parse Queries
_queryRequest = this.ParseQuery(ref memoryStream);
// Read dnsType
memoryStream.Read(nsType, 0, 2);
// Read dnsClass
memoryStream.Read(nsClass, 0, 2);
_nsType = (NsType)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(nsType, 0));
_nsClass = (NsClass)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(nsClass, 0));
// Read in Answer Blocks
for (int i=0; i < _answerRRs; i++)
{
_answers[i] = RecordFactory.Create(ref memoryStream);
}
// Parse Authority Records
for (int i=0; i < _authorityRRs; i++)
{
_authoritiveNameServers[i] = RecordFactory.Create(ref memoryStream);
}
// Parse Additional Records
for (int i=0; i < additionalRRCount; i++)
{
_additionalRecords.Add(RecordFactory.Create(ref memoryStream));
}
}
}
}

View File

@ -0,0 +1,83 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
/// <summary>
/// RFC 1035:
///
/// 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.
///
/// A records cause no additional section processing. The RDATA section of
/// an A line in a master file is an Internet address expressed as four
/// decimal numbers separated by dots without any imbedded spaces (e.g.,
/// "10.2.0.52" or "192.0.5.6").
///
/// </summary>
public sealed class ARecord : DnsRecordBase, IDnsRecord
{
private string _hostAddress;
/// <summary>
/// The answer host address for the DNS A Record.
/// </summary>
public string HostAddress
{
get { return _hostAddress; }
}
internal ARecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
_hostAddress = ms.ReadByte() + "." + ms.ReadByte() + "." + ms.ReadByte() + "." + ms.ReadByte();
_answer = "Address: " + _hostAddress;
}
}
}

View File

@ -0,0 +1,97 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class AaaaRecord : DnsRecordBase
{
private string m_ipAddress;
internal AaaaRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
// TODO: Test and incorporate BinToHex function below
m_ipAddress =
ms.ReadByte().ToString("x2") + ms.ReadByte().ToString("x2") + ":" + ms.ReadByte().ToString("x2") + ms.ReadByte().ToString("x2") + ":" +
ms.ReadByte().ToString("x2") + ms.ReadByte().ToString("x2") + ":" + ms.ReadByte().ToString("x2") + ms.ReadByte().ToString("x2") + ":" +
ms.ReadByte().ToString("x2") + ms.ReadByte().ToString("x2") + ":" + ms.ReadByte().ToString("x2") + ms.ReadByte().ToString("x2") + ":" +
ms.ReadByte().ToString("x2") + ms.ReadByte().ToString("x2") + ":" + ms.ReadByte().ToString("x2") + ms.ReadByte().ToString("x2");
_answer = "IPv6 Address: " + m_ipAddress;
}
// TODO: converted from VB.NET, test to make sure it works properly
private static string BinToHex(byte[] data)
{
if (data != null)
{
StringBuilder sb = new System.Text.StringBuilder(1024);
for (int i = 0; i < data.Length; i++)
{
sb.Append(data[i].ToString("X2"));
}
return sb.ToString();
}
else
{
return null;
}
}
// TODO: converted from VB.NET, test to make sure it works properly
private static byte[] HexToBin(string s)
{
int arraySize = s.Length / 2;
byte[] bytes = new byte[arraySize - 1];
int counter = 0;
for (int i = 0; i < s.Length - 1; i = 2)
{
string hexValue = s.Substring(i, 2);
// Tell convert to interpret the string as a 16 bit hex value
int intValue = Convert.ToInt32(hexValue, 16);
// Convert the integer to a byte and store it in the array
bytes[counter] = Convert.ToByte(intValue);
counter += 1;
}
return bytes;
}
}
}

View File

@ -0,0 +1,78 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
namespace DnDns.Records
{
public sealed class AfsdbRecord : DnsRecordBase
{
private ushort _port;
private string _name;
private short _type;
public ushort Port
{
get { return _port; }
}
public string AfsdbName
{
get { return _name; }
}
public short Type
{
get { return _type; }
}
internal AfsdbRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
byte[] type = new byte[2];
ms.Read(type, 0, 2);
// _port = (ushort)Tools.ByteToUInt(type);
_port = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(type, 0));
_name = DnsRecordBase.ParseName(ref ms);
//_type = (short)Tools.ByteToUInt(type);
_type = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(type, 0));
_answer = "Name: " + _name + ", Port: " + _port + ", Type: " + _type;
}
}
}

View File

@ -0,0 +1,74 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class AtmaRecord : DnsRecordBase
{
private string _address;
private ATMFormat _format;
public string Address
{
get { return _address; }
}
public ATMFormat Format
{
get { return _format; }
}
internal AtmaRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
byte[] address = new byte[this.DnsHeader.DataLength - 1];
_format = (ATMFormat)ms.ReadByte();
ms.Read(address, 0, this.DnsHeader.DataLength - 1);
_address = Encoding.ASCII.GetString(address);
_answer = "Address: " + _address + ", Format: " + _format;
}
public enum ATMFormat : byte
{
E164 = 0x01,
NSAP = 0x02
}
}
}

View File

@ -0,0 +1,346 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Diagnostics;
namespace DnDns.Records
{
/// <summary>
/// Handles a basic Dns record
///
/// From RFC 1035:
///
/// 3.2.1. Format
///
/// All RRs have the same top level format shown below:
///
/// 1 1 1 1 1 1
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | |
/// / /
/// / NAME /
/// | |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | TYPE |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | CLASS |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | TTL |
/// | |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | RDLENGTH |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/// / RDATA /
/// / /
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
///
/// where:
///
/// NAME an owner name, i.e., the name of the node to which this
/// resource record pertains.
///
/// TYPE two octets containing one of the RR TYPE codes.
///
/// CLASS two octets containing one of the RR CLASS codes.
///
/// TTL a 32 bit signed integer that specifies the time interval
/// that the resource record may be cached before the source
/// of the information should again be consulted. Zero
/// values are interpreted to mean that the RR can only be
/// used for the transaction in progress, and should not be
/// cached. For example, SOA records are always distributed
/// with a zero TTL to prohibit caching. Zero values can
/// also be used for extremely volatile data.
///
/// RDLENGTH an unsigned 16 bit integer that specifies the length in
/// octets of the RDATA field.
///
/// RDATA a variable length string of octets that describes the
/// resource. The format of this information varies
/// according to the TYPE and CLASS of the resource record.
/// </summary>
public abstract class DnsRecordBase : IDnsRecord
{
#region Fields
// NAME an owner name, i.e., the name of the node to which this
// resource record pertains.
//private string _name;
// TYPE two octets containing one of the RR TYPE codes.
//protected NsType _nsType;
// CLASS - two octets containing one of the RR CLASS codes.
//private NsClass _nsClass;
// TTL - a 32 bit signed integer that specifies the time interval
// that the resource record may be cached before the source
// of the information should again be consulted. Zero
// values are interpreted to mean that the RR can only be
// used for the transaction in progress, and should not be
// cached. For example, SOA records are always distributed
// with a zero TTL to prohibit caching. Zero values can
/// also be used for extremely volatile data.
//private int _timeToLive;
// RDLENGTH - an unsigned 16 bit integer that specifies the length in
// octets of the RDATA field.
//protected short _dataLength;
protected RecordHeader _dnsHeader;
protected string _answer;
protected string _errorMsg;
#endregion
#region Properties
/// <summary>
/// NAME - an owner name, i.e., the name of the node to which this
/// resource record pertains.
/// </summary>
//public string Name
//{
// get { return _name; }
//}
public RecordHeader DnsHeader
{
get { return _dnsHeader; }
protected set { _dnsHeader = value; }
}
public string Answer
{
get { return _answer; }
}
/// <summary>
/// TYPE two octets containing one of the RR TYPE codes.
/// </summary>
//public NsType NsType
//{
// get { return _nsType; }
//}
/// <summary>
/// CLASS - two octets containing one of the RR CLASS codes.
/// </summary>
//public NsClass NsClass
//{
// get { return _nsClass; }
//}
/// <summary>
/// TTL - a 32 bit signed integer that specifies the time interval
/// that the resource record may be cached before the source
/// of the information should again be consulted. Zero
/// values are interpreted to mean that the RR can only be
/// used for the transaction in progress, and should not be
/// cached. For example, SOA records are always distributed
/// with a zero TTL to prohibit caching. Zero values can
/// also be used for extremely volatile data.
/// </summary>
//public int TimeToLive
//{
// get { return _timeToLive; }
//}
/// <summary>
/// RDLENGTH - an unsigned 16 bit integer that specifies the length in
/// octets of the RDATA field.
/// </summary>
//public short DataLength
//{
// get { return _dataLength; }
//}
public string ErrorMsg
{
get { return _errorMsg; }
}
#endregion
internal DnsRecordBase()
{
}
public virtual void ParseRecord(ref MemoryStream ms)
{
// Default implementation - the most common.
_answer = DnsRecordBase.ParseName(ref ms);
}
internal DnsRecordBase(RecordHeader dnsHeader)
{
_dnsHeader = dnsHeader;
}
// RFC
// 4.1.4. Message compression
//
// In order to reduce the size of messages, the domain system utilizes a
// compression scheme which eliminates the repetition of domain names in a
// message. In this scheme, an entire domain name or a list of labels at
// the end of a domain name is replaced with a pointer to a prior occurance
// of the same name.
//
// The pointer takes the form of a two octet sequence:
//
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | 1 1| OFFSET |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
//
// The first two bits are ones. This allows a pointer to be distinguished
// from a label, since the label must begin with two zero bits because
// labels are restricted to 63 octets or less. (The 10 and 01 combinations
// are reserved for future use.) The OFFSET field specifies an offset from
// the start of the message (i.e., the first octet of the ID field in the
// domain header). A zero offset specifies the first byte of the ID field,
// etc.
//
// The compression scheme allows a domain name in a message to be
// represented as either:
//
// - a sequence of labels ending in a zero octet
// - a pointer
// - a sequence of labels ending with a pointer
//
internal static string ParseName(ref MemoryStream ms)
{
Trace.WriteLine("Reading Name...");
StringBuilder sb = new StringBuilder();
uint next = (uint)ms.ReadByte();
Trace.WriteLine("Next is 0x" + next.ToString("x2"));
int bPointer;
while ((next != 0x00))
{
// Isolate 2 most significat bits -> e.g. 11xx xxxx
// if it's 0xc0 (11000000b} then pointer
switch (0xc0 & next)
{
// 0xc0 -> Name is a pointer.
case 0xc0:
{
// Isolate Offset
int offsetMASK = ~0xc0;
// Example on how to calculate the offset
// e.g.
//
// So if given the following 2 bytes - 0xc1 0x1c (11000001 00011100)
//
// The pointer takes the form of a two octet sequence:
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | 1 1| OFFSET |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | 1 1| 0 0 0 0 0 1 0 0 0 1 1 1 0 0|
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
//
// A pointer is indicated by the a 1 in the two most significant bits
// The Offset is the remaining bits.
//
// The Pointer = 0xc0 (11000000 00000000)
// The offset = 0x11c (00000001 00011100)
// Move offset into the proper position
int offset = (int)(offsetMASK & next) << 8;
// extract the pointer to the data in the stream
bPointer = ms.ReadByte() + offset;
// store the position so we can resume later
long oldPtr = ms.Position;
// Move to the specified position in the stream and
// parse the name (recursive call)
ms.Position = bPointer;
sb.Append(DnsRecordBase.ParseName(ref ms));
Trace.WriteLine(sb.ToString());
// Move back to original position, and continue
ms.Position = oldPtr;
next = 0x00;
break;
}
case 0x00:
{
Debug.Assert(next < 0xc0, "Offset cannot be greater then 0xc0.");
byte[] buffer = new byte[next];
ms.Read(buffer, 0, (int)next);
sb.Append(Encoding.ASCII.GetString(buffer) + ".");
next = (uint)ms.ReadByte();
Trace.WriteLine("0x" + next.ToString("x2"));
break;
}
default:
throw new InvalidOperationException("There was a problem decompressing the DNS Message.");
}
}
return sb.ToString();
}
internal string ParseText(ref MemoryStream ms)
{
StringBuilder sb = new StringBuilder();
int len = ms.ReadByte();
byte[] buffer = new byte[len];
ms.Read(buffer, 0, len);
sb.Append(Encoding.ASCII.GetString(buffer));
return sb.ToString();
}
public override string ToString()
{
return _answer;
}
#region IDnsRecord Members
public virtual byte[] GetMessageBytes()
{
return new byte[]{};
}
#endregion
}
internal class Trace
{
public static void WriteLine(string message)
{
Logging.AddLogMessage(Logging.LoggingType.INFO, message);
}
}
}

View File

@ -0,0 +1,52 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class CNameRecord : DnsRecordBase
{
internal CNameRecord(RecordHeader dnsHeader) : base(dnsHeader) {}
public override void ParseRecord(ref MemoryStream ms)
{
base.ParseRecord(ref ms);
_answer = "Host: " + _answer;
}
}
}

View File

@ -0,0 +1,67 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class HInfoRecord : DnsRecordBase
{
private string _cpuType;
private string _operatingSys;
public string CpuType
{
get { return _cpuType; }
}
public string OperatingSys
{
get { return _operatingSys; }
}
internal HInfoRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
_cpuType = base.ParseText(ref ms);
_operatingSys = base.ParseText(ref ms);
_answer = "CPU: " + _cpuType + ", OS: " + _operatingSys;
}
}
}

View File

@ -0,0 +1,58 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public interface IDnsRecord
{
RecordHeader DnsHeader { get; }
string Answer { get; }
//short DataLength { get; }
string ErrorMsg { get; }
//string Name { get; }
//NsClass NsClass { get; }
//NsType NsType { get; }
//int TimeToLive { get; }
//void ParseRecordHeader(ref MemoryStream ms);
void ParseRecord(ref MemoryStream ms);
string ToString();
byte[] GetMessageBytes();
}
}

View File

@ -0,0 +1,66 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class IsdnRecord : DnsRecordBase
{
private string _address;
private string _subAddress;
public string Address
{
get { return _address; }
}
public string SubAddress
{
get { return _subAddress; }
}
internal IsdnRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
_address = base.ParseText(ref ms);
_subAddress = base.ParseText(ref ms);
_answer = "ISDN Address: " + _address + ", SubAddress: " + _subAddress;
}
}
}

View File

@ -0,0 +1,194 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
namespace DnDns.Records
{
public sealed class LocRecord : DnsRecordBase
{
// For LOC
#region Fields
private byte _version;
private byte _size;
private byte _horPrecision;
private byte _vertPrecision;
private uint _latitude;
private uint _longitude;
private uint _altitude;
#endregion
#region Properties
public byte Version
{
get { return _version; }
}
public byte Size
{
get { return _size; }
}
public byte HorPrecision
{
get { return _horPrecision; }
}
public byte VertPrecision
{
get { return _vertPrecision; }
}
public uint Latitude
{
get { return _latitude; }
}
public uint Longitude
{
get { return _longitude; }
}
public uint Altitude
{
get { return _altitude; }
}
#endregion
private char[] _latDirection = new char[2] {'N', 'S'};
private char[] _longDirection = new char[2] {'E', 'W'};
internal LocRecord(RecordHeader dnsHeader) : base(dnsHeader) {}
public override void ParseRecord(ref MemoryStream ms)
{
byte[] latitude = new Byte[4];
byte[] longitude = new Byte[4];
byte[] altitude = new Byte[4];
_version = (byte)ms.ReadByte();
_size = (byte)ms.ReadByte();
_horPrecision = (byte)ms.ReadByte();
_vertPrecision = (byte)ms.ReadByte();
ms.Read(latitude,0,latitude.Length);
// _latitude = Tools.ByteToUInt(latitude);
_latitude = (uint)IPAddress.NetworkToHostOrder((int)BitConverter.ToUInt32(latitude, 0));
ms.Read(longitude,0,longitude.Length);
// _longitude = Tools.ByteToUInt(longitude);
_longitude = (uint)IPAddress.NetworkToHostOrder((int)BitConverter.ToUInt32(longitude, 0));
ms.Read(altitude,0,altitude.Length);
// _altitude = Tools.ByteToUInt(altitude);
_altitude = (uint)IPAddress.NetworkToHostOrder((int)BitConverter.ToUInt32(altitude, 0));
StringBuilder sb = new StringBuilder();
sb.Append("Version: ");
sb.Append(_version);
sb.Append("\r\n");
sb.Append("Size: ");
sb.Append(CalcSize(_size));
sb.Append(" m\r\n");
sb.Append("Horizontal Precision: ");
sb.Append(CalcSize(_horPrecision));
sb.Append(" m\r\n");
sb.Append("Vertical Precision: ");
sb.Append(CalcSize(_vertPrecision));
sb.Append(" m\r\n");
sb.Append("Latitude: ");
sb.Append(CalcLoc(_latitude, _latDirection));
sb.Append("\r\n");
sb.Append("Longitude: ");
sb.Append(CalcLoc(_longitude, _longDirection));
sb.Append("\r\n");
sb.Append("Altitude: ");
sb.Append((_altitude - 10000000) / 100.0);
sb.Append(" m\r\n");
_answer = sb.ToString();
}
private string CalcLoc(uint angle, char[] nsew)
{
char direction;
if (angle < 0x80000000)
{
angle = 0x80000000 - angle;
direction = nsew[1];
}
else
{
angle = angle - 0x80000000;
direction = nsew[0];
}
uint tsecs = angle % 1000;
angle = angle / 1000;
uint secs = angle % 60;
angle = angle / 60;
uint minutes = angle % 60;
uint degrees = angle / 60;
return degrees + " deg, " + minutes + " min " + secs+ "." + tsecs + " sec " + direction;
}
// return size in meters
private double CalcSize(byte val)
{
double size;
int exponent;
size = (val & 0xF0) >> 4;
exponent = (val & 0x0F);
while (exponent != 0)
{
size *= 10;
exponent--;
}
return size / 100;
}
}
}

View File

@ -0,0 +1,66 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class MInfoRecord : DnsRecordBase
{
private string _responsibleMb;
private string _errorMb;
public string ResponsibleMb
{
get { return _responsibleMb; }
}
public string ErrorMb
{
get { return _errorMb; }
}
internal MInfoRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
_responsibleMb = DnsRecordBase.ParseName(ref ms);
_errorMb = DnsRecordBase.ParseName(ref ms);
_answer = "Responsible MailBox: " + _responsibleMb + ", Error MailBox: " + _errorMb;
}
}
}

View File

@ -0,0 +1,52 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class MbRecord : DnsRecordBase
{
internal MbRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
//public override void ParseRecord(ref MemoryStream ms)
//{
// _answer = BaseDnsRecord.ParseName(ref ms);
//}
}
}

View File

@ -0,0 +1,51 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class MgRecord : DnsRecordBase
{
internal MgRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
//public override void ParseRecord(ref MemoryStream ms)
//{
// _answer = BaseDnsRecord.ParseName(ref ms);
//}
}
}

View File

@ -0,0 +1,51 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class MrRecord : DnsRecordBase
{
internal MrRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
//public override void ParseRecord(ref MemoryStream ms)
//{
// _answer = BaseDnsRecord.ParseName(ref ms);
//}
}
}

View File

@ -0,0 +1,75 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
namespace DnDns.Records
{
public sealed class MxRecord : DnsRecordBase
{
// For MX
private short _preference;
private string _mailExchange;
public short Preference
{
get { return _preference; }
}
public string MailExchange
{
get { return _mailExchange; }
}
internal MxRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
// Preference is a function of MX records
byte[] nsPreference = new byte[2];
ms.Read(nsPreference, 0, 2);
//_preference = (short)Tools.ByteToUInt(nsPreference);
// TODO: Should this be a UShort instead of a short?
_preference = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(nsPreference, 0));
// Parse Name
_mailExchange = DnsRecordBase.ParseName(ref ms);
_answer = "MX Preference: " + _preference + ", Mail Exchanger: " + _mailExchange;
}
}
}

View File

@ -0,0 +1,51 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class NsRecord : DnsRecordBase
{
internal NsRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
//public override void ParseRecord(ref MemoryStream ms)
//{
// _answer = BaseDnsRecord.ParseName(ref ms);
//}
}
}

View File

@ -0,0 +1,51 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class PtrRecord : DnsRecordBase
{
internal PtrRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
//public override void ParseRecord(ref MemoryStream ms)
//{
// _answer = BaseDnsRecord.ParseName(ref ms);
//}
}
}

View File

@ -0,0 +1,191 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using DnDns.Enums;
namespace DnDns.Records
{
class RecordFactory
{
public static IDnsRecord Create(ref MemoryStream ms)
{
IDnsRecord dnsRecord;
// Have to start out with an unknown record, since we have to parse the entire
// header before we can determine the type of DNS record it is.
// TODO: Consider other options.
// start as an unknown type, then create a known type, parse the response
// and return the object.
//DnsRecordBase dr = new DnsUnknownRecord();
//dr.ParseRecordHeader(ref ms);
RecordHeader dnsHeader = new RecordHeader();
dnsHeader.ParseRecordHeader(ref ms);
switch (dnsHeader.NsType)
{
case NsType.A:
{
dnsRecord = new ARecord(dnsHeader);
break;
}
case NsType.AAAA:
{
dnsRecord = new AaaaRecord(dnsHeader);
break;
}
case NsType.MX:
{
dnsRecord = new MxRecord(dnsHeader);
break;
}
case NsType.RP:
{
dnsRecord = new RpRecord(dnsHeader);
break;
}
case NsType.MR:
{
dnsRecord = new MrRecord(dnsHeader);
break;
}
case NsType.MB:
{
dnsRecord = new MbRecord(dnsHeader);
break;
}
case NsType.MG:
{
dnsRecord = new MgRecord(dnsHeader);
break;
}
case NsType.NS:
{
dnsRecord = new NsRecord(dnsHeader);
break;
}
case NsType.CNAME:
{
dnsRecord = new CNameRecord(dnsHeader);
break;
}
case NsType.PTR:
{
dnsRecord = new PtrRecord(dnsHeader);
break;
}
case NsType.HINFO:
{
dnsRecord = new HInfoRecord(dnsHeader);
break;
}
case NsType.MINFO:
{
dnsRecord = new MInfoRecord(dnsHeader);
break;
}
case NsType.X25:
{
dnsRecord = new X25Record(dnsHeader);
break;
}
case NsType.TXT:
{
dnsRecord = new TxtRecord(dnsHeader);
break;
}
case NsType.LOC:
{
dnsRecord = new LocRecord(dnsHeader);
break;
}
case NsType.SOA:
{
dnsRecord = new SoaRecord(dnsHeader);
break;
}
case NsType.SRV:
{
dnsRecord = new SrvRecord(dnsHeader);
break;
}
case NsType.AFSDB:
{
dnsRecord = new AfsdbRecord(dnsHeader);
break;
}
case NsType.ATMA:
{
dnsRecord = new AtmaRecord(dnsHeader);
break;
}
case NsType.ISDN:
{
dnsRecord = new IsdnRecord(dnsHeader);
break;
}
case NsType.RT:
{
dnsRecord = new RtRecord(dnsHeader);
break;
}
case NsType.WKS:
{
dnsRecord = new WksRecord(dnsHeader);
break;
}
case NsType.TSIG:
{
dnsRecord = new TSigRecord(dnsHeader);
break;
}
default:
{
// Unknown type. parse and return the DnsUnknownRecord
dnsRecord = new UnknownRecord(dnsHeader);
break;
}
}
//dnsRecord.ParseRecordHeader(ref ms);
dnsRecord.ParseRecord(ref ms);
return dnsRecord;
}
}
}

View File

@ -0,0 +1,242 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using DnDns.Enums;
namespace DnDns.Records
{
/// <summary>
/// The DnsRecordHeader class contains fields, properties and
/// parsing cababilities within the DNS Record except the the
/// RDATA. The Name, Type, Class, TTL, and RDLength.
///
/// This class is used in the DnsRecordFactory to instantiate
/// concrete DnsRecord Classes.
///
/// RFC 1035
///
/// 3.2.1. Format
///
/// All RRs have the same top level format shown below:
///
/// 1 1 1 1 1 1
/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | |
/// / /
/// / NAME /
/// | |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | TYPE |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | CLASS |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | TTL |
/// | |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
/// | RDLENGTH |
/// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
///
/// where:
///
/// NAME an owner name, i.e., the name of the node to which this
/// resource record pertains.
///
/// TYPE two octets containing one of the RR TYPE codes.
///
/// CLASS two octets containing one of the RR CLASS codes.
///
/// TTL a 32 bit signed integer that specifies the time interval
/// that the resource record may be cached before the source
/// of the information should again be consulted. Zero
/// values are interpreted to mean that the RR can only be
/// used for the transaction in progress, and should not be
/// cached. For example, SOA records are always distributed
/// with a zero TTL to prohibit caching. Zero values can
/// also be used for extremely volatile data.
///
/// RDLENGTH an unsigned 16 bit integer that specifies the length in
/// octets of the RDATA field.
///
/// </summary>
public class RecordHeader
{
#region Fields
// NAME an owner name, i.e., the name of the node to which this
// resource record pertains.
private string _name;
// TYPE two octets containing one of the RR TYPE codes.
private NsType _nsType;
// CLASS - two octets containing one of the RR CLASS codes.
private NsClass _nsClass;
// TTL - a 32 bit signed integer that specifies the time interval
// that the resource record may be cached before the source
// of the information should again be consulted. Zero
// values are interpreted to mean that the RR can only be
// used for the transaction in progress, and should not be
// cached. For example, SOA records are always distributed
// with a zero TTL to prohibit caching. Zero values can
/// also be used for extremely volatile data.
private int _timeToLive;
// RDLENGTH - an unsigned 16 bit integer that specifies the length in
// octets of the RDATA field.
private short _dataLength;
/// <summary>
/// Initalise the <see cref="RecordHeader"/>
/// </summary>
/// <param name="name">The header name</param>
/// <param name="nsType">The resource type</param>
/// <param name="nsClass">The class type</param>
/// <param name="timeToLive">The time to live</param>
public RecordHeader(string name, NsType nsType, NsClass nsClass, int timeToLive)
{
_name = name;
_nsType = nsType;
_nsClass = nsClass;
_timeToLive = timeToLive;
}
public RecordHeader()
{
}
#endregion
#region Properties
/// <summary>
/// NAME - an owner name, i.e., the name of the node to which this
/// resource record pertains.
/// </summary>
public string Name
{
get { return _name; }
}
/// <summary>
/// TYPE two octets containing one of the RR TYPE codes.
/// </summary>
public NsType NsType
{
get { return _nsType; }
}
/// <summary>
/// CLASS - two octets containing one of the RR CLASS codes.
/// </summary>
public NsClass NsClass
{
get { return _nsClass; }
}
/// <summary>
/// TTL - a 32 bit signed integer that specifies the time interval
/// that the resource record may be cached before the source
/// of the information should again be consulted. Zero
/// values are interpreted to mean that the RR can only be
/// used for the transaction in progress, and should not be
/// cached. For example, SOA records are always distributed
/// with a zero TTL to prohibit caching. Zero values can
/// also be used for extremely volatile data.
/// </summary>
public int TimeToLive
{
get { return _timeToLive; }
}
/// <summary>
/// RDLENGTH - an unsigned 16 bit integer that specifies the length in
/// octets of the RDATA field.
/// </summary>
public short DataLength
{
get { return _dataLength; }
}
#endregion
/// <summary>
///
/// </summary>
/// <param name="ms"></param>
public void ParseRecordHeader(ref MemoryStream ms)
{
byte[] nsType = new byte[2];
byte[] nsClass = new byte[2];
byte[] nsTTL = new byte[4];
byte[] nsDataLength = new byte[2];
// Read the name
_name = DnsRecordBase.ParseName(ref ms);
// Read the data header
ms.Read(nsType, 0, 2);
ms.Read(nsClass, 0, 2);
ms.Read(nsTTL, 0, 4);
ms.Read(nsDataLength, 0, 2);
_nsType = (NsType)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(nsType, 0));
_nsClass = (NsClass)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(nsClass, 0));
_timeToLive = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(nsTTL, 0));
_dataLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(nsDataLength, 0));
}
internal byte[] GetMessageBytes()
{
MemoryStream memoryStream = new MemoryStream();
byte[] data = DnsHelpers.CanonicaliseDnsName(_name, false);
memoryStream.Write(data,0,data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder((ushort)_nsType) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder((ushort)_nsClass) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((uint)(IPAddress.HostToNetworkOrder((ushort)_timeToLive) >> 32));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder((ushort)_dataLength) >> 16));
memoryStream.Write(data, 0, data.Length);
return memoryStream.ToArray();
}
}
}

View File

@ -0,0 +1,68 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class RpRecord : DnsRecordBase
{
// For RP
private string _name;
private string _textLocation;
public string RpName
{
get { return _name; }
}
public string TextLocation
{
get { return _textLocation; }
}
internal RpRecord(RecordHeader dnsHeader) : base(dnsHeader) {}
public override void ParseRecord(ref MemoryStream ms)
{
_name = DnsRecordBase.ParseName(ref ms);
_textLocation = DnsRecordBase.ParseName(ref ms);
_answer = _name + ", " + _textLocation;
}
}
}

View File

@ -0,0 +1,71 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
namespace DnDns.Records
{
public sealed class RtRecord : DnsRecordBase
{
private ushort _preference;
private string _intermediateHost;
public ushort Preference
{
get { return _preference; }
}
public string IntermediateHost
{
get { return _intermediateHost; }
}
internal RtRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
byte[] preference = new byte[2];
ms.Read(preference, 0, 2);
//_preference = (ushort)Tools.ByteToUInt(preference);
_preference = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(preference, 0));
_intermediateHost = DnsRecordBase.ParseName(ref ms);
_answer = "Preference: " + _preference + ", Intermediate Host: " + _intermediateHost;
}
}
}

View File

@ -0,0 +1,160 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
namespace DnDns.Records
{
public sealed class SoaRecord : DnsRecordBase
{
// For SOA
#region fields
private string _primaryNameServer;
private string _responsiblePerson;
private uint _serial;
private uint _refreshInterval;
private uint _retryInterval;
private uint _expirationLimit;
// RFC 1034: TTL - only positive values of a signed 32 bit number.
private int _minimumTimeToLive;
#endregion
#region properties
public string PrimaryNameServer
{
get { return _primaryNameServer; }
}
public string ResponsiblePerson
{
get { return _responsiblePerson; }
}
public uint Serial
{
get { return _serial; }
}
public uint RefreshInterval
{
get { return _refreshInterval; }
}
public uint RetryInterval
{
get { return _retryInterval; }
}
public uint ExpirationLimit
{
get { return _expirationLimit; }
}
public int MinimumTimeToLive
{
get { return _minimumTimeToLive; }
}
#endregion
internal SoaRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
StringBuilder sb = new StringBuilder();
// Parse Name
_primaryNameServer = DnsRecordBase.ParseName(ref ms);
sb.Append("Primary NameServer: ");
sb.Append(_primaryNameServer);
sb.Append("\r\n");
// Parse Responsible Persons Mailbox (Parse Name)
_responsiblePerson = DnsRecordBase.ParseName(ref ms);
sb.Append("Responsible Person: ");
sb.Append(_responsiblePerson);
sb.Append("\r\n");
byte[] serial = new Byte[4];
byte[] refreshInterval = new Byte[4];
byte[] retryInterval = new Byte[4];
byte[] expirationLimit = new Byte[4];
byte[] minTTL = new Byte[4];
// Parse Serial (4 bytes)
ms.Read(serial, 0, 4);
//_serial = Tools.ByteToUInt(serial);
_serial = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(serial, 0));
sb.Append("Serial: ");
sb.Append(_serial);
sb.Append("\r\n");
// Parse Refresh Interval (4 bytes)
ms.Read(refreshInterval, 0, 4);
// _refreshInterval = Tools.ByteToUInt(refreshInterval);
_refreshInterval = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(refreshInterval, 0));
sb.Append("Refresh Interval: ");
sb.Append(_refreshInterval);
sb.Append("\r\n");
// Parse Retry Interval (4 bytes)
ms.Read(retryInterval, 0, 4);
//_retryInterval = Tools.ByteToUInt(retryInterval);
_retryInterval = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(retryInterval, 0));
sb.Append("Retry Interval: ");
sb.Append(_retryInterval);
sb.Append("\r\n");
// Parse Expiration limit (4 bytes)
ms.Read(expirationLimit, 0, 4);
// _expirationLimit = Tools.ByteToUInt(expirationLimit);
_expirationLimit = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(expirationLimit, 0));
sb.Append("Expire: ");
sb.Append(_expirationLimit);
sb.Append("\r\n");
// Parse Min TTL (4 bytes)
ms.Read(minTTL, 0, 4);
// _minTTL = Tools.ByteToUInt(minTTL);
_minimumTimeToLive = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(minTTL, 0));
sb.Append("TTL: ");
sb.Append(_minimumTimeToLive);
sb.Append("\r\n");
_answer = sb.ToString();
}
}
}

View File

@ -0,0 +1,95 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
namespace DnDns.Records
{
public sealed class SrvRecord : DnsRecordBase
{
// For SRV
private ushort _priority;
private ushort _weight;
private ushort _port;
private string _hostName;
public ushort Priority
{
get { return _priority; }
}
public ushort Weight
{
get { return _weight; }
}
public ushort Port
{
get { return _port; }
}
public string HostName
{
get { return _hostName; }
}
internal SrvRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
byte[] priority = new byte[2];
ms.Read(priority, 0, 2);
//_priority = (ushort)Tools.ByteToUInt(Priority);
_priority = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(priority, 0));
byte[] weight = new byte[2];
ms.Read(weight, 0, 2);
// _weight = (ushort)Tools.ByteToUInt(Weight);
_weight = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(weight, 0));
byte[] port = new byte[2];
ms.Read(port, 0, 2);
//_port = (ushort)Tools.ByteToUInt(port);
_port = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(port, 0));
_hostName = DnsRecordBase.ParseName(ref ms);
_answer = "Service Location: \r\nPriority: " + _priority + "\r\nWeight: " +
_weight + "\r\nPort: " + _port + "\r\nHostName: " + _hostName + "\r\n";
}
}
}

View File

@ -0,0 +1,235 @@
using System;
using System.IO;
using System.Net;
using System.Text;
using DnDns.Enums;
namespace DnDns.Records
{
/// <summary>
/// Implementation of a TSIG record structure as per RFC 2845
/// </summary>
public sealed class TSigRecord : DnsRecordBase
{
private string _algorithmName;
private RCode _error;
private ushort _fudge;
private ushort _originalId;
private byte[] _otherData;
private byte[] _mac;
private DateTime _timeSigned;
public string AlgorithmName
{
get { return _algorithmName; }
}
public RCode Error
{
get { return _error; }
}
public ushort Fudge
{
get { return _fudge; }
}
public ushort OriginalID
{
get { return _originalId; }
}
public byte[] OtherData
{
get { return _otherData; }
}
public byte[] Mac
{
get { return _mac; }
}
public DateTime TimeSigned
{
get { return _timeSigned; }
}
public TSigRecord(RecordHeader dnsHeader) : base(dnsHeader)
{
}
public TSigRecord(string name, string algorithmName, RCode error, ushort fudge, ushort originalId, byte[] otherData, byte[] mac, DateTime timeSigned)
{
DnsHeader = new RecordHeader(name, NsType.TSIG, NsClass.ANY, 0);
_algorithmName = algorithmName;
_error = error;
_fudge = fudge;
_originalId = originalId;
_otherData = otherData;
_mac = mac;
_timeSigned = timeSigned;
if(otherData == null)
{
_otherData = new byte[]{};
}
}
public override void ParseRecord(ref MemoryStream memoryStream)
{
Byte[] dataUInt16 = new byte[2];
Byte[] dataUInt32 = new byte[4];
_algorithmName = ParseName(ref memoryStream);
memoryStream.Read(dataUInt16, 0, dataUInt16.Length);
long timeHigh = (ushort) IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(dataUInt16, 0));
memoryStream.Read(dataUInt32, 0, dataUInt32.Length);
long timeLow = (uint) IPAddress.NetworkToHostOrder((int)BitConverter.ToUInt32(dataUInt32, 0));
_timeSigned = DnsHelpers.ConvertFromDnsTime(timeLow, timeHigh);
memoryStream.Read(dataUInt16, 0, dataUInt16.Length);
_fudge = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(dataUInt16, 0));
memoryStream.Read(dataUInt16, 0, dataUInt16.Length);
Int32 macLen = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(dataUInt16, 0));
_mac = new byte[macLen];
memoryStream.Read(_mac, 0, macLen);
memoryStream.Read(dataUInt16, 0, dataUInt16.Length);
_originalId = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(dataUInt16, 0));
memoryStream.Read(dataUInt16, 0, dataUInt16.Length);
_error = (RCode)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(dataUInt16, 0));
memoryStream.Read(dataUInt16, 0, dataUInt16.Length);
Int32 otherLen = (ushort)IPAddress.NetworkToHostOrder((short)BitConverter.ToUInt16(dataUInt16, 0));
if(otherLen > 0)
{
_otherData = new byte[otherLen];
memoryStream.Read(_otherData, 0, otherLen);
}
else
{
_otherData = null;
}
_answer = ToString();
}
public override byte[] GetMessageBytes()
{
MemoryStream memoryStream = new MemoryStream();
byte[] data = DnsHeader.GetMessageBytes();
memoryStream.Write(data,0,data.Length);
long rLengthPosition = memoryStream.Position;
data = DnsHelpers.CanonicaliseDnsName(_algorithmName, false);
memoryStream.Write(data, 0, data.Length);
int timeHigh;
long timeLow;
DnsHelpers.ConvertToDnsTime(_timeSigned.ToUniversalTime(), out timeHigh, out timeLow);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder((ushort)timeHigh) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((uint)(IPAddress.HostToNetworkOrder((uint)timeLow) >> 32));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder(_fudge) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder(_mac.Length) >> 16));
memoryStream.Write(data, 0, data.Length);
memoryStream.Write(_mac, 0, _mac.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder(_originalId) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder((ushort)_error) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder((ushort)_otherData.Length) >> 16));
memoryStream.Write(data, 0, data.Length);
if(_otherData.Length != 0)
{
memoryStream.Write(_otherData, 0, _otherData.Length);
}
// Add the rdata lenght
long rlength = memoryStream.Position - rLengthPosition;
memoryStream.Seek(rLengthPosition - 2, SeekOrigin.Begin);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder((ushort)rlength) >> 16));
memoryStream.Write(data, 0, data.Length);
return memoryStream.ToArray();
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append(_algorithmName);
sb.Append(" ");
sb.Append(_timeSigned);
sb.Append(" ");
sb.Append(_fudge);
sb.Append(" ");
sb.Append(_mac.Length);
sb.Append(" ");
sb.Append(Convert.ToBase64String(Mac));
sb.Append(" ");
sb.Append(_error);
sb.Append(" ");
if (_otherData == null)
{
sb.Append(0);
}
else
{
sb.Append(_otherData.Length);
sb.Append(" ");
if (_error == RCode.BADTIME)
{
if (_otherData.Length != 6)
{
sb.Append("<invalid BADTIME other data>");
}
else
{
long time = ((long)(_otherData[0] & 0xFF) << 40) +
((long)(_otherData[1] & 0xFF) << 32) +
((_otherData[2] & 0xFF) << 24) +
((_otherData[3] & 0xFF) << 16) +
((_otherData[4] & 0xFF) << 8) +
((_otherData[5] & 0xFF));
sb.Append("<server time: ");
sb.Append(DnsHelpers.ConvertFromDnsTime(time));
sb.Append(">");
}
}
else
{
sb.Append("<");
sb.Append(Convert.ToBase64String(_otherData));
sb.Append(">");
}
}
return sb.ToString();
}
}
}

View File

@ -0,0 +1,63 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
/// <summary>
///
/// </summary>
public sealed class TxtRecord : DnsRecordBase
{
private string _text;
public string Text
{
get { return _text; }
}
internal TxtRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
_text = base.ParseText(ref ms);
_answer = "text: " + _text;
}
}
}

View File

@ -0,0 +1,69 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
class UnknownRecord : DnsRecordBase
{
public UnknownRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
// Type not implemented so we read it into a buffer and print out the data.
StringBuilder sb = new StringBuilder(this.DnsHeader.DataLength);
byte[] b = new byte[1];
// Loop over data, if char is easily converted to ASCII, convert it.
// Otherwise print a '.'
for (int i = 0; i < this.DnsHeader.DataLength; i++)
{
ms.Read(b, 0, 1);
if ((b[0] > 0x20) && (b[0] < 0x7e))
{
sb.Append(Encoding.ASCII.GetString(b));
}
else
{
sb.Append('.');
}
}
_answer = sb.ToString();
_errorMsg = "Type " + this.DnsHeader.NsType + " not implemented.";
}
}
}

View File

@ -0,0 +1,115 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
namespace DnDns.Records
{
public sealed class WksRecord : DnsRecordBase
{
private ProtocolType _protocolType;
private IPAddress _ipAddress;
private short[] _ports;
public ProtocolType ProtocolType
{
get { return _protocolType; }
set { _protocolType = value; }
}
public IPAddress IpAddress
{
get { return _ipAddress; }
set { _ipAddress = value; }
}
public short[] Ports
{
get { return _ports; }
set { _ports = value; }
}
internal WksRecord(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
// Bit map is the data length minus the IpAddress (4 bytes) and the Protocol (1 byte), 5 bytes total.
int bitMapLen = this.DnsHeader.DataLength - 4 - 1;
byte[] ipAddr = new byte[4];
byte[] BitMap = new byte[bitMapLen];
ms.Read(ipAddr, 0, 4);
// _ipAddress = new IPAddress(Tools.ToUInt32(ipAddr, 0));
_ipAddress = new IPAddress((uint)IPAddress.NetworkToHostOrder(BitConverter.ToUInt32(ipAddr, 0)));
_protocolType = (ProtocolType)ms.ReadByte();
ms.Read(BitMap, 0, BitMap.Length);
_ports = GetKnownServices(BitMap);
_answer = _protocolType + ": " + Tools.GetServByPort(_ports, _protocolType);
}
private short[] GetKnownServices(byte[] BitMap)
{
short[] tempPortArr = new short[1024];
int portCount = 0;
// mask to isolate left most bit
const byte mask = 0x80;
// Iterate through each byte
for (int i = 0; i < BitMap.Length; i++)
{
byte currentByte = BitMap[i];
int count = 0;
// iterate through each bit
for (byte j = 0x07; j != 0xFF; j--)
{
int port = (((i * 8) + count++) + 1);
currentByte = (byte)(currentByte << 1);
// is the flag set?
if ((mask & currentByte) == 0x80)
{
tempPortArr[portCount] = (short)port;
portCount++;
}
}
}
short[] portArr = new short[portCount];
Array.Copy(tempPortArr, 0, portArr, 0, portCount);
return portArr;
}
}
}

View File

@ -0,0 +1,60 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace DnDns.Records
{
public sealed class X25Record : DnsRecordBase
{
private string _x25Address;
public string X25Address
{
get { return _x25Address; }
}
internal X25Record(RecordHeader dnsHeader) : base(dnsHeader) { }
public override void ParseRecord(ref MemoryStream ms)
{
_x25Address = base.ParseText(ref ms);
_answer = "X.25 X.121 Address: " + _x25Address;
}
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
using DnDns.Query;
namespace DnDns.Security
{
public interface IMessageSecurityProvider
{
DnsQueryRequest SecureMessage(DnsQueryRequest dnsQueryRequest);
}
}

View File

@ -0,0 +1,109 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using DnDns.Enums;
using DnDns.Query;
using DnDns.Records;
namespace DnDns.Security
{
/// <summary>
/// Implements TSIG signing of DNS messages as per RFC2845
/// </summary>
/// <remarks>This class only supports the one hashing algorithim, hmac-sha256.
/// It would be trivial to add more.</remarks>
public class TsigMessageSecurityProvider : IMessageSecurityProvider
{
private const string Hmacsha1String = "hmac-sha256.";
private readonly string _name;
private readonly string _algorithimName;
private readonly ushort _fudge;
private readonly byte[] _sharedKey;
private readonly HMACSHA256 _hmac;
/// <summary>
/// Initalise the <see cref="TsigMessageSecurityProvider"/>
/// </summary>
/// <param name="name">The name of the shared key</param>
/// <param name="sharedKey">The shared key in base64 string format</param>
/// <param name="fudge">The signing time fudge value</param>
public TsigMessageSecurityProvider(string name, string sharedKey, ushort fudge)
{
_name = name;
_fudge = fudge;
_sharedKey = Convert.FromBase64String(sharedKey);
if (_sharedKey == null)
{
throw new ArgumentException("Argument is not a valid base64 string", "sharedKey");
}
_hmac = new HMACSHA256(_sharedKey);
_algorithimName = Hmacsha1String;
}
#region IMessageSecurityProvider Members
/// <summary>
/// Apply a TSIG record to the request message.
/// </summary>
/// <param name="dnsQueryRequest">The <see cref="DnsQueryRequest"/> to add the security headers too.</param>
/// <returns>A <see cref="DnsQueryRequest"/> instance with additional security attributes assigned.</returns>
public DnsQueryRequest SecureMessage(DnsQueryRequest dnsQueryRequest)
{
DateTime signDateTime = DateTime.Now;
int timeHigh;
long timeLow;
byte[] messageBytes = dnsQueryRequest.GetMessageBytes();
Trace.WriteLine(String.Format("Message Header Bytes: {0}", DnsHelpers.DumpArrayToString(messageBytes)));
MemoryStream memoryStream = new MemoryStream();
memoryStream.Write(messageBytes, 0, messageBytes.Length);
// the shared key name
byte[] data = DnsHelpers.CanonicaliseDnsName(_name, false);
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort) (IPAddress.HostToNetworkOrder((ushort) NsClass.ANY) >> 16));
memoryStream.Write(data, 0, data.Length);
// the TTL value
data = BitConverter.GetBytes((uint) (IPAddress.HostToNetworkOrder((uint) 0) >> 32));
memoryStream.Write(data, 0, data.Length);
// the algorithim name
data = DnsHelpers.CanonicaliseDnsName(_algorithimName, true);
memoryStream.Write(data, 0, data.Length);
DnsHelpers.ConvertToDnsTime(signDateTime.ToUniversalTime(), out timeHigh, out timeLow);
data = BitConverter.GetBytes((ushort)(IPAddress.HostToNetworkOrder((ushort)timeHigh) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((uint) (IPAddress.HostToNetworkOrder((uint)timeLow) >> 32));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort) (IPAddress.HostToNetworkOrder(_fudge) >> 16));
memoryStream.Write(data, 0, data.Length);
data = BitConverter.GetBytes((ushort) (IPAddress.HostToNetworkOrder((ushort) RCode.NoError) >> 16));
memoryStream.Write(data, 0, data.Length);
// no other data
data = BitConverter.GetBytes((ushort) (IPAddress.HostToNetworkOrder((ushort) 0) >> 16));
memoryStream.Write(data, 0, data.Length);
byte[] dataToHash = memoryStream.ToArray();
Trace.WriteLine(String.Format("Data to hash: {0}", DnsHelpers.DumpArrayToString(dataToHash)));
byte[] mac = _hmac.ComputeHash(dataToHash);
Trace.WriteLine(String.Format("hash: {0}", DnsHelpers.DumpArrayToString(mac)));
dnsQueryRequest.AdditionalRRecords.Add(new TSigRecord(_name, _algorithimName, RCode.NoError, _fudge, dnsQueryRequest.TransactionID, new byte[] { }, mac, signDateTime));
return dnsQueryRequest;
}
#endregion
}
}

View File

@ -0,0 +1,213 @@
/**********************************************************************
* Copyright (c) 2010, j. montgomery *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* *
* + Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* *
* + Redistributions in binary form must reproduce the above copyright*
* notice, this list of conditions and the following disclaimer *
* in the documentation and/or other materials provided with the *
* distribution. *
* *
* + Neither the name of j. montgomery's employer nor the names of *
* its contributors may be used to endorse or promote products *
* derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS*
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,*
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,*
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED*
* OF THE POSSIBILITY OF SUCH DAMAGE. *
**********************************************************************/
using System;
using System.Text;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using DnDns.Enums;
using System.Runtime.InteropServices;
namespace DnDns
{
public class Tools
{
/// <summary>
/// Look up the port names for the given array of port numbers.
/// </summary>
/// <param name="port">An array of port numbers.</param>
/// <param name="proto">The protocol type. (TCP or UPD)</param>
/// <returns>The name of the port.</returns>
public static string GetServByPort(short[] port, ProtocolType proto)
{
StringBuilder sb = new StringBuilder();
for (int i=0; i < port.Length; i++)
{
sb.Append(GetServByPort(port[i], proto));
sb.Append(", ");
}
sb.Remove(sb.Length-2,2);
return sb.ToString();
}
/// <summary>
/// Tests to see if this is running on a Linux or Unix Platform
/// </summary>
/// <returns>true if unix or linux is detected</returns>
public static bool IsPlatformLinuxUnix()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return false;
return true;
}
/// <summary>
/// Look up the port name for any given port number.
/// </summary>
/// <param name="port">The port number.</param>
/// <param name="proto">The protocol type. (TCP or UPD)</param>
/// <returns>The name of the port.</returns>
public static string GetServByPort(short port, ProtocolType proto)
{
StringBuilder ans = new StringBuilder();
switch (proto)
{
case ProtocolType.Tcp:
{
TcpServices tcps;
tcps = (TcpServices)port;
ans.Append(tcps);
ans.Append("(");
ans.Append(port);
ans.Append(")");
break;
}
case ProtocolType.Udp:
{
UdpServices udps;
udps = (UdpServices)port;
ans.Append(udps);
ans.Append("(");
ans.Append(port);
ans.Append(")");
break;
}
default:
{
ans.Append("(");
ans.Append(port);
ans.Append(")");
break;
}
}
return ans.ToString();
}
public static string DiscoverUnixDnsServerAddress()
{
if (System.IO.File.Exists("/etc/resolv.conf"))
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(new System.IO.FileStream("/etc/resolv.conf", System.IO.FileMode.Open)))
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine().TrimStart();
if (line.StartsWith("nameserver") && line.Length > 11)
{
line = line.Substring(10).Trim();
if (!string.IsNullOrEmpty(line))
{
sr.Dispose();
return line;
}
}
}
sr.Dispose();
}
}
return string.Empty;
}
//public static IPAddressCollection DiscoverDnsServerAddresses()
//{
// NetworkInterface[] arrNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
// foreach (NetworkInterface objNetworkInterface in arrNetworkInterfaces)
// {
// if (
// (objNetworkInterface.OperationalStatus == OperationalStatus.Up) &&
// (objNetworkInterface.Speed > 0) &&
// (objNetworkInterface.NetworkInterfaceType != NetworkInterfaceType.Loopback) &&
// (objNetworkInterface.NetworkInterfaceType != NetworkInterfaceType.Tunnel)
// )
// {
// IPAddressCollection candidate = objNetworkInterface.GetIPProperties().DnsAddresses;
// bool found = false;
// foreach (IPAddress addr in candidate)
// {
// // Make this collection contains IPv4 addresses only
// if (addr.AddressFamily == AddressFamily.InterNetwork)
// found = true;
// }
// if (found)
// return candidate;
// }
// }
// return null;
//}
//public static uint ByteToUInt(byte[] theByte)
//{
// uint l = 0;
// for (int i = 0; i < theByte.Length; i++)
// {
// l += (uint)theByte[(theByte.Length - i) - 1] << i * 8;
// }
// return l;
//}
// Use BitConverter.GetBytes()
//public static byte[] IntToByteArray(uint theInt)
//{
// byte[] byteArray = new byte[4];
// int i;
// int shift;
// for (i = 0, shift = 24; i < 4; i++, shift -= 8)
// byteArray[i] = (byte)(0xFF & (theInt >> shift));
// return byteArray;
//}
// use BitConverter.GetBytes()
//public static byte[] UshortToByteArray(ushort theShort)
//{
// byte[] byteArray = new byte[2];
// int i;
// int shift;
// for (i = 0, shift = 16; i < 2; i++, shift -= 8)
// byteArray[i] = (byte)(0xFF & (theShort >> shift));
// return byteArray;
//}
}
}

View File

@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace SMTPServer
{
public class MysqlDB : DbContext
{
const string Hostname = "192.168.178.148";
const string DatabaseName = "vmail";
const string Uid = "root";
const string Pwd = "fabian.11";
public DbSet<Domains> Domains { get; set; }
public DbSet<Accounts> Accounts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseMySql(@"Server=" + Hostname + ";database=" + DatabaseName + ";uid=" + Uid + ";pwd=" + Pwd + ";");
}
public class Domains
{
public int Id { get; set; }
public string Domain { get; set; }
public bool IsMail { get; set; }
}
public class Accounts
{
public int Id { get; set; }
public string Name { get; set; }
public int Domain { get; set; }
public string Password { get; set; }
public bool Active { get; set; }
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SMTPServer.Exceptions
{
class NoMailsInQueueException : Exception
{
public NoMailsInQueueException() : base()
{
}
public NoMailsInQueueException(string message) : base(message)
{
}
public NoMailsInQueueException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -0,0 +1,22 @@
using System;
namespace SMTPServer.Exceptions
{
public class PortUsedException : Exception
{
public PortUsedException() : base()
{
}
public PortUsedException(string message) : base(message)
{
}
public PortUsedException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -0,0 +1,71 @@
using DnsClient;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace SMTPServer
{
public class GetDNSEntries
{
public static async System.Threading.Tasks.Task<string[]> GetMXRecordAsync(string domain) => await GetMXRecordAsync(domain, 0);
public static async System.Threading.Tasks.Task<string[]> GetMXRecordAsync(string domain, int nr)
{
var lookup = new LookupClient();
var result = await lookup.QueryAsync(domain, QueryType.MX);
var records = result.AllRecords.ToArray();
//ToDo priority sorting
var rec = new string[records.Length];
for(int i = 0; i < records.Length; i++)
{
var str = records[i].RecordToString();
var s = str.Split(' ');
if (s.Length == 1)
{
rec[i] = s[0];
} else if (s.Length == 2)
{
rec[i] = s[1];
} else
{
rec[i] = null;
}
}
return rec;
/*
foreach (var r in result.AllRecords)
{
Console.WriteLine(r.RecordToString());
}*/
}
public static async System.Threading.Tasks.Task<string[]> GetIPAddressFromDnsArrayAsync(string[] dnsnames)
{
var ips = new string[dnsnames.Length];
for(int i = 0; i < dnsnames.Length; i++)
{
ips[i] = await GetIpFromDnsNameAsync(dnsnames[i]);
}
return ips;
}
public static async System.Threading.Tasks.Task<string> GetIpFromDnsNameAsync(string dnsname)
{
var lookup = new LookupClient();
var result = await lookup.QueryAsync(dnsname, QueryType.A);
var records = result.AllRecords.ToArray();
if (records.Length < 1) return null;
return records[0].RecordToString();
}
}
}

View File

@ -0,0 +1,20 @@

// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._Count")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._Mail")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._QueueEntered")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._DestinationIps")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._DestinationDnsNames")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._From")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._To")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._Subject")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._Others")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._Date")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._MessageId")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._MIME_Version")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._DestinationIndex")]

View File

@ -0,0 +1,53 @@
using System;
//namespace Logging
//{
public class Logging
{
private static LoggingType[] _ActiveLoggingTypes { get; set; }
public enum LoggingType
{
TRACE,
ERROR,
DEBUG,
WARNING,
INFO,
ALL
}
public static void AddLogMessage(LoggingType messagetype, string message)
{
if(CheckLoggingType(messagetype)) PrintToConsole(messagetype, message);
}
public static void AddException(Exception e)
{
AddLogMessage(LoggingType.ERROR, e.Message);
}
private static bool CheckLoggingType(LoggingType lt){
if (_ActiveLoggingTypes == null ||_ActiveLoggingTypes.Length < 1) {
_ActiveLoggingTypes = new LoggingType[1];
_ActiveLoggingTypes[0] = LoggingType.ALL;
}
bool found = false;
foreach(LoggingType l in _ActiveLoggingTypes)
{
if (l == lt || l == LoggingType.ALL)
{
found = true;
break;
}
}
return found;
}
private static void PrintToConsole(LoggingType lt, string message)
{
Console.WriteLine("[" + lt.ToString() + "]" + message);
}
}
//}

View File

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace SMTPServer
{
public class MTACommandsDict : Dictionary<MTACommands, String>
{
public MTACommandsDict()
{
Add(MTACommands.HELO, "HELO");
Add(MTACommands.DATA, "DATA");
Add(MTACommands.EXPN, "EXPN");
Add(MTACommands.HELP, "HELP");
Add(MTACommands.MAIL_FROM, "MAIL FROM");
Add(MTACommands.NOOP, "NOOP");
Add(MTACommands.QUIT, "QUIT");
Add(MTACommands.RCPT_TO, "RCPT TO");
Add(MTACommands.RSET, "RSET");
Add(MTACommands.SAML_FROM, "SAML FROM");
Add(MTACommands.SEND_FROM, "SEND FROM");
Add(MTACommands.SOML_FROM, "SOML FROM");
Add(MTACommands.TURN, "TURN");
Add(MTACommands.VERB, "VERB");
Add(MTACommands.VRFY, "VRFY");
Add(MTACommands.ATRN, "ATRN");
Add(MTACommands.AUTH, "AUTH");
Add(MTACommands.BDAT, "BDAT");
Add(MTACommands.EHLO, "EHLO");
Add(MTACommands.ETRN, "ETRN");
Add(MTACommands.RCPT, "RCPT");
Add(MTACommands.SAML, "SAML");
Add(MTACommands.SEND, "SEND");
Add(MTACommands.SOML, "SOML");
Add(MTACommands.STARTTL, "STARTTLs");
Add(MTACommands.AUTH_LOGIN, "AUTH LOGIN");
}
}
public enum MTACommands
{
HELO,
MAIL_FROM,
RCPT_TO,
DATA,
RSET,
QUIT,
HELP,
VRFY,
EXPN,
VERB,
NOOP,
TURN,
SEND_FROM,
SOML_FROM,
SAML_FROM,
ATRN,
AUTH,
BDAT,
EHLO,
ETRN,
RCPT,
SAML,
SEND,
SOML,
STARTTL,
AUTH_LOGIN
}
public enum ResponseCodes
{
C211 = 211,
C214 = 214,
C220 = 220,
C221 = 221,
C250 = 250,
C251 = 251,
C252 = 252,
C253 = 253,
C334 = 334,
C354 = 354,
C355 = 355,
C421 = 421,
C432 = 432,
C450 = 450,
C451 = 451,
C452 = 452,
C453 = 453,
C454 = 454,
C458 = 458,
C459 = 459,
C500 = 500,
C501 = 501,
C502 = 502,
C503 = 503,
C504 = 504,
C521 = 521,
C530 = 530,
C534 = 534,
C538 = 538,
C550 = 550,
C551 = 551,
C552 = 552,
C553 = 553,
C554 = 554
}
}

View File

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace SMTPServer
{
class MTASession
{
private static MTACommandsDict MTACommandsDict = new MTACommandsDict();
private ConnectedEventArgs ConnectedEventArgs { get; set; }
private bool Initialized { get; set; }
private string SenderHostname { get; set; }
private bool AuthActive { get; set; }
private string Username { get; set; }
private string Password { get; set; }
private bool Authenticated { get; set; }
private bool DATA { get; set; }
private string Mail_From { get; set; }
public MTASession(ConnectedEventArgs e)
{
ConnectedEventArgs = e;
Thread th = new Thread(new ThreadStart(StartCommunication));
th.Start();
}
private void StartCommunication()
{
ConnectedEventArgs.SendResponse(ResponseCodes.C220, MailTransferAgent.Hostname);
ConnectedEventArgs.NewLine += ConnectedEventArgs_NewLine;
}
private void ConnectedEventArgs_NewLine(string line)
{
if (line.StartsWith(MTACommandsDict[MTACommands.RSET]))
{
// Schauen was alles resettet wird
}
if (!Initialized)
{
if (line.StartsWith(MTACommandsDict[MTACommands.HELO]))
{
ConnectedEventArgs.SendResponse(ResponseCodes.C250, MailTransferAgent.Hostname);
//} else if (line.StartsWith(MTACommandsDict[MTACommands.EHLO])) {
// ConnectedEventArgs.SendResponse(ResponseCodes.C250, MailTransferAgent.Hostname);
}
else
{
ConnectedEventArgs.SendResponse(ResponseCodes.C503, MailTransferAgent.Hostname);
return;
}
Initialized = true;
}
if (line.StartsWith(MTACommandsDict[MTACommands.AUTH_LOGIN]))
{
AuthActive = true;
ConnectedEventArgs.SendResponse(ResponseCodes.C334);
}
else if (line.StartsWith(MTACommandsDict[MTACommands.MAIL_FROM]))
{
var args = line.Split(':');
if (args.Length < 2)
{
ConnectedEventArgs.SendResponse(ResponseCodes.C501);
return;
}
var mailFrom = args[1];
mailFrom.Replace('<', ' ');
}
else if (line.StartsWith(MTACommandsDict[MTACommands.QUIT]))
{
//throw new NotImplementedException();
} else
{
ConnectedEventArgs.SendResponse(ResponseCodes.C500);
}
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SMTPServer
{
public class Mail
{
public string To { get; private set; }
public string From { get; private set; }
public string Subject { get; private set; }
public string MessageId { get; private set; }
public string Date { get; private set; }
public string MIME_Version { get; private set; }
public string Others { get; private set; }
public Mail(string mail)
{
}
public string GetSenderDomain()
{
return From.Split('@')[1];
}
}
}

View File

@ -0,0 +1,87 @@
using DnsClient;
using SMTPServer.Exceptions;
using System;
using System.Collections.Generic;
namespace SMTPServer
{
public class MailQueue
{
private static List<QueueMail> Mails = null;
public static async void AddToMesssageQueueAsync(Mail mail)
{
var qmail = new QueueMail(mail);
qmail.DestinationDnsNames = await GetDNSEntries.GetMXRecordAsync(mail.GetSenderDomain());
qmail.DestinationIps = await GetDNSEntries.GetIPAddressFromDnsArrayAsync(qmail.DestinationDnsNames);
lock (Mails)
{
if (Mails == null)
{
Mails = new List<QueueMail>();
}
Mails.Add(qmail);
}
}
public static QueueMail GetNextMail()
{
lock (Mails)
{
if(Mails.Count < 1)
{
throw new NoMailsInQueueException();
}
var m = Mails[0];
Mails.Remove(m);
m.Count++;
Mails.Add(m);
return m;
}
}
public static void RemoveMailFromQueue(QueueMail mail)
{
lock (Mails)
{
Mails.Remove(mail);
}
}
public class QueueMail
{
public DateTime QueueEntered { get; private set; }
public int Count { get; set; }
public Mail Mail { get; private set; }
public string[] DestinationIps { get; set; }
public string[] DestinationDnsNames { get; set; }
public int DestinationIndex { get; set; }
public QueueMail (Mail mail)
{
Mail = mail;
QueueEntered = DateTime.Now;
Count = 0;
}
public byte[] GetIpBytes() => GetIpBytes(DestinationIndex);
public byte[] GetIpBytes(int index)
{
var parts = DestinationIps[index].Split('.');
var bytes = new byte[parts.Length];
for(int i = 0; i < parts.Length; i++)
{
bytes[i] = byte.Parse(parts[i]);
}
return bytes;
}
}
}
}

View File

@ -0,0 +1,69 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup Label="Configuration">
<NoWarn>1701;1702;1705;1006</NoWarn>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
</ItemGroup>
<ItemGroup>
<Compile Remove="DNSClient\**" />
<Compile Remove="DNS\**" />
<EmbeddedResource Remove="DNSClient\**" />
<EmbeddedResource Remove="DNS\**" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<PackageReference Include="DnsClient">
<Version>1.0.0-beta-1011</Version>
</PackageReference>
<PackageReference Include="Microsoft.DotNet.Cli.Utils">
<Version>1.0.0-preview2-1-003177</Version>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools">
<Version>1.1.0-preview4-final</Version>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet">
<Version>1.1.0-preview4-final</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore">
<Version>5.0.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NET.Sdk">
<Version>1.0.0-alpha-20161104-2</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.Platforms">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.Runtime">
<Version>1.0.2-rc2-24027</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.Targets">
<Version>1.1.0</Version>
</PackageReference>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql">
<Version>1.1.0-rtm-10012</Version>
</PackageReference>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql.Design">
<Version>1.1.0-rtm-10012</Version>
</PackageReference>
<PackageReference Include="System.IO.FileSystem">
<Version>4.0.1</Version>
</PackageReference>
<PackageReference Include="System.Net.NameResolution">
<Version>4.3.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using SMTPServer.Exceptions;
using System.Net;
namespace SMTPServer
{
class MailTransferAgent
{
public const string Hostname = "mail.fstamm.net";
private static MTACommandsDict MTACommandsDict = new MTACommandsDict();
private static Thread Thread;
private static List<MTASession> Sessions { get; set; }
public static void StartMailTransferAgent()
{
Thread = new Thread(new ThreadStart(MTA));
Thread.Start();
var portListener = new PortListener(25);
portListener.OnConnected += PortListener_OnConnected;
}
private static void PortListener_OnConnected(object source, ConnectedEventArgs e)
{
var session = new MTASession(e);
lock (Sessions)
{
Sessions.Add(session);
}
//throw new NotImplementedException();
}
public static void MTA()
{
while (true)
{
MailQueue.QueueMail mail = null;
try
{
MailQueue.GetNextMail();
}
catch (NoMailsInQueueException)
{
Thread.Sleep(100);
continue;
}
var charset = Encoding.UTF8;
var client = new StartTcpConnection(25, new IPAddress(mail.GetIpBytes()), charset);
if (!client.Connected) ; //ToDo Errorfall
}
}
public static string WaitForResponseCode(ResponseCodes code)
{
return null;
}
}
}

View File

@ -0,0 +1,125 @@
using SMTPServer.Exceptions;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace SMTPServer
{
public delegate void ConnectedEventHandler(object source, ConnectedEventArgs e);
public class ConnectedEventArgs : EventArgs
{
public delegate void ReceivedLineEventHandler(string line);
public event ReceivedLineEventHandler NewLine;
private Encoding Encoding { get; set; }
private string Others { get; set; }
public TcpClient Client { get; private set; }
private NetworkStream Stream { get; set; }
public ConnectedEventArgs(TcpClient client, Encoding encoding)
{
Client = client;
Encoding = encoding;
Stream = client.GetStream();
}
public void SendResponse(ResponseCodes responseCode) => SendResponse(responseCode, "");
public void SendResponse (ResponseCodes responseCode, string args)
{
var text = ((int)responseCode).ToString();
text += " " + args;
var bytes = Encoding.UTF8.GetBytes(text);
Stream.Write(bytes, 0 , bytes.Length);
}
private void ReadClientInput()
{
while (true)
{
if (Stream.DataAvailable)
{
byte[] buffer = new byte[Client.ReceiveBufferSize];
Stream.Read(buffer, 0, buffer.Length);
var str = Encoding.GetString(buffer);
lock (Others)
{
Others += str;
}
CheckLines();
} else
{
Thread.Sleep(10);
}
}
}
private void CheckLines()
{
lock (Others)
{
var line = "";
foreach (char c in Others)
{
if (c.Equals('\n'))
{
Console.WriteLine(line);
NewLine(line);
line = "";
}
else line += c;
}
Others = line;
}
}
}
public class PortListener
{
static List<int> _Ports = null;
TcpListener _Listener;
public event ConnectedEventHandler OnConnected;
public PortListener(int port)
{
if(_Ports == null)
{
_Ports = new List<int>();
}
foreach(var i in _Ports)
{
if (i == port)
{
throw new PortUsedException();
}
}
_Ports.Add(port);
StartListening(new TcpListener(IPAddress.Any, port));
}
private void StartListening(TcpListener listener)
{
listener.Start();
_Listener = listener;
Thread thread = new Thread(new ThreadStart(ListenerAsync));
thread.Start();
}
private async void ListenerAsync()
{
var client = await _Listener.AcceptTcpClientAsync();
Console.WriteLine("Connection");
OnConnected(this, new ConnectedEventArgs(client, Encoding.UTF8));
}
}
}

View File

@ -0,0 +1,119 @@
using DnsClient;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace SMTPServer
{
class Program
{
static void Main(string[] args)
{
/*using (var context = new MysqlDB())
{
context.Database.EnsureCreated();
var domain = new Domains()
{
Domain = "HI",
IsMail = true
};
context.Add(domain);
context.SaveChanges();
var res = context.Domains.Find();
}*/
//Console.WriteLine("####################");
/*var request = new DnsQueryRequest();
var response = request.ResolveAsync("fabianstamm.de", DnDns.Enums.NsType.MX, DnDns.Enums.NsClass.ANY, System.Net.Sockets.ProtocolType.Tcp);
OutputResults(response);*/
//var t = new Thread(new ThreadStart(Test));
//t.Start();
//TestAsync();
var listener = new PortListener(5122);
listener.OnConnected += Listener_OnConnected;
while (true)
{
Thread.Sleep(500);
}
//var listener = new PortListener(5122);
//listener.OnConnected += Listener_OnConnected;
}
private static async void TestAsync()
{
var lookup = new LookupClient();
var result = await lookup.QueryAsync("fabianstamm.de.", QueryType.MX);
foreach (var r in result.AllRecords)
{
Console.WriteLine(r.RecordToString());
}
}
private static void Listener_OnConnected(object source, ConnectedEventArgs e)
{
var session = new MTASession(e);
//throw new NotImplementedException();
}
/*
private static async void OutputResults(Task<DnDns.Query.DnsQueryResponse> res)
{
OutputResults(await res);
}
private static void OutputResults(DnDns.Query.DnsQueryResponse response)
{
Console.WriteLine("Bytes received: " + response.BytesReceived);
Console.WriteLine("Name: " + response.Name);
Console.WriteLine("OpCode: " + response.NsClass);
Console.WriteLine("NsFlags: " + response.NsFlags);
Console.WriteLine("NsType: " + response.NsType);
Console.WriteLine("RCode: " + response.RCode);
Console.WriteLine("OpCode: " + response.OpCode);
// Enumerate the Answer Records
Console.WriteLine("Answers:");
foreach (IDnsRecord record in response.Answers)
{
Console.WriteLine(record.Answer);
Console.WriteLine(" |--- RDATA Field Length: " + record.DnsHeader.DataLength);
Console.WriteLine(" |--- Name: " + record.DnsHeader.Name);
Console.WriteLine(" |--- NS Class: " + record.DnsHeader.NsClass);
Console.WriteLine(" |--- NS Type: " + record.DnsHeader.NsType);
Console.WriteLine(" |--- TTL: " + record.DnsHeader.TimeToLive);
Console.WriteLine();
}
foreach (IDnsRecord record in response.AuthoritiveNameServers)
{
Console.WriteLine(record.Answer);
Console.WriteLine(" |--- RDATA Field Length: " + record.DnsHeader.DataLength);
Console.WriteLine(" |--- Name: " + record.DnsHeader.Name);
Console.WriteLine(" |--- NS Class: " + record.DnsHeader.NsClass);
Console.WriteLine(" |--- NS Type: " + record.DnsHeader.NsType);
Console.WriteLine(" |--- TTL: " + record.DnsHeader.TimeToLive);
Console.WriteLine();
}
foreach (IDnsRecord record in response.AdditionalRRecords)
{
Console.WriteLine(record.Answer);
Console.WriteLine(" |--- RDATA Field Length: " + record.DnsHeader.DataLength);
Console.WriteLine(" |--- Name: " + record.DnsHeader.Name);
Console.WriteLine(" |--- NS Class: " + record.DnsHeader.NsClass);
Console.WriteLine(" |--- NS Type: " + record.DnsHeader.NsType);
Console.WriteLine(" |--- TTL: " + record.DnsHeader.TimeToLive);
Console.WriteLine();
}
}*/
}
}

View File

@ -0,0 +1,7 @@
{
"profiles": {
"SMTPServer": {
"commandName": "Project"
}
}
}

View File

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace SMTPServer
{
class StartTcpConnection : Socket
{
private List<string> Lines = new List<string>();
private int LinesAvailable { get
{
return Lines.Count;
}
}
private string Others { get; set; }
private Encoding Encoding { get; set; }
public StartTcpConnection(int port, IPAddress destination, Encoding encoding) : base (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
{
this.Connect(new IPEndPoint(destination, port));
Thread readLines = new Thread(new ThreadStart(ReadClientInput));
}
private void ReadClientInput()
{
while (true)
{
byte[] buffer = new byte[ReceiveBufferSize];
Receive(buffer);
var str = Encoding.GetString(buffer);
lock (Others)
{
Others += str;
}
CheckLines();
}
}
private void CheckLines()
{
lock (Others)
{
var line = "";
foreach (char c in Others)
{
if (c.Equals('\n'))
{
lock (Lines)
{
Lines.Add(line);
line = "";
}
}
else line += c;
}
Others = line;
}
}
public string GetLine()
{
lock (Lines)
{
return Lines[0];
}
}
}
}