/// ************************************************************************
/// Copyright (C) 2001, Patrick Charles and Jonas Lehmann *
/// Distributed under the Mozilla Public License *
/// http://www.mozilla.org/NPL/MPL-1.1.txt *
/// *************************************************************************
///
using System;
using AnsiEscapeSequences_Fields = SharpPcap.Packets.Util.AnsiEscapeSequences_Fields;
using ArrayHelper = SharpPcap.Packets.Util.ArrayHelper;
using Timeval = SharpPcap.Packets.Util.Timeval;
namespace SharpPcap.Packets
{
/// A TCP packet.
///
/// Extends an IP packet, adding a TCP header and TCP data payload.
///
///
[Serializable]
public class TCPPacket : IPPacket
{
/// Fetch the port number on the source host.
virtual public int SourcePort
{
get
{
return ArrayHelper.extractInteger(Bytes, _ipOffset + TCPFields_Fields.TCP_SP_POS, TCPFields_Fields.TCP_PORT_LEN);
}
set
{
ArrayHelper.insertLong(Bytes, value, _ipOffset + TCPFields_Fields.TCP_SP_POS, TCPFields_Fields.TCP_PORT_LEN);
}
}
/// Fetches the port number on the destination host.
virtual public int DestinationPort
{
get
{
return ArrayHelper.extractInteger(Bytes, _ipOffset + TCPFields_Fields.TCP_DP_POS, TCPFields_Fields.TCP_PORT_LEN);
}
set
{
ArrayHelper.insertLong(Bytes, value, _ipOffset + TCPFields_Fields.TCP_DP_POS, TCPFields_Fields.TCP_PORT_LEN);
}
}
/// Fetch the packet sequence number.
virtual public long SequenceNumber
{
get
{
return ArrayHelper.extractLong(Bytes, _ipOffset + TCPFields_Fields.TCP_SEQ_POS, TCPFields_Fields.TCP_SEQ_LEN);
}
set
{
ArrayHelper.insertLong(Bytes, value, _ipOffset + TCPFields_Fields.TCP_SEQ_POS, TCPFields_Fields.TCP_SEQ_LEN);
}
}
/// Fetch the packet acknowledgment number.
virtual public long AcknowledgmentNumber
{
get
{
return ArrayHelper.extractLong(Bytes, _ipOffset + TCPFields_Fields.TCP_ACK_POS, TCPFields_Fields.TCP_ACK_LEN);
}
set
{
ArrayHelper.insertLong(Bytes, value, _ipOffset + TCPFields_Fields.TCP_ACK_POS, TCPFields_Fields.TCP_ACK_LEN);
}
}
/// Fetch the TCP header length in bytes.
virtual public int TCPHeaderLength
{
get
{
return ((ArrayHelper.extractInteger(Bytes, _ipOffset + TCPFields_Fields.TCP_FLAG_POS, TCPFields_Fields.TCP_FLAG_LEN) >> 12) & 0xf) * 4;
}
set
{
value = value / 4;
Bytes[_ipOffset + TCPFields_Fields.TCP_FLAG_POS] &= (byte)(0x0f);
Bytes[_ipOffset + TCPFields_Fields.TCP_FLAG_POS] |= (byte)(((value << 4) & 0xf0));
}
}
/// Fetches the packet TCP header length.
override public int HeaderLength
{
get
{
return TCPHeaderLength;
}
}
/// Fetches the length of the payload data.
virtual public int PayloadDataLength
{
get
{
return (IPPayloadLength - TCPHeaderLength);
}
}
/// Fetch the window size.
virtual public int WindowSize
{
get
{
return ArrayHelper.extractInteger(Bytes, _ipOffset + TCPFields_Fields.TCP_WIN_POS, TCPFields_Fields.TCP_WIN_LEN);
}
set
{
ArrayHelper.insertLong(Bytes, value, _ipOffset + TCPFields_Fields.TCP_WIN_POS, TCPFields_Fields.TCP_WIN_LEN);
}
}
/// Fetch the header checksum.
/// Set the checksum of the TCP header
/// the checksum value
///
virtual public int TCPChecksum
{
get
{
return GetTransportLayerChecksum(_ipOffset + TCPFields_Fields.TCP_CSUM_POS);
}
set
{
base.SetTransportLayerChecksum(value, TCPFields_Fields.TCP_CSUM_POS);
}
}
/// Check if the TCP packet is valid, checksum-wise.
public override bool ValidChecksum
{
get
{
return ValidIPChecksum && ValidTCPChecksum;
}
}
virtual public bool ValidTCPChecksum
{
get
{
return base.IsValidTransportLayerChecksum(true);
}
}
/// The TCP packet length in bytes. This is the size of the
/// IP packet minus the size of the IP header.
///
virtual public int TCPPacketByteLength
{
get
{
return IPPayloadLength;
}
}
private int AllFlags
{
get
{
if (!_allFlagsSet)
{
_allFlags = ArrayHelper.extractInteger(Bytes, _ipOffset + TCPFields_Fields.TCP_FLAG_POS, TCPFields_Fields.TCP_FLAG_LEN);
//tamir: added
_allFlagsSet = true;
}
return _allFlags;
}
set
{
ArrayHelper.insertLong(Bytes, value, _ipOffset + TCPFields_Fields.TCP_FLAG_POS, TCPFields_Fields.TCP_FLAG_LEN);
_allFlagsSet = false;
}
}
/// Check the URG flag, flag indicates if the urgent pointer is valid.
virtual public bool Urg
{
get
{
if (!_isUrgSet)
{
_isUrg = (AllFlags & TCPFields_Fields.TCP_URG_MASK) != 0;
_isUrgSet = true;
}
return _isUrg;
}
set
{
setFlag(value, TCPFields_Fields.TCP_URG_MASK);
_isUrgSet = false;
}
}
/// Check the ACK flag, flag indicates if the ack number is valid.
virtual public bool Ack
{
get
{
if (!_isAckSet)
{
_isAck = (AllFlags & TCPFields_Fields.TCP_ACK_MASK) != 0;
_isAckSet = true;
}
return _isAck;
}
set
{
setFlag(value, TCPFields_Fields.TCP_ACK_MASK);
_isAck = value;
_isAckSet = true;
}
}
/// Check the PSH flag, flag indicates the receiver should pass the
/// data to the application as soon as possible.
///
virtual public bool Psh
{
get
{
if (!_isPshSet)
{
_isPsh = (AllFlags & TCPFields_Fields.TCP_PSH_MASK) != 0;
_isPshSet = true;
}
return _isPsh;
}
set
{
setFlag(value, TCPFields_Fields.TCP_PSH_MASK);
_isPsh = value;
_isPshSet = true;
}
}
/// Check the RST flag, flag indicates the session should be reset between
/// the sender and the receiver.
///
virtual public bool Rst
{
get
{
if (!_isRstSet)
{
_isRst = (AllFlags & TCPFields_Fields.TCP_RST_MASK) != 0;
_isRstSet = true;
}
return _isRst;
}
set
{
setFlag(value, TCPFields_Fields.TCP_RST_MASK);
_isRst = value;
_isRstSet = true;
}
}
/// 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
{
if (!_isSynSet)
{
_isSyn = (AllFlags & TCPFields_Fields.TCP_SYN_MASK) != 0;
_isSynSet = true;
}
return _isSyn;
}
set
{
setFlag(value, TCPFields_Fields.TCP_SYN_MASK);
_isSyn = value;
_isSynSet = true;
}
}
/// Check the FIN flag, flag indicates the sender is finished sending.
virtual public bool Fin
{
get
{
if (!_isFinSet)
{
_isFin = (AllFlags & TCPFields_Fields.TCP_FIN_MASK) != 0;
_isFinSet = true;
}
return _isFin;
}
set
{
setFlag(value, TCPFields_Fields.TCP_FIN_MASK);
_isFin = value;
_isFinSet = true;
}
}
virtual public bool ECN
{
get
{
return (AllFlags & TCPFields_Fields.TCP_ECN_MASK) != 0;
}
set
{
setFlag(value, TCPFields_Fields.TCP_ECN_MASK);
}
}
virtual public bool CWR
{
get
{
return (AllFlags & TCPFields_Fields.TCP_CWR_MASK) != 0;
}
set
{
setFlag(value, TCPFields_Fields.TCP_CWR_MASK);
}
}
/// Fetch the TCP header a byte array.
virtual public byte[] TCPHeader
{
get
{
if (_tcpHeaderBytes == null)
{
_tcpHeaderBytes = PacketEncoding.extractHeader(_ipOffset, TCPHeaderLength, Bytes);
}
return _tcpHeaderBytes;
}
}
/// Fetch the TCP header as a byte array.
override public byte[] Header
{
get
{
return TCPHeader;
}
}
/// Fetch the TCP data as a byte array.
virtual public byte[] TCPData
{
get
{
if (_tcpDataBytes == null)
{
_tcpDataBytes = new byte[PayloadDataLength];
Array.Copy(Bytes, _ipOffset + TCPHeaderLength, _tcpDataBytes, 0, PayloadDataLength);
}
return _tcpDataBytes;
}
set
{
SetData(value);
}
}
/// Fetch ascii escape sequence of the color associated with this packet type.
override public System.String Color
{
get
{
return AnsiEscapeSequences_Fields.YELLOW;
}
}
///
private const long serialVersionUID = 1L;
/// Create a new TCP packet.
public TCPPacket(int lLen, byte[] bytes)
: this(lLen, bytes, false)
{
}
/// Create a new TCP packet.
public TCPPacket(int lLen, byte[] bytes, bool isEmpty)
: base(lLen, bytes)
{
}
/// Create a new TCP packet.
public TCPPacket(int lLen, byte[] bytes, Timeval tv)
: this(lLen, bytes)
{
this._timeval = tv;
}
/// Fetch the header checksum.
public int Checksum
{
get
{
return TCPChecksum;
}
set
{
TCPChecksum=value;
}
}
/// Computes the TCP checksum, optionally updating the TCP checksum header.
///
///
/// Specifies whether or not to update the TCP checksum header
/// after computing the checksum. A value of true indicates the
/// header should be updated, a value of false indicates it should
/// not be updated.
///
/// The computed TCP checksum.
///
public int ComputeTCPChecksum(bool update)
{
return base.ComputeTransportLayerChecksum(TCPFields_Fields.TCP_CSUM_POS, update, true);
}
/// Same as computeTCPChecksum(true);
///
///
/// The computed TCP checksum value.
///
public int ComputeTCPChecksum()
{
return ComputeTCPChecksum(true);
}
private int _urgentPointer;
private bool _urgentPointerSet = false;
/// Fetch the urgent pointer.
public virtual int getUrgentPointer()
{
if (!_urgentPointerSet)
{
_urgentPointer = ArrayHelper.extractInteger(Bytes, _ipOffset + TCPFields_Fields.TCP_URG_POS, TCPFields_Fields.TCP_URG_LEN);
_urgentPointerSet = true;
}
return _urgentPointer;
}
/// Sets the urgent pointer.
///
///
/// The urgent pointer value.
///
public void setUrgentPointer(int pointer)
{
ArrayHelper.insertLong(Bytes, pointer, _ipOffset + TCPFields_Fields.TCP_URG_POS, TCPFields_Fields.TCP_URG_LEN);
_urgentPointerSet = false;
}
// next value holds all the flags
private int _allFlags;
private bool _allFlagsSet = false;
private void setFlag(bool on, int MASK)
{
if (on)
AllFlags = AllFlags | MASK;
else
AllFlags = AllFlags & ~MASK;
}
private bool _isUrg;
private bool _isUrgSet = false;
private bool _isAck;
private bool _isAckSet = false;
private bool _isPsh;
private bool _isPshSet = false;
private bool _isRst;
private bool _isRstSet = false;
private bool _isSyn;
private bool _isSynSet = false;
private bool _isFin;
private bool _isFinSet = false;
private byte[] _tcpHeaderBytes = null;
// cached copy of the payload of the tcp packet
private byte[] _tcpDataBytes = null;
/// Fetch the TCP data as a byte array.
public override byte[] Data
{
get
{
return TCPData;
}
}
/// Sets the data section of this tcp packet
/// the data bytes
///
public virtual void SetData(byte[] data)
{
//reset cached tcp data
_tcpDataBytes = null;
// the new packet is the length of the headers + the size of the TCPPacket data payload
int headerLength = TCPHeaderLength + IPHeaderLength + EthernetHeaderLength;
int newPacketLength = headerLength + data.Length;
byte[] newPacketBytes = new byte[newPacketLength];
// copy the headers into the new packet
Array.Copy(Bytes, newPacketBytes, headerLength);
// copy the data into the new packet, immediately after the headers
Array.Copy(data, 0, newPacketBytes, headerLength, data.Length);
// make the old headers and new data bytes the new packet bytes
this.Bytes = newPacketBytes;
// NOTE: TCPHeaderLength remains the same, we only updated the data portion
// of the tcp packet
//update ip total length
IPPayloadLength = TCPHeaderLength + data.Length;
//update also offset and pcap header
OnOffsetChanged();
}
public enum OptionTypes
{
EndOfList = 0x0,
Nop = 0x1,
MaximumSegmentSize = 0x2,
WindowScale = 0x3,
SelectiveAckSupported = 0x4,
Unknown5 = 0x5,
Unknown6 = 0x6,
Unknown7 = 0x7,
Timestamp = 0x8 // http://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_Timestamps
}
public byte[] Options
{
get
{
if(Urg)
{
throw new System.NotImplementedException("Urg == true not implemented yet");
}
int optionsOffset = TCPFields_Fields.TCP_URG_POS + TCPFields_Fields.TCP_URG_LEN;
int optionsLength = TCPHeaderLength - optionsOffset;
byte[] optionBytes = new byte[optionsLength];
Array.Copy(Bytes, _ipOffset + optionsOffset, optionBytes, 0, optionsLength);
return optionBytes;
}
}
/// Convert this TCP packet to a readable string.
public override System.String ToString()
{
return ToColoredString(false);
}
/// Generate string with contents describing this TCP packet.
/// whether or not the string should contain ansi
/// color escape sequences.
///
public override System.String ToColoredString(bool colored)
{
System.Text.StringBuilder buffer = new System.Text.StringBuilder();
buffer.Append('[');
if (colored)
buffer.Append(Color);
buffer.Append("TCPPacket");
if (colored)
buffer.Append(AnsiEscapeSequences_Fields.RESET);
buffer.Append(": ");
buffer.Append(SourceAddress);
buffer.Append('.');
buffer.Append(IPPort.getName(SourcePort));
buffer.Append(" -> ");
buffer.Append(DestinationAddress);
buffer.Append('.');
buffer.Append(IPPort.getName(DestinationPort));
if (Urg)
buffer.Append(" urg[0x" + System.Convert.ToString(getUrgentPointer(), 16) + "]");
if (Ack)
buffer.Append(" ack[0x" + System.Convert.ToString(AcknowledgmentNumber, 16) + "]");
if (Psh)
buffer.Append(" psh");
if (Rst)
buffer.Append(" rst");
if (Syn)
buffer.Append(" syn[0x" + System.Convert.ToString(SequenceNumber, 16) + "," +
SequenceNumber + "]");
if (Fin)
buffer.Append(" fin");
buffer.Append(" l=" + TCPHeaderLength + "," + PayloadDataLength);
buffer.Append(']');
// append the base class output
buffer.Append(base.ToColoredString(colored));
return buffer.ToString();
}
/// Convert this TCP packet to a verbose.
public override System.String ToColoredVerboseString(bool colored)
{
System.Text.StringBuilder buffer = new System.Text.StringBuilder();
buffer.Append('[');
if (colored)
buffer.Append(Color);
buffer.Append("TCPPacket");
if (colored)
buffer.Append(AnsiEscapeSequences_Fields.RESET);
buffer.Append(": ");
buffer.Append("sport=" + SourcePort + ", ");
buffer.Append("dport=" + DestinationPort + ", ");
buffer.Append("seqn=0x" + System.Convert.ToString(SequenceNumber, 16) + ", ");
buffer.Append("ackn=0x" + System.Convert.ToString(AcknowledgmentNumber, 16) + ", ");
buffer.Append("hlen=" + HeaderLength + ", ");
buffer.Append("urg=" + Urg + ", ");
buffer.Append("ack=" + Ack + ", ");
buffer.Append("psh=" + Psh + ", ");
buffer.Append("rst=" + Rst + ", ");
buffer.Append("syn=" + Syn + ", ");
buffer.Append("fin=" + Fin + ", ");
buffer.Append("wsize=" + WindowSize + ", ");
buffer.Append("sum=0x" + System.Convert.ToString(Checksum, 16));
if (this.ValidTCPChecksum)
buffer.Append(" (correct), ");
else
buffer.Append(" (incorrect, should be " + ComputeTCPChecksum(false) + "), ");
buffer.Append("uptr=0x" + System.Convert.ToString(getUrgentPointer(), 16));
buffer.Append(']');
// append the base class output
buffer.Append(base.ToColoredVerboseString(colored));
return buffer.ToString();
}
public static TCPPacket RandomPacket()
{
return RandomPacket(54);
}
public static TCPPacket RandomPacket(int size)
{
return RandomPacket(size, IPVersions.IPv4);
}
public static TCPPacket RandomPacket(IPVersions ipver)
{
return RandomPacket(ipver==IPVersions.IPv6 ? 74:54, ipver);
}
public static TCPPacket RandomPacket(int size, IPVersions ipver)
{
if(size<54)
throw new Exception("Size should be at least 54 (Eth + IP + TCP)");
if(ipver == IPVersions.IPv6 && size < 74)
throw new Exception("Size should be at least 74 (Eth + IPv6 + TCP)");
byte[] bytes = new byte[size];
SharpPcap.Util.Rand.Instance.GetBytes(bytes);
TCPPacket tcp = new TCPPacket(14, bytes, true);
MakeValid(tcp, ipver);
return tcp;
}
public static void MakeValid(TCPPacket tcp, IPVersions ipver)
{
tcp.IPVersion = ipver;
tcp.IPProtocol = Packets.IPProtocol.IPProtocolType.TCP;
tcp.TCPHeaderLength = TCPFields_Fields.TCP_HEADER_LEN; //Set the correct TCP header length
if (ipver == IPVersions.IPv4)
{
tcp.IPTotalLength = tcp.Bytes.Length - 14; //Set the correct IP length
tcp.IPHeaderLength = IPv4Fields_Fields.IP_HEADER_LEN;
}
else if (ipver == IPVersions.IPv6)
{
tcp.IPPayloadLength = tcp.Bytes.Length - EthernetFields_Fields.ETH_HEADER_LEN - IPv6Fields_Fields.IPv6_HEADER_LEN;
}
else
{
}
//Calculate checksums
tcp.ComputeIPChecksum();
tcp.ComputeTCPChecksum();
}
}
}