/*
This file is part of PacketDotNet
PacketDotNet is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PacketDotNet is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PacketDotNet. If not, see .
*/
/*
* Copyright 2010 Chris Morgan
*/
using System;
using System.Text;
using System.Collections.Generic;
using MiscUtil.Conversion;
using PacketDotNet.Tcp;
using PacketDotNet.Utils;
namespace PacketDotNet
{
///
/// TcpPacket
/// See: http://en.wikipedia.org/wiki/Transmission_Control_Protocol
///
public class TcpPacket : TransportPacket
{
// NOTE: No need to warn about lack of use, the compiler won't
// put any calls to 'log' here but we need 'log' to exist to compile
#pragma warning disable 0169, 0649
private static readonly ILogInactive log;
#pragma warning restore 0169, 0649
///
/// 20 bytes is the smallest tcp header
///
public const int HeaderMinimumLength = 20;
/// Fetch the port number on the source host.
virtual public ushort SourcePort
{
get
{
return EndianBitConverter.Big.ToUInt16(header.Bytes,
header.Offset + TcpFields.SourcePortPosition);
}
set
{
var theValue = value;
EndianBitConverter.Big.CopyBytes(theValue,
header.Bytes,
header.Offset + TcpFields.SourcePortPosition);
}
}
/// Fetches the port number on the destination host.
virtual public ushort DestinationPort
{
get
{
return EndianBitConverter.Big.ToUInt16(header.Bytes,
header.Offset + TcpFields.DestinationPortPosition);
}
set
{
var theValue = value;
EndianBitConverter.Big.CopyBytes(theValue,
header.Bytes,
header.Offset + TcpFields.DestinationPortPosition);
}
}
/// Fetch the packet sequence number.
public uint SequenceNumber
{
get
{
return EndianBitConverter.Big.ToUInt32(header.Bytes,
header.Offset + TcpFields.SequenceNumberPosition);
}
set
{
EndianBitConverter.Big.CopyBytes(value,
header.Bytes,
header.Offset + TcpFields.SequenceNumberPosition);
}
}
/// Fetch the packet acknowledgment number.
public uint AcknowledgmentNumber
{
get
{
return EndianBitConverter.Big.ToUInt32(header.Bytes,
header.Offset + TcpFields.AckNumberPosition);
}
set
{
EndianBitConverter.Big.CopyBytes(value,
header.Bytes,
header.Offset + TcpFields.AckNumberPosition);
}
}
/// The size of the tcp header in 32bit words
virtual public int DataOffset
{
get
{
var theByte = header.Bytes[header.Offset + TcpFields.DataOffsetPosition];
return (theByte >> 4) & 0xF;
}
set
{
// read the original value
var theByte = header.Bytes[header.Offset + TcpFields.DataOffsetPosition];
// mask in the data offset value
theByte = (byte)((theByte & 0x0F) | ((value << 4) & 0xF0));
// write the value back
header.Bytes[header.Offset + TcpFields.DataOffsetPosition] = theByte;
}
}
///
/// The size of the receive window, which specifies the number of
/// bytes (beyond the sequence number in the acknowledgment field) that
/// the receiver is currently willing to receive.
///
virtual public UInt16 WindowSize
{
get
{
return EndianBitConverter.Big.ToUInt16(header.Bytes,
header.Offset + TcpFields.WindowSizePosition);
}
set
{
EndianBitConverter.Big.CopyBytes(value,
header.Bytes,
header.Offset + TcpFields.WindowSizePosition);
}
}
///
/// Tcp checksum field value of type UInt16
///
override public ushort Checksum
{
get
{
return EndianBitConverter.Big.ToUInt16(header.Bytes,
header.Offset + TcpFields.ChecksumPosition);
}
set
{
var theValue = value;
EndianBitConverter.Big.CopyBytes(theValue,
header.Bytes,
header.Offset + TcpFields.ChecksumPosition);
}
}
/// Check if the TCP packet is valid, checksum-wise.
public bool ValidChecksum
{
get
{
// IPv6 has no checksum so only the TCP checksum needs evaluation
if (ParentPacket.GetType() == typeof(IPv6Packet))
return ValidTCPChecksum;
// For IPv4 both the IP layer and the TCP layer contain checksums
else
return ((IPv4Packet)ParentPacket).ValidIPChecksum && ValidTCPChecksum;
}
}
///
/// True if the tcp checksum is valid
///
virtual public bool ValidTCPChecksum
{
get
{
log.Debug("ValidTCPChecksum");
var retval = IsValidChecksum(TransportPacket.TransportChecksumOption.AttachPseudoIPHeader);
log.DebugFormat("ValidTCPChecksum {0}", retval);
return retval;
}
}
///
/// Flags, 9 bits
/// TODO: Handle the NS bit
///
public byte AllFlags
{
get
{
return header.Bytes[header.Offset + TcpFields.FlagsPosition];
}
set
{
header.Bytes[header.Offset + TcpFields.FlagsPosition] = (byte)value;
}
}
/// Check the URG flag, flag indicates if the urgent pointer is valid.
virtual public bool Urg
{
get { return (AllFlags & TcpFields.TCP_URG_MASK) != 0; }
set { setFlag(value, TcpFields.TCP_URG_MASK); }
}
/// Check the ACK flag, flag indicates if the ack number is valid.
virtual public bool Ack
{
get { return (AllFlags & TcpFields.TCP_ACK_MASK) != 0; }
set { setFlag(value, TcpFields.TCP_ACK_MASK); }
}
/// Check the PSH flag, flag indicates the receiver should pass the
/// data to the application as soon as possible.
///
virtual public bool Psh
{
get { return (AllFlags & TcpFields.TCP_PSH_MASK) != 0; }
set { setFlag(value, TcpFields.TCP_PSH_MASK); }
}
/// Check the RST flag, flag indicates the session should be reset between
/// the sender and the receiver.
///
virtual public bool Rst
{
get { return (AllFlags & TcpFields.TCP_RST_MASK) != 0; }
set { setFlag(value, TcpFields.TCP_RST_MASK); }
}
/// Check the SYN flag, flag indicates the sequence numbers should
/// be synchronized between the sender and receiver to initiate
/// a connection.
///
virtual public bool Syn
{
get { return (AllFlags & TcpFields.TCP_SYN_MASK) != 0; }
set { setFlag(value, TcpFields.TCP_SYN_MASK); }
}
/// Check the FIN flag, flag indicates the sender is finished sending.
virtual public bool Fin
{
get { return (AllFlags & TcpFields.TCP_FIN_MASK) != 0; }
set { setFlag(value, TcpFields.TCP_FIN_MASK); }
}
///
/// ECN flag
///
virtual public bool ECN
{
get { return (AllFlags & TcpFields.TCP_ECN_MASK) != 0; }
set { setFlag(value, TcpFields.TCP_ECN_MASK); }
}
///
/// CWR flag
///
virtual public bool CWR
{
get { return (AllFlags & TcpFields.TCP_CWR_MASK) != 0; }
set { setFlag(value, TcpFields.TCP_CWR_MASK); }
}
private void setFlag(bool on, int MASK)
{
if (on)
AllFlags = (byte)(AllFlags | MASK);
else
AllFlags = (byte)(AllFlags & ~MASK);
}
/// Fetch ascii escape sequence of the color associated with this packet type.
override public System.String Color
{
get
{
return AnsiEscapeSequences.Yellow;
}
}
///
/// Create a new TCP packet from values
///
public TcpPacket(ushort SourcePort,
ushort DestinationPort)
{
log.Debug("");
// allocate memory for this packet
int offset = 0;
int length = TcpFields.HeaderLength;
var headerBytes = new byte[length];
header = new ByteArraySegment(headerBytes, offset, length);
// make this packet valid
DataOffset = length / 4;
// set instance values
this.SourcePort = SourcePort;
this.DestinationPort = DestinationPort;
}
///
/// Constructor
///
///
/// A
///
public TcpPacket(ByteArraySegment bas)
{
log.Debug("");
// set the header field, header field values are retrieved from this byte array
header = new ByteArraySegment(bas);
RandomUtils.EnsurePacketLength(this, TcpPacket.HeaderMinimumLength, header.Length);
RandomUtils.EnsurePacketLength(this, DataOffset * 4, header.Length);
// NOTE: we update the Length field AFTER the header field because
// we need the header to be valid to retrieve the value of DataOffset
header.Length = DataOffset * 4;
// store the payload bytes
payloadPacketOrData = new PacketOrByteArraySegment();
payloadPacketOrData.TheByteArraySegment = header.EncapsulatedBytes();
}
///
/// Constructor
///
///
/// A
///
///
/// A
///
public TcpPacket(ByteArraySegment bas,
Packet ParentPacket) :
this(bas)
{
log.DebugFormat("ParentPacket.GetType() {0}", ParentPacket.GetType());
this.ParentPacket = ParentPacket;
// if the parent packet is an IPv4Packet we need to adjust
// the payload length because it is possible for us to have
// X bytes of data but only (X - Y) bytes are actually valid
if(this.ParentPacket is IPv4Packet)
{
// actual total length (tcp header + tcp payload)
var ipv4Parent = (IPv4Packet)this.ParentPacket;
var ipPayloadTotalLength = ipv4Parent.TotalLength - (ipv4Parent.HeaderLength * 4);
log.DebugFormat("ipv4Parent.TotalLength {0}, ipv4Parent.HeaderLength {1}",
ipv4Parent.TotalLength,
ipv4Parent.HeaderLength * 4);
var newTcpPayloadLength = ipPayloadTotalLength - this.Header.Length;
RandomUtils.EnsurePacketLength(this, newTcpPayloadLength, payloadPacketOrData.TheByteArraySegment.Length);
log.DebugFormat("Header.Length {0}, Current payload length: {1}, new payload length {2}",
this.header.Length,
payloadPacketOrData.TheByteArraySegment.Length,
newTcpPayloadLength);
// the length of the payload is the total payload length
// above, minus the length of the tcp header
payloadPacketOrData.TheByteArraySegment.Length = newTcpPayloadLength;
}
}
///
/// Computes the TCP checksum. Does not update the current checksum value
///
/// The calculated TCP checksum.
public int CalculateTCPChecksum()
{
var newChecksum = CalculateChecksum(TransportChecksumOption.AttachPseudoIPHeader);
return newChecksum;
}
///
/// Update the checksum value.
///
public void UpdateTCPChecksum()
{
log.Debug("");
this.Checksum = (ushort)CalculateTCPChecksum();
}
/// Fetch the urgent pointer.
public int UrgentPointer
{
get
{
return EndianBitConverter.Big.ToInt16(header.Bytes,
header.Offset + TcpFields.UrgentPointerPosition);
}
set
{
var theValue = (Int16)value;
EndianBitConverter.Big.CopyBytes(theValue,
header.Bytes,
header.Offset + TcpFields.UrgentPointerPosition);
}
}
///
/// Bytes that represent the tcp options
///
///
/// A
///
public byte[] Options
{
get
{
if(Urg)
{
throw new System.NotImplementedException("Urg == true not implemented yet");
}
int optionsOffset = TcpFields.UrgentPointerPosition + TcpFields.UrgentPointerLength;
int optionsLength = (DataOffset * 4) - optionsOffset;
byte[] optionBytes = new byte[optionsLength];
Array.Copy(header.Bytes, header.Offset + optionsOffset,
optionBytes, 0,
optionsLength);
return optionBytes;
}
}
public override void UpdateCalculatedValues()
{
UpdateTCPChecksum();
}
///
/// Parses options, pointed to by optionBytes into an array of Options
///
///
/// A
///
///
/// A
///
private List