Commit 9842d4a3 authored by Brickner_cp's avatar Brickner_cp

HTTP

Added overload for PacketDumpFile.Dump() with DataLinkKind instead of PcapDataLink.
Added Mood TCP Option (RFC5841).
parent 3ce1f87b
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace PcapDotNet.Base
{
public class HexEncoding : Encoding
{
public static HexEncoding Instance { get { return _instance; } }
public override int GetByteCount(char[] chars, int index, int count)
{
int numHexChars = 0;
// remove all none A-F, 0-9, characters
for (int i=0; i<count; ++i)
{
if (IsHexDigit(chars[index+i]))
++numHexChars;
}
// if odd number of characters, discard last character
return numHexChars / 2; // 2 characters per byte
}
public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
{
int originalByteIndex = byteIndex;
IEnumerable<char> hexChars = chars.Range(charIndex, charCount).Where(IsHexDigit);
IEnumerator<char> hexCharsEnumerator = hexChars.GetEnumerator();
while (hexCharsEnumerator.MoveNext())
{
char firstChar = hexCharsEnumerator.Current;
if (!hexCharsEnumerator.MoveNext())
break;
char secondChar = hexCharsEnumerator.Current;
bytes[byteIndex++] = HexToByte(firstChar, secondChar);
}
return byteIndex - originalByteIndex;
}
public override int GetCharCount(byte[] bytes, int index, int count)
{
return count * 2;
}
public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
{
int originalCharIndex = charIndex;
foreach (byte b in bytes.Range(byteIndex, byteCount))
{
ByteToHex(b, out chars[charIndex], out chars[charIndex + 1]);
charIndex += 2;
}
return charIndex - originalCharIndex;
}
public override int GetMaxByteCount(int charCount)
{
return charCount / 2;
}
public override int GetMaxCharCount(int byteCount)
{
return byteCount * 2;
}
private HexEncoding()
{
}
private static bool IsHexDigit(char c)
{
return (c >= '0' && c <= '9' ||
c >= 'a' && c <= 'f' ||
c >= 'A' && c <= 'F');
}
private static byte HexToByte(char mostSignificantDigit, char leastSignificantDigit)
{
return (byte)(DigitToByte(mostSignificantDigit) * 16 + DigitToByte(leastSignificantDigit));
}
private void ByteToHex(byte b, out char mostSignificantDigit, out char leastSignificantDigit)
{
mostSignificantDigit = ByteToDigit((byte)(b / 16));
leastSignificantDigit = ByteToDigit((byte)(b % 16));
}
private static byte DigitToByte(char digit)
{
if (digit >= '0' && digit <= '9')
return (byte)(digit - '0');
if (digit >= 'a' && digit <= 'f')
return (byte)(digit - 'a' + 10);
if (digit >= 'A' && digit <= 'F')
return (byte)(digit - 'A' + 10);
throw new ArgumentOutOfRangeException("digit", digit, "digit is not a legal hexadecimal character");
}
private char ByteToDigit(byte b)
{
if (b <= 9)
return (char)('0' + b);
if (b <= 15)
return (char)('A' + b);
throw new ArgumentOutOfRangeException("b", b, "value must be between 0 and 15");
}
private static readonly HexEncoding _instance = new HexEncoding();
}
}
\ No newline at end of file
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace PcapDotNet.Base namespace PcapDotNet.Base
...@@ -19,26 +18,7 @@ namespace PcapDotNet.Base ...@@ -19,26 +18,7 @@ namespace PcapDotNet.Base
if (!dictionary2.TryGetValue(pair.Key, out otherValue)) if (!dictionary2.TryGetValue(pair.Key, out otherValue))
return false; return false;
if (!valueComparer.Equals(pair.Value, otherValue)) if (!valueComparer.Equals(pair.Value, otherValue))
{
if (otherValue is string)
{
string otherString = otherValue as string;
string thisString = pair.Value as string;
for (int i = 0; i != otherString.Length; ++i)
{
if (!thisString[i].Equals(otherString[i]))
{
Console.WriteLine("a");
}
else
{
Console.WriteLine("b");
}
}
}
return false; return false;
}
} }
return true; return true;
......
...@@ -87,11 +87,13 @@ ...@@ -87,11 +87,13 @@
<ItemGroup> <ItemGroup>
<Compile Include="CharExtensions.cs" /> <Compile Include="CharExtensions.cs" />
<Compile Include="FuncExtensions.cs" /> <Compile Include="FuncExtensions.cs" />
<Compile Include="HexEncoding.cs" />
<Compile Include="IDictionaryExtensions.cs" /> <Compile Include="IDictionaryExtensions.cs" />
<Compile Include="IEnumerableExtensions.cs" /> <Compile Include="IEnumerableExtensions.cs" />
<Compile Include="IListExtensions.cs" /> <Compile Include="IListExtensions.cs" />
<Compile Include="MatchExtensions.cs" /> <Compile Include="MatchExtensions.cs" />
<Compile Include="PropertyInfoExtensions.cs" /> <Compile Include="PropertyInfoExtensions.cs" />
<Compile Include="StringExtensions.cs" />
<Compile Include="TimeSpanExtensions.cs" /> <Compile Include="TimeSpanExtensions.cs" />
<Compile Include="TypeExtensions.cs" /> <Compile Include="TypeExtensions.cs" />
<Compile Include="UInt128.cs" /> <Compile Include="UInt128.cs" />
......
using System.CodeDom;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.CSharp;
namespace PcapDotNet.Base
{
public static class StringExtensions
{
public static string ToLiteral(this string value)
{
StringBuilder result = new StringBuilder();
for (int i = 0; i != value.Length; ++i)
{
char currentChar = value[i];
switch (currentChar)
{
case '\\':
case '"':
result.Append('\\');
result.Append(currentChar);
break;
case '\t':
// result.Append(@"\t");
result.Append(@"\x9");
break;
case '\r':
result.Append(@"\r");
break;
case '\n':
result.Append(@"\n");
break;
default:
if (currentChar >= 0x7F || currentChar < 0x20)
{
result.Append(@"\x");
result.Append(((int)currentChar).ToString("x"));
break;
}
result.Append(currentChar);
break;
}
}
return result.ToString();
}
public static string ToLowerLiteral(this string literalString)
{
literalString = literalString.ToLowerInvariant();
foreach (byte charValue in Enumerable.Range(0xC0, 0xD7 - 0xC0).Concat(Enumerable.Range(0xD8, 0xDF - 0xD8)))
literalString = literalString.Replace(@"\x" + charValue.ToString("x"), @"\x" + (charValue + 0x20).ToString("x"));
return literalString;
}
}
}
\ No newline at end of file
...@@ -108,6 +108,7 @@ namespace PcapDotNet.Core.Test ...@@ -108,6 +108,7 @@ namespace PcapDotNet.Core.Test
case TcpOptionType.AlternateChecksumRequest: case TcpOptionType.AlternateChecksumRequest:
case TcpOptionType.AlternateChecksumData: case TcpOptionType.AlternateChecksumData:
case TcpOptionType.Md5Signature: case TcpOptionType.Md5Signature:
case TcpOptionType.Mood:
break; break;
case TcpOptionType.SelectiveAcknowledgment: case TcpOptionType.SelectiveAcknowledgment:
......
using System; using System;
using System.CodeDom;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
...@@ -7,6 +8,7 @@ using System.Linq; ...@@ -7,6 +8,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.CSharp;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using PcapDotNet.Base; using PcapDotNet.Base;
using PcapDotNet.Packets; using PcapDotNet.Packets;
...@@ -129,6 +131,7 @@ namespace PcapDotNet.Core.Test ...@@ -129,6 +131,7 @@ namespace PcapDotNet.Core.Test
Gre, Gre,
Udp, Udp,
Tcp, Tcp,
Http,
} }
private static Packet CreateRandomPacket(Random random) private static Packet CreateRandomPacket(Random random)
...@@ -142,7 +145,8 @@ namespace PcapDotNet.Core.Test ...@@ -142,7 +145,8 @@ namespace PcapDotNet.Core.Test
IpV4Layer ipV4Layer = random.NextIpV4Layer(); IpV4Layer ipV4Layer = random.NextIpV4Layer();
PayloadLayer payloadLayer = random.NextPayloadLayer(random.Next(100)); PayloadLayer payloadLayer = random.NextPayloadLayer(random.Next(100));
switch (random.NextEnum<PacketType>()) // switch (random.NextEnum<PacketType>())
switch (PacketType.Http)
{ {
case PacketType.Ethernet: case PacketType.Ethernet:
return PacketBuilder.Build(DateTime.Now, ethernetLayer, payloadLayer); return PacketBuilder.Build(DateTime.Now, ethernetLayer, payloadLayer);
...@@ -184,7 +188,17 @@ namespace PcapDotNet.Core.Test ...@@ -184,7 +188,17 @@ namespace PcapDotNet.Core.Test
ipV4Layer.Protocol = null; ipV4Layer.Protocol = null;
if (random.NextBool()) if (random.NextBool())
ipV4Layer.Fragmentation = IpV4Fragmentation.None; ipV4Layer.Fragmentation = IpV4Fragmentation.None;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, random.NextUdpLayer(), payloadLayer); 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();
tcpLayer.DestinationPort = 80;
tcpLayer.SourcePort = 80;
return PacketBuilder.Build(packetTimestamp, ethernetLayer, ipV4Layer, tcpLayer, random.NextHttpLayer());
default: default:
throw new InvalidOperationException(); throw new InvalidOperationException();
...@@ -578,7 +592,7 @@ namespace PcapDotNet.Core.Test ...@@ -578,7 +592,7 @@ namespace PcapDotNet.Core.Test
break; break;
case "ip.proto": case "ip.proto":
field.AssertShowHex((byte)ipV4Datagram.Protocol); field.AssertShowDecimal((byte)ipV4Datagram.Protocol);
break; break;
case "ip.checksum": case "ip.checksum":
...@@ -1344,6 +1358,22 @@ namespace PcapDotNet.Core.Test ...@@ -1344,6 +1358,22 @@ namespace PcapDotNet.Core.Test
Assert.AreEqual((TcpOptionType)21, option.OptionType); Assert.AreEqual((TcpOptionType)21, option.OptionType);
break; break;
case "tcp.options.sack_perm":
Assert.AreEqual(TcpOptionType.SelectiveAcknowledgmentPermitted, option.OptionType);
++currentOptionIndex;
break;
case "tcp.options.mood":
Assert.AreEqual(TcpOptionType.Mood, option.OptionType);
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;
default: default:
throw new InvalidOperationException("Invalid tcp options field " + field.Name()); throw new InvalidOperationException("Invalid tcp options field " + field.Name());
} }
...@@ -1358,7 +1388,9 @@ namespace PcapDotNet.Core.Test ...@@ -1358,7 +1388,9 @@ namespace PcapDotNet.Core.Test
private static void CompareHttp(XElement httpElement, HttpDatagram httpDatagram) private static void CompareHttp(XElement httpElement, HttpDatagram httpDatagram)
{ {
string httpFieldName; if (httpDatagram.Header != null && httpDatagram.Header.ContentLength != null && httpDatagram.Header.TransferEncoding != null)
return; // todo https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5182
StringBuilder data = new StringBuilder(); StringBuilder data = new StringBuilder();
bool isFirstEmptyName = true; bool isFirstEmptyName = true;
foreach (var field in httpElement.Fields()) foreach (var field in httpElement.Fields())
...@@ -1368,17 +1400,19 @@ namespace PcapDotNet.Core.Test ...@@ -1368,17 +1400,19 @@ namespace PcapDotNet.Core.Test
if (field.Name() == "data") if (field.Name() == "data")
field.AssertNoShow(); field.AssertNoShow();
string previousData = data.ToString(); MoreAssert.AreSequenceEqual(httpDatagram.Take(data.Length / 2), HexEncoding.Instance.GetBytes(data.ToString()));
for (int i = 0; i != previousData.Length / 2; ++i) // 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); // byte value = Convert.ToByte(previousData.Substring(i * 2, 2), 16);
} // Assert.AreEqual(httpDatagram[i], value);
field.AssertValue(httpDatagram.Skip(previousData.Length / 2), field.Name()); // }
field.AssertValue(httpDatagram.Skip(data.Length / 2));
continue; continue;
} }
string fieldShow = field.Show(); string fieldShow = field.Show();
string httpFieldName;
switch (field.Name()) switch (field.Name())
{ {
case "http.request": case "http.request":
...@@ -1408,6 +1442,8 @@ namespace PcapDotNet.Core.Test ...@@ -1408,6 +1442,8 @@ namespace PcapDotNet.Core.Test
} }
else else
{ {
fieldShow = Encoding.GetEncoding(28591).GetString(HexEncoding.Instance.GetBytes(field.Value()));
fieldShow = fieldShow.Substring(0, fieldShow.Length - 2);
int colonIndex = fieldShow.IndexOf(':'); int colonIndex = fieldShow.IndexOf(':');
MoreAssert.IsBiggerOrEqual(0, colonIndex, "Can't find colon in field with empty name"); MoreAssert.IsBiggerOrEqual(0, colonIndex, "Can't find colon in field with empty name");
...@@ -1444,6 +1480,7 @@ namespace PcapDotNet.Core.Test ...@@ -1444,6 +1480,7 @@ namespace PcapDotNet.Core.Test
case "http.server": case "http.server":
case "http.set_cookie": case "http.set_cookie":
case "http.location": case "http.location":
data.Append(field.Value());
httpFieldName = field.Name().Substring(5).Replace('_', '-'); httpFieldName = field.Name().Substring(5).Replace('_', '-');
HttpField httpField = httpDatagram.Header[httpFieldName]; HttpField httpField = httpDatagram.Header[httpFieldName];
if (!field.Value().EndsWith("0d0a")) if (!field.Value().EndsWith("0d0a"))
...@@ -1458,18 +1495,25 @@ namespace PcapDotNet.Core.Test ...@@ -1458,18 +1495,25 @@ namespace PcapDotNet.Core.Test
break; break;
case "http.content_length_header": case "http.content_length_header":
data.Append(field.Value());
field.AssertShowDecimal(httpDatagram.Header.ContentLength.ContentLength.Value); field.AssertShowDecimal(httpDatagram.Header.ContentLength.ContentLength.Value);
break; break;
case "http.content_type": case "http.content_type":
data.Append(field.Value());
string[] mediaType = fieldShow.Split(new[] {';', ' ', '/'}, StringSplitOptions.RemoveEmptyEntries); string[] mediaType = fieldShow.Split(new[] {';', ' ', '/'}, StringSplitOptions.RemoveEmptyEntries);
Assert.AreEqual(httpDatagram.Header.ContentType.MediaType, mediaType[0]); Assert.AreEqual(httpDatagram.Header.ContentType.MediaType, mediaType[0]);
Assert.AreEqual(httpDatagram.Header.ContentType.MediaSubType, mediaType[1]); Assert.AreEqual(httpDatagram.Header.ContentType.MediaSubType, mediaType[1]);
MoreAssert.AreSequenceEqual(httpDatagram.Header.ContentType.Parameters.Select(pair => pair.Key + '=' + pair.Value), mediaType.Skip(2)); 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.ToLiteral()).SequenceToString(';'), fieldShow.Substring(fieldShowParametersStart + 1));
break; break;
case "http.transfer_encoding": case "http.transfer_encoding":
field.AssertShow(httpDatagram.Header.TransferEncoding.TransferCodings.SequenceToString(',')); data.Append(field.Value());
Assert.AreEqual(httpDatagram.Header.TransferEncoding.TransferCodings.SequenceToString(',').ToLiteral(), fieldShow.ToLowerLiteral());
break; break;
default: default:
...@@ -1486,12 +1530,12 @@ namespace PcapDotNet.Core.Test ...@@ -1486,12 +1530,12 @@ namespace PcapDotNet.Core.Test
{ {
case "http.request.method": case "http.request.method":
Assert.IsTrue(httpDatagram.IsRequest, field.Name() + " IsRequest"); Assert.IsTrue(httpDatagram.IsRequest, field.Name() + " IsRequest");
field.AssertShow(((HttpRequestDatagram)httpDatagram).Method); field.AssertShow(((HttpRequestDatagram)httpDatagram).Method.Method);
break; break;
case "http.request.uri": case "http.request.uri":
Assert.IsTrue(httpDatagram.IsRequest, field.Name() + " IsRequest"); Assert.IsTrue(httpDatagram.IsRequest, field.Name() + " IsRequest");
field.AssertShow(((HttpRequestDatagram)httpDatagram).Uri); field.AssertShow(((HttpRequestDatagram)httpDatagram).Uri.ToLiteral());
break; break;
case "http.request.version": case "http.request.version":
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using PcapDotNet.Base; using PcapDotNet.Base;
...@@ -52,9 +54,14 @@ namespace PcapDotNet.Core.Test ...@@ -52,9 +54,14 @@ namespace PcapDotNet.Core.Test
Assert.IsNull(element.Attribute("show")); Assert.IsNull(element.Attribute("show"));
} }
public static void AssertShow(this XElement element, string expectedValue, string message = null) public static void AssertShow(this XElement element, string expectedValue, bool ignoreCase = false, string message = null)
{ {
Assert.AreEqual(expectedValue, element.Show(), message ?? element.Name()); Assert.AreEqual(expectedValue, element.Show(), ignoreCase, message ?? element.Name());
}
public static void AssertShow(this XElement element, string expectedValue, string message)
{
element.AssertShow(expectedValue, false, message);
} }
public static void AssertShow(this XElement element, IEnumerable<byte> expectedValue) public static void AssertShow(this XElement element, IEnumerable<byte> expectedValue)
......
...@@ -41,6 +41,11 @@ void PacketDumpFile::Dump(String^ fileName, PcapDataLink dataLink, int snapshotL ...@@ -41,6 +41,11 @@ void PacketDumpFile::Dump(String^ fileName, PcapDataLink dataLink, int snapshotL
} }
} }
void PacketDumpFile::Dump(String^ fileName, DataLinkKind dataLink, int snapshotLength, IEnumerable<Packet^>^ packets)
{
Dump(fileName, PcapDataLink(dataLink), snapshotLength, packets);
}
void PacketDumpFile::Dump(Packet^ packet) void PacketDumpFile::Dump(Packet^ packet)
{ {
if (packet == nullptr) if (packet == nullptr)
......
...@@ -20,6 +20,8 @@ namespace PcapDotNet { namespace Core ...@@ -20,6 +20,8 @@ namespace PcapDotNet { namespace Core
/// <param name="snapshotLength">The dimension of the packet portion (in bytes) that is used when writing the packets. 65536 guarantees that the whole packet will be captured on all the link layers.</param> /// <param name="snapshotLength">The dimension of the packet portion (in bytes) that is used when writing the packets. 65536 guarantees that the whole packet will be captured on all the link layers.</param>
/// <param name="packets">The packets to save to the dump file.</param> /// <param name="packets">The packets to save to the dump file.</param>
static void Dump(System::String^ fileName, PcapDataLink dataLink, int snapshotLength, System::Collections::Generic::IEnumerable<Packets::Packet^>^ packets); static void Dump(System::String^ fileName, PcapDataLink dataLink, int snapshotLength, System::Collections::Generic::IEnumerable<Packets::Packet^>^ packets);
static void Dump(System::String^ fileName, PcapDotNet::Packets::DataLinkKind dataLink, int snapshotLength, System::Collections::Generic::IEnumerable<Packets::Packet^>^ packets);
/// <summary> /// <summary>
/// Save a packet to disk. /// Save a packet to disk.
......
...@@ -327,9 +327,10 @@ namespace PcapDotNet.Packets.Test ...@@ -327,9 +327,10 @@ namespace PcapDotNet.Packets.Test
"--THIS_STRING_SEPARATES--"); "--THIS_STRING_SEPARATES--");
} }
private static void TestHttpRequest(string httpString, string expectedMethod = null, string expectedUri = null, HttpVersion expectedVersion = null, HttpHeader expectedHeader = null, string expectedBodyString = null) private static void TestHttpRequest(string httpString, string expectedMethodString = null, string expectedUri = null, HttpVersion expectedVersion = null, HttpHeader expectedHeader = null, string expectedBodyString = null)
{ {
Datagram expectedBody = expectedBodyString == null ? null : new Datagram(Encoding.ASCII.GetBytes(expectedBodyString)); Datagram expectedBody = expectedBodyString == null ? null : new Datagram(Encoding.ASCII.GetBytes(expectedBodyString));
HttpRequestMethod expectedMethod = expectedMethodString == null ? null : new HttpRequestMethod(expectedMethodString);
Packet packet = BuildPacket(httpString); Packet packet = BuildPacket(httpString);
......
...@@ -442,6 +442,8 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -442,6 +442,8 @@ namespace PcapDotNet.Packets.TestUtils
impossibleOptionTypes.Add(TcpOptionType.AlternateChecksumData); impossibleOptionTypes.Add(TcpOptionType.AlternateChecksumData);
if (maximumOptionLength < TcpOptionMd5Signature.OptionLength) if (maximumOptionLength < TcpOptionMd5Signature.OptionLength)
impossibleOptionTypes.Add(TcpOptionType.Md5Signature); impossibleOptionTypes.Add(TcpOptionType.Md5Signature);
if (maximumOptionLength < TcpOptionMood.OptionMaximumLength)
impossibleOptionTypes.Add(TcpOptionType.Mood);
impossibleOptionTypes.Add(TcpOptionType.QuickStartResponse); impossibleOptionTypes.Add(TcpOptionType.QuickStartResponse);
impossibleOptionTypes.Add(TcpOptionType.UserTimeout); impossibleOptionTypes.Add(TcpOptionType.UserTimeout);
...@@ -504,6 +506,9 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -504,6 +506,9 @@ namespace PcapDotNet.Packets.TestUtils
case TcpOptionType.Md5Signature: case TcpOptionType.Md5Signature:
return new TcpOptionMd5Signature(random.NextBytes(TcpOptionMd5Signature.OptionValueLength)); return new TcpOptionMd5Signature(random.NextBytes(TcpOptionMd5Signature.OptionValueLength));
case TcpOptionType.Mood:
return new TcpOptionMood(random.NextEnum(TcpOptionMoodEmotion.None));
default: default:
throw new InvalidOperationException("optionType = " + optionType); throw new InvalidOperationException("optionType = " + optionType);
} }
...@@ -958,7 +963,7 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -958,7 +963,7 @@ namespace PcapDotNet.Packets.TestUtils
HttpRequestLayer httpRequestLayer = new HttpRequestLayer(); HttpRequestLayer httpRequestLayer = new HttpRequestLayer();
if (random.NextBool()) if (random.NextBool())
{ {
httpRequestLayer.Method = random.NextHttpToken(); httpRequestLayer.Method = random.NextHttpRequestMethod();
httpRequestLayer.Uri = httpRequestLayer.Method == null ? null : random.NextHttpUri(); httpRequestLayer.Uri = httpRequestLayer.Method == null ? null : random.NextHttpUri();
httpRequestLayer.Version = httpRequestLayer.Uri == null ? null : random.NextHttpVersion(); httpRequestLayer.Version = httpRequestLayer.Uri == null ? null : random.NextHttpVersion();
httpRequestLayer.Header = httpRequestLayer.Version == null ? null : random.NextHttpHeader(); httpRequestLayer.Header = httpRequestLayer.Version == null ? null : random.NextHttpHeader();
...@@ -976,6 +981,14 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -976,6 +981,14 @@ namespace PcapDotNet.Packets.TestUtils
// } // }
} }
public static HttpRequestMethod NextHttpRequestMethod(this Random random)
{
HttpRequestKnownMethod knownMethod = random.NextEnum<HttpRequestKnownMethod>();
if (knownMethod == HttpRequestKnownMethod.Unknown)
return new HttpRequestMethod(random.NextHttpToken());
return new HttpRequestMethod(knownMethod);
}
public static string NextHttpToken(this Random random) public static string NextHttpToken(this Random random)
{ {
int tokenLength = random.Next(1, 100); int tokenLength = random.Next(1, 100);
......
...@@ -198,9 +198,6 @@ namespace PcapDotNet.Packets.Http ...@@ -198,9 +198,6 @@ namespace PcapDotNet.Packets.Http
/// </summary> /// </summary>
public abstract class HttpDatagram : Datagram public abstract class HttpDatagram : Datagram
{ {
private const string FieldNameGroupName = "FieldNameGroupName";
private const string FieldValueGroupName = "FieldValueGroupName";
internal class ParseInfoBase internal class ParseInfoBase
{ {
public int Length { get; set; } public int Length { get; set; }
......
using System; using System;
using System.CodeDom;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Microsoft.CSharp;
using PcapDotNet.Base; using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http namespace PcapDotNet.Packets.Http
......
...@@ -6,7 +6,7 @@ namespace PcapDotNet.Packets.Http ...@@ -6,7 +6,7 @@ namespace PcapDotNet.Packets.Http
{ {
private class ParseInfo : ParseInfoBase private class ParseInfo : ParseInfoBase
{ {
public string Method { get; set; } public HttpRequestMethod Method { get; set; }
public string Uri { get; set; } public string Uri { get; set; }
} }
...@@ -15,7 +15,7 @@ namespace PcapDotNet.Packets.Http ...@@ -15,7 +15,7 @@ namespace PcapDotNet.Packets.Http
get { return true; } get { return true; }
} }
public string Method { get; private set; } public HttpRequestMethod Method { get; private set; }
public string Uri { get; private set; } public string Uri { get; private set; }
public override ILayer ExtractLayer() public override ILayer ExtractLayer()
...@@ -54,7 +54,7 @@ namespace PcapDotNet.Packets.Http ...@@ -54,7 +54,7 @@ namespace PcapDotNet.Packets.Http
{ {
Length = length, Length = length,
Version = version, Version = version,
Method = method, Method = method == null ? null : new HttpRequestMethod(method),
Uri = uri, Uri = uri,
}; };
if (!parser.Success) if (!parser.Success)
......
namespace PcapDotNet.Packets.Http
{
public enum HttpRequestKnownMethod
{
Options,
Get,
Head,
Post,
Put,
Delete,
Trace,
Connect,
Unknown,
}
}
\ No newline at end of file
...@@ -5,7 +5,7 @@ namespace PcapDotNet.Packets.Http ...@@ -5,7 +5,7 @@ namespace PcapDotNet.Packets.Http
{ {
public class HttpRequestLayer : HttpLayer, IEquatable<HttpRequestLayer> public class HttpRequestLayer : HttpLayer, IEquatable<HttpRequestLayer>
{ {
public string Method { get; set; } public HttpRequestMethod Method { get; set; }
public string Uri { get; set; } public string Uri { get; set; }
public override bool Equals(HttpLayer other) public override bool Equals(HttpLayer other)
...@@ -43,7 +43,7 @@ namespace PcapDotNet.Packets.Http ...@@ -43,7 +43,7 @@ namespace PcapDotNet.Packets.Http
{ {
if (Method == null) if (Method == null)
return; return;
buffer.Write(ref offset, Method, Encoding.ASCII); Method.Write(buffer, ref offset);
buffer.Write(ref offset, AsciiBytes.Space); buffer.Write(ref offset, AsciiBytes.Space);
if (Uri == null) if (Uri == null)
......
using System;
using System.Collections.Generic;
using System.Text;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http
{
public class HttpRequestMethod : IEquatable<HttpRequestMethod>
{
public HttpRequestMethod(string method)
{
Method = method;
}
public HttpRequestMethod(HttpRequestKnownMethod method)
{
Method = method.ToString().ToUpper();
if (!_knownMethods.ContainsKey(Method))
throw new ArgumentException("Invalid known request method given: " + method, "method");
}
public string Method { get; private set; }
public HttpRequestKnownMethod KnownMethod
{
get
{
HttpRequestKnownMethod knownMethod;
if (_knownMethods.TryGetValue(Method, out knownMethod))
return knownMethod;
return HttpRequestKnownMethod.Unknown;
}
}
public int Length
{
get { return Method.Length; }
}
internal void Write(byte[] buffer, ref int offset)
{
buffer.Write(ref offset, Method, Encoding.ASCII);
}
private static Dictionary<string, HttpRequestKnownMethod> CreateKnownMethodsTable()
{
Dictionary<string, HttpRequestKnownMethod> result = new Dictionary<string, HttpRequestKnownMethod>();
foreach (HttpRequestKnownMethod method in typeof(HttpRequestKnownMethod).GetEnumValues<HttpRequestKnownMethod>())
{
if (method != HttpRequestKnownMethod.Unknown)
result.Add(method.ToString().ToUpperInvariant(), method);
}
return result;
}
public bool Equals(HttpRequestMethod other)
{
return other != null && Method.Equals(other.Method);
}
public override bool Equals(object obj)
{
return Equals(obj as HttpRequestMethod);
}
private static readonly Dictionary<string, HttpRequestKnownMethod> _knownMethods = CreateKnownMethodsTable();
}
}
\ No newline at end of file
...@@ -121,7 +121,9 @@ ...@@ -121,7 +121,9 @@
<Compile Include="Http\HttpParser.cs" /> <Compile Include="Http\HttpParser.cs" />
<Compile Include="Http\HttpRegex.cs" /> <Compile Include="Http\HttpRegex.cs" />
<Compile Include="Http\HttpRequestDatagram.cs" /> <Compile Include="Http\HttpRequestDatagram.cs" />
<Compile Include="Http\HttpRequestKnownMethod.cs" />
<Compile Include="Http\HttpRequestLayer.cs" /> <Compile Include="Http\HttpRequestLayer.cs" />
<Compile Include="Http\HttpRequestMethod.cs" />
<Compile Include="Http\HttpResponseDatagram.cs" /> <Compile Include="Http\HttpResponseDatagram.cs" />
<Compile Include="Http\HttpResponseLayer.cs" /> <Compile Include="Http\HttpResponseLayer.cs" />
<Compile Include="Http\HttpTransferEncodingField.cs" /> <Compile Include="Http\HttpTransferEncodingField.cs" />
...@@ -251,6 +253,8 @@ ...@@ -251,6 +253,8 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SimpleLayer.cs" /> <Compile Include="SimpleLayer.cs" />
<Compile Include="Transport\TcpLayer.cs" /> <Compile Include="Transport\TcpLayer.cs" />
<Compile Include="Transport\TcpOptionMood.cs" />
<Compile Include="Transport\TcpOptionMoodEmotion.cs" />
<Compile Include="Transport\TransportLayer.cs" /> <Compile Include="Transport\TransportLayer.cs" />
<Compile Include="Transport\TcpDatagram.cs" /> <Compile Include="Transport\TcpDatagram.cs" />
<Compile Include="Transport\TcpControlBits.cs" /> <Compile Include="Transport\TcpControlBits.cs" />
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace PcapDotNet.Packets.Transport
{
/// <summary>
/// TCP Mood Option:
/// <pre>
/// +---------+--------+------------+
/// | Kind=25 | Length | ASCII Mood |
/// +---------+--------+------------+
/// </pre>
///
/// <para>
/// It is proposed that option 25 (released 2000-12-18) be used to define packet mood.
/// This option would have a length value of 4 or 5 bytes.
/// All the simple emotions described as expressible via this mechanism can be displayed with two or three 7-bit, ASCII-encoded characters.
/// Multiple mood options may appear in a TCP header, so as to express more complex moods than those defined here (for instance if a packet were happy and surprised).
/// </para>
///
/// <para>
/// It is proposed that common emoticons be used to denote packet mood.
/// Packets do not "feel" per se. The emotions they could be tagged with are a reflection of the user mood expressed through packets.
/// So the humanity expressed in a packet would be entirely sourced from humans.
/// To this end, it is proposed that simple emotions be used convey mood as follows.
///
/// <pre>
/// ASCII Mood
/// ===== ====
/// :) Happy
/// :( Sad
/// :D Amused
/// %( Confused
/// :o Bored
/// :O Surprised
/// :P Silly
/// :@ Frustrated
/// >:@ Angry
/// :| Apathetic
/// ;) Sneaky
/// >:) Evil
/// </pre>
///
/// Proposed ASCII character encoding
/// <pre>
/// Binary Dec Hex Character
/// ======== === === =========
/// 010 0101 37 25 %
/// 010 1000 40 28 (
/// 010 1001 41 29 )
/// 011 1010 58 3A :
/// 011 1011 59 3B ;
/// 011 1110 62 3E >
/// 100 0000 64 40 @
/// 100 0100 68 44 D
/// 100 1111 79 4F O
/// 101 0000 80 50 P
/// 110 1111 111 6F o
/// 111 1100 124 7C |
/// </pre>
/// </para>
/// </summary>
[OptionTypeRegistration(typeof(TcpOptionType), TcpOptionType.Mood)]
public class TcpOptionMood : TcpOptionComplex, IOptionComplexFactory, IEquatable<TcpOptionMood>
{
/// <summary>
/// The minimum number of bytes this option take.
/// </summary>
public const int OptionMinimumLength = OptionHeaderLength + OptionValueMinimumLength;
public const int OptionMaximumLength = OptionHeaderLength + OptionValueMaximumLength;
/// <summary>
/// The minimum number of bytes this option value take.
/// </summary>
public const int OptionValueMinimumLength = 2;
/// <summary>
/// The maximum number of bytes this option value take.
/// </summary>
public const int OptionValueMaximumLength = 3;
/// <summary>
/// Creates the option using the given emotion.
/// </summary>
public TcpOptionMood(TcpOptionMoodEmotion emotion)
: base(TcpOptionType.Mood)
{
Emotion = emotion;
}
/// <summary>
/// The default emotion is confused.
/// </summary>
public TcpOptionMood()
: this(TcpOptionMoodEmotion.Confused)
{
}
/// <summary>
/// The emotion of the option.
/// </summary>
public TcpOptionMoodEmotion Emotion { get; private set; }
public string EmotionString
{
get
{
int emotionValue = (int)Emotion;
if (emotionValue >= _emotionToString.Length)
throw new InvalidOperationException("No string value for emotion " + Emotion);
return _emotionToString[emotionValue];
}
}
/// <summary>
/// The number of bytes this option will take.
/// </summary>
public override int Length
{
get { return OptionHeaderLength + ValueLength; }
}
public int ValueLength
{
get { return EmotionString.Length; }
}
/// <summary>
/// True iff this option may appear at most once in a datagram.
/// </summary>
public override bool IsAppearsAtMostOnce
{
get { return false; }
}
/// <summary>
/// Two mood options are equal if they have the same emotion.
/// </summary>
public bool Equals(TcpOptionMood other)
{
if (other == null)
return false;
return Emotion == other.Emotion;
}
/// <summary>
/// Two mood options are equal if they have the same emotion.
/// </summary>
public override bool Equals(TcpOption other)
{
return Equals(other as TcpOptionMood);
}
/// <summary>
/// The hash code of the echo option is the hash code of the option type xored with the hash code info.
/// </summary>
public override int GetHashCode()
{
return base.GetHashCode() ^ Emotion.GetHashCode();
}
/// <summary>
/// Tries to read the option from a buffer starting from the option value (after the type and length).
/// </summary>
/// <param name="buffer">The buffer to read the option from.</param>
/// <param name="offset">The offset to the first byte to read the buffer. Will be incremented by the number of bytes read.</param>
/// <param name="valueLength">The number of bytes the option value should take according to the length field that was already read.</param>
/// <returns>On success - the complex option read. On failure - null.</returns>
Option IOptionComplexFactory.CreateInstance(byte[] buffer, ref int offset, byte valueLength)
{
if (valueLength < OptionValueMinimumLength || valueLength > OptionValueMaximumLength)
return null;
byte[] emotionBuffer = buffer.ReadBytes(ref offset, valueLength);
TcpOptionMoodEmotion emotion = StringToEmotion(Encoding.ASCII.GetString(emotionBuffer));
if (emotion == TcpOptionMoodEmotion.None)
return null;
return new TcpOptionMood(emotion);
}
internal override void Write(byte[] buffer, ref int offset)
{
base.Write(buffer, ref offset);
buffer.Write(ref offset, Encoding.ASCII.GetBytes(EmotionString));
}
private static TcpOptionMoodEmotion StringToEmotion(string emotionString)
{
TcpOptionMoodEmotion emotion;
if (_stringToEmotion.TryGetValue(emotionString, out emotion))
return emotion;
return TcpOptionMoodEmotion.None;
}
private static readonly Dictionary<string, TcpOptionMoodEmotion> _stringToEmotion = new Dictionary<string, TcpOptionMoodEmotion>
{
{":)", TcpOptionMoodEmotion.Happy},
{":(", TcpOptionMoodEmotion.Sad},
{":D", TcpOptionMoodEmotion.Amused},
{"%(", TcpOptionMoodEmotion.Confused},
{":o", TcpOptionMoodEmotion.Bored},
{":O", TcpOptionMoodEmotion.Surprised},
{":P", TcpOptionMoodEmotion.Silly},
{":@", TcpOptionMoodEmotion.Frustrated},
{">:@", TcpOptionMoodEmotion.Angry},
{":|", TcpOptionMoodEmotion.Apathetic},
{";)", TcpOptionMoodEmotion.Sneaky},
{">:)", TcpOptionMoodEmotion.Evil}
};
private static readonly string[] _emotionToString = _stringToEmotion.OrderBy(pair => pair.Value).Select(pair => pair.Key).ToArray();
}
}
\ No newline at end of file
namespace PcapDotNet.Packets.Transport
{
public enum TcpOptionMoodEmotion
{
/// <summary>
/// :)
/// </summary>
Happy,
/// <summary>
/// :(
/// </summary>
Sad,
/// <summary>
/// :D
/// </summary>
Amused,
/// <summary>
/// %(
/// </summary>
Confused,
/// <summary>
/// :o
/// </summary>
Bored,
/// <summary>
/// :O
/// </summary>
Surprised,
/// <summary>
/// :P
/// </summary>
Silly,
/// <summary>
/// :@
/// </summary>
Frustrated,
/// <summary>
/// >:@
/// </summary>
Angry,
/// <summary>
/// :|
/// </summary>
Apathetic,
/// <summary>
/// ;)
/// </summary>
Sneaky,
/// <summary>
/// >:)
/// </summary>
Evil,
None,
}
}
\ No newline at end of file
namespace PcapDotNet.Packets.Transport namespace PcapDotNet.Packets.Transport
{ {
/// <summary> /// <summary>
/// A simple IPv4 option - holds only the type. /// A simple TCP option - holds only the type.
/// </summary> /// </summary>
public class TcpOptionSimple : TcpOption public class TcpOptionSimple : TcpOption
{ {
......
...@@ -25,6 +25,11 @@ namespace PcapDotNet.Packets.Transport ...@@ -25,6 +25,11 @@ namespace PcapDotNet.Packets.Transport
/// </summary> /// </summary>
WindowScale = 3, WindowScale = 3,
/// <summary>
/// Denote Packet Mood (RFC5841)
/// </summary>
Mood = 25,
/// <summary> /// <summary>
/// SACK Permitted (RFC2018) /// SACK Permitted (RFC2018)
/// </summary> /// </summary>
......
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