Commit 4c59dc64 authored by Brickner_cp's avatar Brickner_cp

TCP

parent 8f749c59
...@@ -11,6 +11,11 @@ namespace PcapDotNet.Base ...@@ -11,6 +11,11 @@ namespace PcapDotNet.Base
/// </summary> /// </summary>
public static class MoreIEnumerable public static class MoreIEnumerable
{ {
public static bool IsEmpty<T>(this IEnumerable<T> sequence)
{
return !sequence.GetEnumerator().MoveNext();
}
/// <summary> /// <summary>
/// Concatenates a sequence with more values. /// Concatenates a sequence with more values.
/// </summary> /// </summary>
......
...@@ -77,7 +77,7 @@ namespace PcapDotNet.Core.Test ...@@ -77,7 +77,7 @@ namespace PcapDotNet.Core.Test
[TestMethod] [TestMethod]
public void ComparePacketsToWiresharkTest() public void ComparePacketsToWiresharkTest()
{ {
for (int i = 0; i != 1000; ++i) for (int i = 0; i != 10; ++i)
{ {
// Create packets // Create packets
List<Packet> packets = new List<Packet>(CreateRandomPackets(100)); List<Packet> packets = new List<Packet>(CreateRandomPackets(100));
...@@ -209,11 +209,12 @@ namespace PcapDotNet.Core.Test ...@@ -209,11 +209,12 @@ namespace PcapDotNet.Core.Test
int i = 1; int i = 1;
foreach (var documentPacket in document.Element("pdml").Elements("packet")) foreach (var documentPacket in document.Element("pdml").Elements("packet"))
{ {
Console.WriteLine("Checking packet " + i++); Console.WriteLine("Checking packet " + i);
packetEnumerator.MoveNext(); packetEnumerator.MoveNext();
Packet packet = packetEnumerator.Current; Packet packet = packetEnumerator.Current;
ComparePacket(packet, documentPacket); ComparePacket(packet, documentPacket);
++i;
} }
} }
......
namespace PcapDotNet.Packets
{
/// <summary>
/// This interface is used to create all complex options.
/// Every complex option should implement such a factory to create itself from a buffer.
/// </summary>
internal interface IOptionComplexFactory
{
/// <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 CreateInstance(byte[] buffer, ref int offset, byte valueLength);
}
}
\ No newline at end of file
...@@ -5,7 +5,7 @@ namespace PcapDotNet.Packets.IpV4 ...@@ -5,7 +5,7 @@ namespace PcapDotNet.Packets.IpV4
/// <summary> /// <summary>
/// Represents an ip option according to rfc 791. /// Represents an ip option according to rfc 791.
/// </summary> /// </summary>
public abstract class IpV4Option : IEquatable<IpV4Option> public abstract class IpV4Option : Option, IEquatable<IpV4Option>
{ {
///<summary> ///<summary>
/// This option indicates the end of the option list. /// This option indicates the end of the option list.
...@@ -35,29 +35,13 @@ namespace PcapDotNet.Packets.IpV4 ...@@ -35,29 +35,13 @@ namespace PcapDotNet.Packets.IpV4
get { return _type; } get { return _type; }
} }
/// <summary>
/// The number of bytes this option will take.
/// </summary>
public abstract int Length
{
get;
}
/// <summary>
/// True iff this option may appear at most once in a datagram.
/// </summary>
public abstract bool IsAppearsAtMostOnce
{
get;
}
/// <summary> /// <summary>
/// Checks whether two options have equivalent type. /// Checks whether two options have equivalent type.
/// Useful to check if an option that must appear at most once appears in the list. /// Useful to check if an option that must appear at most once appears in the list.
/// </summary> /// </summary>
public bool Equivalent(IpV4Option option) public override bool Equivalent(Option option)
{ {
return OptionType == option.OptionType; return OptionType == ((IpV4Option)option).OptionType;
} }
/// <summary> /// <summary>
...@@ -95,7 +79,7 @@ namespace PcapDotNet.Packets.IpV4 ...@@ -95,7 +79,7 @@ namespace PcapDotNet.Packets.IpV4
return OptionType.ToString(); return OptionType.ToString();
} }
internal static IpV4Option Read(byte[] buffer, ref int offset, int length) internal override Option Read(byte[] buffer, ref int offset, int length)
{ {
int offsetEnd = offset + length; int offsetEnd = offset + length;
if (offset == offsetEnd) if (offset == offsetEnd)
...@@ -114,7 +98,7 @@ namespace PcapDotNet.Packets.IpV4 ...@@ -114,7 +98,7 @@ namespace PcapDotNet.Packets.IpV4
} }
} }
internal virtual void Write(byte[] buffer, ref int offset) internal override void Write(byte[] buffer, ref int offset)
{ {
buffer[offset++] = (byte)OptionType; buffer[offset++] = (byte)OptionType;
} }
......
using System;
namespace PcapDotNet.Packets.IpV4 namespace PcapDotNet.Packets.IpV4
{ {
/// <summary> /// <summary>
......
...@@ -12,7 +12,7 @@ namespace PcapDotNet.Packets.IpV4 ...@@ -12,7 +12,7 @@ namespace PcapDotNet.Packets.IpV4
/// They must be implemented by all IP modules (host and gateways). /// They must be implemented by all IP modules (host and gateways).
/// What is optional is their transmission in any particular datagram, not their implementation. /// What is optional is their transmission in any particular datagram, not their implementation.
/// </summary> /// </summary>
public class IpV4Options : ReadOnlyCollection<IpV4Option>, IEquatable<IpV4Options> public class IpV4Options : Options<IpV4Option>
{ {
/// <summary> /// <summary>
/// The maximum number of bytes the options may take. /// The maximum number of bytes the options may take.
...@@ -32,10 +32,8 @@ namespace PcapDotNet.Packets.IpV4 ...@@ -32,10 +32,8 @@ namespace PcapDotNet.Packets.IpV4
/// </summary> /// </summary>
/// <param name="options">The list of options.</param> /// <param name="options">The list of options.</param>
public IpV4Options(IList<IpV4Option> options) public IpV4Options(IList<IpV4Option> options)
: this(EndOptions(options), true) : base(options, IpV4Option.End, MaximumBytesLength)
{ {
if (BytesLength > MaximumBytesLength)
throw new ArgumentException("given options take " + BytesLength + " bytes and maximum number of bytes for options is " + MaximumBytesLength, "options");
} }
/// <summary> /// <summary>
...@@ -47,134 +45,11 @@ namespace PcapDotNet.Packets.IpV4 ...@@ -47,134 +45,11 @@ namespace PcapDotNet.Packets.IpV4
{ {
} }
/// <summary>
/// The number of bytes the options take.
/// </summary>
public int BytesLength
{
get { return _bytesLength; }
}
/// <summary>
/// Whether or not the options parsed ok.
/// </summary>
public bool IsValid
{
get { return _isValid; }
}
/// <summary>
/// Two options are equal iff they have the exact same options.
/// </summary>
public bool Equals(IpV4Options other)
{
if (other == null)
return false;
if (BytesLength != other.BytesLength)
return false;
return this.SequenceEqual(other);
}
/// <summary>
/// Two options are equal iff they have the exact same options.
/// </summary>
public override bool Equals(object obj)
{
return Equals(obj as IpV4Options);
}
/// <summary>
/// The hash code is the xor of the following hash codes: number of bytes the options take and all the options.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return BytesLength.GetHashCode() ^
this.SequenceGetHashCode();
}
/// <summary>
/// A string of all the option type names.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return this.SequenceToString(", ", typeof(IpV4Options).Name + " {", "}");
}
internal IpV4Options(byte[] buffer, int offset, int length) internal IpV4Options(byte[] buffer, int offset, int length)
: this(Read(buffer, offset, length)) : base(buffer, offset, length, IpV4Option.End)
{
_bytesLength = length;
}
internal void Write(byte[] buffer, int offset)
{
int offsetEnd = offset + BytesLength;
foreach (IpV4Option option in this)
option.Write(buffer, ref offset);
// Padding
while (offset < offsetEnd)
buffer[offset++] = 0;
}
private IpV4Options(IList<IpV4Option> options, bool isValid)
: base(options)
{
_isValid = isValid;
_bytesLength = SumBytesLength(this);
if (_bytesLength % 4 != 0)
_bytesLength = (_bytesLength / 4 + 1) * 4;
}
private static IList<IpV4Option> EndOptions(IList<IpV4Option> options)
{
if (options.Count == 0 || options.Last().Equals(IpV4Option.End) || SumBytesLength(options) % 4 == 0)
return options;
return new List<IpV4Option>(options.Concat(IpV4Option.End));
}
private static int SumBytesLength(IEnumerable<IpV4Option> options)
{
return options.Sum(option => option.Length);
}
private IpV4Options(Tuple<IList<IpV4Option>, bool> optionsAndIsValid)
: this(optionsAndIsValid.Value1, optionsAndIsValid.Value2)
{
}
private static Tuple<IList<IpV4Option>, bool> Read(byte[] buffer, int offset, int length)
{ {
int offsetEnd = offset + length;
List<IpV4Option> options = new List<IpV4Option>();
while (offset != offsetEnd)
{
IpV4Option option = IpV4Option.Read(buffer, ref offset, offsetEnd - offset);
if (option == null ||
option.IsAppearsAtMostOnce && options.Any(option.Equivalent))
{
// Invalid
return new Tuple<IList<IpV4Option>, bool>(options, false);
}
options.Add(option);
if (option.OptionType == IpV4OptionType.EndOfOptionList)
break; // Valid?
}
return new Tuple<IList<IpV4Option>, bool>(options, true);
} }
private readonly int _bytesLength;
private readonly bool _isValid;
private static readonly IpV4Options _none = new IpV4Options(); private static readonly IpV4Options _none = new IpV4Options();
} }
} }
\ No newline at end of file
namespace PcapDotNet.Packets
{
public abstract class Option
{
/// <summary>
/// The number of bytes this option will take.
/// </summary>
public abstract int Length { get; }
/// <summary>
/// True iff this option may appear at most once in a datagram.
/// </summary>
public abstract bool IsAppearsAtMostOnce { get; }
/// <summary>
/// Checks whether two options have equivalent type.
/// Useful to check if an option that must appear at most once appears in the list.
/// </summary>
public abstract bool Equivalent(Option other);
internal abstract Option Read(byte[] buffer, ref int offset, int length);
internal abstract void Write(byte[] buffer, ref int offset);
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using PcapDotNet.Base;
namespace PcapDotNet.Packets
{
public class OptionComplexFactory<TOptionType>
{
/// <summary>
/// The header length in bytes for the option (type and size).
/// </summary>
public const int OptionHeaderLength = 2;
internal static Option Read(TOptionType optionType, byte[] buffer, ref int offset, int length)
{
if (length < 1)
return null;
byte optionLength = buffer[offset++];
if (length + 1 < optionLength)
return null;
byte optionValueLength = (byte)(optionLength - OptionHeaderLength);
IOptionComplexFactory prototype;
if (!_complexOptions.TryGetValue(optionType, out prototype))
return null;
return prototype.CreateInstance(buffer, ref offset, optionValueLength);
}
private static Dictionary<TOptionType, IOptionComplexFactory> InitializeComplexOptions()
{
var complexOptions =
from type in Assembly.GetExecutingAssembly().GetTypes()
where typeof(IOptionComplexFactory).IsAssignableFrom(type) &&
GetRegistrationAttribute(type) != null
select new
{
GetRegistrationAttribute(type).OptionType,
Option = (IOptionComplexFactory)Activator.CreateInstance(type)
};
return complexOptions.ToDictionary(option => (TOptionType)option.OptionType, option => option.Option);
}
private static OptionTypeRegistrationAttribute GetRegistrationAttribute(Type type)
{
var registraionAttributes =
from attribute in type.GetCustomAttributes(typeof(OptionTypeRegistrationAttribute), false).Cast<OptionTypeRegistrationAttribute>()
where attribute.OptionTypeType == typeof(TOptionType)
select attribute;
if (registraionAttributes.IsEmpty())
return null;
return registraionAttributes.First();
}
private static readonly Dictionary<TOptionType, IOptionComplexFactory> _complexOptions = InitializeComplexOptions();
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using PcapDotNet.Base;
namespace PcapDotNet.Packets
{
public abstract class Options<T> : ReadOnlyCollection<T> where T : Option
{
/// <summary>
/// The number of bytes the options take.
/// </summary>
public int BytesLength { get; private set; }
/// <summary>
/// Whether or not the options parsed ok.
/// </summary>
public bool IsValid { get; private set; }
/// <summary>
/// Two options are equal iff they have the exact same options.
/// </summary>
public bool Equals(Options<T> other)
{
if (other == null)
return false;
if (BytesLength != other.BytesLength)
return false;
return this.SequenceEqual(other);
}
/// <summary>
/// Two options are equal iff they have the exact same options.
/// </summary>
public override bool Equals(object obj)
{
return Equals(obj as Options<T>);
}
/// <summary>
/// The hash code is the xor of the following hash codes: number of bytes the options take and all the options.
/// </summary>
public override int GetHashCode()
{
return BytesLength.GetHashCode() ^
this.SequenceGetHashCode();
}
/// <summary>
/// A string of all the option type names.
/// </summary>
public override string ToString()
{
return this.SequenceToString(", ", GetType().Name + " {", "}");
}
internal Options(byte[] buffer, int offset, int length, T end)
: this(Read(buffer, offset, length, end))
{
BytesLength = length;
}
internal void Write(byte[] buffer, int offset)
{
int offsetEnd = offset + BytesLength;
foreach (T option in this)
option.Write(buffer, ref offset);
// Padding
while (offset < offsetEnd)
buffer[offset++] = 0;
}
protected Options(IList<T> options, T end, int maximumBytesLength)
: this(EndOptions(options, end), true)
{
if (BytesLength > maximumBytesLength)
throw new ArgumentException("given options take " + BytesLength + " bytes and maximum number of bytes for options is " + maximumBytesLength, "options");
}
private Options(IList<T> options, bool isValid)
: base(options)
{
IsValid = isValid;
BytesLength = SumBytesLength(this);
if (BytesLength % 4 != 0)
BytesLength = (BytesLength / 4 + 1) * 4;
}
private static IList<T> EndOptions(IList<T> options, T end)
{
if (options.Count == 0 || options.Last().Equivalent(end) || SumBytesLength(options) % 4 == 0)
return options;
return new List<T>(options.Concat(end));
}
private static int SumBytesLength(IEnumerable<T> options)
{
return options.Sum(option => option.Length);
}
private Options(Tuple<IList<T>, bool> optionsAndIsValid)
: this(optionsAndIsValid.Value1, optionsAndIsValid.Value2)
{
}
private static Tuple<IList<T>, bool> Read(byte[] buffer, int offset, int length, T end)
{
int offsetEnd = offset + length;
List<T> options = new List<T>();
while (offset != offsetEnd)
{
T option = (T)end.Read(buffer, ref offset, offsetEnd - offset);
if (option == null ||
option.IsAppearsAtMostOnce && options.Any(option.Equivalent))
{
// Invalid
return new Tuple<IList<T>, bool>(options, false);
}
options.Add(option);
if (option.Equivalent(end))
break; // Valid?
}
return new Tuple<IList<T>, bool>(options, true);
}
}
}
\ No newline at end of file
...@@ -68,6 +68,8 @@ ...@@ -68,6 +68,8 @@
<Compile Include="Ethernet\EthernetType.cs" /> <Compile Include="Ethernet\EthernetType.cs" />
<Compile Include="Ethernet\MacAddress.cs" /> <Compile Include="Ethernet\MacAddress.cs" />
<Compile Include="IDataLink.cs" /> <Compile Include="IDataLink.cs" />
<Compile Include="Option.cs" />
<Compile Include="IOptionComplexFactory.cs" />
<Compile Include="IpV4\IIpv4OptionComplexFactory.cs" /> <Compile Include="IpV4\IIpv4OptionComplexFactory.cs" />
<Compile Include="IpV4\IpV4Address.cs" /> <Compile Include="IpV4\IpV4Address.cs" />
<Compile Include="IpV4\IpV4Datagram.cs" /> <Compile Include="IpV4\IpV4Datagram.cs" />
...@@ -98,6 +100,9 @@ ...@@ -98,6 +100,9 @@
<Compile Include="IpV4\IpV4OptionType.cs" /> <Compile Include="IpV4\IpV4OptionType.cs" />
<Compile Include="IpV4\IpV4Protocol.cs" /> <Compile Include="IpV4\IpV4Protocol.cs" />
<Compile Include="MoreByteArray.cs" /> <Compile Include="MoreByteArray.cs" />
<Compile Include="OptionComplexFactory.cs" />
<Compile Include="Options.cs" />
<Compile Include="OptionTypeRegistrationAttribute.cs" />
<Compile Include="Packet.cs" /> <Compile Include="Packet.cs" />
<Compile Include="PacketBuilder.cs" /> <Compile Include="PacketBuilder.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
...@@ -105,6 +110,7 @@ ...@@ -105,6 +110,7 @@
<Compile Include="Transport\TcpFlags.cs" /> <Compile Include="Transport\TcpFlags.cs" />
<Compile Include="Transport\TcpOption.cs" /> <Compile Include="Transport\TcpOption.cs" />
<Compile Include="Transport\TcpOptions.cs" /> <Compile Include="Transport\TcpOptions.cs" />
<Compile Include="Transport\TcpOptionSimple.cs" />
<Compile Include="Transport\TransportDatagram.cs" /> <Compile Include="Transport\TransportDatagram.cs" />
<Compile Include="Transport\UdpDatagram.cs" /> <Compile Include="Transport\UdpDatagram.cs" />
</ItemGroup> </ItemGroup>
......
...@@ -23,6 +23,7 @@ namespace PcapDotNet.Packets.Transport ...@@ -23,6 +23,7 @@ namespace PcapDotNet.Packets.Transport
public class TcpDatagram : TransportDatagram public class TcpDatagram : TransportDatagram
{ {
public const int HeaderMinimumLength = 20; public const int HeaderMinimumLength = 20;
public const int HeaderMaximumLength = 60;
internal static class Offset internal static class Offset
{ {
......
using System;
namespace PcapDotNet.Packets.Transport namespace PcapDotNet.Packets.Transport
{ {
public class TcpOption public enum TcpOptionType : byte
{ {
EndOfOptionList = 0,
NoOperation = 1
}
public abstract class TcpOption : Option
{
public static TcpOption End
{
get { return _end; }
}
public static TcpOption Nop
{
get { return _nop; }
}
public TcpOption(TcpOptionType optionType)
{
OptionType = optionType;
}
public override bool Equivalent(Option other)
{
return OptionType == ((TcpOption)other).OptionType;
}
public TcpOptionType OptionType { get; private set; }
internal override Option Read(byte[] buffer, ref int offset, int length)
{
int offsetEnd = offset + length;
if (offset == offsetEnd)
return null;
TcpOptionType optionType = (TcpOptionType)buffer[offset++];
switch (optionType)
{
case TcpOptionType.EndOfOptionList:
return End;
case TcpOptionType.NoOperation:
return Nop;
default:
return OptionComplexFactory<TcpOptionType>.Read(optionType, buffer, ref offset, offsetEnd - offset);
}
}
internal override void Write(byte[] buffer, ref int offset)
{
buffer[offset++] = (byte)OptionType;
}
private static readonly TcpOption _end = new TcpOptionSimple(TcpOptionType.EndOfOptionList);
private static readonly TcpOption _nop = new TcpOptionSimple(TcpOptionType.NoOperation);
} }
} }
\ No newline at end of file
namespace PcapDotNet.Packets.Transport
{
/// <summary>
/// A simple IPv4 option - holds only the type.
/// </summary>
public class TcpOptionSimple : TcpOption
{
/// <summary>
/// The number of bytes this option will take.
/// </summary>
public const int OptionLength = 1;
/// <summary>
/// The number of bytes this option will take.
/// </summary>
public override int Length
{
get { return OptionLength; }
}
/// <summary>
/// True iff this option may appear at most once in a datagram.
/// </summary>
public override bool IsAppearsAtMostOnce
{
get { return false; }
}
internal TcpOptionSimple(TcpOptionType optionType)
: base(optionType)
{
}
}
}
\ No newline at end of file
using System; using System;
using System.Collections.Generic;
namespace PcapDotNet.Packets.Transport namespace PcapDotNet.Packets.Transport
{ {
public class TcpOptions public class TcpOptions : Options<TcpOption>
{ {
public const int MaximumBytesLength = TcpDatagram.HeaderMaximumLength - TcpDatagram.HeaderMinimumLength;
public static TcpOptions None public static TcpOptions None
{ {
get { return _none; } get { return _none; }
} }
public TcpOptions(IList<TcpOption> options)
: base(options, TcpOption.End, MaximumBytesLength)
{
}
/// <summary> /// <summary>
/// Creates options from a list of options. /// Creates options from a list of options.
/// </summary> /// </summary>
/// <param name="options">The list of options.</param> /// <param name="options">The list of options.</param>
public TcpOptions(params TcpOption[] options) public TcpOptions(params TcpOption[] options)
: this((IList<TcpOption>)options)
{ {
_bytesLength = 0;
} }
internal TcpOptions(byte[] buffer, int offset, int length) internal TcpOptions(byte[] buffer, int offset, int length)
// : this(Read(buffer, offset, length)) : base(buffer, offset, length, TcpOption.End)
{
_bytesLength = length;
}
public int BytesLength
{ {
get { return _bytesLength; }
} }
internal void Write(byte[] buffer, int offset)
{
// throw new NotImplementedException();
}
private readonly int _bytesLength;
private readonly bool _isValid;
private static readonly TcpOptions _none = new TcpOptions(); private static readonly TcpOptions _none = new TcpOptions();
} }
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment