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