Commit fb777528 authored by Brickner_cp's avatar Brickner_cp

HTTP

parent d6b96212
...@@ -44,16 +44,74 @@ namespace PcapDotNet.Packets.Test ...@@ -44,16 +44,74 @@ namespace PcapDotNet.Packets.Test
#endregion #endregion
[TestMethod] [TestMethod]
public void RandomHttpTest() public void HttpParsingTest()
{
TestHttpRequest("", null, null, null);
TestHttpRequest(" ", null, null, null);
TestHttpRequest("GET", "GET", null, null);
TestHttpRequest("GET /url", "GET", "/url", null);
TestHttpRequest("GET /url HTTP/1.0", "GET", "/url", HttpVersion.Version10);
TestHttpRequest("GET /url HTTP/1.1", "GET", "/url", HttpVersion.Version11);
TestHttpRequest("GET /url HTTP/1.1", "GET", "/url", null);
TestHttpRequest("GET HTTP/1.1", "GET", "", HttpVersion.Version11);
TestHttpResponse("HTTP/", null, null, null);
TestHttpResponse("HTTP/1", null, null, null);
TestHttpResponse("HTTP/1.", null, null, null);
TestHttpResponse("HTTP/1.0", HttpVersion.Version10, null, null);
TestHttpResponse("HTTP/1.0 ", HttpVersion.Version10, null, null);
TestHttpResponse("HTTP/1.0 A", HttpVersion.Version10, null, null);
TestHttpResponse("HTTP/1.0 200", HttpVersion.Version10, 200, null);
TestHttpResponse("HTTP/1.0 200 ", HttpVersion.Version10, 200, "");
TestHttpResponse("HTTP/1.0 200 OK", HttpVersion.Version10, 200, "OK");
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, 200, "OK");
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, null, null);
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, 200, " OK");
}
private static void TestHttpRequest(string httpString, string expectedMethod, string expectedUri, HttpVersion expectedVersion)
{
Packet packet = BuildPacket(httpString);
// HTTP
HttpDatagram http = packet.Ethernet.IpV4.Tcp.Http;
Assert.IsTrue(http.IsRequest, "IsRequest " + httpString);
Assert.IsFalse(http.IsResponse, "IsResponse " + httpString);
Assert.AreEqual(expectedVersion, http.Version, "Version " + httpString);
HttpRequestDatagram request = (HttpRequestDatagram)http;
Assert.AreEqual(expectedMethod, request.Method, "Method " + httpString);
Assert.AreEqual(expectedUri, request.Uri, "Uri " + httpString);
// HttpHeader header = http.Header;
// Assert.IsNotNull(header);
}
private static void TestHttpResponse(string httpString, HttpVersion expectedVersion, uint? expectedStatusCode, string expectedReasonPhrase)
{
Packet packet = BuildPacket(httpString);
// HTTP
HttpDatagram http = packet.Ethernet.IpV4.Tcp.Http;
Assert.IsFalse(http.IsRequest, "IsRequest " + httpString);
Assert.IsTrue(http.IsResponse, "IsResponse " + httpString);
Assert.AreEqual(expectedVersion, http.Version, "Version " + httpString);
HttpResponseDatagram response = (HttpResponseDatagram)http;
Assert.AreEqual(expectedStatusCode, response.StatusCode, "StatusCode " + httpString);
Assert.AreEqual(expectedReasonPhrase == null ? null : new Datagram(Encoding.ASCII.GetBytes(expectedReasonPhrase)), response.ReasonPhrase, "ReasonPhrase " + httpString);
}
private static Packet BuildPacket(string httpString)
{ {
MacAddress ethernetSource = new MacAddress("00:01:02:03:04:05"); MacAddress ethernetSource = new MacAddress("00:01:02:03:04:05");
MacAddress ethernetDestination = new MacAddress("A0:A1:A2:A3:A4:A5"); MacAddress ethernetDestination = new MacAddress("A0:A1:A2:A3:A4:A5");
EthernetLayer ethernetLayer = new EthernetLayer EthernetLayer ethernetLayer = new EthernetLayer
{ {
Source = ethernetSource, Source = ethernetSource,
Destination = ethernetDestination Destination = ethernetDestination
}; };
Random random = new Random(); Random random = new Random();
...@@ -61,36 +119,30 @@ namespace PcapDotNet.Packets.Test ...@@ -61,36 +119,30 @@ namespace PcapDotNet.Packets.Test
ipV4Layer.HeaderChecksum = null; ipV4Layer.HeaderChecksum = null;
TcpLayer tcpLayer = random.NextTcpLayer(); TcpLayer tcpLayer = random.NextTcpLayer();
for (int i = 0; i != 1000; ++i) PayloadLayer payloadLayer = new PayloadLayer
{ {
PayloadLayer payloadLayer = new PayloadLayer Data = new Datagram(Encoding.ASCII.GetBytes(httpString))
{ };
Data = new Datagram(Encoding.ASCII.GetBytes("GET /url HTTP/1.1"))
}; Packet packet = new PacketBuilder(ethernetLayer, ipV4Layer, tcpLayer, payloadLayer).Build(DateTime.Now);
Packet packet = new PacketBuilder(ethernetLayer, ipV4Layer, tcpLayer, payloadLayer).Build(DateTime.Now); Assert.IsTrue(packet.IsValid);
Assert.IsTrue(packet.IsValid); // Ethernet
ethernetLayer.EtherType = EthernetType.IpV4;
// Ethernet Assert.AreEqual(ethernetLayer, packet.Ethernet.ExtractLayer(), "Ethernet Layer");
ethernetLayer.EtherType = EthernetType.IpV4;
Assert.AreEqual(ethernetLayer, packet.Ethernet.ExtractLayer(), "Ethernet Layer"); // IpV4
ipV4Layer.Protocol = IpV4Protocol.Tcp;
// IpV4 ipV4Layer.HeaderChecksum = ((IpV4Layer)packet.Ethernet.IpV4.ExtractLayer()).HeaderChecksum;
ipV4Layer.Protocol = IpV4Protocol.Tcp; Assert.AreEqual(ipV4Layer, packet.Ethernet.IpV4.ExtractLayer(), "IP Layer");
ipV4Layer.HeaderChecksum = ((IpV4Layer)packet.Ethernet.IpV4.ExtractLayer()).HeaderChecksum; ipV4Layer.HeaderChecksum = null;
Assert.AreEqual(ipV4Layer, packet.Ethernet.IpV4.ExtractLayer(), "IP Layer");
ipV4Layer.HeaderChecksum = null; // TCP
tcpLayer.Checksum = packet.Ethernet.IpV4.Tcp.Checksum;
// TCP Assert.AreEqual(tcpLayer, packet.Ethernet.IpV4.Tcp.ExtractLayer(), "TCP Layer");
tcpLayer.Checksum = packet.Ethernet.IpV4.Tcp.Checksum;
Assert.AreEqual(tcpLayer, packet.Ethernet.IpV4.Tcp.ExtractLayer(), "TCP Layer"); return packet;
// HTTP
HttpDatagram http = packet.Ethernet.IpV4.Tcp.Http;
HttpHeader header = http.Header;
Assert.IsNotNull(header);
}
} }
} }
} }
\ No newline at end of file
...@@ -148,6 +148,11 @@ namespace PcapDotNet.Packets ...@@ -148,6 +148,11 @@ namespace PcapDotNet.Packets
return Length.GetHashCode() ^ this.BytesSequenceGetHashCode(); return Length.GetHashCode() ^ this.BytesSequenceGetHashCode();
} }
public override string ToString()
{
return _buffer.Range(StartOffset, Length).BytesSequenceToHexadecimalString();
}
internal void Write(byte[] buffer, int offset) internal void Write(byte[] buffer, int offset)
{ {
_buffer.BlockCopy(StartOffset, buffer, offset, Length); _buffer.BlockCopy(StartOffset, buffer, offset, Length);
......
...@@ -200,22 +200,24 @@ namespace PcapDotNet.Packets.Http ...@@ -200,22 +200,24 @@ namespace PcapDotNet.Packets.Http
public abstract bool IsRequest { get; } public abstract bool IsRequest { get; }
public bool IsResponse { get { return !IsRequest; } } public bool IsResponse { get { return !IsRequest; } }
public HttpHeader Header public HttpVersion Version
{ {
get get
{ {
Parse(); ParseFirstLine();
return _header; return _version;
} }
}
private void Parse()
{
HttpParser parser = new HttpParser(Buffer, StartOffset, Length);
ParseFirstLine(parser);
_header = new HttpHeader(ParseHeader(parser));
} }
// public HttpHeader Header
// {
// get
// {
// Parse();
// return _header;
// }
// }
internal static HttpDatagram CreateDatagram(byte[] buffer, int offset, int length) internal static HttpDatagram CreateDatagram(byte[] buffer, int offset, int length)
{ {
if (length >= _httpSlash.Length && buffer.SequenceEqual(offset, _httpSlash, 0, _httpSlash.Length)) if (length >= _httpSlash.Length && buffer.SequenceEqual(offset, _httpSlash, 0, _httpSlash.Length))
...@@ -228,22 +230,50 @@ namespace PcapDotNet.Packets.Http ...@@ -228,22 +230,50 @@ namespace PcapDotNet.Packets.Http
{ {
} }
internal abstract void ParseFirstLine(HttpParser parser); internal void ParseFirstLine()
private IEnumerable<KeyValuePair<string, IEnumerable<byte>>> ParseHeader(HttpParser parser)
{ {
while (parser.Success) if (_isParsedFirstLine)
{ return;
string fieldName;
IEnumerable<byte> fieldValue; ParseSpecificFirstLine(out _version, out _headerOffset);
parser.Token(out fieldName).Colon().FieldValue(out fieldValue); _isParsedFirstLine = true;
if (parser.Success)
yield return new KeyValuePair<string, IEnumerable<byte>>(fieldName, fieldValue);
}
} }
internal abstract void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset);
// private void ParseHeader()
// {
// ParseFirstLine();
// if (_headerStart == null)
// return;
//
// int headerStartValue = _headerStart.Value;
// HttpParser parser = new HttpParser(Buffer, StartOffset + headerStartValue, Length - headerStartValue);
// if (_isParsed)
// return;
// HttpParser parser = new HttpParser(Buffer, StartOffset, Length);
// HttpVersion version;
// ParseFirstLine(parser, out _version);
// _header = new HttpHeader(ParseHeader(parser));
// }
// private IEnumerable<KeyValuePair<string, IEnumerable<byte>>> ParseHeader(HttpParser parser)
// {
// while (parser.Success)
// {
// string fieldName;
// IEnumerable<byte> fieldValue;
// parser.Token(out fieldName).Colon().FieldValue(out fieldValue);
// if (parser.Success)
// yield return new KeyValuePair<string, IEnumerable<byte>>(fieldName, fieldValue);
// }
// }
private static readonly byte[] _httpSlash = Encoding.ASCII.GetBytes("HTTP/"); private static readonly byte[] _httpSlash = Encoding.ASCII.GetBytes("HTTP/");
private bool _isParsedFirstLine;
private int? _headerOffset;
private HttpVersion _version;
private HttpHeader _header; private HttpHeader _header;
} }
......
...@@ -18,6 +18,11 @@ namespace PcapDotNet.Packets.Http ...@@ -18,6 +18,11 @@ namespace PcapDotNet.Packets.Http
public bool Success { get; private set; } public bool Success { get; private set; }
public int Offset
{
get { return _offset; }
}
public HttpParser Token(out string token) public HttpParser Token(out string token)
{ {
if (!Success) if (!Success)
...@@ -156,10 +161,6 @@ namespace PcapDotNet.Packets.Http ...@@ -156,10 +161,6 @@ namespace PcapDotNet.Packets.Http
get { return _buffer.Range(_offset, _totalLength - _offset); } get { return _buffer.Range(_offset, _totalLength - _offset); }
} }
private readonly byte[] _buffer;
private int _offset;
private readonly int _totalLength;
public HttpParser RequestUri(out string uri) public HttpParser RequestUri(out string uri)
{ {
if (!Success) if (!Success)
...@@ -177,10 +178,10 @@ namespace PcapDotNet.Packets.Http ...@@ -177,10 +178,10 @@ namespace PcapDotNet.Packets.Http
public HttpParser Version(out HttpVersion version) public HttpParser Version(out HttpVersion version)
{ {
uint major; uint? major;
uint minor; uint? minor;
Bytes(_httpSlash).DecimalNumber(out major).Dot().DecimalNumber(out minor); Bytes(_httpSlash).DecimalNumber(out major).Dot().DecimalNumber(out minor);
version = new HttpVersion(major, minor); version = major != null && minor != null ? new HttpVersion(major.Value, minor.Value) : null;
return this; return this;
} }
...@@ -189,18 +190,21 @@ namespace PcapDotNet.Packets.Http ...@@ -189,18 +190,21 @@ namespace PcapDotNet.Packets.Http
return Bytes(AsciiBytes.CarriageReturn, AsciiBytes.LineFeed); return Bytes(AsciiBytes.CarriageReturn, AsciiBytes.LineFeed);
} }
public HttpParser DecimalNumber(int numDigits, out uint number) public HttpParser DecimalNumber(int numDigits, out uint? number)
{ {
if (numDigits > 8 || numDigits < 0)
throw new ArgumentOutOfRangeException("numDigits", numDigits, "Only between 0 and 8 digits are supported");
if (!Success) if (!Success)
{ {
number = 0; number = null;
return this; return this;
} }
var digits = Range.Take(numDigits).TakeWhile(value => value.IsDigit()); var digits = Range.Take(numDigits).TakeWhile(value => value.IsDigit());
if (digits.Count() != numDigits) if (digits.Count() != numDigits)
{ {
number = 0; number = null;
return Fail(); return Fail();
} }
number = digits.Select(value => (uint)(value - AsciiBytes.Zero)).Aggregate<uint, uint>(0, (accumulated, value) => 10 * accumulated + value); number = digits.Select(value => (uint)(value - AsciiBytes.Zero)).Aggregate<uint, uint>(0, (accumulated, value) => 10 * accumulated + value);
...@@ -208,28 +212,35 @@ namespace PcapDotNet.Packets.Http ...@@ -208,28 +212,35 @@ namespace PcapDotNet.Packets.Http
return this; return this;
} }
public HttpParser DecimalNumber(out uint number) public HttpParser DecimalNumber(out uint? number)
{ {
if (!Success) if (!Success)
{ {
number = 0; number = null;
return this; return this;
} }
var digits = Range.TakeWhile(value => value.IsDigit()); var digits = Range.TakeWhile(value => value.IsDigit());
if (!digits.Any()) if (!digits.Any())
{ {
number = 0; number = null;
return Fail(); return Fail();
} }
int numDigits = digits.Count(); int numDigits = digits.Count();
number = digits.Select(value => (uint)(value - AsciiBytes.Zero)).Aggregate<uint, uint>(0, (accumulated, value) => 10 * accumulated + value); int numInsignificantDigits = digits.TakeWhile(value => value == AsciiBytes.Zero).Count();
uint numberValue = digits.Select(value => (uint)(value - AsciiBytes.Zero)).Aggregate<uint, uint>(0, (accumulated, value) => 10 * accumulated + value);
if (numDigits - numInsignificantDigits >= 8)
{
number = null;
return Fail();
}
number = numberValue;
_offset += numDigits; _offset += numDigits;
return this; return this;
} }
public HttpParser ReasonPhrase(out IEnumerable<byte> reasonPhrase) public HttpParser ReasonPhrase(out Datagram reasonPhrase)
{ {
if (!Success) if (!Success)
{ {
...@@ -237,23 +248,16 @@ namespace PcapDotNet.Packets.Http ...@@ -237,23 +248,16 @@ namespace PcapDotNet.Packets.Http
return this; return this;
} }
reasonPhrase = Range.TakeWhile(value => !value.IsControl() || value == AsciiBytes.HorizontalTab); int reasonPhraseLength = Range.Count(value => !value.IsControl() || value == AsciiBytes.HorizontalTab);
_offset += reasonPhrase.Count(); reasonPhrase = new Datagram(_buffer, _offset, reasonPhraseLength);
_offset += reasonPhraseLength;
return this; return this;
} }
private static readonly byte[] _httpSlash = Encoding.ASCII.GetBytes("HTTP/"); private static readonly byte[] _httpSlash = Encoding.ASCII.GetBytes("HTTP/");
}
private readonly byte[] _buffer;
public class HttpVersion private int _offset;
{ private readonly int _totalLength;
public HttpVersion(uint major, uint minor)
{
Major = major;
Minor = minor;
}
public uint Major { get; private set; }
public uint Minor { get; private set; }
} }
} }
\ No newline at end of file
...@@ -14,12 +14,32 @@ namespace PcapDotNet.Packets.Http ...@@ -14,12 +14,32 @@ namespace PcapDotNet.Packets.Http
get { return true; } get { return true; }
} }
internal override void ParseFirstLine(HttpParser parser) public string Method
{ {
string method; get
string uri; {
HttpVersion version; ParseFirstLine();
parser.Token(out method).Space().RequestUri(out uri).Space().Version(out version).CarraigeReturnLineFeed(); return _method;
}
} }
public string Uri
{
get
{
ParseFirstLine();
return _uri;
}
}
internal override void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset)
{
HttpParser parser = new HttpParser(Buffer, StartOffset, Length);
parser.Token(out _method).Space().RequestUri(out _uri).Space().Version(out version).CarraigeReturnLineFeed();
headerOffset = parser.Success ? (int?)(parser.Offset - StartOffset) : null;
}
private string _uri;
private string _method;
} }
} }
\ No newline at end of file
...@@ -15,12 +15,32 @@ namespace PcapDotNet.Packets.Http ...@@ -15,12 +15,32 @@ namespace PcapDotNet.Packets.Http
get { return false; } get { return false; }
} }
internal override void ParseFirstLine(HttpParser parser) public uint? StatusCode
{ {
uint statusCode; get
HttpVersion version; {
IEnumerable<byte> reasonPhrase; ParseFirstLine();
parser.Version(out version).Space().DecimalNumber(3, out statusCode).Space().ReasonPhrase(out reasonPhrase).CarraigeReturnLineFeed(); return _statusCode;
}
} }
public Datagram ReasonPhrase
{
get
{
ParseFirstLine();
return _reasonPhrase;
}
}
internal override void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset)
{
HttpParser parser = new HttpParser(Buffer, StartOffset, Length);
parser.Version(out version).Space().DecimalNumber(3, out _statusCode).Space().ReasonPhrase(out _reasonPhrase).CarraigeReturnLineFeed();
headerOffset = parser.Success ? (int?)(parser.Offset - StartOffset) : null;
}
private uint? _statusCode;
private Datagram _reasonPhrase;
} }
} }
\ No newline at end of file
using System;
namespace PcapDotNet.Packets.Http
{
public class HttpVersion : IEquatable<HttpVersion>
{
public HttpVersion(uint major, uint minor)
{
Major = major;
Minor = minor;
}
public static HttpVersion Version10 { get { return _version10; } }
public static HttpVersion Version11 { get { return _version11; } }
public uint Major { get; private set; }
public uint Minor { get; private set; }
public override string ToString()
{
return string.Format("HTTP/{0}.{1}", Major, Minor);
}
public bool Equals(HttpVersion other)
{
return other != null &&
Major == other.Major &&
Minor == other.Minor;
}
public override bool Equals(object obj)
{
return Equals(obj as HttpVersion);
}
private static readonly HttpVersion _version10 = new HttpVersion(1,0);
private static readonly HttpVersion _version11 = new HttpVersion(1,1);
}
}
\ No newline at end of file
...@@ -114,6 +114,7 @@ ...@@ -114,6 +114,7 @@
<Compile Include="Http\HttpParser.cs" /> <Compile Include="Http\HttpParser.cs" />
<Compile Include="Http\HttpRequestDatagram.cs" /> <Compile Include="Http\HttpRequestDatagram.cs" />
<Compile Include="Http\HttpResponseDatagram.cs" /> <Compile Include="Http\HttpResponseDatagram.cs" />
<Compile Include="Http\HttpVersion.cs" />
<Compile Include="Icmp\IcmpAddressMaskReplyDatagram.cs" /> <Compile Include="Icmp\IcmpAddressMaskReplyDatagram.cs" />
<Compile Include="Icmp\IcmpAddressMaskReplyLayer.cs" /> <Compile Include="Icmp\IcmpAddressMaskReplyLayer.cs" />
<Compile Include="Icmp\IcmpAddressMaskRequestLayer.cs" /> <Compile Include="Icmp\IcmpAddressMaskRequestLayer.cs" />
......
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