Coverage Report

Created: 2025-07-11 07:47

/src/PcapPlusPlus/Packet++/header/TextBasedProtocol.h
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
3
#include <map>
4
#include "Layer.h"
5
6
/// @file
7
8
namespace pcpp
9
{
10
11
/// End of header
12
67.8k
#define PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER ""
13
14
  class TextBasedProtocolMessage;
15
16
  // -------- Class HeaderField -----------------
17
18
  /// @class HeaderField
19
  /// A wrapper class for each text-based-protocol header field, e.g "Host", "Cookie", "Content-Length", "Via",
20
  /// "Call-ID", etc. Each field contains a name (e.g "Host") and a value (e.g "www.wikipedia.org"). The user can get
21
  /// and set both of them through dedicated methods. The separator between header fields is either CRLF ("\r\n\") or
22
  /// LF ("\n") in more rare cases, which means every HeaderField instance is responsible for wrapping and parsing a
23
  /// header field from the previous CRLF (not inclusive) until the next CRLF/LF (inclusive) A special case is with
24
  /// the end of a header, meaning 2 consecutive CRLFs ("\r\n\r\n") or consecutive LFs ("\n\n"). PcapPlusPlus treats
25
  /// the first CRLF/LF as part of the last field in the header, and the second CRLF is an HeaderField instance of its
26
  /// own which name and values are an empty string ("") or pcpp::PCPP_END_OF_TEXT_BASED_PROTOCOL_HEADER
27
  class HeaderField
28
  {
29
    friend class TextBasedProtocolMessage;
30
31
  public:
32
    ~HeaderField();
33
34
    /// A copy constructor that creates a new instance out of an existing HeaderField instance. The copied instance
35
    /// will not have shared resources with the original instance, meaning all members and properties are copied
36
    /// @param[in] other The original instance to copy from
37
    HeaderField(const HeaderField& other);
38
39
    /// Assignment operator for this class. This method copies the data from the other instance and will not share
40
    /// any resources with it. Also, if the instance already contains data it will be deleted or zeroed
41
    /// @param[in] other The instance to assign from
42
    /// @return A reference to the assignee
43
    HeaderField& operator=(const HeaderField& other);
44
45
    /// @return The field length in bytes, meaning count of all characters from the previous CRLF (not inclusive)
46
    /// until the next CRLF (inclusive) For example: the field "Host: www.wikipedia.org\r\n" will have the length of
47
    /// 25
48
    size_t getFieldSize() const
49
1.04M
    {
50
1.04M
      return m_FieldSize;
51
1.04M
    }
52
53
    /// @return The field name as string. Notice the return data is copied data, so changing it won't change the
54
    /// packet data
55
    std::string getFieldName() const;
56
57
    /// @return The field value as string. Notice the return data is copied data, so changing it won't change the
58
    /// packet data
59
    std::string getFieldValue() const;
60
61
    /// A setter for field value
62
    /// @param[in] newValue The new value to set to the field. Old value will be deleted
63
    /// @return True if setting the value was completed successfully, false otherwise
64
    bool setFieldValue(const std::string& newValue);
65
66
    /// Get an indication whether the field is a field that ends the header (meaning contain only CRLF - see class
67
    /// explanation)
68
    /// @return True if this is a end-of-header field, false otherwise
69
    bool isEndOfHeader() const
70
386k
    {
71
386k
      return m_IsEndOfHeaderField;
72
386k
    }
73
74
  private:
75
    HeaderField(const std::string& name, const std::string& value, char nameValueSeparator,
76
                bool spacesAllowedBetweenNameAndValue);
77
    HeaderField(TextBasedProtocolMessage* TextBasedProtocolMessage, int offsetInMessage, char nameValueSeparator,
78
                bool spacesAllowedBetweenNameAndValue);
79
80
    char* getData() const;
81
    void setNextField(HeaderField* nextField);
82
    HeaderField* getNextField() const;
83
    void initNewField(const std::string& name, const std::string& value);
84
    void attachToTextBasedProtocolMessage(TextBasedProtocolMessage* message, int fieldOffsetInMessage);
85
86
    uint8_t* m_NewFieldData;
87
    TextBasedProtocolMessage* m_TextBasedProtocolMessage;
88
    int m_NameOffsetInMessage;
89
    size_t m_FieldNameSize;
90
    int m_ValueOffsetInMessage;
91
    size_t m_FieldValueSize;
92
    size_t m_FieldSize;
93
    HeaderField* m_NextField;
94
    bool m_IsEndOfHeaderField;
95
    char m_NameValueSeparator;
96
    bool m_SpacesAllowedBetweenNameAndValue;
97
  };
98
99
  // -------- Class TextBasedProtocolMessage -----------------
100
101
  /// @class TextBasedProtocolMessage
102
  /// An abstract base class that wraps text-based-protocol header layers (both requests and responses). It is the
103
  /// base class for all those layers. This class is not meant to be instantiated, hence the protected c'tor
104
  class TextBasedProtocolMessage : public Layer
105
  {
106
    friend class HeaderField;
107
108
  public:
109
    ~TextBasedProtocolMessage() override;
110
111
    /// Get a pointer to a header field by name. The search is case insensitive, meaning if a field with name "Host"
112
    /// exists and the fieldName parameter is "host" (all letter are lower case), this method will return a pointer
113
    /// to "Host" field
114
    /// @param[in] fieldName The field name
115
    /// @param[in] index Optional parameter. If the field name appears more than once, this parameter will indicate
116
    /// which field to get. The default value is 0 (get the first appearance of the field name as appears on the
117
    /// packet)
118
    /// @return A pointer to an HeaderField instance, or nullptr if field doesn't exist
119
    HeaderField* getFieldByName(std::string fieldName, int index = 0) const;
120
121
    /// @return A pointer to the first header field exists in this message, or nullptr if no such field exists
122
    HeaderField* getFirstField() const
123
916
    {
124
916
      return m_FieldList;
125
916
    }
126
127
    /// Get the field which appears after a certain field
128
    /// @param[in] prevField A pointer to the field
129
    /// @return The field after prevField or nullptr if prevField is the last field. If prevField is nullptr, this
130
    /// method will return nullptr
131
    HeaderField* getNextField(HeaderField* prevField) const
132
0
    {
133
0
      if (prevField != nullptr)
134
0
        return prevField->getNextField();
135
0
      else
136
0
        return nullptr;
137
0
    }
138
139
    /// @return The number of header fields currently in the layer (not including CRLF at the end of the header)
140
    int getFieldCount() const;
141
142
    /// Add a new header field to this message. This field will be added last (before the end-of-header field)
143
    /// @param[in] fieldName The field name
144
    /// @param[in] fieldValue The field value
145
    /// @return A pointer to the newly created header field, or nullptr if the field could not be created
146
    virtual HeaderField* addField(const std::string& fieldName, const std::string& fieldValue);
147
148
    /// Add a new header field to this message. This field will be added last (before the end-of-header field)
149
    /// @param[in] newField The header field to add
150
    /// @return A pointer to the newly created header field, or nullptr if the field could not be created
151
    virtual HeaderField* addField(const HeaderField& newField);
152
153
    /// Add the special end-of-header field (see the explanation in HeaderField)
154
    /// @return A pointer to the newly created header field, or nullptr if the field could not be created
155
    HeaderField* addEndOfHeader();
156
157
    /// Insert a new field after an existing field
158
    /// @param[in] prevField A pointer to the existing field. If it's nullptr the new field will be added as first
159
    /// field
160
    /// @param[in] fieldName The field name
161
    /// @param[in] fieldValue The field value
162
    /// @return A pointer to the newly created header field, or nullptr if the field could not be created
163
    virtual HeaderField* insertField(HeaderField* prevField, const std::string& fieldName,
164
                                     const std::string& fieldValue);
165
166
    /// Insert a new field after an existing field
167
    /// @param[in] prevFieldName A name of an existing field. If the field doesn't exist nullptr will be returned.
168
    /// If field name is empty ('') the new field will be added as first field
169
    /// @param[in] fieldName The field name
170
    /// @param[in] fieldValue The field value
171
    /// @return A pointer to the newly created header field, or nullptr if the field could not be created
172
    virtual HeaderField* insertField(std::string prevFieldName, const std::string& fieldName,
173
                                     const std::string& fieldValue);
174
175
    /// Insert a new field after an existing field
176
    /// @param[in] prevField A pointer to the existing field
177
    /// @param[in] newField The header field to add
178
    /// @return A pointer to the newly created header field, or nullptr if the field could not be created
179
    virtual HeaderField* insertField(HeaderField* prevField, const HeaderField& newField);
180
181
    /// Remove a field from the message
182
    /// @param[in] fieldToRemove A pointer to the field that should be removed
183
    /// @return True if the field was removed successfully, or false otherwise (for example: if fieldToRemove is
184
    /// nullptr, if it doesn't exist in the message, or if the removal failed)
185
    bool removeField(HeaderField* fieldToRemove);
186
187
    /// Remove a field from the message
188
    /// @param[in] fieldName The name of the field that should be removed
189
    /// @param[in] index Optional parameter. If the field name appears more than once, this parameter will indicate
190
    /// which field to remove. The default value is 0 (remove the first appearance of the field name as appears on
191
    /// the packet)
192
    /// @return True if the field was removed successfully, or false otherwise (for example: if fieldName doesn't
193
    /// exist in the message, or if the removal failed)
194
    bool removeField(std::string fieldName, int index = 0);
195
196
    /// Indicate whether the header is complete (ending with end-of-header "\r\n\r\n" or "\n\n") or spread over more
197
    /// packets
198
    /// @return True if the header is complete or false if not
199
    bool isHeaderComplete() const;
200
201
    // implement Layer's abstract methods
202
203
    /// Currently set only PayloadLayer for the rest of the data
204
    void parseNextLayer() override;
205
206
    /// @return The message length
207
    size_t getHeaderLen() const override;
208
209
    /// Does nothing for this class
210
    void computeCalculateFields() override;
211
212
  protected:
213
    TextBasedProtocolMessage(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet,
214
                             ProtocolType protocol);
215
    TextBasedProtocolMessage() : m_FieldList(nullptr), m_LastField(nullptr), m_FieldsOffset(0)
216
0
    {}
217
218
    // copy c'tor
219
    TextBasedProtocolMessage(const TextBasedProtocolMessage& other);
220
    TextBasedProtocolMessage& operator=(const TextBasedProtocolMessage& other);
221
222
    void copyDataFrom(const TextBasedProtocolMessage& other);
223
224
    void parseFields();
225
    void shiftFieldsOffset(HeaderField* fromField, int numOfBytesToShift);
226
227
    // abstract methods
228
    virtual char getHeaderFieldNameValueSeparator() const = 0;
229
    virtual bool spacesAllowedBetweenHeaderFieldNameAndValue() const = 0;
230
231
    HeaderField* m_FieldList;
232
    HeaderField* m_LastField;
233
    int m_FieldsOffset;
234
    std::multimap<std::string, HeaderField*> m_FieldNameToFieldMap;
235
  };
236
}  // namespace pcpp