Commit 4c1b2b1e authored by Brickner_cp's avatar Brickner_cp

Refactor ComparePacketsToWiresharkTest.

parent 5df9d87d
...@@ -23,719 +23,353 @@ using PcapDotNet.TestUtils; ...@@ -23,719 +23,353 @@ using PcapDotNet.TestUtils;
namespace PcapDotNet.Core.Test namespace PcapDotNet.Core.Test
{ {
/// <summary> internal abstract class WiresharkDatagramComparer
/// Summary description for WiresharkCompareTests
/// </summary>
[TestClass]
public class WiresharkCompareTests
{ {
private const string WiresharkDiretory = @"C:\Program Files\Wireshark\"; public Datagram Compare(XElement layer, object datagramParent)
private const string WiresharkTsharkPath = WiresharkDiretory + @"tshark.exe"; {
PropertyInfo property = datagramParent.GetType().GetProperty(PropertyName);
if (property == null)
return null;
private const bool IsRetry Datagram datagram = (Datagram)property.GetValue(datagramParent);
// = true; if (!Ignore(datagram))
= false; CompareDatagram(layer, datagramParent as Datagram, datagram);
private const byte RetryNumber = 61; return datagram;
}
/// <summary> protected abstract string PropertyName { get; }
/// Gets or sets the test context which provides
/// information about and functionality for the current test run.
/// </summary>
public TestContext TestContext { get; set; }
#region Additional test attributes protected abstract bool CompareField(XElement field, Datagram parentDatagram, Datagram datagram);
//
// You can use the following additional attributes as you write your tests:
//
// Use ClassInitialize to run code before running the first test in the class
// [ClassInitialize()]
// public static void MyClassInitialize(TestContext testContext) { }
//
// Use ClassCleanup to run code after all tests in a class have run
// [ClassCleanup()]
// public static void MyClassCleanup() { }
//
// Use TestInitialize to run code before running each test
// [TestInitialize()]
// public void MyTestInitialize() { }
//
// Use TestCleanup to run code after each test has run
// [TestCleanup()]
// public void MyTestCleanup() { }
//
#endregion
[TestMethod] protected virtual bool Ignore(Datagram datagram)
public void ComparePacketsToWiresharkTest()
{ {
#pragma warning disable 162 // This code is unreachable on purpose return false;
if (IsRetry) }
protected void CompareDatagram(XElement layer, Datagram parentDatagram, Datagram datagram)
{ {
ComparePacketsToWireshark(null); foreach (var field in layer.Fields())
return; {
if (!CompareField(field, parentDatagram, datagram))
break;
} }
#pragma warning restore 162
Random random = new Random(); WiresharkCompareTests.CompareProtocols(datagram, layer);
for (int i = 0; i != 10; ++i) }
}
internal abstract class WiresharkDatagramComparerSimple : WiresharkDatagramComparer
{ {
// Create packets protected override sealed bool CompareField(XElement field, Datagram parentDatagram, Datagram datagram)
List<Packet> packets = new List<Packet>(CreateRandomPackets(random, 200)); {
return CompareField(field, datagram);
}
// Compare packets to wireshark protected abstract bool CompareField(XElement field, Datagram parentDatagram);
ComparePacketsToWireshark(packets);
} }
//[WiresharkDatagramComparerRegistration("eth")]
internal class WiresharkDatagramComparerEthernet : WiresharkDatagramComparerSimple
{
protected override string PropertyName
{
get { return "Ethernet"; }
} }
[TestMethod] protected override bool CompareField(XElement field, Datagram datagram)
public void CompareTimestampPacketsToWiresharkTest()
{ {
const long Ticks = 633737178954260865; EthernetDatagram ethernetDatagram = (EthernetDatagram)datagram;
DateTime timestamp = new DateTime(Ticks).ToUniversalTime().ToLocalTime(); switch (field.Name())
{
case "eth.dst":
CompareEthernetAddress(field, ethernetDatagram.Destination);
break;
// Create packet case "eth.src":
Packet packet = new Packet(new byte[14], timestamp, DataLinkKind.Ethernet); CompareEthernetAddress(field, ethernetDatagram.Source);
break;
// Compare packet to wireshark case "eth.type":
ComparePacketsToWireshark(new[] { packet }); field.AssertShowHex((ushort)ethernetDatagram.EtherType);
break;
// BUG: Limited timestamp due to Windows bug: https://connect.microsoft.com/VisualStudio/feedback/details/559198/net-4-datetime-tolocaltime-is-sometimes-wrong case "eth.trailer":
// Now check different dates. if (ethernetDatagram.Trailer != null)
// packet = new Packet(new byte[14], new DateTime(2004,08,25,10,36,41, DateTimeKind.Utc).ToLocalTime(), DataLinkKind.Ethernet); field.AssertValue(ethernetDatagram.Trailer);
// ComparePacketsToWireshark(new[] { packet }); break;
case "":
if (ethernetDatagram.Trailer != null)
field.AssertValue(ethernetDatagram.FrameCheckSequence);
break;
default:
throw new InvalidOperationException("Invalid etherent field " + field.Name());
} }
[TestMethod] return true;
public void CompareEthernetTrailerToWiresharkTest()
{
const string PacketString = "001120cf0900000c29566988080045000029627b00008006de80c0a8640bc0a81477a42cc03bdd3c481c6cfcd72050104278a5a90000000e01bf0101";
Packet packet = Packet.FromHexadecimalString(PacketString, DateTime.Now, DataLinkKind.Ethernet);
ComparePacketsToWireshark(packet);
} }
[TestMethod] private static void CompareEthernetAddress(XElement element, MacAddress address)
public void CompareEthernetFcsToWiresharkTest()
{ {
Packet packet = PacketBuilder.Build(DateTime.Now, foreach (var field in element.Fields())
new EthernetLayer(),
new IpV4Layer
{ {
Protocol = IpV4Protocol.Udp switch (field.Name())
}); {
byte[] buffer = new byte[packet.Length + 100]; case "eth.addr":
new Random().NextBytes(buffer); field.AssertShow(address.ToString().ToLower());
packet.CopyTo(buffer, 0); break;
packet = new Packet(buffer, DateTime.Now, DataLinkKind.Ethernet);
ComparePacketsToWireshark(packet); case "eth.ig":
case "eth.lg":
break;
default:
throw new InvalidOperationException("Invalid ethernet address field " + field.Name());
}
}
}
} }
private enum PacketType internal class WiresharkDatagramComparerArp : WiresharkDatagramComparerSimple
{ {
Ethernet, protected override string PropertyName
Arp, {
IpV4, get { return "Arp"; }
IpV4OverIpV4,
Igmp,
Icmp,
Gre,
Udp,
Tcp,
Http,
} }
private static Packet CreateRandomPacket(Random random) protected override bool CompareField(XElement field, Datagram datagram)
{ {
// BUG: Limited timestamp due to Windows bug: https://connect.microsoft.com/VisualStudio/feedback/details/559198/net-4-datetime-tolocaltime-is-sometimes-wrong ArpDatagram arpDatagram = (ArpDatagram)datagram;
DateTime packetTimestamp = switch (field.Name())
random.NextDateTime(new DateTime(2010,1,1), new DateTime(2010,12,31)).ToUniversalTime().ToLocalTime(); {
//random.NextDateTime(PacketTimestamp.MinimumPacketTimestamp, PacketTimestamp.MaximumPacketTimestamp).ToUniversalTime().ToLocalTime(); case "arp.hw.type":
field.AssertShowHex((ushort)arpDatagram.HardwareType);
break;
EthernetLayer ethernetLayer = random.NextEthernetLayer(); case "arp.proto.type":
IpV4Layer ipV4Layer = random.NextIpV4Layer(); field.AssertShowHex((ushort)arpDatagram.ProtocolType);
PayloadLayer payloadLayer = random.NextPayloadLayer(random.Next(100)); break;
switch (random.NextEnum<PacketType>()) case "arp.hw.size":
// switch (PacketType.IpV4OverIpV4) field.AssertShowDecimal(arpDatagram.HardwareLength);
{ break;
case PacketType.Ethernet:
return PacketBuilder.Build(DateTime.Now, ethernetLayer, payloadLayer);
case PacketType.Arp: case "arp.proto.size":
ethernetLayer.EtherType = EthernetType.None; field.AssertShowDecimal(arpDatagram.ProtocolLength);
ethernetLayer.Destination = MacAddress.Zero; break;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, random.NextArpLayer());
case PacketType.IpV4: case "arp.opcode":
ethernetLayer.EtherType = EthernetType.None; field.AssertShowHex((ushort)arpDatagram.Operation);
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, payloadLayer); break;
case PacketType.IpV4OverIpV4: case "arp.src.hw":
ethernetLayer.EtherType = EthernetType.None; case "arp.src.hw_mac":
ipV4Layer.Protocol = null; field.AssertShow(arpDatagram.SenderHardwareAddress);
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, random.NextIpV4Layer(), payloadLayer); break;
case PacketType.Igmp:
ethernetLayer.EtherType = EthernetType.None;
ipV4Layer.Protocol = null;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, random.NextIgmpLayer());
case PacketType.Icmp: case "arp.src.proto":
ethernetLayer.EtherType = EthernetType.None; field.AssertShow(arpDatagram.SenderProtocolAddress);
ipV4Layer.Protocol = null; break;
IcmpLayer icmpLayer = random.NextIcmpLayer();
IEnumerable<ILayer> icmpPayloadLayers = random.NextIcmpPayloadLayers(icmpLayer);
return PacketBuilder.Build(packetTimestamp, new ILayer[]{ethernetLayer, ipV4Layer, icmpLayer}.Concat(icmpPayloadLayers));
case PacketType.Gre: case "arp.src.proto_ipv4":
ethernetLayer.EtherType = EthernetType.None; field.AssertShow(arpDatagram.SenderProtocolIpV4Address.ToString());
ipV4Layer.Protocol = null; break;
GreLayer greLayer = random.NextGreLayer();
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, greLayer, payloadLayer);
case PacketType.Udp: case "arp.dst.hw":
ethernetLayer.EtherType = EthernetType.None; case "arp.dst.hw_mac":
ipV4Layer.Protocol = null; field.AssertShow(arpDatagram.TargetHardwareAddress);
if (random.NextBool()) break;
ipV4Layer.Fragmentation = IpV4Fragmentation.None;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, random.NextUdpLayer(), payloadLayer);
case PacketType.Tcp: case "arp.dst.proto":
ethernetLayer.EtherType = EthernetType.None; field.AssertShow(arpDatagram.TargetProtocolAddress);
ipV4Layer.Protocol = null; break;
if (random.NextBool())
ipV4Layer.Fragmentation = IpV4Fragmentation.None;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, random.NextTcpLayer(), payloadLayer);
case PacketType.Http: case "arp.dst.proto_ipv4":
ethernetLayer.EtherType = EthernetType.None; field.AssertShow(arpDatagram.TargetProtocolIpV4Address.ToString());
ipV4Layer.Protocol = null; break;
if (random.NextBool())
ipV4Layer.Fragmentation = IpV4Fragmentation.None;
TcpLayer tcpLayer = random.NextTcpLayer();
HttpLayer httpLayer = random.NextHttpLayer(); case "arp.isgratuitous":
if (httpLayer.IsRequest) break;
tcpLayer.DestinationPort = 80;
else
tcpLayer.SourcePort = 80;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, tcpLayer, random.NextHttpLayer());
default: default:
throw new InvalidOperationException(); throw new InvalidOperationException("Invalid arp field " + field.Name());
}
return true;
} }
} }
private static IEnumerable<Packet> CreateRandomPackets(Random random, int numPackets) internal class WiresharkDatagramComparerIpV4 : WiresharkDatagramComparerSimple
{ {
for (int i = 0; i != numPackets; ++i) protected override string PropertyName
yield return CreateRandomPacket(random); {
get { return "IpV4"; }
} }
private static void ComparePacketsToWireshark(params Packet[] packets) protected override bool CompareField(XElement field, Datagram datagram)
{ {
ComparePacketsToWireshark((IEnumerable<Packet>)packets); IpV4Datagram ipV4Datagram = (IpV4Datagram)datagram;
} switch (field.Name())
private static void ComparePacketsToWireshark(IEnumerable<Packet> packets)
{
string pcapFilename = Path.GetTempPath() + "temp." + new Random().NextByte() + ".pcap";
#pragma warning disable 162
// ReSharper disable ConditionIsAlwaysTrueOrFalse
// ReSharper disable HeuristicUnreachableCode
if (!IsRetry)
{
PacketDumpFile.Dump(pcapFilename, new PcapDataLink(DataLinkKind.Ethernet), PacketDevice.DefaultSnapshotLength, packets);
}
else
{
pcapFilename = Path.GetTempPath() + "temp." + RetryNumber + ".pcap";
List<Packet> packetsList = new List<Packet>();
using (PacketCommunicator communicator = new OfflinePacketDevice(pcapFilename).Open())
{
communicator.ReceivePackets(-1, packetsList.Add);
}
packets = packetsList;
}
// ReSharper restore HeuristicUnreachableCode
// ReSharper restore ConditionIsAlwaysTrueOrFalse
#pragma warning restore 162
// Create pdml file
string documentFilename = pcapFilename + ".pdml";
using (Process process = new Process())
{
process.StartInfo = new ProcessStartInfo
{
// Wireshark's preferences file is %APPDATA%\Wireshark\preferences
FileName = WiresharkTsharkPath,
Arguments = "-o udp.check_checksum:TRUE " +
"-o tcp.relative_sequence_numbers:FALSE " +
"-o tcp.analyze_sequence_numbers:FALSE " +
"-o tcp.track_bytes_in_flight:FALSE " +
"-o tcp.desegment_tcp_streams:FALSE " +
"-o tcp.check_checksum:TRUE " +
"-o http.dechunk_body:FALSE " +
"-t r -n -r \"" + pcapFilename + "\" -T pdml",
WorkingDirectory = WiresharkDiretory,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
Console.WriteLine("Starting process " + process.StartInfo.FileName + " " + process.StartInfo.Arguments);
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
File.WriteAllText(documentFilename, output);
}
// Fix pdml file
string fixedDocumentFilename = documentFilename + ".fixed";
File.WriteAllBytes(fixedDocumentFilename, new List<byte>(from b in File.ReadAllBytes(documentFilename)
select b == 0x0A || b == 0x0D
? b
: Math.Max((byte)0x20, Math.Min(b, (byte)0x7F))).ToArray());
try
{
Compare(XDocument.Load(fixedDocumentFilename), packets);
}
catch (AssertFailedException exception)
{
throw new AssertFailedException("Failed comparing packets in file " + pcapFilename + ". Message: " + exception.Message, exception);
}
}
private static void Compare(XDocument document, IEnumerable<Packet> packets)
{
IEnumerator<Packet> packetEnumerator = packets.GetEnumerator();
// Parse XML
int i = 1;
foreach (var documentPacket in document.Element("pdml").Elements("packet"))
{
packetEnumerator.MoveNext();
Packet packet = packetEnumerator.Current;
try
{
ComparePacket(packet, documentPacket);
}
catch (Exception e)
{
throw new AssertFailedException("Failed comparing packet " + i + ". " + e.Message, e);
}
++i;
}
}
private static void ComparePacket(Packet packet, XElement documentPacket)
{
object currentDatagram = packet;
CompareProtocols(currentDatagram, documentPacket);
}
private static void CompareProtocols(object currentDatagram, XElement layersContainer)
{
foreach (var layer in layersContainer.Protocols())
{
switch (layer.Name())
{ {
case "geninfo": case "ip.version":
field.AssertShowDecimal(ipV4Datagram.Version);
break; break;
case "frame": case "ip.hdr_len":
CompareFrame(layer, (Packet)currentDatagram); field.AssertShowDecimal(ipV4Datagram.HeaderLength);
break; break;
case "eth": case "ip.dsfield":
PropertyInfo ethernetProperty = currentDatagram.GetType().GetProperty("Ethernet"); field.AssertShowDecimal((int)ipV4Datagram.TypeOfService);
if (ethernetProperty == null)
break;
currentDatagram = ethernetProperty.GetValue(currentDatagram);
CompareEtherent(layer, (EthernetDatagram)currentDatagram);
break; break;
case "arp": case "ip.len":
PropertyInfo arpProperty = currentDatagram.GetType().GetProperty("Arp"); field.AssertShowDecimal(ipV4Datagram.TotalLength);
if (arpProperty == null)
break;
currentDatagram = arpProperty.GetValue(currentDatagram);
CompareArp(layer, (ArpDatagram)currentDatagram);
break; break;
case "ip": case "ip.id":
PropertyInfo ipV4Property = currentDatagram.GetType().GetProperty("IpV4"); field.AssertShowHex(ipV4Datagram.Identification);
if (ipV4Property == null)
break;
currentDatagram = ipV4Property.GetValue(currentDatagram);
CompareIpV4(layer, (IpV4Datagram)currentDatagram);
break; break;
case "igmp": case "ip.flags":
PropertyInfo igmpProperty = currentDatagram.GetType().GetProperty("Igmp"); field.AssertShowHex((byte)((ushort)ipV4Datagram.Fragmentation.Options >> 13));
if (igmpProperty == null)
break; break;
currentDatagram = igmpProperty.GetValue(currentDatagram);
CompareIgmp(layer, (IgmpDatagram)currentDatagram); case "ip.frag_offset":
field.AssertShowDecimal(ipV4Datagram.Fragmentation.Offset);
break; break;
case "icmp": case "ip.ttl":
PropertyInfo icmpProperty = currentDatagram.GetType().GetProperty("Icmp"); field.AssertShowDecimal(ipV4Datagram.Ttl);
if (icmpProperty == null)
break; break;
currentDatagram = icmpProperty.GetValue(currentDatagram);
CompareIcmp(layer, (IcmpDatagram)currentDatagram); case "ip.proto":
field.AssertShowDecimal((byte)ipV4Datagram.Protocol);
break; break;
case "gre": case "ip.checksum":
PropertyInfo greProperty = currentDatagram.GetType().GetProperty("Gre"); field.AssertShowHex(ipV4Datagram.HeaderChecksum);
if (greProperty == null) foreach (var checksumField in field.Fields())
{
switch (checksumField.Name())
{
case "ip.checksum_good":
checksumField.AssertShowDecimal(ipV4Datagram.IsHeaderChecksumCorrect);
break; break;
currentDatagram = greProperty.GetValue(currentDatagram);
CompareGre(layer, (GreDatagram)currentDatagram); case "ip.checksum_bad":
if (ipV4Datagram.Length < IpV4Datagram.HeaderMinimumLength ||
ipV4Datagram.Length < ipV4Datagram.HeaderLength)
break; break;
case "udp": checksumField.AssertShowDecimal(!ipV4Datagram.IsHeaderChecksumCorrect);
PropertyInfo udpProperty = currentDatagram.GetType().GetProperty("Udp");
if (udpProperty == null)
break; break;
{ }
Datagram ipDatagram = (Datagram)currentDatagram;
currentDatagram = udpProperty.GetValue(currentDatagram);
CompareUdp(layer, (IpV4Datagram)ipDatagram);
} }
break; break;
case "tcp": case "ip.src":
PropertyInfo tcpProperty = currentDatagram.GetType().GetProperty("Tcp"); case "ip.src_host":
if (tcpProperty == null) field.AssertShow(ipV4Datagram.Source.ToString());
break; break;
{
Datagram ipDatagram = (Datagram)currentDatagram; case "ip.dst":
currentDatagram = tcpProperty.GetValue(currentDatagram); case "ip.dst_host":
CompareTcp(layer, (IpV4Datagram)ipDatagram); field.AssertShow(ipV4Datagram.Destination.ToString());
}
break; break;
case "http": case "ip.addr":
PropertyInfo httpProperty = currentDatagram.GetType().GetProperty("Http"); case "ip.host":
if (httpProperty == null) Assert.IsTrue(field.Show() == ipV4Datagram.Source.ToString() ||
field.Show() == ipV4Datagram.Destination.ToString());
break; break;
{
currentDatagram = httpProperty.GetValue(currentDatagram); case "":
CompareHttp(layer, (HttpDatagram)currentDatagram); CompareIpV4Options(field, ipV4Datagram.Options);
}
break; break;
default: default:
return; throw new InvalidOperationException("Invalid ip field " + field.Name());
}
} }
return true;
} }
private static void CompareFrame(XElement frame, Packet packet) private static void CompareIpV4Options(XElement element, IpV4Options options)
{ {
foreach (var field in frame.Fields()) int currentOptionIndex = 0;
foreach (var field in element.Fields())
{ {
switch (field.Name()) if (currentOptionIndex >= options.Count)
{ {
// case "frame.time": Assert.IsFalse(options.IsValid);
// string fieldShow = field.Show(); Assert.IsTrue(field.Show() == "Commercial IP security option" ||
// if (fieldShow == "Not representable") field.Show() == "Loose source route (length byte past end of options)" ||
// break; field.Show() == "Time stamp:" ||
// fieldShow = fieldShow.Substring(0, fieldShow.Length - 2); field.Show().StartsWith("Unknown") ||
// DateTime fieldTimestamp = fieldShow[4] == ' ' field.Show().StartsWith("Security") ||
// ? DateTime.ParseExact(fieldShow, "MMM d, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture) field.Show().StartsWith("Router Alert (with option length = ") ||
// : DateTime.ParseExact(fieldShow, "MMM dd, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture); field.Show().StartsWith("Stream identifier (with option length = ") ||
// MoreAssert.IsInRange(fieldTimestamp.AddSeconds(-2), fieldTimestamp.AddSeconds(2), packet.Timestamp, "Timestamp"); field.Show().Contains("with too") ||
// break; field.Show().Contains(" bytes says option goes past end of options") ||
field.Show().Contains("(length byte past end of options)") ||
case "frame.time_epoch": field.Fields().First().Show().StartsWith("Pointer: ") && field.Fields().First().Show().EndsWith(" (points to middle of address)") ||
double timeEpoch = double.Parse(field.Show()); field.Fields().Where(value => value.Show() == "(suboption would go past end of option)").Count() != 0, field.Show());
DateTime fieldTimestamp = new DateTime(1970, 1, 1).AddSeconds(timeEpoch);
MoreAssert.IsInRange(fieldTimestamp.AddMilliseconds(-1), fieldTimestamp.AddMilliseconds(1), packet.Timestamp.ToUniversalTime(), "Timestamp");
break; break;
}
IpV4Option option = options[currentOptionIndex++];
if (option.OptionType == IpV4OptionType.BasicSecurity ||
option.OptionType == IpV4OptionType.TraceRoute)
{
Assert.IsTrue(field.Show().StartsWith(option.GetWiresharkString()));
continue; // Wireshark doesn't support
}
field.AssertShow(option.GetWiresharkString());
case "frame.len": if ((option is IpV4OptionUnknown))
field.AssertShowDecimal(packet.Length); continue;
break;
var optionShows = from f in field.Fields() select f.Show();
MoreAssert.AreSequenceEqual(optionShows, option.GetWiresharkSubfieldStrings());
} }
} }
} }
private static void CompareEtherent(XElement ethernet, EthernetDatagram ethernetDatagram) internal class WiresharkDatagramComparerIgmp : WiresharkDatagramComparerSimple
{
protected override string PropertyName
{ {
foreach (var field in ethernet.Fields()) get { return "Igmp"; }
}
protected override bool CompareField(XElement field, Datagram datagram)
{ {
IgmpDatagram igmpDatagram = (IgmpDatagram)datagram;
switch (field.Name()) switch (field.Name())
{ {
case "eth.dst": case "igmp.version":
CompareEthernetAddress(field, ethernetDatagram.Destination); if (field.Show() == "0")
return false; // TODO: support IGMP version 0.
field.AssertShowDecimal(igmpDatagram.Version);
break; break;
case "eth.src": case "igmp.type":
CompareEthernetAddress(field, ethernetDatagram.Source); field.AssertShowHex((byte)igmpDatagram.MessageType);
break; break;
case "eth.type": case "igmp.checksum":
field.AssertShowHex((ushort)ethernetDatagram.EtherType); field.AssertShowHex(igmpDatagram.Checksum);
break; break;
case "eth.trailer": case "igmp.maddr":
if (ethernetDatagram.Trailer != null) field.AssertShow(igmpDatagram.GroupAddress.ToString());
field.AssertValue(ethernetDatagram.Trailer);
break; break;
case "": case "igmp.max_resp":
if (ethernetDatagram.Trailer != null) field.AssertShowDecimal((int)((igmpDatagram.MaxResponseTime.TotalSeconds + 0.05) * 10));
field.AssertValue(ethernetDatagram.FrameCheckSequence);
break;
default:
throw new InvalidOperationException("Invalid etherent field " + field.Name());
}
}
}
private static void CompareArp(XElement arp, ArpDatagram arpDatagram)
{
foreach (var field in arp.Fields())
{
switch (field.Name())
{
case "arp.hw.type":
field.AssertShowHex((ushort)arpDatagram.HardwareType);
break;
case "arp.proto.type":
field.AssertShowHex((ushort)arpDatagram.ProtocolType);
break;
case "arp.hw.size":
field.AssertShowDecimal(arpDatagram.HardwareLength);
break;
case "arp.proto.size":
field.AssertShowDecimal(arpDatagram.ProtocolLength);
break;
case "arp.opcode":
field.AssertShowHex((ushort)arpDatagram.Operation);
break;
case "arp.src.hw":
case "arp.src.hw_mac":
field.AssertShow(arpDatagram.SenderHardwareAddress);
break;
case "arp.src.proto":
field.AssertShow(arpDatagram.SenderProtocolAddress);
break;
case "arp.src.proto_ipv4":
field.AssertShow(arpDatagram.SenderProtocolIpV4Address.ToString());
break;
case "arp.dst.hw":
case "arp.dst.hw_mac":
field.AssertShow(arpDatagram.TargetHardwareAddress);
break;
case "arp.dst.proto":
field.AssertShow(arpDatagram.TargetProtocolAddress);
break;
case "arp.dst.proto_ipv4":
field.AssertShow(arpDatagram.TargetProtocolIpV4Address.ToString());
break;
case "arp.isgratuitous":
break;
default:
throw new InvalidOperationException("Invalid arp field " + field.Name());
}
}
}
private static void CompareEthernetAddress(XElement element, MacAddress address)
{
foreach (var field in element.Fields())
{
switch (field.Name())
{
case "eth.addr":
field.AssertShow(address.ToString().ToLower());
break;
case "eth.ig":
case "eth.lg":
break;
default:
throw new InvalidOperationException("Invalid ethernet address field " + field.Name());
}
}
}
private static void CompareIpV4(XElement ethernet, IpV4Datagram ipV4Datagram)
{
foreach (var field in ethernet.Fields())
{
switch (field.Name())
{
case "ip.version":
field.AssertShowDecimal(ipV4Datagram.Version);
break;
case "ip.hdr_len":
field.AssertShowDecimal(ipV4Datagram.HeaderLength);
break;
case "ip.dsfield":
field.AssertShowDecimal((int)ipV4Datagram.TypeOfService);
break;
case "ip.len":
field.AssertShowDecimal(ipV4Datagram.TotalLength);
break;
case "ip.id":
field.AssertShowHex(ipV4Datagram.Identification);
break;
case "ip.flags":
field.AssertShowHex((byte)((ushort)ipV4Datagram.Fragmentation.Options >> 13));
break;
case "ip.frag_offset":
field.AssertShowDecimal(ipV4Datagram.Fragmentation.Offset);
break;
case "ip.ttl":
field.AssertShowDecimal(ipV4Datagram.Ttl);
break;
case "ip.proto":
field.AssertShowDecimal((byte)ipV4Datagram.Protocol);
break;
case "ip.checksum":
field.AssertShowHex(ipV4Datagram.HeaderChecksum);
foreach (var checksumField in field.Fields())
{
switch (checksumField.Name())
{
case "ip.checksum_good":
checksumField.AssertShowDecimal(ipV4Datagram.IsHeaderChecksumCorrect);
break;
case "ip.checksum_bad":
if (ipV4Datagram.Length < IpV4Datagram.HeaderMinimumLength ||
ipV4Datagram.Length < ipV4Datagram.HeaderLength)
break;
checksumField.AssertShowDecimal(!ipV4Datagram.IsHeaderChecksumCorrect);
break;
}
}
break;
case "ip.src":
case "ip.src_host":
field.AssertShow(ipV4Datagram.Source.ToString());
break;
case "ip.dst":
case "ip.dst_host":
field.AssertShow(ipV4Datagram.Destination.ToString());
break;
case "ip.addr":
case "ip.host":
Assert.IsTrue(field.Show() == ipV4Datagram.Source.ToString() ||
field.Show() == ipV4Datagram.Destination.ToString());
break;
case "":
CompareIpV4Options(field, ipV4Datagram.Options);
break;
default:
throw new InvalidOperationException("Invalid ip field " + field.Name());
}
}
}
private static void CompareIpV4Options(XElement element, IpV4Options options)
{
int currentOptionIndex = 0;
foreach (var field in element.Fields())
{
if (currentOptionIndex >= options.Count)
{
Assert.IsFalse(options.IsValid);
Assert.IsTrue(field.Show() == "Commercial IP security option" ||
field.Show() == "Loose source route (length byte past end of options)" ||
field.Show() == "Time stamp:" ||
field.Show().StartsWith("Unknown") ||
field.Show().StartsWith("Security") ||
field.Show().StartsWith("Router Alert (with option length = ") ||
field.Show().StartsWith("Stream identifier (with option length = ") ||
field.Show().Contains("with too") ||
field.Show().Contains(" bytes says option goes past end of options") ||
field.Show().Contains("(length byte past end of options)") ||
field.Fields().First().Show().StartsWith("Pointer: ") && field.Fields().First().Show().EndsWith(" (points to middle of address)") ||
field.Fields().Where(value => value.Show() == "(suboption would go past end of option)").Count() != 0, field.Show());
break;
}
IpV4Option option = options[currentOptionIndex++];
if (option.OptionType == IpV4OptionType.BasicSecurity ||
option.OptionType == IpV4OptionType.TraceRoute)
{
Assert.IsTrue(field.Show().StartsWith(option.GetWiresharkString()));
continue; // Wireshark doesn't support
}
field.AssertShow(option.GetWiresharkString());
if ((option is IpV4OptionUnknown))
continue;
var optionShows = from f in field.Fields() select f.Show();
MoreAssert.AreSequenceEqual(optionShows, option.GetWiresharkSubfieldStrings());
}
}
private static void CompareIgmp(XElement igmp, IgmpDatagram igmpDatagram)
{
int groupRecordIndex = 0;
int sourceAddressIndex = 0;
foreach (var field in igmp.Fields())
{
switch (field.Name())
{
case "igmp.version":
if (field.Show() == "0")
return;
field.AssertShowDecimal(igmpDatagram.Version);
break;
case "igmp.type":
field.AssertShowHex((byte)igmpDatagram.MessageType);
break;
case "igmp.checksum":
field.AssertShowHex(igmpDatagram.Checksum);
break;
case "igmp.maddr":
field.AssertShow(igmpDatagram.GroupAddress.ToString());
break;
case "igmp.max_resp":
field.AssertShowDecimal((int)((igmpDatagram.MaxResponseTime.TotalSeconds + 0.05) * 10));
break; break;
case "igmp.checksum_bad": case "igmp.checksum_bad":
...@@ -750,11 +384,11 @@ namespace PcapDotNet.Core.Test ...@@ -750,11 +384,11 @@ namespace PcapDotNet.Core.Test
switch (igmpDatagram.MessageType) switch (igmpDatagram.MessageType)
{ {
case IgmpMessageType.MembershipReportVersion3: case IgmpMessageType.MembershipReportVersion3:
CompareIgmpGroupRecord(field, igmpDatagram.GroupRecords[groupRecordIndex++]); CompareIgmpGroupRecord(field, igmpDatagram.GroupRecords[_groupRecordIndex++]);
break; break;
case IgmpMessageType.MembershipQuery: case IgmpMessageType.MembershipQuery:
CompareIgmp(field, igmpDatagram); CompareDatagram(field, null, igmpDatagram);
break; break;
default: default:
...@@ -785,7 +419,7 @@ namespace PcapDotNet.Core.Test ...@@ -785,7 +419,7 @@ namespace PcapDotNet.Core.Test
break; break;
case "igmp.saddr": case "igmp.saddr":
field.AssertShow(igmpDatagram.SourceAddresses[sourceAddressIndex++].ToString()); field.AssertShow(igmpDatagram.SourceAddresses[_sourceAddressIndex++].ToString());
break; break;
case "igmp.identifier": case "igmp.identifier":
...@@ -799,7 +433,8 @@ namespace PcapDotNet.Core.Test ...@@ -799,7 +433,8 @@ namespace PcapDotNet.Core.Test
default: default:
throw new InvalidOperationException("Invalid igmp field " + field.Name()); throw new InvalidOperationException("Invalid igmp field " + field.Name());
} }
}
return true;
} }
private static void CompareIgmpGroupRecord(XElement groupRecord, IgmpGroupRecordDatagram groupRecordDatagram) private static void CompareIgmpGroupRecord(XElement groupRecord, IgmpGroupRecordDatagram groupRecordDatagram)
...@@ -839,11 +474,20 @@ namespace PcapDotNet.Core.Test ...@@ -839,11 +474,20 @@ namespace PcapDotNet.Core.Test
} }
} }
private static void CompareIcmp(XElement icmp, IcmpDatagram icmpDatagram) private int _groupRecordIndex;
private int _sourceAddressIndex;
}
internal class WiresharkDatagramComparerIcmp : WiresharkDatagramComparerSimple
{ {
int routerIndex = 0; protected override string PropertyName
foreach (var field in icmp.Fields())
{ {
get { return "Icmp"; }
}
protected override bool CompareField(XElement field, Datagram datagram)
{
IcmpDatagram icmpDatagram = (IcmpDatagram)datagram;
switch (field.Name()) switch (field.Name())
{ {
case "icmp.type": case "icmp.type":
...@@ -936,11 +580,11 @@ namespace PcapDotNet.Core.Test ...@@ -936,11 +580,11 @@ namespace PcapDotNet.Core.Test
break; break;
case "Router address": case "Router address":
field.AssertShow(fieldName + ": " + routerAdvertisementDatagram.Entries[routerIndex].RouterAddress); field.AssertShow(fieldName + ": " + routerAdvertisementDatagram.Entries[_routerIndex].RouterAddress);
break; break;
case "Preference level": case "Preference level":
field.AssertShow(fieldName + ": " + routerAdvertisementDatagram.Entries[routerIndex++].RouterAddressPreference); field.AssertShow(fieldName + ": " + routerAdvertisementDatagram.Entries[_routerIndex++].RouterAddressPreference);
break; break;
default: default:
...@@ -979,16 +623,23 @@ namespace PcapDotNet.Core.Test ...@@ -979,16 +623,23 @@ namespace PcapDotNet.Core.Test
throw new InvalidOperationException("Invalid icmp field " + field.Name()); throw new InvalidOperationException("Invalid icmp field " + field.Name());
break; break;
} }
return true;
} }
CompareProtocols(icmpDatagram, icmp); private int _routerIndex;
} }
private static void CompareGre(XElement greElement, GreDatagram greDatagram) internal class WiresharkDatagramComparerGre : WiresharkDatagramComparerSimple
{ {
int currentEntry = -1; protected override string PropertyName
foreach (var field in greElement.Fields())
{ {
get { return "Gre"; }
}
protected override bool CompareField(XElement field, Datagram datagram)
{
GreDatagram greDatagram = (GreDatagram)datagram;
switch (field.Name()) switch (field.Name())
{ {
case "": case "":
...@@ -1077,24 +728,24 @@ namespace PcapDotNet.Core.Test ...@@ -1077,24 +728,24 @@ namespace PcapDotNet.Core.Test
} }
else if (field.Show().StartsWith("Address family: ")) else if (field.Show().StartsWith("Address family: "))
{ {
++currentEntry; ++_currentEntry;
if (currentEntry < greDatagram.Routing.Count) if (_currentEntry < greDatagram.Routing.Count)
field.AssertValue((ushort)greDatagram.Routing[currentEntry].AddressFamily); field.AssertValue((ushort)greDatagram.Routing[_currentEntry].AddressFamily);
else if (currentEntry > greDatagram.Routing.Count) else if (_currentEntry > greDatagram.Routing.Count)
Assert.IsFalse(greDatagram.IsValid); Assert.IsFalse(greDatagram.IsValid);
} }
else if (field.Show().StartsWith("SRE offset: ")) else if (field.Show().StartsWith("SRE offset: "))
{ {
if (currentEntry < greDatagram.Routing.Count) if (_currentEntry < greDatagram.Routing.Count)
field.AssertValue(greDatagram.Routing[currentEntry].PayloadOffset); field.AssertValue(greDatagram.Routing[_currentEntry].PayloadOffset);
else if (currentEntry > greDatagram.Routing.Count) else if (_currentEntry > greDatagram.Routing.Count)
Assert.IsFalse(greDatagram.IsValid); Assert.IsFalse(greDatagram.IsValid);
} }
else if (field.Show().StartsWith("SRE length: ")) else if (field.Show().StartsWith("SRE length: "))
{ {
if (currentEntry < greDatagram.Routing.Count) if (_currentEntry < greDatagram.Routing.Count)
field.AssertValue(greDatagram.Routing[currentEntry].PayloadLength); field.AssertValue(greDatagram.Routing[_currentEntry].PayloadLength);
else if (currentEntry > greDatagram.Routing.Count) else if (_currentEntry > greDatagram.Routing.Count)
Assert.IsFalse(greDatagram.IsValid); Assert.IsFalse(greDatagram.IsValid);
} }
else else
...@@ -1137,15 +788,25 @@ namespace PcapDotNet.Core.Test ...@@ -1137,15 +788,25 @@ namespace PcapDotNet.Core.Test
Assert.Fail("Invalid field name: " + field.Name()); Assert.Fail("Invalid field name: " + field.Name());
break; break;
} }
return true;
} }
private int _currentEntry = -1;
} }
private static void CompareUdp(XElement udpElement, IpV4Datagram ipV4Datagram) internal class WiresharkDatagramComparerUdp : WiresharkDatagramComparer
{
protected override string PropertyName
{ {
UdpDatagram udpDatagram = ipV4Datagram.Udp; get { return "Udp"; }
}
foreach (var field in udpElement.Fields()) protected override bool CompareField(XElement field, Datagram parentDatagram, Datagram datagram)
{ {
IpV4Datagram ipV4Datagram = (IpV4Datagram)parentDatagram;
UdpDatagram udpDatagram = (UdpDatagram)datagram;
switch (field.Name()) switch (field.Name())
{ {
case "udp.srcport": case "udp.srcport":
...@@ -1195,15 +856,23 @@ namespace PcapDotNet.Core.Test ...@@ -1195,15 +856,23 @@ namespace PcapDotNet.Core.Test
default: default:
throw new InvalidOperationException("Invalid udp field " + field.Name()); throw new InvalidOperationException("Invalid udp field " + field.Name());
} }
return true;
} }
} }
private static void CompareTcp(XElement tcpElement, IpV4Datagram ipV4Datagram) internal class WiresharkDatagramComparerTcp : WiresharkDatagramComparer
{
protected override string PropertyName
{ {
TcpDatagram tcpDatagram = ipV4Datagram.Tcp; get { return "Tcp"; }
}
foreach (var field in tcpElement.Fields()) protected override bool CompareField(XElement field, Datagram parentDatagram, Datagram datagram)
{ {
IpV4Datagram ipV4Datagram = (IpV4Datagram)parentDatagram;
TcpDatagram tcpDatagram = (TcpDatagram)datagram;
switch (field.Name()) switch (field.Name())
{ {
case "tcp.len": case "tcp.len":
...@@ -1329,7 +998,8 @@ namespace PcapDotNet.Core.Test ...@@ -1329,7 +998,8 @@ namespace PcapDotNet.Core.Test
default: default:
throw new InvalidOperationException("Invalid tcp field " + field.Name()); throw new InvalidOperationException("Invalid tcp field " + field.Name());
} }
}
return true;
} }
private static void CompareTcpOptions(XElement element, TcpOptions options) private static void CompareTcpOptions(XElement element, TcpOptions options)
...@@ -1418,193 +1088,605 @@ namespace PcapDotNet.Core.Test ...@@ -1418,193 +1088,605 @@ namespace PcapDotNet.Core.Test
field.AssertValue(Encoding.ASCII.GetBytes(((TcpOptionMood)option).EmotionString)); field.AssertValue(Encoding.ASCII.GetBytes(((TcpOptionMood)option).EmotionString));
break; break;
case "tcp.options.mood_val": case "tcp.options.mood_val":
Assert.AreEqual(TcpOptionType.Mood, option.OptionType); Assert.AreEqual(TcpOptionType.Mood, option.OptionType);
field.AssertShow(((TcpOptionMood)option).EmotionString); field.AssertShow(((TcpOptionMood)option).EmotionString);
++currentOptionIndex; ++currentOptionIndex;
break; break;
default:
throw new InvalidOperationException("Invalid tcp options field " + field.Name());
}
if ((option is TcpOptionUnknown))
continue;
var optionShows = from f in field.Fields() select f.Show();
MoreAssert.AreSequenceEqual(optionShows, option.GetWiresharkSubfieldStrings());
}
}
}
internal class WiresharkDatagramComparerHttp : WiresharkDatagramComparerSimple
{
protected override string PropertyName
{
get { return "Http"; }
}
protected override bool Ignore(Datagram datagram)
{
HttpDatagram httpDatagram = (HttpDatagram)datagram;
return (httpDatagram.Header != null && httpDatagram.Header.ContentLength != null && httpDatagram.Header.TransferEncoding != null);
}
protected override bool CompareField(XElement field, Datagram datagram)
{
HttpDatagram httpDatagram = (HttpDatagram)datagram;
if (field.Name() == "data" || field.Name() == "data.data")
{
if (field.Name() == "data")
field.AssertNoShow();
MoreAssert.AreSequenceEqual(httpDatagram.Take(_data.Length / 2), HexEncoding.Instance.GetBytes(_data.ToString()));
// string previousData = data.ToString();
// for (int i = 0; i != previousData.Length / 2; ++i)
// {
// byte value = Convert.ToByte(previousData.Substring(i * 2, 2), 16);
// Assert.AreEqual(httpDatagram[i], value);
// }
field.AssertValue(httpDatagram.Skip(_data.Length / 2));
return false;
}
string fieldShow = field.Show();
string httpFieldName;
switch (field.Name())
{
case "http.request":
field.AssertShowDecimal(httpDatagram.IsRequest);
break;
case "http.response":
field.AssertShowDecimal(httpDatagram.IsResponse);
break;
case "":
if (fieldShow == "HTTP chunked response")
{
throw new InvalidOperationException("HTTP chunked response");
}
_data.Append(field.Value());
if (fieldShow == @"\r\n")
break;
if (_isFirstEmptyName)
{
CompareHttpFirstLine(field, httpDatagram);
_isFirstEmptyName = false;
}
else if (fieldShow.StartsWith("Content-encoded entity body"))
{
break;
}
else
{
fieldShow = EncodingExtensions.Iso88591.GetString(HexEncoding.Instance.GetBytes(field.Value()));
fieldShow = fieldShow.Substring(0, fieldShow.Length - 2);
int colonIndex = fieldShow.IndexOf(':');
MoreAssert.IsBiggerOrEqual(0, colonIndex, "Can't find colon in field with empty name");
if (httpDatagram.Header == null)
{
Assert.IsTrue(httpDatagram.IsRequest);
Assert.IsNull(httpDatagram.Version);
break;
}
httpFieldName = fieldShow.Substring(0, colonIndex);
string fieldValue = fieldShow.Substring(colonIndex + 1).SkipWhile(c => c == ' ').TakeWhile(c => c != '\\').SequenceToString();
string expectedFieldValue = httpDatagram.Header[httpFieldName].ValueString;
Assert.IsTrue(expectedFieldValue.Contains(fieldValue),
string.Format("{0} <{1}> doesn't contain <{2}>", field.Name(), expectedFieldValue, fieldValue));
}
break;
case "data.len":
field.AssertShowDecimal(httpDatagram.Length - _data.Length / 2);
break;
case "http.host":
case "http.user_agent":
case "http.accept":
case "http.accept_language":
case "http.accept_encoding":
case "http.connection":
case "http.cookie":
case "http.cache_control":
case "http.content_encoding":
case "http.date":
case "http.referer":
case "http.last_modified":
case "http.server":
case "http.set_cookie":
case "http.location":
_data.Append(field.Value());
httpFieldName = field.Name().Substring(5).Replace('_', '-');
HttpField httpField = httpDatagram.Header[httpFieldName];
if (!field.Value().EndsWith("0d0a"))
Assert.IsNull(httpField);
else
{
string fieldValue = field.Show().Replace("\\\"", "\"");
string expectedFieldValue = httpField.ValueString;
Assert.IsTrue(expectedFieldValue.Contains(fieldValue),
string.Format("{0} <{1}> doesn't contain <{2}>", field.Name(), expectedFieldValue, fieldValue));
}
break;
case "http.content_length_header":
_data.Append(field.Value());
field.AssertShowDecimal(httpDatagram.Header.ContentLength.ContentLength.Value);
break;
case "http.content_type":
_data.Append(field.Value());
string[] mediaType = fieldShow.Split(new[] {';', ' ', '/'}, StringSplitOptions.RemoveEmptyEntries);
Assert.AreEqual(httpDatagram.Header.ContentType.MediaType, mediaType[0]);
Assert.AreEqual(httpDatagram.Header.ContentType.MediaSubtype, mediaType[1]);
int fieldShowParametersStart = fieldShow.IndexOf(';');
if (fieldShowParametersStart == -1)
Assert.IsFalse(httpDatagram.Header.ContentType.Parameters.Any());
else
Assert.AreEqual(
httpDatagram.Header.ContentType.Parameters.Select(pair => pair.Key + '=' + pair.Value.ToWiresharkLiteral()).SequenceToString(';'),
fieldShow.Substring(fieldShowParametersStart + 1));
break;
case "http.transfer_encoding":
_data.Append(field.Value());
Assert.AreEqual(fieldShow.ToWiresharkLowerLiteral(),
httpDatagram.Header.TransferEncoding.TransferCodings.SequenceToString(',').ToWiresharkLiteral());
break;
default:
throw new InvalidOperationException("Invalid HTTP field " + field.Name());
}
return true;
}
private static void CompareHttpFirstLine(XElement httpFirstLineElement, HttpDatagram httpDatagram)
{
foreach (var field in httpFirstLineElement.Fields())
{
switch (field.Name())
{
case "http.request.method":
Assert.IsTrue(httpDatagram.IsRequest, field.Name() + " IsRequest");
field.AssertShow(((HttpRequestDatagram)httpDatagram).Method.Method);
break;
case "http.request.uri":
Assert.IsTrue(httpDatagram.IsRequest, field.Name() + " IsRequest");
field.AssertShow(((HttpRequestDatagram)httpDatagram).Uri.ToWiresharkLiteral());
break;
case "http.request.version":
if (httpDatagram.Version == null)
field.AssertShow(string.Empty);
else
field.AssertShow(httpDatagram.Version.ToString());
break;
case "http.response.code":
Assert.IsTrue(httpDatagram.IsResponse, field.Name() + " IsResponse");
field.AssertShowDecimal(((HttpResponseDatagram)httpDatagram).StatusCode.Value);
break;
default:
throw new InvalidOperationException("Invalid HTTP first line field " + field.Name());
}
}
}
readonly StringBuilder _data = new StringBuilder();
bool _isFirstEmptyName = true;
}
/// <summary>
/// Summary description for WiresharkCompareTests
/// </summary>
[TestClass]
public class WiresharkCompareTests
{
private const string WiresharkDiretory = @"C:\Program Files\Wireshark\";
private const string WiresharkTsharkPath = WiresharkDiretory + @"tshark.exe";
private const bool IsRetry
// = true;
= false;
private const byte RetryNumber = 190;
/// <summary>
/// Gets or sets the test context which provides
/// information about and functionality for the current test run.
/// </summary>
public TestContext TestContext { get; set; }
#region Additional test attributes
//
// You can use the following additional attributes as you write your tests:
//
// Use ClassInitialize to run code before running the first test in the class
// [ClassInitialize()]
// public static void MyClassInitialize(TestContext testContext) { }
//
// Use ClassCleanup to run code after all tests in a class have run
// [ClassCleanup()]
// public static void MyClassCleanup() { }
//
// Use TestInitialize to run code before running each test
// [TestInitialize()]
// public void MyTestInitialize() { }
//
// Use TestCleanup to run code after each test has run
// [TestCleanup()]
// public void MyTestCleanup() { }
//
#endregion
[TestMethod]
public void ComparePacketsToWiresharkTest()
{
#pragma warning disable 162 // This code is unreachable on purpose
if (IsRetry)
{
ComparePacketsToWireshark(null);
return;
}
#pragma warning restore 162
Random random = new Random();
for (int i = 0; i != 10; ++i)
{
// Create packets
List<Packet> packets = new List<Packet>(CreateRandomPackets(random, 200));
// Compare packets to wireshark
ComparePacketsToWireshark(packets);
}
}
[TestMethod]
public void CompareTimestampPacketsToWiresharkTest()
{
const long Ticks = 633737178954260865;
DateTime timestamp = new DateTime(Ticks).ToUniversalTime().ToLocalTime();
// Create packet
Packet packet = new Packet(new byte[14], timestamp, DataLinkKind.Ethernet);
// Compare packet to wireshark
ComparePacketsToWireshark(new[] { packet });
// BUG: Limited timestamp due to Windows bug: https://connect.microsoft.com/VisualStudio/feedback/details/559198/net-4-datetime-tolocaltime-is-sometimes-wrong
// Now check different dates.
// packet = new Packet(new byte[14], new DateTime(2004,08,25,10,36,41, DateTimeKind.Utc).ToLocalTime(), DataLinkKind.Ethernet);
// ComparePacketsToWireshark(new[] { packet });
}
[TestMethod]
public void CompareEthernetTrailerToWiresharkTest()
{
const string PacketString = "001120cf0900000c29566988080045000029627b00008006de80c0a8640bc0a81477a42cc03bdd3c481c6cfcd72050104278a5a90000000e01bf0101";
Packet packet = Packet.FromHexadecimalString(PacketString, DateTime.Now, DataLinkKind.Ethernet);
ComparePacketsToWireshark(packet);
}
[TestMethod]
public void CompareEthernetFcsToWiresharkTest()
{
Packet packet = PacketBuilder.Build(DateTime.Now,
new EthernetLayer(),
new IpV4Layer
{
Protocol = IpV4Protocol.Udp
});
byte[] buffer = new byte[packet.Length + 100];
new Random().NextBytes(buffer);
packet.CopyTo(buffer, 0);
packet = new Packet(buffer, DateTime.Now, DataLinkKind.Ethernet);
ComparePacketsToWireshark(packet);
}
private enum PacketType
{
Ethernet,
Arp,
IpV4,
IpV4OverIpV4,
Igmp,
Icmp,
Gre,
Udp,
Tcp,
Http,
}
private static Packet CreateRandomPacket(Random random)
{
// BUG: Limited timestamp due to Windows bug: https://connect.microsoft.com/VisualStudio/feedback/details/559198/net-4-datetime-tolocaltime-is-sometimes-wrong
DateTime packetTimestamp =
random.NextDateTime(new DateTime(2010,1,1), new DateTime(2010,12,31)).ToUniversalTime().ToLocalTime();
//random.NextDateTime(PacketTimestamp.MinimumPacketTimestamp, PacketTimestamp.MaximumPacketTimestamp).ToUniversalTime().ToLocalTime();
EthernetLayer ethernetLayer = random.NextEthernetLayer();
IpV4Layer ipV4Layer = random.NextIpV4Layer();
PayloadLayer payloadLayer = random.NextPayloadLayer(random.Next(100));
switch (random.NextEnum<PacketType>())
// switch (PacketType.Icmp)
{
case PacketType.Ethernet:
return PacketBuilder.Build(DateTime.Now, ethernetLayer, payloadLayer);
case PacketType.Arp:
ethernetLayer.EtherType = EthernetType.None;
ethernetLayer.Destination = MacAddress.Zero;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, random.NextArpLayer());
case PacketType.IpV4:
ethernetLayer.EtherType = EthernetType.None;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, payloadLayer);
case PacketType.IpV4OverIpV4:
ethernetLayer.EtherType = EthernetType.None;
ipV4Layer.Protocol = null;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, random.NextIpV4Layer(), payloadLayer);
case PacketType.Igmp:
ethernetLayer.EtherType = EthernetType.None;
ipV4Layer.Protocol = null;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, random.NextIgmpLayer());
case PacketType.Icmp:
ethernetLayer.EtherType = EthernetType.None;
ipV4Layer.Protocol = null;
IcmpLayer icmpLayer = random.NextIcmpLayer();
IEnumerable<ILayer> icmpPayloadLayers = random.NextIcmpPayloadLayers(icmpLayer);
return PacketBuilder.Build(packetTimestamp, new ILayer[]{ethernetLayer, ipV4Layer, icmpLayer}.Concat(icmpPayloadLayers));
case PacketType.Gre:
ethernetLayer.EtherType = EthernetType.None;
ipV4Layer.Protocol = null;
GreLayer greLayer = random.NextGreLayer();
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, greLayer, payloadLayer);
case PacketType.Udp:
ethernetLayer.EtherType = EthernetType.None;
ipV4Layer.Protocol = null;
if (random.NextBool())
ipV4Layer.Fragmentation = IpV4Fragmentation.None;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, random.NextUdpLayer(), payloadLayer);
case PacketType.Tcp:
ethernetLayer.EtherType = EthernetType.None;
ipV4Layer.Protocol = null;
if (random.NextBool())
ipV4Layer.Fragmentation = IpV4Fragmentation.None;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, random.NextTcpLayer(), payloadLayer);
case PacketType.Http:
ethernetLayer.EtherType = EthernetType.None;
ipV4Layer.Protocol = null;
if (random.NextBool())
ipV4Layer.Fragmentation = IpV4Fragmentation.None;
TcpLayer tcpLayer = random.NextTcpLayer();
HttpLayer httpLayer = random.NextHttpLayer();
if (httpLayer.IsRequest)
tcpLayer.DestinationPort = 80;
else
tcpLayer.SourcePort = 80;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, tcpLayer, random.NextHttpLayer());
default: default:
throw new InvalidOperationException("Invalid tcp options field " + field.Name()); throw new InvalidOperationException();
} }
if ((option is TcpOptionUnknown))
continue;
var optionShows = from f in field.Fields() select f.Show();
MoreAssert.AreSequenceEqual(optionShows, option.GetWiresharkSubfieldStrings());
} }
private static IEnumerable<Packet> CreateRandomPackets(Random random, int numPackets)
{
for (int i = 0; i != numPackets; ++i)
yield return CreateRandomPacket(random);
} }
private static void CompareHttp(XElement httpElement, HttpDatagram httpDatagram) private static void ComparePacketsToWireshark(params Packet[] packets)
{ {
if (httpDatagram.Header != null && httpDatagram.Header.ContentLength != null && httpDatagram.Header.TransferEncoding != null) ComparePacketsToWireshark((IEnumerable<Packet>)packets);
return; // todo https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5182 }
StringBuilder data = new StringBuilder(); private static void ComparePacketsToWireshark(IEnumerable<Packet> packets)
bool isFirstEmptyName = true;
foreach (var field in httpElement.Fields())
{ {
if (field.Name() == "data" || field.Name() == "data.data") string pcapFilename = Path.GetTempPath() + "temp." + new Random().NextByte() + ".pcap";
#pragma warning disable 162
// ReSharper disable ConditionIsAlwaysTrueOrFalse
// ReSharper disable HeuristicUnreachableCode
if (!IsRetry)
{ {
if (field.Name() == "data") PacketDumpFile.Dump(pcapFilename, new PcapDataLink(DataLinkKind.Ethernet), PacketDevice.DefaultSnapshotLength, packets);
field.AssertNoShow(); }
else
MoreAssert.AreSequenceEqual(httpDatagram.Take(data.Length / 2), HexEncoding.Instance.GetBytes(data.ToString())); {
// string previousData = data.ToString(); pcapFilename = Path.GetTempPath() + "temp." + RetryNumber + ".pcap";
// for (int i = 0; i != previousData.Length / 2; ++i) List<Packet> packetsList = new List<Packet>();
// { using (PacketCommunicator communicator = new OfflinePacketDevice(pcapFilename).Open())
// byte value = Convert.ToByte(previousData.Substring(i * 2, 2), 16); {
// Assert.AreEqual(httpDatagram[i], value); communicator.ReceivePackets(-1, packetsList.Add);
// } }
field.AssertValue(httpDatagram.Skip(data.Length / 2)); packets = packetsList;
continue;
} }
// ReSharper restore HeuristicUnreachableCode
// ReSharper restore ConditionIsAlwaysTrueOrFalse
#pragma warning restore 162
string fieldShow = field.Show(); // Create pdml file
string httpFieldName; string documentFilename = pcapFilename + ".pdml";
switch (field.Name()) using (Process process = new Process())
{ {
case "http.request": process.StartInfo = new ProcessStartInfo
field.AssertShowDecimal(httpDatagram.IsRequest); {
break; // Wireshark's preferences file is %APPDATA%\Wireshark\preferences
FileName = WiresharkTsharkPath,
Arguments = "-o udp.check_checksum:TRUE " +
"-o tcp.relative_sequence_numbers:FALSE " +
"-o tcp.analyze_sequence_numbers:FALSE " +
"-o tcp.track_bytes_in_flight:FALSE " +
"-o tcp.desegment_tcp_streams:FALSE " +
"-o tcp.check_checksum:TRUE " +
"-o http.dechunk_body:FALSE " +
"-t r -n -r \"" + pcapFilename + "\" -T pdml",
WorkingDirectory = WiresharkDiretory,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
};
Console.WriteLine("Starting process " + process.StartInfo.FileName + " " + process.StartInfo.Arguments);
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
File.WriteAllText(documentFilename, output);
}
case "http.response": // Fix pdml file
field.AssertShowDecimal(httpDatagram.IsResponse); string fixedDocumentFilename = documentFilename + ".fixed";
break; File.WriteAllBytes(fixedDocumentFilename, new List<byte>(from b in File.ReadAllBytes(documentFilename)
select b == 0x0A || b == 0x0D
? b
: Math.Max((byte)0x20, Math.Min(b, (byte)0x7F))).ToArray());
case "": try
if (fieldShow == "HTTP chunked response")
{ {
throw new InvalidOperationException("HTTP chunked response"); Compare(XDocument.Load(fixedDocumentFilename), packets);
}
catch (AssertFailedException exception)
{
throw new AssertFailedException("Failed comparing packets in file " + pcapFilename + ". Message: " + exception.Message, exception);
}
} }
data.Append(field.Value()); private static void Compare(XDocument document, IEnumerable<Packet> packets)
if (fieldShow == @"\r\n") {
break; IEnumerator<Packet> packetEnumerator = packets.GetEnumerator();
if (isFirstEmptyName) // Parse XML
int i = 1;
foreach (var documentPacket in document.Element("pdml").Elements("packet"))
{ {
CompareHttpFirstLine(field, httpDatagram); packetEnumerator.MoveNext();
isFirstEmptyName = false; Packet packet = packetEnumerator.Current;
try
{
ComparePacket(packet, documentPacket);
} }
else if (fieldShow.StartsWith("Content-encoded entity body")) catch (Exception e)
{ {
break; throw new AssertFailedException("Failed comparing packet " + i + ". " + e.Message, e);
} }
else ++i;
}
}
private static void ComparePacket(Packet packet, XElement documentPacket)
{ {
fieldShow = EncodingExtensions.Iso88591.GetString(HexEncoding.Instance.GetBytes(field.Value())); object currentDatagram = packet;
fieldShow = fieldShow.Substring(0, fieldShow.Length - 2); CompareProtocols(currentDatagram, documentPacket);
int colonIndex = fieldShow.IndexOf(':'); }
MoreAssert.IsBiggerOrEqual(0, colonIndex, "Can't find colon in field with empty name");
if (httpDatagram.Header == null) internal static void CompareProtocols(object currentDatagram, XElement layersContainer)
{ {
Assert.IsTrue(httpDatagram.IsRequest); foreach (var layer in layersContainer.Protocols())
Assert.IsNull(httpDatagram.Version); {
switch (layer.Name())
{
case "geninfo":
break; break;
}
httpFieldName = fieldShow.Substring(0, colonIndex); case "frame":
string fieldValue = fieldShow.Substring(colonIndex + 1).SkipWhile(c => c == ' ').TakeWhile(c => c != '\\').SequenceToString(); CompareFrame(layer, (Packet)currentDatagram);
string expectedFieldValue = httpDatagram.Header[httpFieldName].ValueString;
Assert.IsTrue(expectedFieldValue.Contains(fieldValue),
string.Format("{0} <{1}> doesn't contain <{2}>", field.Name(), expectedFieldValue, fieldValue));
}
break; break;
case "data.len": case "eth":
field.AssertShowDecimal(httpDatagram.Length - data.Length / 2); currentDatagram = new WiresharkDatagramComparerEthernet().Compare(layer, currentDatagram);
break; break;
case "http.host": case "arp":
case "http.user_agent": currentDatagram = new WiresharkDatagramComparerArp().Compare(layer, currentDatagram);
case "http.accept":
case "http.accept_language":
case "http.accept_encoding":
case "http.connection":
case "http.cookie":
case "http.cache_control":
case "http.content_encoding":
case "http.date":
case "http.referer":
case "http.last_modified":
case "http.server":
case "http.set_cookie":
case "http.location":
data.Append(field.Value());
httpFieldName = field.Name().Substring(5).Replace('_', '-');
HttpField httpField = httpDatagram.Header[httpFieldName];
if (!field.Value().EndsWith("0d0a"))
Assert.IsNull(httpField);
else
{
string fieldValue = field.Show().Replace("\\\"", "\"");
string expectedFieldValue = httpField.ValueString;
Assert.IsTrue(expectedFieldValue.Contains(fieldValue),
string.Format("{0} <{1}> doesn't contain <{2}>", field.Name(), expectedFieldValue, fieldValue));
}
break; break;
case "http.content_length_header": case "ip":
data.Append(field.Value()); currentDatagram = new WiresharkDatagramComparerIpV4().Compare(layer, currentDatagram);
field.AssertShowDecimal(httpDatagram.Header.ContentLength.ContentLength.Value);
break; break;
case "http.content_type": case "igmp":
data.Append(field.Value()); currentDatagram = new WiresharkDatagramComparerIgmp().Compare(layer, currentDatagram);
string[] mediaType = fieldShow.Split(new[] {';', ' ', '/'}, StringSplitOptions.RemoveEmptyEntries);
Assert.AreEqual(httpDatagram.Header.ContentType.MediaType, mediaType[0]);
Assert.AreEqual(httpDatagram.Header.ContentType.MediaSubtype, mediaType[1]);
int fieldShowParametersStart = fieldShow.IndexOf(';');
if (fieldShowParametersStart == -1)
Assert.IsFalse(httpDatagram.Header.ContentType.Parameters.Any());
else
Assert.AreEqual(httpDatagram.Header.ContentType.Parameters.Select(pair => pair.Key + '=' + pair.Value.ToWiresharkLiteral()).SequenceToString(';'), fieldShow.Substring(fieldShowParametersStart + 1));
break; break;
case "http.transfer_encoding": case "icmp":
data.Append(field.Value()); currentDatagram = new WiresharkDatagramComparerIcmp().Compare(layer, currentDatagram);
Assert.AreEqual(fieldShow.ToWiresharkLowerLiteral(), break;
httpDatagram.Header.TransferEncoding.TransferCodings.SequenceToString(',').ToWiresharkLiteral());
case "gre":
currentDatagram = new WiresharkDatagramComparerGre().Compare(layer, currentDatagram);
break;
case "udp":
currentDatagram = new WiresharkDatagramComparerUdp().Compare(layer, currentDatagram);
break;
case "tcp":
currentDatagram = new WiresharkDatagramComparerTcp().Compare(layer, currentDatagram);
break;
case "http":
currentDatagram = new WiresharkDatagramComparerHttp().Compare(layer, currentDatagram);
break; break;
default: default:
throw new InvalidOperationException("Invalid HTTP field " + field.Name()); return;
} }
} }
} }
private static void CompareHttpFirstLine(XElement httpFirstLineElement, HttpDatagram httpDatagram) private static void CompareFrame(XElement frame, Packet packet)
{ {
foreach (var field in httpFirstLineElement.Fields()) foreach (var field in frame.Fields())
{ {
switch (field.Name()) switch (field.Name())
{ {
case "http.request.method": // case "frame.time":
Assert.IsTrue(httpDatagram.IsRequest, field.Name() + " IsRequest"); // string fieldShow = field.Show();
field.AssertShow(((HttpRequestDatagram)httpDatagram).Method.Method); // if (fieldShow == "Not representable")
break; // break;
// fieldShow = fieldShow.Substring(0, fieldShow.Length - 2);
case "http.request.uri": // DateTime fieldTimestamp = fieldShow[4] == ' '
Assert.IsTrue(httpDatagram.IsRequest, field.Name() + " IsRequest"); // ? DateTime.ParseExact(fieldShow, "MMM d, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture)
field.AssertShow(((HttpRequestDatagram)httpDatagram).Uri.ToWiresharkLiteral()); // : DateTime.ParseExact(fieldShow, "MMM dd, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture);
break; // MoreAssert.IsInRange(fieldTimestamp.AddSeconds(-2), fieldTimestamp.AddSeconds(2), packet.Timestamp, "Timestamp");
// break;
case "http.request.version": case "frame.time_epoch":
if (httpDatagram.Version == null) double timeEpoch = double.Parse(field.Show());
field.AssertShow(string.Empty); DateTime fieldTimestamp = new DateTime(1970, 1, 1).AddSeconds(timeEpoch);
else MoreAssert.IsInRange(fieldTimestamp.AddMilliseconds(-1), fieldTimestamp.AddMilliseconds(1), packet.Timestamp.ToUniversalTime(), "Timestamp");
field.AssertShow(httpDatagram.Version.ToString());
break; break;
case "http.response.code": case "frame.len":
Assert.IsTrue(httpDatagram.IsResponse, field.Name() + " IsResponse"); field.AssertShowDecimal(packet.Length);
field.AssertShowDecimal(((HttpResponseDatagram)httpDatagram).StatusCode.Value);
break; break;
default:
throw new InvalidOperationException("Invalid HTTP first line field " + field.Name());
} }
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment