#define LOG_MODULE PacketLogModuleTcpLayer
#include "EndianPortable.h"
#include "TcpLayer.h"
#include "IPv4Layer.h"
#include "IPv6Layer.h"
#include "PayloadLayer.h"
#include "HttpLayer.h"
#include "SSLLayer.h"
#include "SipLayer.h"
#include "BgpLayer.h"
#include "SSHLayer.h"
#include "DnsLayer.h"
#include "TelnetLayer.h"
#include "TpktLayer.h"
#include "FtpLayer.h"
#include "SomeIpLayer.h"
#include "PacketUtils.h"
#include "Logger.h"
#include <string.h>
#include <sstream>
namespace pcpp
{
#define TCPOPT_DUMMY 0xff
TcpOptionBuilder::TcpOptionBuilder(NopEolOptionTypes optionType)
{
switch (optionType)
{
case EOL:
init((uint8_t)PCPP_TCPOPT_EOL, nullptr, 0);
break;
case NOP:
default:
init((uint8_t)PCPP_TCPOPT_NOP, nullptr, 0);
break;
}
}
TcpOption TcpOptionBuilder::build() const
{
uint8_t recType = static_cast<uint8_t>(m_RecType);
size_t optionSize = m_RecValueLen + 2*sizeof(uint8_t);
if (recType == (uint8_t)PCPP_TCPOPT_EOL || recType == (uint8_t)PCPP_TCPOPT_NOP)
{
if (m_RecValueLen != 0)
{
PCPP_LOG_ERROR("TCP NOP and TCP EOL options are 1-byte long and don't have option value. Tried to set option value of size " << m_RecValueLen);
return TcpOption(nullptr);
}
optionSize = 1;
}
uint8_t* recordBuffer = new uint8_t[optionSize];
memset(recordBuffer, 0, optionSize);
recordBuffer[0] = recType;
if (optionSize > 1)
{
recordBuffer[1] = static_cast<uint8_t>(optionSize);
if (optionSize > 2 && m_RecValue != nullptr)
memcpy(recordBuffer+2, m_RecValue, m_RecValueLen);
}
return TcpOption(recordBuffer);
}
uint16_t TcpLayer::getSrcPort() const
{
return be16toh(getTcpHeader()->portSrc);
}
uint16_t TcpLayer::getDstPort() const
{
return be16toh(getTcpHeader()->portDst);
}
TcpOption TcpLayer::getTcpOption(TcpOptionType option) const
{
return m_OptionReader.getTLVRecord((uint8_t)option, getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr));
}
TcpOption TcpLayer::getFirstTcpOption() const
{
return m_OptionReader.getFirstTLVRecord(getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr));
}
TcpOption TcpLayer::getNextTcpOption(TcpOption& tcpOption) const
{
TcpOption nextOpt = m_OptionReader.getNextTLVRecord(tcpOption, getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr));
if (nextOpt.isNotNull() && nextOpt.getType() == TCPOPT_DUMMY)
return TcpOption(nullptr);
return nextOpt;
}
size_t TcpLayer::getTcpOptionCount() const
{
return m_OptionReader.getTLVRecordCount(getOptionsBasePtr(), getHeaderLen() - sizeof(tcphdr));
}
TcpOption TcpLayer::addTcpOption(const TcpOptionBuilder& optionBuilder)
{
return addTcpOptionAt(optionBuilder, getHeaderLen()-m_NumOfTrailingBytes);
}
TcpOption TcpLayer::addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType)
{
int offset = 0;
if (prevOptionType == TCPOPT_Unknown)
{
offset = sizeof(tcphdr);
}
else
{
TcpOption prevOpt = getTcpOption(prevOptionType);
if (prevOpt.isNull())
{
PCPP_LOG_ERROR("Previous option of type " << (int)prevOptionType << " not found, cannot add a new TCP option");
return TcpOption(nullptr);
}
offset = prevOpt.getRecordBasePtr() + prevOpt.getTotalSize() - m_Data;
}
return addTcpOptionAt(optionBuilder, offset);
}
bool TcpLayer::removeTcpOption(TcpOptionType optionType)
{
TcpOption opt = getTcpOption(optionType);
if (opt.isNull())
{
return false;
}
TcpOption curOpt = getFirstTcpOption();
size_t totalOptSize = 0;
while (!curOpt.isNull())
{
totalOptSize += curOpt.getTotalSize();
curOpt = getNextTcpOption(curOpt);
}
totalOptSize -= opt.getTotalSize();
int offset = opt.getRecordBasePtr() - m_Data;
if (!shortenLayer(offset, opt.getTotalSize()))
{
return false;
}
adjustTcpOptionTrailer(totalOptSize);
m_OptionReader.changeTLVRecordCount(-1);
return true;
}
bool TcpLayer::removeAllTcpOptions()
{
int offset = sizeof(tcphdr);
if (!shortenLayer(offset, getHeaderLen()-offset))
return false;
getTcpHeader()->dataOffset = sizeof(tcphdr)/4;
m_NumOfTrailingBytes = 0;
m_OptionReader.changeTLVRecordCount(0-getTcpOptionCount());
return true;
}
TcpOption TcpLayer::addTcpOptionAt(const TcpOptionBuilder& optionBuilder, int offset)
{
TcpOption newOption = optionBuilder.build();
if (newOption.isNull())
return newOption;
TcpOption curOpt = getFirstTcpOption();
size_t totalOptSize = 0;
while (!curOpt.isNull())
{
totalOptSize += curOpt.getTotalSize();
curOpt = getNextTcpOption(curOpt);
}
totalOptSize += newOption.getTotalSize();
size_t sizeToExtend = newOption.getTotalSize();
if (!extendLayer(offset, sizeToExtend))
{
PCPP_LOG_ERROR("Could not extend TcpLayer in [" << sizeToExtend << "] bytes");
newOption.purgeRecordData();
return TcpOption(nullptr);
}
memcpy(m_Data + offset, newOption.getRecordBasePtr(), newOption.getTotalSize());
newOption.purgeRecordData();
adjustTcpOptionTrailer(totalOptSize);
m_OptionReader.changeTLVRecordCount(1);
uint8_t* newOptPtr = m_Data + offset;
return TcpOption(newOptPtr);
}
void TcpLayer::adjustTcpOptionTrailer(size_t totalOptSize)
{
int newNumberOfTrailingBytes = 0;
while ((totalOptSize + newNumberOfTrailingBytes) % 4 != 0)
newNumberOfTrailingBytes++;
if (newNumberOfTrailingBytes < m_NumOfTrailingBytes)
shortenLayer(sizeof(tcphdr)+totalOptSize, m_NumOfTrailingBytes - newNumberOfTrailingBytes - 1);
else if (newNumberOfTrailingBytes > m_NumOfTrailingBytes)
extendLayer(sizeof(tcphdr)+totalOptSize, newNumberOfTrailingBytes - m_NumOfTrailingBytes);
m_NumOfTrailingBytes = newNumberOfTrailingBytes;
for (int i = 0; i < m_NumOfTrailingBytes; i++)
m_Data[sizeof(tcphdr) + totalOptSize + i] = TCPOPT_DUMMY;
getTcpHeader()->dataOffset = (sizeof(tcphdr) + totalOptSize + m_NumOfTrailingBytes)/4;
}
uint16_t TcpLayer::calculateChecksum(bool writeResultToPacket)
{
tcphdr* tcpHdr = getTcpHeader();
uint16_t checksumRes = 0;
uint16_t currChecksumValue = tcpHdr->headerChecksum;
if (m_PrevLayer != nullptr)
{
tcpHdr->headerChecksum = 0;
PCPP_LOG_DEBUG("TCP data len = " << m_DataLen);
if (m_PrevLayer->getProtocol() == IPv4)
{
IPv4Address srcIP = ((IPv4Layer*)m_PrevLayer)->getSrcIPv4Address();
IPv4Address dstIP = ((IPv4Layer*)m_PrevLayer)->getDstIPv4Address();
checksumRes = pcpp::computePseudoHdrChecksum((uint8_t *) tcpHdr, getDataLen(), IPAddress::IPv4AddressType,
PACKETPP_IPPROTO_TCP, srcIP, dstIP);
PCPP_LOG_DEBUG("calculated IPv4 TCP checksum = 0x" << std::uppercase << std::hex << checksumRes);
}
else if (m_PrevLayer->getProtocol() == IPv6)
{
IPv6Address srcIP = ((IPv6Layer*)m_PrevLayer)->getSrcIPv6Address();
IPv6Address dstIP = ((IPv6Layer*)m_PrevLayer)->getDstIPv6Address();
checksumRes = computePseudoHdrChecksum((uint8_t *) tcpHdr, getDataLen(), IPAddress::IPv6AddressType,
PACKETPP_IPPROTO_TCP, srcIP, dstIP);
PCPP_LOG_DEBUG("calculated IPv6 TCP checksum = 0xX" << std::uppercase << std::hex << checksumRes);
}
}
if(writeResultToPacket)
tcpHdr->headerChecksum = htobe16(checksumRes);
else
tcpHdr->headerChecksum = currChecksumValue;
return checksumRes;
}
void TcpLayer::initLayer()
{
m_DataLen = sizeof(tcphdr);
m_Data = new uint8_t[m_DataLen];
memset(m_Data, 0, m_DataLen);
m_Protocol = TCP;
m_NumOfTrailingBytes = 0;
getTcpHeader()->dataOffset = sizeof(tcphdr)/4;
}
TcpLayer::TcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : Layer(data, dataLen, prevLayer, packet)
{
m_Protocol = TCP;
m_NumOfTrailingBytes = 0;
}
TcpLayer::TcpLayer()
{
initLayer();
}
TcpLayer::TcpLayer(uint16_t portSrc, uint16_t portDst)
{
initLayer();
getTcpHeader()->portDst = htobe16(portDst);
getTcpHeader()->portSrc = htobe16(portSrc);
}
void TcpLayer::copyLayerData(const TcpLayer& other)
{
m_OptionReader = other.m_OptionReader;
m_NumOfTrailingBytes = other.m_NumOfTrailingBytes;
}
TcpLayer::TcpLayer(const TcpLayer& other) : Layer(other)
{
copyLayerData(other);
}
TcpLayer& TcpLayer::operator=(const TcpLayer& other)
{
Layer::operator=(other);
copyLayerData(other);
return *this;
}
void TcpLayer::parseNextLayer()
{
size_t headerLen = getHeaderLen();
if (m_DataLen <= headerLen)
return;
uint8_t* payload = m_Data + headerLen;
size_t payloadLen = m_DataLen - headerLen;
uint16_t portDst = getDstPort();
uint16_t portSrc = getSrcPort();
if (HttpMessage::isHttpPort(portDst) && HttpRequestFirstLine::parseMethod((char*)payload, payloadLen) != HttpRequestLayer::HttpMethodUnknown)
m_NextLayer = new HttpRequestLayer(payload, payloadLen, this, m_Packet);
else if (HttpMessage::isHttpPort(portSrc) && HttpResponseFirstLine::parseVersion((char*)payload, payloadLen) != HttpVersion::HttpVersionUnknown && !HttpResponseFirstLine::parseStatusCode((char*)payload, payloadLen).isUnsupportedCode())
m_NextLayer = new HttpResponseLayer(payload, payloadLen, this, m_Packet);
else if (SSLLayer::IsSSLMessage(portSrc, portDst, payload, payloadLen))
m_NextLayer = SSLLayer::createSSLMessage(payload, payloadLen, this, m_Packet);
else if (SipLayer::isSipPort(portDst) || SipLayer::isSipPort(portSrc))
{
if (SipRequestFirstLine::parseMethod((char*)payload, payloadLen) != SipRequestLayer::SipMethodUnknown)
m_NextLayer = new SipRequestLayer(payload, payloadLen, this, m_Packet);
else if (SipResponseFirstLine::parseStatusCode((char*)payload, payloadLen) != SipResponseLayer::SipStatusCodeUnknown)
m_NextLayer = new SipResponseLayer(payload, payloadLen, this, m_Packet);
else
m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet);
}
else if (BgpLayer::isBgpPort(portSrc, portDst))
{
m_NextLayer = BgpLayer::parseBgpLayer(payload, payloadLen, this, m_Packet);
if (!m_NextLayer)
m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet);
}
else if (SSHLayer::isSSHPort(portSrc, portDst))
m_NextLayer = SSHLayer::createSSHMessage(payload, payloadLen, this, m_Packet);
else if (DnsLayer::isDataValid(payload, payloadLen, true) && (DnsLayer::isDnsPort(portDst) || DnsLayer::isDnsPort(portSrc)))
m_NextLayer = new DnsOverTcpLayer(payload, payloadLen, this, m_Packet);
else if (TelnetLayer::isDataValid(payload, payloadLen) && (TelnetLayer::isTelnetPort(portDst) || TelnetLayer::isTelnetPort(portSrc)))
m_NextLayer = new TelnetLayer(payload, payloadLen, this, m_Packet);
else if (FtpLayer::isFtpPort(portSrc) && FtpLayer::isDataValid(payload, payloadLen))
m_NextLayer = new FtpResponseLayer(payload, payloadLen, this, m_Packet);
else if (FtpLayer::isFtpPort(portDst) && FtpLayer::isDataValid(payload, payloadLen))
m_NextLayer = new FtpRequestLayer(payload, payloadLen, this, m_Packet);
else if (FtpLayer::isFtpDataPort(portSrc) || FtpLayer::isFtpDataPort(portDst))
m_NextLayer = new FtpDataLayer(payload, payloadLen, this, m_Packet);
else if (SomeIpLayer::isSomeIpPort(portSrc) || SomeIpLayer::isSomeIpPort(portDst))
m_NextLayer = SomeIpLayer::parseSomeIpLayer(payload, payloadLen, this, m_Packet);
else if (TpktLayer::isDataValid(payload, payloadLen) && TpktLayer::isTpktPort(portSrc, portDst))
m_NextLayer = new TpktLayer(payload, payloadLen, this, m_Packet);
else
m_NextLayer = new PayloadLayer(payload, payloadLen, this, m_Packet);
}
void TcpLayer::computeCalculateFields()
{
tcphdr* tcpHdr = getTcpHeader();
tcpHdr->dataOffset = getHeaderLen() >> 2;
calculateChecksum(true);
}
std::string TcpLayer::toString() const
{
tcphdr* hdr = getTcpHeader();
std::string result = "TCP Layer, ";
if (hdr->synFlag)
{
if (hdr->ackFlag)
result += "[SYN, ACK], ";
else
result += "[SYN], ";
}
else if (hdr->finFlag)
{
if (hdr->ackFlag)
result += "[FIN, ACK], ";
else
result += "[FIN], ";
}
else if (hdr->ackFlag)
result += "[ACK], ";
std::ostringstream srcPortStream;
srcPortStream << getSrcPort();
std::ostringstream dstPortStream;
dstPortStream << getDstPort();
result += "Src port: " + srcPortStream.str() + ", Dst port: " + dstPortStream.str();
return result;
}
}