Commit 4c1b2b1e authored by Brickner_cp's avatar Brickner_cp

Refactor ComparePacketsToWiresharkTest.

parent 5df9d87d
......@@ -23,719 +23,353 @@ using PcapDotNet.TestUtils;
namespace PcapDotNet.Core.Test
{
/// <summary>
/// Summary description for WiresharkCompareTests
/// </summary>
[TestClass]
public class WiresharkCompareTests
internal abstract class WiresharkDatagramComparer
{
private const string WiresharkDiretory = @"C:\Program Files\Wireshark\";
private const string WiresharkTsharkPath = WiresharkDiretory + @"tshark.exe";
public Datagram Compare(XElement layer, object datagramParent)
{
PropertyInfo property = datagramParent.GetType().GetProperty(PropertyName);
if (property == null)
return null;
private const bool IsRetry
// = true;
= false;
private const byte RetryNumber = 61;
Datagram datagram = (Datagram)property.GetValue(datagramParent);
if (!Ignore(datagram))
CompareDatagram(layer, datagramParent as Datagram, datagram);
return datagram;
}
/// <summary>
/// Gets or sets the test context which provides
/// information about and functionality for the current test run.
/// </summary>
public TestContext TestContext { get; set; }
protected abstract string PropertyName { get; }
#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
protected abstract bool CompareField(XElement field, Datagram parentDatagram, Datagram datagram);
[TestMethod]
public void ComparePacketsToWiresharkTest()
protected virtual bool Ignore(Datagram datagram)
{
#pragma warning disable 162 // This code is unreachable on purpose
if (IsRetry)
return false;
}
protected void CompareDatagram(XElement layer, Datagram parentDatagram, Datagram datagram)
{
ComparePacketsToWireshark(null);
return;
foreach (var field in layer.Fields())
{
if (!CompareField(field, parentDatagram, datagram))
break;
}
#pragma warning restore 162
Random random = new Random();
for (int i = 0; i != 10; ++i)
WiresharkCompareTests.CompareProtocols(datagram, layer);
}
}
internal abstract class WiresharkDatagramComparerSimple : WiresharkDatagramComparer
{
// Create packets
List<Packet> packets = new List<Packet>(CreateRandomPackets(random, 200));
protected override sealed bool CompareField(XElement field, Datagram parentDatagram, Datagram datagram)
{
return CompareField(field, datagram);
}
// Compare packets to wireshark
ComparePacketsToWireshark(packets);
protected abstract bool CompareField(XElement field, Datagram parentDatagram);
}
//[WiresharkDatagramComparerRegistration("eth")]
internal class WiresharkDatagramComparerEthernet : WiresharkDatagramComparerSimple
{
protected override string PropertyName
{
get { return "Ethernet"; }
}
[TestMethod]
public void CompareTimestampPacketsToWiresharkTest()
protected override bool CompareField(XElement field, Datagram datagram)
{
const long Ticks = 633737178954260865;
DateTime timestamp = new DateTime(Ticks).ToUniversalTime().ToLocalTime();
EthernetDatagram ethernetDatagram = (EthernetDatagram)datagram;
switch (field.Name())
{
case "eth.dst":
CompareEthernetAddress(field, ethernetDatagram.Destination);
break;
// Create packet
Packet packet = new Packet(new byte[14], timestamp, DataLinkKind.Ethernet);
case "eth.src":
CompareEthernetAddress(field, ethernetDatagram.Source);
break;
// Compare packet to wireshark
ComparePacketsToWireshark(new[] { packet });
case "eth.type":
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
// 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 });
case "eth.trailer":
if (ethernetDatagram.Trailer != null)
field.AssertValue(ethernetDatagram.Trailer);
break;
case "":
if (ethernetDatagram.Trailer != null)
field.AssertValue(ethernetDatagram.FrameCheckSequence);
break;
default:
throw new InvalidOperationException("Invalid etherent field " + field.Name());
}
[TestMethod]
public void CompareEthernetTrailerToWiresharkTest()
{
const string PacketString = "001120cf0900000c29566988080045000029627b00008006de80c0a8640bc0a81477a42cc03bdd3c481c6cfcd72050104278a5a90000000e01bf0101";
Packet packet = Packet.FromHexadecimalString(PacketString, DateTime.Now, DataLinkKind.Ethernet);
ComparePacketsToWireshark(packet);
return true;
}
[TestMethod]
public void CompareEthernetFcsToWiresharkTest()
private static void CompareEthernetAddress(XElement element, MacAddress address)
{
Packet packet = PacketBuilder.Build(DateTime.Now,
new EthernetLayer(),
new IpV4Layer
foreach (var field in element.Fields())
{
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);
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 enum PacketType
internal class WiresharkDatagramComparerArp : WiresharkDatagramComparerSimple
{
Ethernet,
Arp,
IpV4,
IpV4OverIpV4,
Igmp,
Icmp,
Gre,
Udp,
Tcp,
Http,
protected override string PropertyName
{
get { return "Arp"; }
}
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
DateTime packetTimestamp =
random.NextDateTime(new DateTime(2010,1,1), new DateTime(2010,12,31)).ToUniversalTime().ToLocalTime();
//random.NextDateTime(PacketTimestamp.MinimumPacketTimestamp, PacketTimestamp.MaximumPacketTimestamp).ToUniversalTime().ToLocalTime();
ArpDatagram arpDatagram = (ArpDatagram)datagram;
switch (field.Name())
{
case "arp.hw.type":
field.AssertShowHex((ushort)arpDatagram.HardwareType);
break;
EthernetLayer ethernetLayer = random.NextEthernetLayer();
IpV4Layer ipV4Layer = random.NextIpV4Layer();
PayloadLayer payloadLayer = random.NextPayloadLayer(random.Next(100));
case "arp.proto.type":
field.AssertShowHex((ushort)arpDatagram.ProtocolType);
break;
switch (random.NextEnum<PacketType>())
// switch (PacketType.IpV4OverIpV4)
{
case PacketType.Ethernet:
return PacketBuilder.Build(DateTime.Now, ethernetLayer, payloadLayer);
case "arp.hw.size":
field.AssertShowDecimal(arpDatagram.HardwareLength);
break;
case PacketType.Arp:
ethernetLayer.EtherType = EthernetType.None;
ethernetLayer.Destination = MacAddress.Zero;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, random.NextArpLayer());
case "arp.proto.size":
field.AssertShowDecimal(arpDatagram.ProtocolLength);
break;
case PacketType.IpV4:
ethernetLayer.EtherType = EthernetType.None;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, payloadLayer);
case "arp.opcode":
field.AssertShowHex((ushort)arpDatagram.Operation);
break;
case PacketType.IpV4OverIpV4:
ethernetLayer.EtherType = EthernetType.None;
ipV4Layer.Protocol = null;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, random.NextIpV4Layer(), payloadLayer);
case "arp.src.hw":
case "arp.src.hw_mac":
field.AssertShow(arpDatagram.SenderHardwareAddress);
break;
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 "arp.src.proto":
field.AssertShow(arpDatagram.SenderProtocolAddress);
break;
case PacketType.Gre:
ethernetLayer.EtherType = EthernetType.None;
ipV4Layer.Protocol = null;
GreLayer greLayer = random.NextGreLayer();
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, greLayer, payloadLayer);
case "arp.src.proto_ipv4":
field.AssertShow(arpDatagram.SenderProtocolIpV4Address.ToString());
break;
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 "arp.dst.hw":
case "arp.dst.hw_mac":
field.AssertShow(arpDatagram.TargetHardwareAddress);
break;
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 "arp.dst.proto":
field.AssertShow(arpDatagram.TargetProtocolAddress);
break;
case PacketType.Http:
ethernetLayer.EtherType = EthernetType.None;
ipV4Layer.Protocol = null;
if (random.NextBool())
ipV4Layer.Fragmentation = IpV4Fragmentation.None;
TcpLayer tcpLayer = random.NextTcpLayer();
case "arp.dst.proto_ipv4":
field.AssertShow(arpDatagram.TargetProtocolIpV4Address.ToString());
break;
HttpLayer httpLayer = random.NextHttpLayer();
if (httpLayer.IsRequest)
tcpLayer.DestinationPort = 80;
else
tcpLayer.SourcePort = 80;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, tcpLayer, random.NextHttpLayer());
case "arp.isgratuitous":
break;
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)
yield return CreateRandomPacket(random);
protected override string PropertyName
{
get { return "IpV4"; }
}
private static void ComparePacketsToWireshark(params Packet[] packets)
protected override bool CompareField(XElement field, Datagram datagram)
{
ComparePacketsToWireshark((IEnumerable<Packet>)packets);
}
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())
IpV4Datagram ipV4Datagram = (IpV4Datagram)datagram;
switch (field.Name())
{
case "geninfo":
case "ip.version":
field.AssertShowDecimal(ipV4Datagram.Version);
break;
case "frame":
CompareFrame(layer, (Packet)currentDatagram);
case "ip.hdr_len":
field.AssertShowDecimal(ipV4Datagram.HeaderLength);
break;
case "eth":
PropertyInfo ethernetProperty = currentDatagram.GetType().GetProperty("Ethernet");
if (ethernetProperty == null)
break;
currentDatagram = ethernetProperty.GetValue(currentDatagram);
CompareEtherent(layer, (EthernetDatagram)currentDatagram);
case "ip.dsfield":
field.AssertShowDecimal((int)ipV4Datagram.TypeOfService);
break;
case "arp":
PropertyInfo arpProperty = currentDatagram.GetType().GetProperty("Arp");
if (arpProperty == null)
break;
currentDatagram = arpProperty.GetValue(currentDatagram);
CompareArp(layer, (ArpDatagram)currentDatagram);
case "ip.len":
field.AssertShowDecimal(ipV4Datagram.TotalLength);
break;
case "ip":
PropertyInfo ipV4Property = currentDatagram.GetType().GetProperty("IpV4");
if (ipV4Property == null)
break;
currentDatagram = ipV4Property.GetValue(currentDatagram);
CompareIpV4(layer, (IpV4Datagram)currentDatagram);
case "ip.id":
field.AssertShowHex(ipV4Datagram.Identification);
break;
case "igmp":
PropertyInfo igmpProperty = currentDatagram.GetType().GetProperty("Igmp");
if (igmpProperty == null)
case "ip.flags":
field.AssertShowHex((byte)((ushort)ipV4Datagram.Fragmentation.Options >> 13));
break;
currentDatagram = igmpProperty.GetValue(currentDatagram);
CompareIgmp(layer, (IgmpDatagram)currentDatagram);
case "ip.frag_offset":
field.AssertShowDecimal(ipV4Datagram.Fragmentation.Offset);
break;
case "icmp":
PropertyInfo icmpProperty = currentDatagram.GetType().GetProperty("Icmp");
if (icmpProperty == null)
case "ip.ttl":
field.AssertShowDecimal(ipV4Datagram.Ttl);
break;
currentDatagram = icmpProperty.GetValue(currentDatagram);
CompareIcmp(layer, (IcmpDatagram)currentDatagram);
case "ip.proto":
field.AssertShowDecimal((byte)ipV4Datagram.Protocol);
break;
case "gre":
PropertyInfo greProperty = currentDatagram.GetType().GetProperty("Gre");
if (greProperty == null)
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;
currentDatagram = greProperty.GetValue(currentDatagram);
CompareGre(layer, (GreDatagram)currentDatagram);
case "ip.checksum_bad":
if (ipV4Datagram.Length < IpV4Datagram.HeaderMinimumLength ||
ipV4Datagram.Length < ipV4Datagram.HeaderLength)
break;
case "udp":
PropertyInfo udpProperty = currentDatagram.GetType().GetProperty("Udp");
if (udpProperty == null)
checksumField.AssertShowDecimal(!ipV4Datagram.IsHeaderChecksumCorrect);
break;
{
Datagram ipDatagram = (Datagram)currentDatagram;
currentDatagram = udpProperty.GetValue(currentDatagram);
CompareUdp(layer, (IpV4Datagram)ipDatagram);
}
}
break;
case "tcp":
PropertyInfo tcpProperty = currentDatagram.GetType().GetProperty("Tcp");
if (tcpProperty == null)
case "ip.src":
case "ip.src_host":
field.AssertShow(ipV4Datagram.Source.ToString());
break;
{
Datagram ipDatagram = (Datagram)currentDatagram;
currentDatagram = tcpProperty.GetValue(currentDatagram);
CompareTcp(layer, (IpV4Datagram)ipDatagram);
}
case "ip.dst":
case "ip.dst_host":
field.AssertShow(ipV4Datagram.Destination.ToString());
break;
case "http":
PropertyInfo httpProperty = currentDatagram.GetType().GetProperty("Http");
if (httpProperty == null)
case "ip.addr":
case "ip.host":
Assert.IsTrue(field.Show() == ipV4Datagram.Source.ToString() ||
field.Show() == ipV4Datagram.Destination.ToString());
break;
{
currentDatagram = httpProperty.GetValue(currentDatagram);
CompareHttp(layer, (HttpDatagram)currentDatagram);
}
case "":
CompareIpV4Options(field, ipV4Datagram.Options);
break;
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":
// string fieldShow = field.Show();
// if (fieldShow == "Not representable")
// break;
// fieldShow = fieldShow.Substring(0, fieldShow.Length - 2);
// DateTime fieldTimestamp = fieldShow[4] == ' '
// ? DateTime.ParseExact(fieldShow, "MMM d, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture)
// : DateTime.ParseExact(fieldShow, "MMM dd, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture);
// MoreAssert.IsInRange(fieldTimestamp.AddSeconds(-2), fieldTimestamp.AddSeconds(2), packet.Timestamp, "Timestamp");
// break;
case "frame.time_epoch":
double timeEpoch = double.Parse(field.Show());
DateTime fieldTimestamp = new DateTime(1970, 1, 1).AddSeconds(timeEpoch);
MoreAssert.IsInRange(fieldTimestamp.AddMilliseconds(-1), fieldTimestamp.AddMilliseconds(1), packet.Timestamp.ToUniversalTime(), "Timestamp");
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());
case "frame.len":
field.AssertShowDecimal(packet.Length);
break;
if ((option is IpV4OptionUnknown))
continue;
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())
{
case "eth.dst":
CompareEthernetAddress(field, ethernetDatagram.Destination);
case "igmp.version":
if (field.Show() == "0")
return false; // TODO: support IGMP version 0.
field.AssertShowDecimal(igmpDatagram.Version);
break;
case "eth.src":
CompareEthernetAddress(field, ethernetDatagram.Source);
case "igmp.type":
field.AssertShowHex((byte)igmpDatagram.MessageType);
break;
case "eth.type":
field.AssertShowHex((ushort)ethernetDatagram.EtherType);
case "igmp.checksum":
field.AssertShowHex(igmpDatagram.Checksum);
break;
case "eth.trailer":
if (ethernetDatagram.Trailer != null)
field.AssertValue(ethernetDatagram.Trailer);
case "igmp.maddr":
field.AssertShow(igmpDatagram.GroupAddress.ToString());
break;
case "":
if (ethernetDatagram.Trailer != null)
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));
case "igmp.max_resp":
field.AssertShowDecimal((int)((igmpDatagram.MaxResponseTime.TotalSeconds + 0.05) * 10));
break;
case "igmp.checksum_bad":
......@@ -750,11 +384,11 @@ namespace PcapDotNet.Core.Test
switch (igmpDatagram.MessageType)
{
case IgmpMessageType.MembershipReportVersion3:
CompareIgmpGroupRecord(field, igmpDatagram.GroupRecords[groupRecordIndex++]);
CompareIgmpGroupRecord(field, igmpDatagram.GroupRecords[_groupRecordIndex++]);
break;
case IgmpMessageType.MembershipQuery:
CompareIgmp(field, igmpDatagram);
CompareDatagram(field, null, igmpDatagram);
break;
default:
......@@ -785,7 +419,7 @@ namespace PcapDotNet.Core.Test
break;
case "igmp.saddr":
field.AssertShow(igmpDatagram.SourceAddresses[sourceAddressIndex++].ToString());
field.AssertShow(igmpDatagram.SourceAddresses[_sourceAddressIndex++].ToString());
break;
case "igmp.identifier":
......@@ -799,7 +433,8 @@ namespace PcapDotNet.Core.Test
default:
throw new InvalidOperationException("Invalid igmp field " + field.Name());
}
}
return true;
}
private static void CompareIgmpGroupRecord(XElement groupRecord, IgmpGroupRecordDatagram groupRecordDatagram)
......@@ -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;
foreach (var field in icmp.Fields())
protected override string PropertyName
{
get { return "Icmp"; }
}
protected override bool CompareField(XElement field, Datagram datagram)
{
IcmpDatagram icmpDatagram = (IcmpDatagram)datagram;
switch (field.Name())
{
case "icmp.type":
......@@ -936,11 +580,11 @@ namespace PcapDotNet.Core.Test
break;
case "Router address":
field.AssertShow(fieldName + ": " + routerAdvertisementDatagram.Entries[routerIndex].RouterAddress);
field.AssertShow(fieldName + ": " + routerAdvertisementDatagram.Entries[_routerIndex].RouterAddress);
break;
case "Preference level":
field.AssertShow(fieldName + ": " + routerAdvertisementDatagram.Entries[routerIndex++].RouterAddressPreference);
field.AssertShow(fieldName + ": " + routerAdvertisementDatagram.Entries[_routerIndex++].RouterAddressPreference);
break;
default:
......@@ -979,16 +623,23 @@ namespace PcapDotNet.Core.Test
throw new InvalidOperationException("Invalid icmp field " + field.Name());
break;
}
return true;
}
CompareProtocols(icmpDatagram, icmp);
private int _routerIndex;
}
private static void CompareGre(XElement greElement, GreDatagram greDatagram)
internal class WiresharkDatagramComparerGre : WiresharkDatagramComparerSimple
{
int currentEntry = -1;
foreach (var field in greElement.Fields())
protected override string PropertyName
{
get { return "Gre"; }
}
protected override bool CompareField(XElement field, Datagram datagram)
{
GreDatagram greDatagram = (GreDatagram)datagram;
switch (field.Name())
{
case "":
......@@ -1077,24 +728,24 @@ namespace PcapDotNet.Core.Test
}
else if (field.Show().StartsWith("Address family: "))
{
++currentEntry;
if (currentEntry < greDatagram.Routing.Count)
field.AssertValue((ushort)greDatagram.Routing[currentEntry].AddressFamily);
else if (currentEntry > greDatagram.Routing.Count)
++_currentEntry;
if (_currentEntry < greDatagram.Routing.Count)
field.AssertValue((ushort)greDatagram.Routing[_currentEntry].AddressFamily);
else if (_currentEntry > greDatagram.Routing.Count)
Assert.IsFalse(greDatagram.IsValid);
}
else if (field.Show().StartsWith("SRE offset: "))
{
if (currentEntry < greDatagram.Routing.Count)
field.AssertValue(greDatagram.Routing[currentEntry].PayloadOffset);
else if (currentEntry > greDatagram.Routing.Count)
if (_currentEntry < greDatagram.Routing.Count)
field.AssertValue(greDatagram.Routing[_currentEntry].PayloadOffset);
else if (_currentEntry > greDatagram.Routing.Count)
Assert.IsFalse(greDatagram.IsValid);
}
else if (field.Show().StartsWith("SRE length: "))
{
if (currentEntry < greDatagram.Routing.Count)
field.AssertValue(greDatagram.Routing[currentEntry].PayloadLength);
else if (currentEntry > greDatagram.Routing.Count)
if (_currentEntry < greDatagram.Routing.Count)
field.AssertValue(greDatagram.Routing[_currentEntry].PayloadLength);
else if (_currentEntry > greDatagram.Routing.Count)
Assert.IsFalse(greDatagram.IsValid);
}
else
......@@ -1137,15 +788,25 @@ namespace PcapDotNet.Core.Test
Assert.Fail("Invalid field name: " + field.Name());
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())
{
case "udp.srcport":
......@@ -1195,15 +856,23 @@ namespace PcapDotNet.Core.Test
default:
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())
{
case "tcp.len":
......@@ -1329,7 +998,8 @@ namespace PcapDotNet.Core.Test
default:
throw new InvalidOperationException("Invalid tcp field " + field.Name());
}
}
return true;
}
private static void CompareTcpOptions(XElement element, TcpOptions options)
......@@ -1418,193 +1088,605 @@ namespace PcapDotNet.Core.Test
field.AssertValue(Encoding.ASCII.GetBytes(((TcpOptionMood)option).EmotionString));
break;
case "tcp.options.mood_val":
Assert.AreEqual(TcpOptionType.Mood, option.OptionType);
field.AssertShow(((TcpOptionMood)option).EmotionString);
++currentOptionIndex;
break;
case "tcp.options.mood_val":
Assert.AreEqual(TcpOptionType.Mood, option.OptionType);
field.AssertShow(((TcpOptionMood)option).EmotionString);
++currentOptionIndex;
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:
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)
return; // todo https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5182
ComparePacketsToWireshark((IEnumerable<Packet>)packets);
}
StringBuilder data = new StringBuilder();
bool isFirstEmptyName = true;
foreach (var field in httpElement.Fields())
private static void ComparePacketsToWireshark(IEnumerable<Packet> packets)
{
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")
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));
continue;
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
string fieldShow = field.Show();
string httpFieldName;
switch (field.Name())
// Create pdml file
string documentFilename = pcapFilename + ".pdml";
using (Process process = new Process())
{
case "http.request":
field.AssertShowDecimal(httpDatagram.IsRequest);
break;
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);
}
case "http.response":
field.AssertShowDecimal(httpDatagram.IsResponse);
break;
// 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());
case "":
if (fieldShow == "HTTP chunked response")
try
{
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());
if (fieldShow == @"\r\n")
break;
private static void Compare(XDocument document, IEnumerable<Packet> packets)
{
IEnumerator<Packet> packetEnumerator = packets.GetEnumerator();
if (isFirstEmptyName)
// Parse XML
int i = 1;
foreach (var documentPacket in document.Element("pdml").Elements("packet"))
{
CompareHttpFirstLine(field, httpDatagram);
isFirstEmptyName = false;
packetEnumerator.MoveNext();
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()));
fieldShow = fieldShow.Substring(0, fieldShow.Length - 2);
int colonIndex = fieldShow.IndexOf(':');
MoreAssert.IsBiggerOrEqual(0, colonIndex, "Can't find colon in field with empty name");
object currentDatagram = packet;
CompareProtocols(currentDatagram, documentPacket);
}
if (httpDatagram.Header == null)
internal static void CompareProtocols(object currentDatagram, XElement layersContainer)
{
Assert.IsTrue(httpDatagram.IsRequest);
Assert.IsNull(httpDatagram.Version);
foreach (var layer in layersContainer.Protocols())
{
switch (layer.Name())
{
case "geninfo":
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));
}
case "frame":
CompareFrame(layer, (Packet)currentDatagram);
break;
case "data.len":
field.AssertShowDecimal(httpDatagram.Length - data.Length / 2);
case "eth":
currentDatagram = new WiresharkDatagramComparerEthernet().Compare(layer, currentDatagram);
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));
}
case "arp":
currentDatagram = new WiresharkDatagramComparerArp().Compare(layer, currentDatagram);
break;
case "http.content_length_header":
data.Append(field.Value());
field.AssertShowDecimal(httpDatagram.Header.ContentLength.ContentLength.Value);
case "ip":
currentDatagram = new WiresharkDatagramComparerIpV4().Compare(layer, currentDatagram);
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));
case "igmp":
currentDatagram = new WiresharkDatagramComparerIgmp().Compare(layer, currentDatagram);
break;
case "http.transfer_encoding":
data.Append(field.Value());
Assert.AreEqual(fieldShow.ToWiresharkLowerLiteral(),
httpDatagram.Header.TransferEncoding.TransferCodings.SequenceToString(',').ToWiresharkLiteral());
case "icmp":
currentDatagram = new WiresharkDatagramComparerIcmp().Compare(layer, currentDatagram);
break;
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;
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())
{
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 "frame.time":
// string fieldShow = field.Show();
// if (fieldShow == "Not representable")
// break;
// fieldShow = fieldShow.Substring(0, fieldShow.Length - 2);
// DateTime fieldTimestamp = fieldShow[4] == ' '
// ? DateTime.ParseExact(fieldShow, "MMM d, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture)
// : DateTime.ParseExact(fieldShow, "MMM dd, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture);
// MoreAssert.IsInRange(fieldTimestamp.AddSeconds(-2), fieldTimestamp.AddSeconds(2), packet.Timestamp, "Timestamp");
// break;
case "http.request.version":
if (httpDatagram.Version == null)
field.AssertShow(string.Empty);
else
field.AssertShow(httpDatagram.Version.ToString());
case "frame.time_epoch":
double timeEpoch = double.Parse(field.Show());
DateTime fieldTimestamp = new DateTime(1970, 1, 1).AddSeconds(timeEpoch);
MoreAssert.IsInRange(fieldTimestamp.AddMilliseconds(-1), fieldTimestamp.AddMilliseconds(1), packet.Timestamp.ToUniversalTime(), "Timestamp");
break;
case "http.response.code":
Assert.IsTrue(httpDatagram.IsResponse, field.Name() + " IsResponse");
field.AssertShowDecimal(((HttpResponseDatagram)httpDatagram).StatusCode.Value);
case "frame.len":
field.AssertShowDecimal(packet.Length);
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