/* 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