Commit 65b9170e authored by Brickner_cp's avatar Brickner_cp

Added projects to Developer's Pack

parent e21fb60c
......@@ -15,9 +15,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SavingPacketsToADumpFile",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReadingPacketsFromADumpFile", "ReadingPacketsFromADumpFile\ReadingPacketsFromADumpFile.csproj", "{E94AA408-15EE-426B-8478-1453EA5A515A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SendingASinglePacketWithSendPacket", "SendingASinglePacketWithSendPacket\SendingASinglePacketWithSendPacket.csproj", "{0E24449E-879B-43C6-9121-5AE50E95D87E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SendingPacketsUsingSendBuffer", "SendingPacketsUsingSendBuffer\SendingPacketsUsingSendBuffer.csproj", "{6BEDD1BC-579B-4C1B-AAAE-1CD4BDF14EC3}"
EndProject
Global
GlobalSection(TeamFoundationVersionControl) = preSolution
SccNumberOfProjects = 8
SccNumberOfProjects = 10
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
SccTeamFoundationServer = https://tfs06.codeplex.com/
SccLocalPath0 = .
......@@ -42,6 +46,12 @@ Global
SccProjectUniqueName7 = ReadingPacketsFromADumpFile\\ReadingPacketsFromADumpFile.csproj
SccProjectName7 = ReadingPacketsFromADumpFile
SccLocalPath7 = ReadingPacketsFromADumpFile
SccProjectUniqueName8 = SendingASinglePacketWithSendPacket\\SendingASinglePacketWithSendPacket.csproj
SccProjectName8 = SendingASinglePacketWithSendPacket
SccLocalPath8 = SendingASinglePacketWithSendPacket
SccProjectUniqueName9 = SendingPacketsUsingSendBuffer\\SendingPacketsUsingSendBuffer.csproj
SccProjectName9 = SendingPacketsUsingSendBuffer
SccLocalPath9 = SendingPacketsUsingSendBuffer
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -76,6 +86,14 @@ Global
{E94AA408-15EE-426B-8478-1453EA5A515A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E94AA408-15EE-426B-8478-1453EA5A515A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E94AA408-15EE-426B-8478-1453EA5A515A}.Release|Any CPU.Build.0 = Release|Any CPU
{0E24449E-879B-43C6-9121-5AE50E95D87E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E24449E-879B-43C6-9121-5AE50E95D87E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E24449E-879B-43C6-9121-5AE50E95D87E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E24449E-879B-43C6-9121-5AE50E95D87E}.Release|Any CPU.Build.0 = Release|Any CPU
{6BEDD1BC-579B-4C1B-AAAE-1CD4BDF14EC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6BEDD1BC-579B-4C1B-AAAE-1CD4BDF14EC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6BEDD1BC-579B-4C1B-AAAE-1CD4BDF14EC3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6BEDD1BC-579B-4C1B-AAAE-1CD4BDF14EC3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Packets;
using PcapDotNet.Core;
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Packets;
using PcapDotNet.Core;
namespace SendingASinglePacketWithSendPacket
{
class Program
{
static void Main(string[] args)
{
// Retrieve the device list from the local machine
IList<LivePacketDevice> allDevices = LivePacketDevice.AllLocalMachine;
if (allDevices.Count == 0)
{
Console.WriteLine("No interfaces found! Make sure WinPcap is installed.");
return;
}
// Print the list
for (int i = 0; i != allDevices.Count; ++i)
{
LivePacketDevice device = allDevices[i];
Console.Write((i + 1) + ". " + device.Name);
if (device.Description != null)
Console.WriteLine(" (" + device.Description + ")");
else
Console.WriteLine(" (No description available)");
}
int deviceIndex = 0;
do
{
Console.WriteLine("Enter the interface number (1-" + allDevices.Count + "):");
string deviceIndexString = Console.ReadLine();
if (!int.TryParse(deviceIndexString, out deviceIndex) ||
deviceIndex < 1 || deviceIndex > allDevices.Count)
{
deviceIndex = 0;
}
} while (deviceIndex == 0);
// Take the selected adapter
PacketDevice selectedDevice = allDevices[deviceIndex - 1];
// Open the output device
using (PacketCommunicator communicator = selectedDevice.Open(100, // name of the device
PacketDeviceOpenAttributes.Promiscuous, // promiscuous mode
1000)) // read timeout
{
// Supposing to be on ethernet, set mac destination to 1:1:1:1:1:1
MacAddress source = new MacAddress("1:1:1:1:1:1");
// set mac source to 2:2:2:2:2:2
MacAddress destination = new MacAddress("2:2:2:2:2:2");
// Fill the rest of the packet (ethernet payload)
byte[] ethernetPayloadBuffer = new byte[100];
for (int i = 0; i != ethernetPayloadBuffer.Length; ++i)
ethernetPayloadBuffer[i] = (byte)(i % 256);
Datagram ethernetPayload = new Datagram(ethernetPayloadBuffer);
// Create the packet
Packet packet = PacketBuilder.Ethernet(DateTime.Now, source, destination, EthernetType.IpV4, ethernetPayload);
// Send down the packet
communicator.SendPacket(packet);
}
}
}
}
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SendingASinglePacketWithSendPacket")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SendingASinglePacketWithSendPacket")]
[assembly: AssemblyCopyright("Copyright © 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("8c12f589-7cfd-42a7-8121-c49fc1e62d06")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{0E24449E-879B-43C6-9121-5AE50E95D87E}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SendingASinglePacketWithSendPacket</RootNamespace>
<AssemblyName>SendingASinglePacketWithSendPacket</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Packets, Version=0.1.0.33384, Culture=neutral, PublicKeyToken=58cf32d85728e684, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\3rdParty\PcapDotNet\Packets.dll</HintPath>
</Reference>
<Reference Include="PcapDotNet.Core, Version=0.1.0.39245, Culture=neutral, PublicKeyToken=58cf32d85728e684, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\3rdParty\PcapDotNet\PcapDotNet.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Packets;
using PcapDotNet.Core;
namespace SendingPacketsUsingSendBuffer
{
class Program
{
static void Main(string[] args)
{
// Check the validity of the command line
if (args.Length == 0 || args.Length > 2)
{
Usage();
return;
}
// Retrieve the device list from the local machine
IList<LivePacketDevice> allDevices = LivePacketDevice.AllLocalMachine;
if (allDevices.Count == 0)
{
Console.WriteLine("No interfaces found! Make sure WinPcap is installed.");
return;
}
// Print the list
for (int i = 0; i != allDevices.Count; ++i)
{
LivePacketDevice device = allDevices[i];
Console.Write((i + 1) + ". " + device.Name);
if (device.Description != null)
Console.WriteLine(" (" + device.Description + ")");
else
Console.WriteLine(" (No description available)");
}
int deviceIndex = 0;
do
{
Console.WriteLine("Enter the interface number (1-" + allDevices.Count + "):");
string deviceIndexString = Console.ReadLine();
if (!int.TryParse(deviceIndexString, out deviceIndex) ||
deviceIndex < 1 || deviceIndex > allDevices.Count)
{
deviceIndex = 0;
}
} while (deviceIndex == 0);
// Take the selected adapter
PacketDevice selectedOutputDevice = allDevices[deviceIndex - 1];
// Retrieve the length of the capture file
long capLength = new FileInfo(args[0]).Length;
// Chek if the timestamps must be respected
bool isSync = (args.Length == 2 && args[1][0] == 's');
// Open the capture file
OfflinePacketDevice selectedInputDevice = new OfflinePacketDevice(args[0]);
using (PacketCommunicator inputCommunicator =
selectedInputDevice.Open(65536, // portion of the packet to capture
// 65536 guarantees that the whole packet will be captured on all the link layers
PacketDeviceOpenAttributes.Promiscuous, // promiscuous mode
1000)) // read timeout
{
using (PacketCommunicator outputCommunicator =
selectedOutputDevice.Open(100, PacketDeviceOpenAttributes.Promiscuous, 1000))
{
// Check the MAC type
if (inputCommunicator.DataLink != outputCommunicator.DataLink)
{
Console.WriteLine(
"Warning: the datalink of the capture differs from the one of the selected interface.");
Console.WriteLine("Press a key to continue, or CTRL+C to stop.");
Console.ReadKey();
}
// Allocate a send buffer
using (PacketSendBuffer sendBuffer = new PacketSendBuffer((uint)capLength))
{
// Fill the buffer with the packets from the file
int numPackets = 0;
Packet packet;
while (inputCommunicator.ReceivePacket(out packet) == PacketCommunicatorReceiveResult.Ok)
{
sendBuffer.Enqueue(packet);
++numPackets;
}
// Transmit the queue
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
long startTimeMs = stopwatch.ElapsedMilliseconds;
Console.WriteLine(startTimeMs);
outputCommunicator.Transmit(sendBuffer, isSync);
long endTimeMs = stopwatch.ElapsedMilliseconds;
Console.WriteLine(endTimeMs);
long elapsedTimeMs = endTimeMs - startTimeMs;
Console.WriteLine(elapsedTimeMs);
double averagePacketsPerSecond = elapsedTimeMs == 0 ? double.MaxValue : (double)numPackets / elapsedTimeMs * 1000;
Console.WriteLine("Elapsed time: " + elapsedTimeMs + " ms");
Console.WriteLine("Total packets generated = " + numPackets);
Console.WriteLine("Average packets per second = " + averagePacketsPerSecond);
Console.WriteLine();
}
}
}
}
private static void Usage()
{
Console.WriteLine("Sends a libpcap/tcpdump capture file to the net.");
Console.WriteLine("Usage:");
Console.WriteLine("\t" + Environment.GetCommandLineArgs()[0] + " <filename> [s]");
Console.WriteLine();
Console.WriteLine("Parameters:");
Console.WriteLine("\tfilename: the name of the dump file that will be sent to the network");
Console.WriteLine(
"\ts: if present, forces the packets to be sent synchronously, i.e. respecting the timestamps in the dump file. This option will work only under Windows NTx");
}
}
}
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SendingPacketsUsingSendBuffer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SendingPacketsUsingSendBuffer")]
[assembly: AssemblyCopyright("Copyright © 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("3a5ef0ba-d3aa-425e-ae10-5712489d4506")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{6BEDD1BC-579B-4C1B-AAAE-1CD4BDF14EC3}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SendingPacketsUsingSendBuffer</RootNamespace>
<AssemblyName>SendingPacketsUsingSendBuffer</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Packets, Version=0.1.0.33384, Culture=neutral, PublicKeyToken=58cf32d85728e684, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\3rdParty\PcapDotNet\Packets.dll</HintPath>
</Reference>
<Reference Include="PcapDotNet.Core, Version=0.1.0.39245, Culture=neutral, PublicKeyToken=58cf32d85728e684, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\3rdParty\PcapDotNet\PcapDotNet.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
\ No newline at end of file
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
......@@ -31,18 +31,7 @@ ReadOnlyCollection<LivePacketDevice^>^ LivePacketDevice::AllLocalMachine::get()
List<LivePacketDevice^>^ result = gcnew List<LivePacketDevice^>();
for (pcap_if_t *d = alldevs; d != NULL; d = d->next)
{
// IP addresses
List<DeviceAddress^>^ addresses = gcnew List<DeviceAddress^>();
for (pcap_addr_t *a = d->addresses; a; a = a->next)
{
DeviceAddress^ deviceAddress = gcnew DeviceAddress(a);
addresses->Add(deviceAddress);
}
result->Add(gcnew LivePacketDevice(gcnew String(d->name),
gcnew String(d->description),
safe_cast<DeviceAttributes>(d->flags),
gcnew ReadOnlyCollection<DeviceAddress^>(addresses)));
result->Add(gcnew LivePacketDevice(*d));
}
return gcnew ReadOnlyCollection<LivePacketDevice^>(result);
}
......@@ -88,10 +77,18 @@ PacketCommunicator^ LivePacketDevice::Open(int snapshotLength, PacketDeviceOpenA
// Private Methods
LivePacketDevice::LivePacketDevice(String^ name, String^ description, DeviceAttributes attributes, ReadOnlyCollection<DeviceAddress^>^ addresses)
LivePacketDevice::LivePacketDevice(const pcap_if_t& device)
{
_name = name;
_description = description;
_attributes = attributes;
_addresses = addresses;
// IP addresses
List<DeviceAddress^>^ addresses = gcnew List<DeviceAddress^>();
for (pcap_addr_t *a = device.addresses; a; a = a->next)
{
DeviceAddress^ deviceAddress = gcnew DeviceAddress(a);
addresses->Add(deviceAddress);
}
_name = gcnew String(device.name);
_description = gcnew String(device.description);
_attributes = safe_cast<DeviceAttributes>(device.flags);
_addresses = gcnew ReadOnlyCollection<DeviceAddress^>(addresses);
}
#pragma once
#include "PacketDevice.h"
#include "PcapDeclarations.h"
namespace PcapDotNet { namespace Core
{
......@@ -77,7 +78,7 @@ namespace PcapDotNet { namespace Core
virtual PacketCommunicator^ Open(int snapshotLength, PacketDeviceOpenAttributes attributes, int readTimeout) override;
private:
LivePacketDevice(System::String^ name, System::String^ description, DeviceAttributes attributes, System::Collections::ObjectModel::ReadOnlyCollection<DeviceAddress^>^ addresses);
LivePacketDevice(const pcap_if_t& device);
private:
System::String^ _name;
......
......@@ -11,3 +11,4 @@ struct pcap_stat;
typedef struct pcap_addr pcap_addr_t;
typedef struct pcap_dumper pcap_dumper_t;
typedef struct pcap pcap_t;
typedef struct pcap_if pcap_if_t;
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