Coverage Report

Created: 2026-01-17 08:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/src/SingleCommandTextProtocol.cpp
Line
Count
Source
1
#include "SingleCommandTextProtocol.h"
2
#include <cstring>
3
#include <algorithm>
4
#include <vector>
5
6
8.78k
#define ASCII_HYPHEN 0x2d
7
8.78k
#define ASCII_SPACE 0x20
8
17.4k
#define MAX_COMMAND_LENGTH 9  // From SMTP command "STARTTLS" + 1 byte hyphen or space
9
10.2k
#define MIN_PACKET_LENGTH 2   // CRLF
10
11
namespace pcpp
12
{
13
14
  size_t SingleCommandTextProtocol::getArgumentFieldOffset() const
15
8.78k
  {
16
8.78k
    size_t maxLen;
17
8.78k
    if (m_DataLen < MAX_COMMAND_LENGTH)
18
170
      maxLen = m_DataLen;
19
8.61k
    else
20
8.61k
      maxLen = MAX_COMMAND_LENGTH;
21
22
    // To correctly detect multi-line packets with the option containing a space in
23
    // the first MAX_CONTENT_LENGTH bytes, search the both of hyphen and space to take
24
    // correct command delimiter
25
26
8.78k
    std::string field(reinterpret_cast<char*>(m_Data), maxLen);
27
28
8.78k
    size_t posHyphen = field.find_first_of(ASCII_HYPHEN);
29
8.78k
    size_t posSpace = field.find_first_of(ASCII_SPACE);
30
8.78k
    size_t posCRLF = field.rfind("\r\n");
31
32
    // No delimiter or packet end
33
8.78k
    if (posHyphen == std::string::npos && posSpace == std::string::npos && posCRLF == std::string::npos)
34
5.25k
      return 0;
35
    // Both hyphen and space found
36
3.52k
    else if (posHyphen != std::string::npos || posSpace != std::string::npos)
37
2.47k
      return std::min(posSpace, posHyphen);
38
    // If nothing found but there is a CRLF it is a only command packet
39
1.05k
    else if (posCRLF != std::string::npos)
40
1.05k
      return posCRLF;
41
42
0
    return 0;
43
8.78k
  }
44
45
  void SingleCommandTextProtocol::setDelimiter(bool hyphen)
46
0
  {
47
0
    if (hyphen)
48
0
      memset(&m_Data[getArgumentFieldOffset()], ASCII_HYPHEN, 1);
49
0
    else
50
0
      memset(&m_Data[getArgumentFieldOffset()], ASCII_SPACE, 1);
51
0
  }
52
53
  bool SingleCommandTextProtocol::hyphenRequired(const std::string& value)
54
0
  {
55
0
    size_t firstPos = value.find("\r\n");
56
0
    size_t lastPos = value.rfind("\r\n");
57
0
    return (firstPos != std::string::npos) && (lastPos != std::string::npos) && (firstPos != lastPos);
58
0
  }
59
60
  SingleCommandTextProtocol::SingleCommandTextProtocol(const std::string& command, const std::string& option,
61
                                                       ProtocolType protocol)
62
0
  {
63
0
    m_Protocol = protocol;
64
0
    m_Data = new uint8_t[MIN_PACKET_LENGTH];
65
0
    m_DataLen = MIN_PACKET_LENGTH;
66
0
    if (!command.empty())
67
0
      setCommandInternal(command);
68
0
    if (!option.empty())
69
0
      setCommandOptionInternal(option);
70
0
  }
71
72
  bool SingleCommandTextProtocol::setCommandInternal(std::string value)
73
0
  {
74
0
    size_t currentOffset = getArgumentFieldOffset();
75
0
    if (currentOffset == m_DataLen - 1)
76
0
      currentOffset = 0;
77
0
    if (!currentOffset)
78
0
      value += " \r\n";
79
80
0
    if (value.size() < currentOffset)
81
0
    {
82
0
      if (!shortenLayer(0, currentOffset - value.size()))
83
0
        return false;
84
0
    }
85
0
    else if (m_Data && value.size() > currentOffset)
86
0
    {
87
0
      if (!extendLayer(0, value.size() - currentOffset))
88
0
        return false;
89
0
    }
90
91
0
    memcpy(m_Data, value.c_str(), value.size());
92
0
    return true;
93
0
  }
94
95
  bool SingleCommandTextProtocol::setCommandOptionInternal(std::string value)
96
0
  {
97
0
    size_t lastPos = value.rfind("\r\n");
98
0
    if (lastPos == std::string::npos || lastPos != value.size() - 2)
99
0
      value += "\r\n";
100
101
0
    size_t currentOffset = getArgumentFieldOffset() + 1;
102
103
0
    if (value.size() < (m_DataLen - currentOffset))
104
0
    {
105
0
      if (!shortenLayer(currentOffset, (m_DataLen - currentOffset) - value.size()))
106
0
        return false;
107
0
    }
108
0
    else if (m_Data && value.size() > (m_DataLen - currentOffset))
109
0
    {
110
0
      if (!extendLayer(currentOffset, value.size() - (m_DataLen - currentOffset)))
111
0
        return false;
112
0
    }
113
114
0
    memcpy(&m_Data[currentOffset], value.c_str(), value.size());
115
116
0
    if (hyphenRequired(value))
117
0
      setDelimiter(true);
118
0
    else
119
0
      setDelimiter(false);
120
0
    return true;
121
0
  }
122
123
  std::string SingleCommandTextProtocol::getCommandInternal() const
124
6.15k
  {
125
6.15k
    size_t offset = getArgumentFieldOffset();
126
127
    // If there is no option remove trailing newline characters
128
6.15k
    if (offset == (m_DataLen - 1) && offset > 1)
129
0
      return std::string(reinterpret_cast<char*>(m_Data), offset - 1);
130
6.15k
    return std::string(reinterpret_cast<char*>(m_Data), offset);
131
6.15k
  }
132
133
  std::string SingleCommandTextProtocol::getCommandOptionInternal() const
134
2.63k
  {
135
2.63k
    size_t offset = getArgumentFieldOffset();
136
137
    // We don't want to get delimiter so add 1 for start unless there is no command,
138
2.63k
    int addition = offset ? 1 : 0;
139
140
    // Check if command-only packet (-2 to account for len/position comparison and size of CRLF)
141
2.63k
    if (offset != (m_DataLen - 2))
142
2.21k
    {
143
      // We don't want to trailing newline characters so remove 2 and remove addition from start point
144
2.21k
      auto option =
145
2.21k
          std::string(reinterpret_cast<char*>(&m_Data[offset + addition]), m_DataLen - (offset + 2 + addition));
146
147
      // Remove XXX- and XXX<SP> since they are delimiters of the protocol where XXX is the usually status code
148
      // Check RFC821 (SMTP) Section 3.3 and RFC959 (FTP) Section 4.2
149
2.21k
      auto code = getCommandInternal();
150
2.21k
      auto vDelim = std::vector<std::string>{ code + " ", code + "-" };
151
152
2.21k
      for (const auto& delim : vDelim)
153
4.42k
      {
154
4.42k
        size_t pos = 0;
155
4.94k
        while ((pos = option.find(delim, pos)) != std::string::npos)
156
526
        {
157
526
          option.replace(pos, delim.length(), "");
158
526
        }
159
4.42k
      }
160
2.21k
      return option;
161
2.21k
    }
162
420
    return "";
163
2.63k
  }
164
165
  bool SingleCommandTextProtocol::isMultiLine() const
166
0
  {
167
0
    return m_Data[getArgumentFieldOffset()] == ASCII_HYPHEN;
168
0
  }
169
170
  bool SingleCommandTextProtocol::isDataValid(const uint8_t* data, size_t dataSize)
171
10.2k
  {
172
10.2k
    if (data == nullptr || dataSize < MIN_PACKET_LENGTH)
173
70
      return false;
174
175
10.1k
    std::string payload = std::string(reinterpret_cast<const char*>(data), dataSize);
176
10.1k
    return payload.rfind("\r\n") == dataSize - 2;
177
10.2k
  }
178
179
}  // namespace pcpp