Skip to content

Commit

Permalink
feat(MasterFile): handle unknown RDATA
Browse files Browse the repository at this point in the history
  • Loading branch information
richardschneider committed Aug 18, 2018
1 parent a7e8327 commit 77ba4a7
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 10 deletions.
44 changes: 44 additions & 0 deletions src/MasterReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,49 @@ public IPAddress ReadIPAddress(int length = 4)
return IPAddress.Parse(ReadToken());
}

/// <summary>
/// Read hex encoded RDATA.
/// </summary>
/// <returns>
/// A byte array containing the RDATA.
/// </returns>
/// <remarks>
/// See <see href="https://tools.ietf.org/html/rfc3597#section-5"/> for all
/// the details.
/// </remarks>
public byte[] ReadResourceData()
{
var leadin = ReadToken();
if (leadin != "#")
throw new FormatException($"Expected RDATA leadin '\\#', not '{leadin}'.");
var length = ReadUInt32();
if (length == 0)
return new byte[0];

// Get the hex string.
var sb = new StringBuilder();
while (sb.Length < length * 2)
{
var word = ReadToken();
if (word.Length == 0)
break;
if (word.Length % 2 != 0)
throw new FormatException($"The hex word ('{word}') must have an even number of digits.");
sb.Append(word);
}
if (sb.Length != length * 2)
throw new FormatException("Wrong number of RDATA hex digits.");

// Convert hex string into byte array.
var bytes = new byte[length];
for (int i = 0; i < length * 2; i += 2)
{
bytes[i / 2] = Convert.ToByte(sb.ToString(i, 2), 16);
}

return bytes;
}

/// <summary>
/// Read a resource record.
/// </summary>
Expand Down Expand Up @@ -290,6 +333,7 @@ string ReadToken()
c = text.Read();
// TODO: \DDD
sb.Append((char)c);
skipWhitespace = false;
continue;
}

Expand Down
2 changes: 1 addition & 1 deletion src/NULLRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected override void ReadData(DnsReader reader, int length)
/// <inheritdoc />
internal override void ReadData(MasterReader reader)
{
Data = Convert.FromBase64String(reader.ReadString());
Data = reader.ReadResourceData();
}

/// <inheritdoc />
Expand Down
11 changes: 8 additions & 3 deletions src/ResourceRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -339,13 +339,18 @@ public void Write(TextWriter writer)
/// <remarks>
/// Derived classes should implement this method.
/// <para>
/// By default, this will write the base64 encoding of
/// the <see cref="GetData">RDATA</see>.
/// By default, this will write the hex encoding of
/// the <see cref="GetData">RDATA</see> preceeded by
/// "\#" and the number integer bytes.
/// </para>
/// </remarks>
protected virtual void WriteData(TextWriter writer)
{
writer.Write(Convert.ToBase64String(GetData()));
var rdata = GetData();
writer.Write("\\# ");
writer.Write(rdata.Length);
writer.Write(' ');
writer.Write(BitConverter.ToString(rdata).Replace("-", ""));
}

/// <summary>
Expand Down
6 changes: 6 additions & 0 deletions src/UnknownRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ protected override void ReadData(DnsReader reader, int length)
Data = reader.ReadBytes(length);
}

/// <inheritdoc />
internal override void ReadData(MasterReader reader)
{
Data = reader.ReadResourceData();
}

/// <inheritdoc />
protected override void WriteData(DnsWriter writer)
{
Expand Down
54 changes: 53 additions & 1 deletion test/MasterReaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void ReadResourceWithUnknownClass()
[TestMethod]
public void ReadResourceWithUnknownType()
{
var reader = new MasterReader(new StringReader("me CH TYPE1234"));
var reader = new MasterReader(new StringReader("me CH TYPE1234 \\# 0"));
var resource = reader.ReadResourceRecord();
Assert.AreEqual("me", resource.Name);
Assert.AreEqual(Class.CH, resource.Class);
Expand Down Expand Up @@ -185,5 +185,57 @@ example.com. IN SOA ns.example.com. username.example.com. ( 2007120710 1 2 4
}
Assert.AreEqual(15, resources.Count);
}

[TestMethod]
public void ReadResourceData()
{
var reader = new MasterReader(new StringReader("\\# 0"));
var rdata = reader.ReadResourceData();
Assert.AreEqual(0, rdata.Length);

reader = new MasterReader(new StringReader("\\# 3 abcdef"));
rdata = reader.ReadResourceData();
CollectionAssert.AreEqual(new byte[] { 0xab, 0xcd, 0xef }, rdata);

reader = new MasterReader(new StringReader("\\# 3 ab cd ef"));
rdata = reader.ReadResourceData();
CollectionAssert.AreEqual(new byte[] { 0xab, 0xcd, 0xef }, rdata);

reader = new MasterReader(new StringReader("\\# 3 abcd (\r\n ef )"));
rdata = reader.ReadResourceData();
CollectionAssert.AreEqual(new byte[] { 0xab, 0xcd, 0xef }, rdata);
}

[TestMethod]
[ExpectedException(typeof(FormatException))]
public void ReadResourceData_MissingLeadin()
{
var reader = new MasterReader(new StringReader("0"));
var _ = reader.ReadResourceData();
}

[TestMethod]
[ExpectedException(typeof(FormatException))]
public void ReadResourceData_BadHex_BadDigit()
{
var reader = new MasterReader(new StringReader("\\# 3 ab cd ez"));
var _ = reader.ReadResourceData();
}

[TestMethod]
[ExpectedException(typeof(FormatException))]
public void ReadResourceData_BadHex_NotEven()
{
var reader = new MasterReader(new StringReader("\\# 3 ab cd e"));
var _ = reader.ReadResourceData();
}

[TestMethod]
[ExpectedException(typeof(FormatException))]
public void ReadResourceData_BadHex_TooFew()
{
var reader = new MasterReader(new StringReader("\\# 3 abcd"));
var _ = reader.ReadResourceData();
}
}
}
10 changes: 5 additions & 5 deletions test/ResourceRecordTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,23 +141,23 @@ public void Stringing()
Name = "x.emanon.org",
Type = DnsType.A
};
Assert.AreEqual("x.emanon.org IN A", a.ToString());
Assert.AreEqual("x.emanon.org IN A \\# 0", a.ToString());

a = new ResourceRecord
{
Name = "x.emanon.org",
Type = DnsType.A,
Class = Class.CH
};
Assert.AreEqual("x.emanon.org CH A", a.ToString());
Assert.AreEqual("x.emanon.org CH A \\# 0", a.ToString());

a = new ResourceRecord
{
Name = "x.emanon.org",
Type = DnsType.A,
TTL = TimeSpan.FromSeconds(123)
};
Assert.AreEqual("x.emanon.org 123 IN A", a.ToString());
Assert.AreEqual("x.emanon.org 123 IN A \\# 0", a.ToString());
}

[TestMethod]
Expand Down Expand Up @@ -192,7 +192,7 @@ public void Stringing_UnknownClass()
Class = (Class)1234,
Type = DnsType.A
};
Assert.AreEqual("x.emanon.org CLASS1234 A", a.ToString());
Assert.AreEqual("x.emanon.org CLASS1234 A \\# 0", a.ToString());
}

[TestMethod]
Expand All @@ -203,7 +203,7 @@ public void Stringing_UnknownType()
Name = "x.emanon.org",
Type = (DnsType)1234
};
Assert.AreEqual("x.emanon.org IN TYPE1234", a.ToString());
Assert.AreEqual("x.emanon.org IN TYPE1234 \\# 0", a.ToString());
}
}
}
18 changes: 18 additions & 0 deletions test/UnknownRecordTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,23 @@ public void Equality()
Assert.IsFalse(a.Equals(null));
}

[TestMethod]
public void Roundtrip_Master()
{
var a = new UnknownRecord
{
Name = "a.example",
Class = (Class)32,
Type = (DnsType)731,
Data = new byte[] { 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45 }
};
var b = (UnknownRecord)new ResourceRecord().Read(a.ToString());
Assert.AreEqual(a.Name, b.Name);
Assert.AreEqual(a.Class, b.Class);
Assert.AreEqual(a.Type, b.Type);
Assert.AreEqual(a.TTL, b.TTL);
CollectionAssert.AreEqual(a.Data, b.Data);
}

}
}

0 comments on commit 77ba4a7

Please sign in to comment.