Commit 8a527dbb authored by Brickner_cp's avatar Brickner_cp

Unicode filenames support for OfflinePacketDevice.

ISO-8859-1 filenames support for PacketCommunicator.OpenDump().
parent 8dbd2073
using System.Text;
namespace PcapDotNet.Base
{
/// <summary>
/// Extension methods for Encoding class.
/// </summary>
public static class EncodingExtensions
{
/// <summary>
/// ISO-8859-1 Encoding.
/// </summary>
public static Encoding Iso88591
{
get { return _iso88591; }
}
private static readonly Encoding _iso88591 = Encoding.GetEncoding(28591);
}
}
\ No newline at end of file
......@@ -86,6 +86,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CharExtensions.cs" />
<Compile Include="EncodingExtensions.cs" />
<Compile Include="FuncExtensions.cs" />
<Compile Include="IDictionaryExtensions.cs" />
<Compile Include="IEnumerableExtensions.cs" />
......
......@@ -158,12 +158,12 @@ namespace PcapDotNet.Core.Test
TestReceivePackets(NumPacketsToSend, NumPacketsToSend, int.MaxValue, 2, PacketSize, PacketCommunicatorReceiveResult.Ok, NumPacketsToSend, 0, 0.063);
// Wait for less packets
TestReceivePackets(NumPacketsToSend, NumPacketsToSend / 2, int.MaxValue, 2, PacketSize, PacketCommunicatorReceiveResult.Ok, NumPacketsToSend / 2, 0, 0.027);
TestReceivePackets(NumPacketsToSend, NumPacketsToSend / 2, int.MaxValue, 2, PacketSize, PacketCommunicatorReceiveResult.Ok, NumPacketsToSend / 2, 0, 0.04);
// Wait for more packets
TestReceivePackets(NumPacketsToSend, 0, int.MaxValue, 2, PacketSize, PacketCommunicatorReceiveResult.None, NumPacketsToSend, 2, 2.14);
TestReceivePackets(NumPacketsToSend, -1, int.MaxValue, 2, PacketSize, PacketCommunicatorReceiveResult.None, NumPacketsToSend, 2, 2.3);
TestReceivePackets(NumPacketsToSend, NumPacketsToSend + 1, int.MaxValue, 2, PacketSize, PacketCommunicatorReceiveResult.None, NumPacketsToSend, 2, 2.051);
TestReceivePackets(NumPacketsToSend, NumPacketsToSend + 1, int.MaxValue, 2, PacketSize, PacketCommunicatorReceiveResult.None, NumPacketsToSend, 2, 2.12);
// Break loop
TestReceivePackets(NumPacketsToSend, NumPacketsToSend, 0, 2, PacketSize, PacketCommunicatorReceiveResult.BreakLoop, 0, 0, 0.027);
......@@ -329,7 +329,7 @@ namespace PcapDotNet.Core.Test
TestGetStatistics(SourceMac, DestinationMac, NumPacketsToSend, NumStatisticsToGather, 0, 5, PacketSize,
PacketCommunicatorReceiveResult.BreakLoop, 0, 0, 0, 0.04);
TestGetStatistics(SourceMac, DestinationMac, NumPacketsToSend, NumStatisticsToGather, NumStatisticsToGather / 2, 5, PacketSize,
PacketCommunicatorReceiveResult.BreakLoop, NumStatisticsToGather / 2, NumPacketsToSend, NumStatisticsToGather / 2, NumStatisticsToGather / 2 + 0.1);
PacketCommunicatorReceiveResult.BreakLoop, NumStatisticsToGather / 2, NumPacketsToSend, NumStatisticsToGather / 2, NumStatisticsToGather / 2 + 0.22);
}
[TestMethod]
......
......@@ -4,6 +4,7 @@ using System.Linq;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PcapDotNet.Packets;
using PcapDotNet.Packets.Ethernet;
using PcapDotNet.Packets.TestUtils;
using PcapDotNet.TestUtils;
......@@ -244,6 +245,65 @@ namespace PcapDotNet.Core.Test
OpenOfflineDevice(10, _random.NextEthernetPacket(100), TimeSpan.Zero, string.Empty);
}
[TestMethod]
public void ReadWriteIso88591FilenameTest()
{
const string DumpFilename = "abc_\u00F9\u00E8.pcap";
const int NumPackets = 10;
Packet expectedPacket = PacketBuilder.Build(DateTime.Now, new EthernetLayer { EtherType = EthernetType.IpV4 });
using (PacketCommunicator communicator = OpenOfflineDevice(NumPackets, expectedPacket, TimeSpan.FromSeconds(0.1), DumpFilename))
{
for (int i = 0; i != NumPackets; ++i)
{
Packet actualPacket;
Assert.AreEqual(PacketCommunicatorReceiveResult.Ok, communicator.ReceivePacket(out actualPacket));
Assert.AreEqual(expectedPacket, actualPacket);
}
}
Assert.IsTrue(File.Exists(DumpFilename), "File " + DumpFilename, " doesn't exist");
}
// TODO: Add this test once Dumping to files with Unicode filenames is supported. See http://www.winpcap.org/pipermail/winpcap-users/2011-February/004273.html
// [TestMethod]
// public void ReadWriteUnicodeFilenameTest()
// {
// const string DumpFilename = "abc_\u00F9_\u05D0\u05D1\u05D2.pcap";
// const int NumPackets = 10;
// Packet expectedPacket = PacketBuilder.Build(DateTime.Now, new EthernetLayer {EtherType = EthernetType.IpV4});
// using (PacketCommunicator communicator = OpenOfflineDevice(NumPackets, expectedPacket, TimeSpan.FromSeconds(0.1), DumpFilename))
// {
// for (int i = 0; i != NumPackets; ++i)
// {
// Packet actualPacket;
// Assert.AreEqual(PacketCommunicatorReceiveResult.Ok, communicator.ReceivePacket(out actualPacket));
// Assert.AreEqual(expectedPacket, actualPacket);
// }
// }
//
// Assert.IsTrue(File.Exists(DumpFilename), "File " + DumpFilename, " doesn't exist");
// }
[TestMethod]
public void ReadUnicodeFilenameTest()
{
const string ReadUnicodeFilename = "abc_\u00F9_\u05D0\u05D1\u05D2.pcap";
const string DumpAsciiFilename = "abc.pcap";
const int NumPackets = 10;
Packet expectedPacket = PacketBuilder.Build(DateTime.Now, new EthernetLayer { EtherType = EthernetType.IpV4 });
using (PacketCommunicator communicator = OpenOfflineDevice(NumPackets, expectedPacket, TimeSpan.FromSeconds(0.1), DumpAsciiFilename, ReadUnicodeFilename))
{
for (int i = 0; i != NumPackets; ++i)
{
Packet actualPacket;
Assert.AreEqual(PacketCommunicatorReceiveResult.Ok, communicator.ReceivePacket(out actualPacket));
Assert.AreEqual(expectedPacket, actualPacket);
}
}
Assert.IsTrue(File.Exists(ReadUnicodeFilename), "File " + ReadUnicodeFilename, " doesn't exist");
}
private static void TestGetSomePackets(int numPacketsToSend, int numPacketsToGet, int numPacketsToBreakLoop,
PacketCommunicatorReceiveResult expectedResult, int expectedNumPackets,
double expectedMinSeconds, double expectedMaxSeconds)
......@@ -325,8 +385,10 @@ namespace PcapDotNet.Core.Test
return OpenOfflineDevice(numPackets, packet, intervalBetweenPackets, Path.GetTempPath() + @"dump.pcap");
}
private static PacketCommunicator OpenOfflineDevice(int numPackets, Packet packet, TimeSpan intervalBetweenPackets, string dumpFilename)
private static PacketCommunicator OpenOfflineDevice(int numPackets, Packet packet, TimeSpan intervalBetweenPackets, string dumpFilename, string readFilename = null)
{
if (readFilename == null)
readFilename = dumpFilename;
PacketCommunicator communicator;
using (communicator = LivePacketDeviceTests.OpenLiveDevice())
{
......@@ -349,11 +411,14 @@ namespace PcapDotNet.Core.Test
}
}
IPacketDevice device = new OfflinePacketDevice(dumpFilename);
if (readFilename != dumpFilename)
File.Move(dumpFilename, readFilename);
IPacketDevice device = new OfflinePacketDevice(readFilename);
Assert.AreEqual(0, device.Addresses.Count);
Assert.AreEqual(string.Empty, device.Description);
Assert.AreEqual(DeviceAttributes.None, device.Attributes);
Assert.AreEqual(dumpFilename, device.Name);
Assert.AreEqual(readFilename, device.Name);
communicator = device.Open();
try
{
......
......@@ -1490,7 +1490,7 @@ namespace PcapDotNet.Core.Test
}
else
{
fieldShow = Encoding.GetEncoding(28591).GetString(HexEncoding.Instance.GetBytes(field.Value()));
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");
......
......@@ -3,13 +3,9 @@
#include "Pcap.h"
using namespace System;
using namespace System::Globalization;
using namespace PcapDotNet::Core;
LivePacketCommunicator::LivePacketCommunicator(const char* source, int snapshotLength, PacketDeviceOpenAttributes attributes, int readTimeout, pcap_rmtauth* auth, SocketAddress^ netmask)
: PacketCommunicator(source, snapshotLength, attributes, readTimeout, auth, netmask)
{
}
PacketTotalStatistics^ LivePacketCommunicator::TotalStatistics::get()
{
int statisticsSize;
......@@ -27,3 +23,31 @@ void LivePacketCommunicator::Transmit(PacketSendBuffer^ sendBuffer, bool isSync)
sendBuffer->Transmit(PcapDescriptor, isSync);
}
// Internal
LivePacketCommunicator::LivePacketCommunicator(const char* source, int snapshotLength, PacketDeviceOpenAttributes attributes, int readTimeout, pcap_rmtauth* auth, SocketAddress^ netmask)
: PacketCommunicator(PcapOpen(source, snapshotLength, attributes, readTimeout, auth), netmask)
{
}
// Private
// static
pcap_t* LivePacketCommunicator::PcapOpen(const char* source, int snapshotLength, PacketDeviceOpenAttributes attributes, int readTimeout, pcap_rmtauth *auth)
{
// Open the device
char errorBuffer[PCAP_ERRBUF_SIZE];
pcap_t *pcapDescriptor = pcap_open(source, // name of the device
snapshotLength, // portion of the packet to capture
// 65536 guarantees that the whole packet will be captured on all the link layers
safe_cast<int>(attributes),
readTimeout, // read timeout
auth, // authentication on the remote machine
errorBuffer); // error buffer
if (pcapDescriptor == NULL)
throw gcnew InvalidOperationException(String::Format(CultureInfo::InvariantCulture, "Unable to open the adapter. Adapter name: {0}. Error: {1}", gcnew String(source), gcnew String(errorBuffer)));
return pcapDescriptor;
}
......@@ -41,5 +41,7 @@ namespace PcapDotNet { namespace Core
LivePacketCommunicator(const char* source, int snapshotLength, PacketDeviceOpenAttributes attributes, int readTimeout, pcap_rmtauth* auth,
SocketAddress^ netmask);
private:
static pcap_t* PcapOpen(const char* source, int snapshotLength, PacketDeviceOpenAttributes attributes, int readTimeout, pcap_rmtauth *auth);
};
}}
\ No newline at end of file
#include "MarshalingServices.h"
#include <Vcclr.h>
using namespace System;
using namespace System::Text;
using namespace System::Runtime::InteropServices;
using namespace PcapDotNet::Base;
using namespace PcapDotNet::Core;
// static
std::string MarshalingServices::ManagedToUnmanagedString(String^ managedString)
{
// Marshal the managed string to unmanaged memory.
if (String::IsNullOrEmpty(managedString))
return std::string();
array<Byte>^ managedBytes = Encoding::ASCII->GetBytes(managedString);
array<Byte>^ managedBytes = EncodingExtensions::Iso88591->GetBytes(managedString);
pin_ptr<Byte> pinManagedBytes = &managedBytes[0];
Byte* unmanagedBytes = pinManagedBytes;
std::string unmanagedString = std::string((char*)unmanagedBytes);
std::string unmanagedString = std::string(reinterpret_cast<const char*>(unmanagedBytes));
return unmanagedString;
}
// static
std::wstring MarshalingServices::ManagedToUnmanagedWideString(String^ managedString)
{
if (String::IsNullOrEmpty(managedString))
return std::wstring();
pin_ptr<const wchar_t> managedChars = PtrToStringChars(managedString);
std::wstring unmanagedString = std::wstring(managedChars);
return unmanagedString;
}
......
......@@ -9,6 +9,8 @@ namespace PcapDotNet { namespace Core
public:
static std::string ManagedToUnmanagedString(System::String^ managedString);
static std::wstring ManagedToUnmanagedWideString(System::String^ managedString);
static array<System::Byte>^ UnamangedToManagedByteArray(const unsigned char* unmanagedByteArray, int offset, int count);
private:
......
#include "OfflinePacketCommunicator.h"
#include "MarshalingServices.h"
#include "Pcap.h"
using namespace System;
using namespace System::Globalization;
using namespace PcapDotNet::Core;
PacketTotalStatistics^ OfflinePacketCommunicator::TotalStatistics::get()
......@@ -13,7 +17,43 @@ void OfflinePacketCommunicator::Transmit(PacketSendBuffer^, bool)
throw gcnew InvalidOperationException("Can't transmit queue to an offline device");
}
OfflinePacketCommunicator::OfflinePacketCommunicator(const char* source, int snapshotLength, PacketDeviceOpenAttributes attributes, int readTimeout, pcap_rmtauth* auth)
: PacketCommunicator(source, snapshotLength, attributes, readTimeout, auth, nullptr)
OfflinePacketCommunicator::~OfflinePacketCommunicator()
{
if (_file != NULL)
CloseFile(_file);
}
OfflinePacketCommunicator::OfflinePacketCommunicator(String^ filename)
: PacketCommunicator(OpenFile(filename), nullptr)
{
}
// Private
pcap_t* OfflinePacketCommunicator::OpenFile(String^ fileName)
{
std::wstring unamangedFilename = MarshalingServices::ManagedToUnmanagedWideString(fileName);
FILE* file = _wfopen(unamangedFilename.c_str(), L"rb");
if (file == NULL)
throw gcnew InvalidOperationException(String::Format(CultureInfo::InvariantCulture, "Failed opening file {0}.", fileName));
char errorBuffer[PCAP_ERRBUF_SIZE];
pcap_t *pcapDescriptor = pcap_fopen_offline(file, errorBuffer);
if (pcapDescriptor == NULL)
{
CloseFile(file);
throw gcnew InvalidOperationException(String::Format(CultureInfo::InvariantCulture, "Failed opening file {0}. Error: {1}", fileName, gcnew String(errorBuffer)));
}
_file = file;
return pcapDescriptor;
}
// static
void OfflinePacketCommunicator::CloseFile(FILE* file)
{
int result = fclose(file);
if (result != 0)
throw gcnew InvalidOperationException("Failed closing file.");
}
\ No newline at end of file
......@@ -2,6 +2,7 @@
#include "PacketCommunicator.h"
#include "PcapDeclarations.h"
#include <cstdio>
namespace PcapDotNet { namespace Core
{
......@@ -23,7 +24,16 @@ namespace PcapDotNet { namespace Core
/// <exception cref="System::InvalidOperationException">Thrown always.</exception>
virtual void Transmit(PacketSendBuffer^ sendBuffer, bool isSync) override;
~OfflinePacketCommunicator();
internal:
OfflinePacketCommunicator(const char* source, int snapshotLength, PacketDeviceOpenAttributes attributes, int readTimeout, pcap_rmtauth* auth);
OfflinePacketCommunicator(System::String^ fileName);
private:
pcap_t* OpenFile(System::String^ filename);
static void CloseFile(FILE* file);
FILE* _file;
};
}}
}}
\ No newline at end of file
......@@ -3,16 +3,14 @@
#include <string>
#include "Pcap.h"
#include "MarshalingServices.h"
#include "OfflinePacketCommunicator.h"
using namespace System;
using namespace System::Globalization;
using namespace System::Collections::Generic;
using namespace System::Collections::ObjectModel;
using namespace PcapDotNet::Core;
OfflinePacketDevice::OfflinePacketDevice(System::String^ fileName)
OfflinePacketDevice::OfflinePacketDevice(String^ fileName)
{
_fileName = fileName;
}
......@@ -37,23 +35,7 @@ ReadOnlyCollection<DeviceAddress^>^ OfflinePacketDevice::Addresses::get()
return gcnew ReadOnlyCollection<DeviceAddress^>(gcnew List<DeviceAddress^>());
}
PacketCommunicator^ OfflinePacketDevice::Open(int snapshotLength, PacketDeviceOpenAttributes attributes, int readTimeout)
PacketCommunicator^ OfflinePacketDevice::Open(int /*snapshotLength*/, PacketDeviceOpenAttributes /*attributes*/, int /*readTimeout*/)
{
std::string unamangedFilename = MarshalingServices::ManagedToUnmanagedString(_fileName);
// Create the source string according to the new WinPcap syntax
char source[PCAP_BUF_SIZE];
char errorBuffer[PCAP_ERRBUF_SIZE];
if (pcap_createsrcstr(source, // variable that will keep the source string
PCAP_SRC_FILE, // we want to open a file
NULL, // remote host
NULL, // port on the remote host
unamangedFilename.c_str(), // name of the file we want to open
errorBuffer // error buffer
) != 0)
{
throw gcnew InvalidOperationException(String::Format(CultureInfo::InvariantCulture, "Error creating a source string from filename {0}. Error: {1}", _fileName, gcnew String(errorBuffer)));
}
return gcnew OfflinePacketCommunicator(source, snapshotLength, attributes, readTimeout, NULL);
return gcnew OfflinePacketCommunicator(_fileName);
}
......@@ -285,23 +285,9 @@ PacketCommunicator::~PacketCommunicator()
// Internal
PacketCommunicator::PacketCommunicator(const char* source, int snapshotLength, PacketDeviceOpenAttributes attributes, int readTimeout, pcap_rmtauth *auth, SocketAddress^ netmask)
PacketCommunicator::PacketCommunicator(pcap_t* pcapDescriptor, SocketAddress^ netmask)
: _pcapDescriptor(pcapDescriptor), _ipV4Netmask(dynamic_cast<IpV4SocketAddress^>(netmask))
{
// Open the device
char errorBuffer[PCAP_ERRBUF_SIZE];
pcap_t *pcapDescriptor = pcap_open(source, // name of the device
snapshotLength, // portion of the packet to capture
// 65536 guarantees that the whole packet will be captured on all the link layers
safe_cast<int>(attributes),
readTimeout, // read timeout
auth, // authentication on the remote machine
errorBuffer); // error buffer
if (pcapDescriptor == NULL)
throw gcnew InvalidOperationException(String::Format(CultureInfo::InvariantCulture, "Unable to open the adapter. Adapter name: {0}. Error: {1}", gcnew String(source), gcnew String(errorBuffer)));
_pcapDescriptor = pcapDescriptor;
_ipV4Netmask = dynamic_cast<IpV4SocketAddress^>(netmask);
}
// Protected
......
......@@ -367,6 +367,7 @@ namespace PcapDotNet { namespace Core
/// <exception cref="System::InvalidOperationException">Thrown on failure.</exception>
/// <remarks>
/// The created dump file should be disposed by the user.
/// Only ISO-8859-1 characters filenames are supported.
/// </remarks>
PacketDumpFile^ OpenDump(System::String^ fileName);
......@@ -376,10 +377,9 @@ namespace PcapDotNet { namespace Core
~PacketCommunicator();
internal:
PacketCommunicator(const char* source, int snapshotLength, PacketDeviceOpenAttributes attributes, int readTimeout, pcap_rmtauth* auth,
SocketAddress^ netmask);
PacketCommunicator(pcap_t* pcapDescriptor, SocketAddress^ netmask);
protected:
protected:
property pcap_t* PcapDescriptor
{
pcap_t* get();
......
......@@ -83,8 +83,10 @@ PacketDumpFile::~PacketDumpFile()
PacketDumpFile::PacketDumpFile(pcap_t* pcapDescriptor, System::String^ filename)
{
_filename = filename;
std::string unmanagedString = MarshalingServices::ManagedToUnmanagedString(_filename);
_pcapDumper = pcap_dump_open(pcapDescriptor, unmanagedString.c_str());
std::string unamangedFilename = MarshalingServices::ManagedToUnmanagedString(filename);
// TODO: Use pcap_dump_fopen() to support Unicode filenames once it's available. See http://www.winpcap.org/pipermail/winpcap-users/2011-February/004273.html
_pcapDumper = pcap_dump_open(pcapDescriptor, unamangedFilename.c_str());
if (_pcapDumper == NULL)
throw gcnew InvalidOperationException("Error opening output file " + filename + " Error: " + PcapError::GetErrorMessage(pcapDescriptor));
}
......@@ -999,7 +999,7 @@ namespace PcapDotNet.Packets.TestUtils
else
reasonPhrase.Append('\t');
}
return new Datagram(Encoding.GetEncoding(28591).GetBytes(reasonPhrase.ToString()));
return new Datagram(EncodingExtensions.Iso88591.GetBytes(reasonPhrase.ToString()));
}
public static HttpRequestMethod NextHttpRequestMethod(this Random random)
......@@ -1222,7 +1222,7 @@ namespace PcapDotNet.Packets.TestUtils
chunkedBody.Add((byte)';');
chunkedBody.AddRange(Encoding.ASCII.GetBytes(parameter.Key));
chunkedBody.Add((byte)'=');
chunkedBody.AddRange(Encoding.GetEncoding(28591).GetBytes(parameter.Key));
chunkedBody.AddRange(EncodingExtensions.Iso88591.GetBytes(parameter.Key));
}
chunkedBody.AddRange(Encoding.ASCII.GetBytes("\r\n"));
chunkedBody.AddRange(random.NextDatagram(chunkSize));
......@@ -1237,7 +1237,7 @@ namespace PcapDotNet.Packets.TestUtils
chunkedBody.Add((byte)';');
chunkedBody.AddRange(Encoding.ASCII.GetBytes(parameter.Key));
chunkedBody.Add((byte)'=');
chunkedBody.AddRange(Encoding.GetEncoding(28591).GetBytes(parameter.Key));
chunkedBody.AddRange(EncodingExtensions.Iso88591.GetBytes(parameter.Key));
}
chunkedBody.AddRange(Encoding.ASCII.GetBytes("\r\n"));
var trailer = random.NextHttpHeader();
......@@ -1260,7 +1260,7 @@ namespace PcapDotNet.Packets.TestUtils
{
List<byte> boundedBody = new List<byte>(random.NextDatagram(random.Next(1000)));
boundedBody.AddRange(Encoding.GetEncoding(28591).GetBytes("--" + httpHeader.ContentType.Parameters["boundary"] + "--"));
boundedBody.AddRange(EncodingExtensions.Iso88591.GetBytes("--" + httpHeader.ContentType.Parameters["boundary"] + "--"));
return new Datagram(boundedBody.ToArray());
}
......
......@@ -200,6 +200,6 @@ namespace PcapDotNet.Packets.Http
return stringBuilder.ToString();
}
private static readonly Encoding _defaultEncoding = Encoding.GetEncoding(28591);
private static readonly Encoding _defaultEncoding = EncodingExtensions.Iso88591;
}
}
\ No newline at end of file
......@@ -128,6 +128,6 @@ namespace PcapDotNet.Packets.Http
private static readonly Regex _parameterRegex = Concat(Capture(_tokenRegex, ParameterNameGroupName), Build("="), Capture(_valueRegex, ParameterValueGroupName));
private static readonly Regex _optionalParametersRegex = Any(Concat(Build(";"), Optional(_linearWhiteSpaceRegex), _parameterRegex));
private static readonly Encoding _encoding = Encoding.GetEncoding(28591);
private static readonly Encoding _encoding = EncodingExtensions.Iso88591;
}
}
\ No newline at end of file
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