Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
P
Pcap-Net
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Pcap-Net
Commits
7ea0becf
Commit
7ea0becf
authored
Feb 13, 2010
by
Brickner_cp
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ICMP wireshark tests
parent
2eef92c2
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
493 additions
and
133 deletions
+493
-133
IEnumerableExtensions.cs
PcapDotNet/src/PcapDotNet.Base/IEnumerableExtensions.cs
+5
-0
PcapDotNet.Core.Test.csproj
...tNet/src/PcapDotNet.Core.Test/PcapDotNet.Core.Test.csproj
+1
-1
WiresharkCompareTests.cs
PcapDotNet/src/PcapDotNet.Core.Test/WiresharkCompareTests.cs
+189
-10
XElementExtensions.cs
PcapDotNet/src/PcapDotNet.Core.Test/XElementExtensions.cs
+140
-0
PacketCommunicator.cpp
PcapDotNet/src/PcapDotNet.Core/PacketCommunicator.cpp
+1
-0
IcmpTests.cs
PcapDotNet/src/PcapDotNet.Packets.Test/IcmpTests.cs
+11
-86
RandomPacketsExtensions.cs
...c/PcapDotNet.Packets.TestUtils/RandomPacketsExtensions.cs
+90
-21
IcmpDestinationUnreachableDatagram.cs
...DotNet.Packets/Icmp/IcmpDestinationUnreachableDatagram.cs
+31
-13
IcmpDestinationUnreachableLayer.cs
...capDotNet.Packets/Icmp/IcmpDestinationUnreachableLayer.cs
+18
-0
IcmpIpV4PayloadDatagram.cs
...et/src/PcapDotNet.Packets/Icmp/IcmpIpV4PayloadDatagram.cs
+1
-1
IcmpMessageType.cs
PcapDotNet/src/PcapDotNet.Packets/Icmp/IcmpMessageType.cs
+1
-1
PacketBuilder.cs
PcapDotNet/src/PcapDotNet.Packets/PacketBuilder.cs
+5
-0
No files found.
PcapDotNet/src/PcapDotNet.Base/IEnumerableExtensions.cs
View file @
7ea0becf
...
...
@@ -111,6 +111,11 @@ namespace PcapDotNet.Base
});
}
public
static
string
BytesSequenceToHexadecimalString
(
this
IEnumerable
<
byte
>
sequence
)
{
return
sequence
.
BytesSequenceToHexadecimalString
(
string
.
Empty
);
}
/// <summary>
/// Creates a hash code by xoring the hash codes of the elements in the sequence.
/// </summary>
...
...
PcapDotNet/src/PcapDotNet.Core.Test/PcapDotNet.Core.Test.csproj
View file @
7ea0becf
...
...
@@ -50,7 +50,7 @@
<Compile Include="LivePacketDeviceTests.cs" />
<Compile Include="MoreIpV4Option.cs" />
<Compile Include="MoreTcpOption.cs" />
<Compile Include="
MoreXElement
.cs" />
<Compile Include="
XElementExtensions
.cs" />
<Compile Include="PacketDumpFileTests.cs" />
<Compile Include="PacketHandler.cs" />
<Compile Include="OfflinePacketDeviceTests.cs" />
...
...
PcapDotNet/src/PcapDotNet.Core.Test/WiresharkCompareTests.cs
View file @
7ea0becf
This diff is collapsed.
Click to expand it.
PcapDotNet/src/PcapDotNet.Core.Test/
MoreXElement
.cs
→
PcapDotNet/src/PcapDotNet.Core.Test/
XElementExtensions
.cs
View file @
7ea0becf
using
System
;
using
System.Collections.Generic
;
using
System.Xml.Linq
;
using
Microsoft.VisualStudio.TestTools.UnitTesting
;
using
PcapDotNet.Base
;
namespace
PcapDotNet.Core.Test
{
internal
static
class
MoreXElement
internal
static
class
XElementExtensions
{
public
static
IEnumerable
<
XElement
>
Fields
(
this
XElement
element
)
{
return
element
.
Elements
(
"field"
);
}
public
static
IEnumerable
<
XElement
>
Protocols
(
this
XElement
element
)
{
return
element
.
Elements
(
"proto"
);
}
public
static
string
GetAttributeValue
(
this
XElement
element
,
string
attributeName
)
{
XAttribute
attribute
=
element
.
Attribute
(
attributeName
);
if
(
attribute
==
null
)
throw
new
ArgumentException
(
"element "
+
element
.
Name
+
" doesn't contain attribute "
+
attributeName
,
"attributeName"
);
return
attribute
.
Value
;
}
public
static
string
Name
(
this
XElement
element
)
{
return
element
.
Attribute
(
"name"
).
Value
;
return
element
.
GetAttributeValue
(
"name"
)
;
}
public
static
string
Show
(
this
XElement
element
)
{
return
element
.
Attribute
(
"show"
).
Value
;
return
element
.
GetAttributeValue
(
"show"
)
;
}
public
static
string
Value
(
this
XElement
element
)
{
return
element
.
Attribute
(
"value"
).
Value
;
return
element
.
GetAttributeValue
(
"value"
)
;
}
public
static
void
AssertShow
(
this
XElement
element
,
string
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
value
);
Assert
.
AreEqual
(
element
.
Show
(),
value
,
element
.
Name
());
}
public
static
void
AssertShow
(
this
XElement
element
,
IEnumerable
<
byte
>
value
)
{
element
.
AssertShow
(
value
.
BytesSequenceToHexadecimalString
(
":"
));
}
public
static
void
AssertShowDecimal
(
this
XElement
element
,
bool
value
)
...
...
@@ -38,72 +59,82 @@ namespace PcapDotNet.Core.Test
public
static
void
AssertShowDecimal
(
this
XElement
element
,
byte
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
value
.
ToString
());
element
.
AssertShow
(
value
.
ToString
());
}
public
static
void
AssertShowDecimal
(
this
XElement
element
,
short
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
value
.
ToString
());
element
.
AssertShow
(
value
.
ToString
());
}
public
static
void
AssertShowDecimal
(
this
XElement
element
,
ushort
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
value
.
ToString
());
element
.
AssertShow
(
value
.
ToString
());
}
public
static
void
AssertShowDecimal
(
this
XElement
element
,
int
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
value
.
ToString
(),
element
.
Name
());
element
.
AssertShow
(
value
.
ToString
());
}
public
static
void
AssertShowDecimal
(
this
XElement
element
,
uint
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
value
.
ToString
());
element
.
AssertShow
(
value
.
ToString
());
}
public
static
void
AssertShowDecimal
(
this
XElement
element
,
long
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
value
.
ToString
());
element
.
AssertShow
(
value
.
ToString
());
}
public
static
void
AssertShowDecimal
(
this
XElement
element
,
ulong
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
value
.
ToString
());
element
.
AssertShow
(
value
.
ToString
());
}
public
static
void
AssertShowHex
(
this
XElement
element
,
byte
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
byte
)));
element
.
AssertShow
(
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
byte
)));
}
public
static
void
AssertShowHex
(
this
XElement
element
,
short
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
u
short
)));
element
.
AssertShow
(
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
short
)));
}
public
static
void
AssertShowHex
(
this
XElement
element
,
ushort
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
ushort
)));
element
.
AssertShow
(
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
ushort
)));
}
public
static
void
AssertShowHex
(
this
XElement
element
,
int
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
int
)));
element
.
AssertShow
(
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
int
)));
}
public
static
void
AssertShowHex
(
this
XElement
element
,
uint
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
uint
)));
element
.
AssertShow
(
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
uint
)));
}
public
static
void
AssertShowHex
(
this
XElement
element
,
long
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
long
)));
element
.
AssertShow
(
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
long
)));
}
public
static
void
AssertShowHex
(
this
XElement
element
,
ulong
value
)
{
Assert
.
AreEqual
(
element
.
Show
(),
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
ulong
)));
element
.
AssertShow
(
"0x"
+
value
.
ToString
(
"x"
+
2
*
sizeof
(
ulong
)));
}
public
static
void
AssertValue
(
this
XElement
element
,
string
value
)
{
Assert
.
AreEqual
(
element
.
Value
(),
value
,
element
.
Name
());
}
public
static
void
AssertValue
(
this
XElement
element
,
IEnumerable
<
byte
>
bytes
)
{
element
.
AssertValue
(
bytes
.
BytesSequenceToHexadecimalString
());
}
}
}
\ No newline at end of file
PcapDotNet/src/PcapDotNet.Core/PacketCommunicator.cpp
View file @
7ea0becf
...
...
@@ -28,6 +28,7 @@ void PacketCommunicator::DataLink::set(PcapDataLink value)
ReadOnlyCollection
<
PcapDataLink
>^
PacketCommunicator
::
SupportedDataLinks
::
get
()
{
throw
gcnew
NotSupportedException
(
"Supported DataLinks is unsupported to avoid winpcap memory leak"
);
// pcap_free_datalinks(NULL);
/*
int* dataLinks;
int numDatalinks = pcap_list_datalinks(_pcapDescriptor, &dataLinks);
...
...
PcapDotNet/src/PcapDotNet.Packets.Test/IcmpTests.cs
View file @
7ea0becf
...
...
@@ -83,103 +83,28 @@ namespace PcapDotNet.Packets.Test
{
IcmpLayer
icmpLayer
=
random
.
NextIcmpLayer
();
icmpLayer
.
Checksum
=
null
;
IpV4Layer
icmpIpV4Layer
=
null
;
IEnumerable
<
ILayer
>
icmpIpV4PayloadLayers
=
null
;
switch
(
icmpLayer
.
MessageType
)
if
(
icmpLayer
.
MessageType
==
IcmpMessageType
.
DestinationUnreachable
&&
icmpLayer
.
MessageTypeAndCode
!=
IcmpMessageTypeAndCode
.
DestinationUnreachableFragmentationNeededAndDontFragmentSet
)
{
case
IcmpMessageType
.
DestinationUnreachable
:
case
IcmpMessageType
.
TimeExceeded
:
case
IcmpMessageType
.
ParameterProblem
:
case
IcmpMessageType
.
SourceQuench
:
case
IcmpMessageType
.
Redirect
:
case
IcmpMessageType
.
SecurityFailures
:
icmpIpV4Layer
=
random
.
NextIpV4Layer
();
icmpIpV4PayloadLayers
=
new
[]
{
random
.
NextPayloadLayer
(
IcmpIpV4HeaderPlus64BitsPayloadDatagram
.
OriginalDatagramPayloadLength
)};
break
;
case
IcmpMessageType
.
ConversionFailed
:
icmpIpV4Layer
=
random
.
NextIpV4Layer
();
if
(
icmpLayer
.
MessageTypeAndCode
==
IcmpMessageTypeAndCode
.
ConversionFailedUnsupportedTransportProtocol
)
icmpIpV4PayloadLayers
=
new
[]
{
random
.
NextPayloadLayer
(
IcmpConversionFailedDatagram
.
OriginalDatagramLengthForUnsupportedTransportProtocol
-
icmpIpV4Layer
.
Length
)
};
else
{
switch
(
icmpIpV4Layer
.
Protocol
)
{
case
IpV4Protocol
.
Udp
:
icmpIpV4PayloadLayers
=
new
ILayer
[]
{
random
.
NextUdpLayer
(),
random
.
NextPayloadLayer
(
random
.
Next
(
100
))
};
break
;
case
IpV4Protocol
.
Tcp
:
icmpIpV4PayloadLayers
=
new
ILayer
[]
{
random
.
NextTcpLayer
(),
random
.
NextPayloadLayer
(
random
.
Next
(
100
))
};
break
;
default
:
icmpIpV4PayloadLayers
=
new
[]
{
random
.
NextPayloadLayer
(
random
.
Next
(
200
))
};
break
;
}
}
break
;
case
IcmpMessageType
.
Echo
:
case
IcmpMessageType
.
EchoReply
:
case
IcmpMessageType
.
Timestamp
:
case
IcmpMessageType
.
TimestampReply
:
case
IcmpMessageType
.
InformationRequest
:
case
IcmpMessageType
.
InformationReply
:
case
IcmpMessageType
.
RouterAdvertisement
:
case
IcmpMessageType
.
RouterSolicitation
:
case
IcmpMessageType
.
AddressMaskRequest
:
case
IcmpMessageType
.
AddressMaskReply
:
case
IcmpMessageType
.
Traceroute
:
case
IcmpMessageType
.
DomainNameRequest
:
break
;
case
IcmpMessageType
.
DomainNameReply
:
default
:
throw
new
InvalidOperationException
(
"Invalid icmpMessageType "
+
icmpLayer
.
MessageType
);
((
IcmpDestinationUnreachableLayer
)
icmpLayer
).
NextHopMtu
=
0
;
}
int
icmpPayloadLength
=
(
icmpIpV4Layer
!=
null
?
icmpIpV4Layer
.
Length
+
icmpIpV4PayloadLayers
.
Select
(
layer
=>
layer
.
Length
).
Sum
()
:
0
);
IEnumerable
<
ILayer
>
icmpPayloadLayers
=
random
.
NextIcmpPayloadLayers
(
icmpLayer
);
int
icmpPayloadLength
=
icmpPayloadLayers
.
Select
(
layer
=>
layer
.
Length
).
Sum
();
switch
(
icmpLayer
.
MessageType
)
{
// case IcmpMessageType.DestinationUnreachable:
// case IcmpMessageType.TimeExceeded:
case
IcmpMessageType
.
ParameterProblem
:
((
IcmpParameterProblemLayer
)
icmpLayer
).
Pointer
%=
(
byte
)
icmpPayloadLength
;
break
;
// case IcmpMessageType.SourceQuench:
// case IcmpMessageType.Redirect:
case
IcmpMessageType
.
SecurityFailures
:
((
IcmpSecurityFailuresLayer
)
icmpLayer
).
Pointer
%=
(
ushort
)
icmpPayloadLength
;
// icmpIpV4Layer = random.NextIpV4Layer();
// icmpIpV4PayloadLayers = new[] {random.NextPayloadLayer(IcmpIpV4HeaderPlus64BitsPayloadDatagram.OriginalDatagramPayloadLength)};
break
;
// case IcmpMessageType.ConversionFailed:
}
PacketBuilder
packetBuilder
;
if
(
icmpIpV4Layer
!=
null
)
packetBuilder
=
new
PacketBuilder
(
new
ILayer
[]
{
ethernetLayer
,
ipV4Layer
,
icmpLayer
,
icmpIpV4Layer
}.
Concat
(
icmpIpV4PayloadLayers
));
else
packetBuilder
=
new
PacketBuilder
(
ethernetLayer
,
ipV4Layer
,
icmpLayer
);
PacketBuilder
packetBuilder
=
new
PacketBuilder
(
new
ILayer
[]
{
ethernetLayer
,
ipV4Layer
,
icmpLayer
}.
Concat
(
icmpPayloadLayers
));
Packet
packet
=
packetBuilder
.
Build
(
DateTime
.
Now
);
Assert
.
IsTrue
(
packet
.
IsValid
,
"IsValid"
);
...
...
@@ -221,7 +146,8 @@ namespace PcapDotNet.Packets.Test
IcmpLayer
actualIcmpLayer
=
(
IcmpLayer
)
actualIcmp
.
ExtractLayer
();
icmpLayer
.
Checksum
=
actualIcmpLayer
.
Checksum
;
Assert
.
AreEqual
(
icmpLayer
,
actualIcmpLayer
);
Assert
.
AreNotEqual
(
random
.
NextIcmpLayer
(),
actualIcmpLayer
);
if
(
actualIcmpLayer
.
MessageType
!=
IcmpMessageType
.
RouterSolicitation
)
Assert
.
AreNotEqual
(
random
.
NextIcmpLayer
(),
actualIcmpLayer
);
Assert
.
IsTrue
(
actualIcmp
.
IsChecksumCorrect
);
Assert
.
AreEqual
(
icmpLayer
.
MessageType
,
actualIcmp
.
MessageType
);
Assert
.
AreEqual
(
icmpLayer
.
CodeValue
,
actualIcmp
.
Code
);
...
...
@@ -231,13 +157,13 @@ namespace PcapDotNet.Packets.Test
switch
(
packet
.
Ethernet
.
IpV4
.
Icmp
.
MessageType
)
{
case
IcmpMessageType
.
DestinationUnreachable
:
case
IcmpMessageType
.
RouterSolicitation
:
case
IcmpMessageType
.
SourceQuench
:
case
IcmpMessageType
.
TimeExceeded
:
Assert
.
AreEqual
<
uint
>(
0
,
actualIcmp
.
Variable
);
break
;
case
IcmpMessageType
.
DestinationUnreachable
:
case
IcmpMessageType
.
ParameterProblem
:
case
IcmpMessageType
.
Redirect
:
case
IcmpMessageType
.
ConversionFailed
:
...
...
@@ -257,7 +183,6 @@ namespace PcapDotNet.Packets.Test
case
IcmpMessageType
.
DomainNameRequest
:
case
IcmpMessageType
.
SecurityFailures
:
break
;
break
;
case
IcmpMessageType
.
DomainNameReply
:
default
:
...
...
PcapDotNet/src/PcapDotNet.Packets.TestUtils/RandomPacketsExtensions.cs
View file @
7ea0becf
This diff is collapsed.
Click to expand it.
PcapDotNet/src/PcapDotNet.Packets/Icmp/IcmpDestinationUnreachableDatagram.cs
View file @
7ea0becf
...
...
@@ -4,37 +4,55 @@ using PcapDotNet.Base;
namespace
PcapDotNet.Packets.Icmp
{
/// <summary>
/// RFC 792.
/// RFC 792
and RFC 1191
.
/// <pre>
/// +-----+------+------+-----------+
/// | Bit | 0-7 | 8-15 | 16-31 |
/// +-----+------+------+-----------+
/// | 0 | Type | Code | Checksum |
/// +-----+------+------+-----------+
/// | 32 | unused
|
/// +-----+-------------------------+
/// | 64 | Internet Header |
/// | | + 64 bits of |
/// | | Original Data Datagram |
/// +-----+-------------------------+
/// +-----+------+------+-----------
---
+
/// | Bit | 0-7 | 8-15 | 16-31
|
/// +-----+------+------+-----------
---
+
/// | 0 | Type | Code | Checksum
|
/// +-----+------+------+-----------
---
+
/// | 32 | unused
| Next-Hop MTU
|
/// +-----+-------------
+--
------------+
/// | 64 | Internet Header
|
/// | | + 64 bits of
|
/// | | Original Data Datagram
|
/// +-----+-------------------------
---
+
/// </pre>
/// </summary>
public
class
IcmpDestinationUnreachableDatagram
:
IcmpIpV4HeaderPlus64BitsPayloadDatagram
{
private
static
class
Offset
{
public
const
int
NextHopMtu
=
6
;
}
public
IcmpDestinationUnreachableDatagram
(
byte
[]
buffer
,
int
offset
,
int
length
)
:
base
(
buffer
,
offset
,
length
)
{
}
public
ushort
NextHopMtu
{
get
{
return
ReadUShort
(
Offset
.
NextHopMtu
,
Endianity
.
Big
);
}
}
public
override
ILayer
ExtractLayer
()
{
return
new
IcmpDestinationUnreachableLayer
{
Code
=
(
IcmpCodeDestinationUnrechable
)
Code
,
Checksum
=
Checksum
Checksum
=
Checksum
,
NextHopMtu
=
NextHopMtu
,
};
}
protected
override
bool
CalculateIsValid
()
{
return
base
.
CalculateIsValid
()
&&
(((
IcmpCodeDestinationUnrechable
)
Code
==
IcmpCodeDestinationUnrechable
.
FragmentationNeededAndDontFragmentSet
)
||
NextHopMtu
==
0
);
}
protected
override
byte
MinCodeValue
{
get
{
return
_minCode
;
}
...
...
PcapDotNet/src/PcapDotNet.Packets/Icmp/IcmpDestinationUnreachableLayer.cs
View file @
7ea0becf
...
...
@@ -13,5 +13,23 @@ namespace PcapDotNet.Packets.Icmp
{
get
{
return
(
byte
)
Code
;
}
}
public
ushort
NextHopMtu
{
get
;
set
;
}
public
bool
Equals
(
IcmpDestinationUnreachableLayer
other
)
{
return
other
!=
null
&&
NextHopMtu
==
other
.
NextHopMtu
;
}
public
override
sealed
bool
Equals
(
IcmpLayer
other
)
{
return
base
.
Equals
(
other
)
&&
Equals
(
other
as
IcmpDestinationUnreachableLayer
);
}
protected
override
uint
Value
{
get
{
return
NextHopMtu
;
}
}
}
}
\ No newline at end of file
PcapDotNet/src/PcapDotNet.Packets/Icmp/IcmpIpV4PayloadDatagram.cs
View file @
7ea0becf
...
...
@@ -22,7 +22,7 @@ namespace PcapDotNet.Packets.Icmp
{
}
p
rotected
IpV4Datagram
IpV4
p
ublic
IpV4Datagram
IpV4
{
get
{
...
...
PcapDotNet/src/PcapDotNet.Packets/Icmp/IcmpMessageType.cs
View file @
7ea0becf
...
...
@@ -28,7 +28,7 @@ namespace PcapDotNet.Packets.Icmp
/// Codes 2 and 3 may be received from a host.
/// </para>
/// </summary>
DestinationUnreachable
=
3
,
DestinationUnreachable
=
0x0
3
,
/// <summary>
/// RFC 792.
...
...
PcapDotNet/src/PcapDotNet.Packets/PacketBuilder.cs
View file @
7ea0becf
...
...
@@ -11,6 +11,11 @@ namespace PcapDotNet.Packets
return
new
PacketBuilder
(
layers
).
Build
(
timestamp
);
}
public
static
Packet
Build
(
DateTime
timestamp
,
IEnumerable
<
ILayer
>
layers
)
{
return
new
PacketBuilder
(
layers
).
Build
(
timestamp
);
}
public
PacketBuilder
(
params
ILayer
[]
layers
)
{
if
(
layers
.
Length
==
0
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment