/src/PcapPlusPlus/Packet++/src/HttpLayer.cpp
Line | Count | Source |
1 | 36.1k | #define LOG_MODULE PacketLogModuleHttpLayer |
2 | | |
3 | | #include "Logger.h" |
4 | | #include "GeneralUtils.h" |
5 | | #include "HttpLayer.h" |
6 | | #include <cstring> |
7 | | #include <algorithm> |
8 | | #include <exception> |
9 | | #include <unordered_map> |
10 | | |
11 | | namespace pcpp |
12 | | { |
13 | | // -------- Class HttpMessage ----------------- |
14 | | |
15 | | HeaderField* HttpMessage::addField(const std::string& fieldName, const std::string& fieldValue) |
16 | 0 | { |
17 | 0 | if (getFieldByName(fieldName) != nullptr) |
18 | 0 | { |
19 | 0 | PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!"); |
20 | 0 | return nullptr; |
21 | 0 | } |
22 | | |
23 | 0 | return TextBasedProtocolMessage::addField(fieldName, fieldValue); |
24 | 0 | } |
25 | | |
26 | | HeaderField* HttpMessage::addField(const HeaderField& newField) |
27 | 0 | { |
28 | 0 | if (getFieldByName(newField.getFieldName()) != nullptr) |
29 | 0 | { |
30 | 0 | PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!"); |
31 | 0 | return nullptr; |
32 | 0 | } |
33 | | |
34 | 0 | return TextBasedProtocolMessage::addField(newField); |
35 | 0 | } |
36 | | |
37 | | HeaderField* HttpMessage::insertField(HeaderField* prevField, const std::string& fieldName, |
38 | | const std::string& fieldValue) |
39 | 0 | { |
40 | 0 | if (getFieldByName(fieldName) != nullptr) |
41 | 0 | { |
42 | 0 | PCPP_LOG_ERROR("Field '" << fieldName << "' already exists!"); |
43 | 0 | return nullptr; |
44 | 0 | } |
45 | | |
46 | 0 | return TextBasedProtocolMessage::insertField(prevField, fieldName, fieldValue); |
47 | 0 | } |
48 | | |
49 | | HeaderField* HttpMessage::insertField(HeaderField* prevField, const HeaderField& newField) |
50 | 0 | { |
51 | 0 | if (getFieldByName(newField.getFieldName()) != nullptr) |
52 | 0 | { |
53 | 0 | PCPP_LOG_ERROR("Field '" << newField.getFieldName() << "' already exists!"); |
54 | 0 | return nullptr; |
55 | 0 | } |
56 | | |
57 | 0 | return TextBasedProtocolMessage::insertField(prevField, newField); |
58 | 0 | } |
59 | | |
60 | | // -------- Class HttpRequestLayer ----------------- |
61 | | |
62 | | HttpRequestLayer::HttpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) |
63 | 83.6k | : HttpMessage(data, dataLen, prevLayer, packet, HTTPRequest) |
64 | 83.6k | { |
65 | 83.6k | m_FirstLine = new HttpRequestFirstLine(this); |
66 | 83.6k | m_FieldsOffset = m_FirstLine->getSize(); |
67 | 83.6k | parseFields(); |
68 | 83.6k | } |
69 | | |
70 | | HttpRequestLayer::HttpRequestLayer(HttpMethod method, const std::string& uri, HttpVersion version) |
71 | 0 | { |
72 | 0 | m_Protocol = HTTPRequest; |
73 | 0 | m_FirstLine = new HttpRequestFirstLine(this, method, version, uri); |
74 | 0 | m_FieldsOffset = m_FirstLine->getSize(); |
75 | 0 | } |
76 | | |
77 | 0 | HttpRequestLayer::HttpRequestLayer(const HttpRequestLayer& other) : HttpMessage(other) |
78 | 0 | { |
79 | 0 | m_FirstLine = new HttpRequestFirstLine(this); |
80 | 0 | } |
81 | | |
82 | | HttpRequestLayer& HttpRequestLayer::operator=(const HttpRequestLayer& other) |
83 | 0 | { |
84 | 0 | HttpMessage::operator=(other); |
85 | |
|
86 | 0 | if (m_FirstLine != nullptr) |
87 | 0 | delete m_FirstLine; |
88 | |
|
89 | 0 | m_FirstLine = new HttpRequestFirstLine(this); |
90 | |
|
91 | 0 | return *this; |
92 | 0 | } |
93 | | |
94 | | std::string HttpRequestLayer::getUrl() const |
95 | 0 | { |
96 | 0 | HeaderField* hostField = getFieldByName(PCPP_HTTP_HOST_FIELD); |
97 | 0 | if (hostField == nullptr) |
98 | 0 | return m_FirstLine->getUri(); |
99 | | |
100 | 0 | return hostField->getFieldValue() + m_FirstLine->getUri(); |
101 | 0 | } |
102 | | |
103 | | HttpRequestLayer::~HttpRequestLayer() |
104 | 83.6k | { |
105 | 83.6k | delete m_FirstLine; |
106 | 83.6k | } |
107 | | |
108 | | std::string HttpRequestLayer::toString() const |
109 | 32.3k | { |
110 | 32.3k | static const int maxLengthToPrint = 120; |
111 | 32.3k | std::string result = "HTTP request, "; |
112 | 32.3k | int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line |
113 | 32.3k | if (size <= 0) |
114 | 0 | { |
115 | 0 | result += std::string("CORRUPT DATA"); |
116 | 0 | return result; |
117 | 0 | } |
118 | 32.3k | if (size <= maxLengthToPrint) |
119 | 23.5k | { |
120 | 23.5k | char* firstLine = new char[size + 1]; |
121 | 23.5k | strncpy(firstLine, reinterpret_cast<char*>(m_Data), size); |
122 | 23.5k | firstLine[size] = 0; |
123 | 23.5k | result += std::string(firstLine); |
124 | 23.5k | delete[] firstLine; |
125 | 23.5k | } |
126 | 8.75k | else |
127 | 8.75k | { |
128 | 8.75k | char firstLine[maxLengthToPrint + 1]; |
129 | 8.75k | strncpy(firstLine, reinterpret_cast<char*>(m_Data), maxLengthToPrint - 3); |
130 | 8.75k | firstLine[maxLengthToPrint - 3] = '.'; |
131 | 8.75k | firstLine[maxLengthToPrint - 2] = '.'; |
132 | 8.75k | firstLine[maxLengthToPrint - 1] = '.'; |
133 | 8.75k | firstLine[maxLengthToPrint] = 0; |
134 | 8.75k | result += std::string(firstLine); |
135 | 8.75k | } |
136 | | |
137 | 32.3k | return result; |
138 | 32.3k | } |
139 | | |
140 | | // -------- Class HttpRequestFirstLine ----------------- |
141 | | |
142 | | const std::string MethodEnumToString[9] = { "GET", "HEAD", "POST", "PUT", "DELETE", |
143 | | "TRACE", "OPTIONS", "CONNECT", "PATCH" }; |
144 | | |
145 | | const std::unordered_map<std::string, HttpRequestLayer::HttpMethod> HttpMethodStringToEnum{ |
146 | | { "GET", HttpRequestLayer::HttpMethod::HttpGET }, |
147 | | { "HEAD", HttpRequestLayer::HttpMethod::HttpHEAD }, |
148 | | { "POST", HttpRequestLayer::HttpMethod::HttpPOST }, |
149 | | { "PUT", HttpRequestLayer::HttpMethod::HttpPUT }, |
150 | | { "DELETE", HttpRequestLayer::HttpMethod::HttpDELETE }, |
151 | | { "TRACE", HttpRequestLayer::HttpMethod::HttpTRACE }, |
152 | | { "OPTIONS", HttpRequestLayer::HttpMethod::HttpOPTIONS }, |
153 | | { "CONNECT", HttpRequestLayer::HttpMethod::HttpCONNECT }, |
154 | | { "PATCH", HttpRequestLayer::HttpMethod::HttpPATCH } |
155 | | }; |
156 | | |
157 | | const std::string VersionEnumToString[3] = { "0.9", "1.0", "1.1" }; |
158 | | |
159 | | // clang-format off |
160 | | const std::unordered_map<std::string, HttpVersion> HttpVersionStringToEnum{ |
161 | | { "0.9", HttpVersion::ZeroDotNine }, |
162 | | { "1.0", HttpVersion::OneDotZero }, |
163 | | { "1.1", HttpVersion::OneDotOne } |
164 | | }; |
165 | | // clang-format on |
166 | 83.6k | HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest) : m_HttpRequest(httpRequest) |
167 | 83.6k | { |
168 | 83.6k | m_Method = parseMethod((char*)m_HttpRequest->m_Data, m_HttpRequest->getDataLen()); |
169 | 83.6k | if (m_Method == HttpRequestLayer::HttpMethodUnknown) |
170 | 0 | { |
171 | 0 | m_UriOffset = -1; |
172 | 0 | PCPP_LOG_DEBUG("Couldn't resolve HTTP request method"); |
173 | 0 | m_IsComplete = false; |
174 | 0 | m_Version = HttpVersionUnknown; |
175 | 0 | m_VersionOffset = -1; |
176 | 0 | m_FirstLineEndOffset = m_HttpRequest->getDataLen(); |
177 | 0 | return; |
178 | 0 | } |
179 | 83.6k | else |
180 | 83.6k | { |
181 | 83.6k | m_UriOffset = MethodEnumToString[m_Method].length() + 1; |
182 | 83.6k | } |
183 | | |
184 | 83.6k | parseVersion(); |
185 | 83.6k | if (m_VersionOffset < 0) |
186 | 18.1k | { |
187 | 18.1k | m_IsComplete = false; |
188 | 18.1k | m_FirstLineEndOffset = m_HttpRequest->getDataLen(); |
189 | 18.1k | return; |
190 | 18.1k | } |
191 | | |
192 | 65.5k | char* endOfFirstLine; |
193 | 65.5k | if ((endOfFirstLine = (char*)memchr((char*)(m_HttpRequest->m_Data + m_VersionOffset), '\n', |
194 | 65.5k | m_HttpRequest->m_DataLen - (size_t)m_VersionOffset)) != nullptr) |
195 | 64.8k | { |
196 | 64.8k | m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpRequest->m_Data + 1; |
197 | 64.8k | m_IsComplete = true; |
198 | 64.8k | } |
199 | 738 | else |
200 | 738 | { |
201 | 738 | m_FirstLineEndOffset = m_HttpRequest->getDataLen(); |
202 | 738 | m_IsComplete = false; |
203 | 738 | } |
204 | | |
205 | 65.5k | if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer)) |
206 | 0 | { |
207 | 0 | std::string method = |
208 | 0 | m_Method == HttpRequestLayer::HttpMethodUnknown ? "Unknown" : MethodEnumToString[m_Method]; |
209 | 0 | PCPP_LOG_DEBUG("Method='" << method << "'; " |
210 | 0 | << "HTTP version='" << VersionEnumToString[m_Version] << "'; " |
211 | 0 | << "URI='" << getUri() << "'"); |
212 | 0 | } |
213 | 65.5k | } |
214 | | |
215 | | HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest, HttpRequestLayer::HttpMethod method, |
216 | | HttpVersion version, const std::string& uri) |
217 | 0 | { |
218 | 0 | try // throw(HttpRequestFirstLineException) |
219 | 0 | { |
220 | 0 | if (method == HttpRequestLayer::HttpMethodUnknown) |
221 | 0 | { |
222 | 0 | m_Exception.setMessage("Method supplied was HttpMethodUnknown"); |
223 | 0 | throw m_Exception; |
224 | 0 | } |
225 | | |
226 | 0 | if (version == HttpVersionUnknown) |
227 | 0 | { |
228 | 0 | m_Exception.setMessage("Version supplied was HttpVersionUnknown"); |
229 | 0 | throw m_Exception; |
230 | 0 | } |
231 | | |
232 | 0 | m_HttpRequest = httpRequest; |
233 | |
|
234 | 0 | m_Method = method; |
235 | 0 | m_Version = version; |
236 | |
|
237 | 0 | std::string firstLine = |
238 | 0 | MethodEnumToString[m_Method] + " " + uri + " " + "HTTP/" + VersionEnumToString[m_Version] + "\r\n"; |
239 | |
|
240 | 0 | m_UriOffset = MethodEnumToString[m_Method].length() + 1; |
241 | 0 | m_FirstLineEndOffset = firstLine.length(); |
242 | 0 | m_VersionOffset = m_UriOffset + uri.length() + 6; |
243 | | |
244 | | // TODO: Alloc Data. |
245 | 0 | m_HttpRequest->m_DataLen = firstLine.length(); |
246 | 0 | m_HttpRequest->m_Data = new uint8_t[m_HttpRequest->m_DataLen]; |
247 | 0 | memcpy(m_HttpRequest->m_Data, firstLine.c_str(), m_HttpRequest->m_DataLen); |
248 | |
|
249 | 0 | m_IsComplete = true; |
250 | 0 | } |
251 | 0 | catch (const HttpRequestFirstLineException&) |
252 | 0 | { |
253 | 0 | throw; |
254 | 0 | } |
255 | 0 | catch (...) |
256 | 0 | { |
257 | 0 | std::terminate(); |
258 | 0 | } |
259 | 0 | } |
260 | | |
261 | | HttpRequestLayer::HttpMethod HttpRequestFirstLine::parseMethod(const char* data, size_t dataLen) |
262 | 192k | { |
263 | 192k | if (!data || dataLen < 4) |
264 | 1.81k | { |
265 | 1.81k | return HttpRequestLayer::HttpMethodUnknown; |
266 | 1.81k | } |
267 | | |
268 | 191k | size_t spaceIndex = 0; |
269 | 1.80M | while (spaceIndex < dataLen && data[spaceIndex] != ' ') |
270 | 1.61M | { |
271 | 1.61M | spaceIndex++; |
272 | 1.61M | } |
273 | | |
274 | 191k | if (spaceIndex == 0 || spaceIndex == dataLen) |
275 | 3.88k | { |
276 | 3.88k | return HttpRequestLayer::HttpMethodUnknown; |
277 | 3.88k | } |
278 | | |
279 | 187k | auto methodAdEnum = HttpMethodStringToEnum.find(std::string(data, data + spaceIndex)); |
280 | 187k | if (methodAdEnum == HttpMethodStringToEnum.end()) |
281 | 19.8k | { |
282 | 19.8k | return HttpRequestLayer::HttpMethodUnknown; |
283 | 19.8k | } |
284 | 167k | return methodAdEnum->second; |
285 | 187k | } |
286 | | |
287 | | void HttpRequestFirstLine::parseVersion() |
288 | 83.6k | { |
289 | 83.6k | char* data = (char*)(m_HttpRequest->m_Data + m_UriOffset); |
290 | 83.6k | char* verPos = cross_platform_memmem(data, m_HttpRequest->getDataLen() - m_UriOffset, " HTTP/", 6); |
291 | 83.6k | if (verPos == nullptr) |
292 | 18.1k | { |
293 | 18.1k | m_Version = HttpVersionUnknown; |
294 | 18.1k | m_VersionOffset = -1; |
295 | 18.1k | return; |
296 | 18.1k | } |
297 | | |
298 | | // verify packet doesn't end before the version, meaning still left place for " HTTP/x.y" (9 chars) |
299 | 65.5k | std::ptrdiff_t actualLen = verPos + 9 - (char*)m_HttpRequest->m_Data; |
300 | 65.5k | if (static_cast<size_t>(actualLen) > m_HttpRequest->getDataLen()) |
301 | 0 | { |
302 | 0 | m_Version = HttpVersionUnknown; |
303 | 0 | m_VersionOffset = -1; |
304 | 0 | return; |
305 | 0 | } |
306 | | |
307 | | // skip " HTTP/" (6 chars) |
308 | 65.5k | verPos += 6; |
309 | 65.5k | auto versionAsEnum = HttpVersionStringToEnum.find(std::string(verPos, verPos + 3)); |
310 | 65.5k | if (versionAsEnum == HttpVersionStringToEnum.end()) |
311 | 9.99k | { |
312 | 9.99k | m_Version = HttpVersionUnknown; |
313 | 9.99k | } |
314 | 55.5k | else |
315 | 55.5k | { |
316 | 55.5k | m_Version = versionAsEnum->second; |
317 | 55.5k | } |
318 | | |
319 | 65.5k | m_VersionOffset = verPos - (char*)m_HttpRequest->m_Data; |
320 | 65.5k | } |
321 | | |
322 | | bool HttpRequestFirstLine::setMethod(HttpRequestLayer::HttpMethod newMethod) |
323 | 0 | { |
324 | 0 | if (newMethod == HttpRequestLayer::HttpMethodUnknown) |
325 | 0 | { |
326 | 0 | PCPP_LOG_ERROR("Requested method is HttpMethodUnknown"); |
327 | 0 | return false; |
328 | 0 | } |
329 | | |
330 | | // extend or shorten layer |
331 | 0 | int lengthDifference = MethodEnumToString[newMethod].length() - MethodEnumToString[m_Method].length(); |
332 | 0 | if (lengthDifference > 0) |
333 | 0 | { |
334 | 0 | if (!m_HttpRequest->extendLayer(0, lengthDifference)) |
335 | 0 | { |
336 | 0 | PCPP_LOG_ERROR("Cannot change layer size"); |
337 | 0 | return false; |
338 | 0 | } |
339 | 0 | } |
340 | 0 | else if (lengthDifference < 0) |
341 | 0 | { |
342 | 0 | if (!m_HttpRequest->shortenLayer(0, 0 - lengthDifference)) |
343 | 0 | { |
344 | 0 | PCPP_LOG_ERROR("Cannot change layer size"); |
345 | 0 | return false; |
346 | 0 | } |
347 | 0 | } |
348 | | |
349 | 0 | if (lengthDifference != 0) |
350 | 0 | m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference); |
351 | |
|
352 | 0 | memcpy(m_HttpRequest->m_Data, MethodEnumToString[newMethod].c_str(), MethodEnumToString[newMethod].length()); |
353 | |
|
354 | 0 | m_Method = newMethod; |
355 | 0 | m_UriOffset += lengthDifference; |
356 | 0 | m_VersionOffset += lengthDifference; |
357 | |
|
358 | 0 | return true; |
359 | 0 | } |
360 | | |
361 | | std::string HttpRequestFirstLine::getUri() const |
362 | 0 | { |
363 | 0 | std::string result; |
364 | 0 | if (m_UriOffset != -1 && m_VersionOffset != -1) |
365 | 0 | result.assign((const char*)m_HttpRequest->m_Data + m_UriOffset, m_VersionOffset - 6 - m_UriOffset); |
366 | | |
367 | | // else first line is illegal, return empty string |
368 | |
|
369 | 0 | return result; |
370 | 0 | } |
371 | | |
372 | | bool HttpRequestFirstLine::setUri(std::string newUri) |
373 | 0 | { |
374 | | // make sure the new URI begins with "/" |
375 | 0 | if (newUri.compare(0, 1, "/") != 0) |
376 | 0 | newUri = "/" + newUri; |
377 | | |
378 | | // extend or shorten layer |
379 | 0 | std::string currentUri = getUri(); |
380 | 0 | int lengthDifference = newUri.length() - currentUri.length(); |
381 | 0 | if (lengthDifference > 0) |
382 | 0 | { |
383 | 0 | if (!m_HttpRequest->extendLayer(m_UriOffset, lengthDifference)) |
384 | 0 | { |
385 | 0 | PCPP_LOG_ERROR("Cannot change layer size"); |
386 | 0 | return false; |
387 | 0 | } |
388 | 0 | } |
389 | 0 | else if (lengthDifference < 0) |
390 | 0 | { |
391 | 0 | if (!m_HttpRequest->shortenLayer(m_UriOffset, 0 - lengthDifference)) |
392 | 0 | { |
393 | 0 | PCPP_LOG_ERROR("Cannot change layer size"); |
394 | 0 | return false; |
395 | 0 | } |
396 | 0 | } |
397 | | |
398 | 0 | if (lengthDifference != 0) |
399 | 0 | m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference); |
400 | |
|
401 | 0 | memcpy(m_HttpRequest->m_Data + m_UriOffset, newUri.c_str(), newUri.length()); |
402 | |
|
403 | 0 | m_VersionOffset += lengthDifference; |
404 | |
|
405 | 0 | return true; |
406 | 0 | } |
407 | | |
408 | | void HttpRequestFirstLine::setVersion(HttpVersion newVersion) |
409 | 0 | { |
410 | 0 | if (m_VersionOffset == -1) |
411 | 0 | return; |
412 | | |
413 | 0 | if (newVersion == HttpVersionUnknown) |
414 | 0 | return; |
415 | | |
416 | 0 | char* verPos = (char*)(m_HttpRequest->m_Data + m_VersionOffset); |
417 | 0 | memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3); |
418 | |
|
419 | 0 | m_Version = newVersion; |
420 | 0 | } |
421 | | |
422 | | // -------- Class HttpResponseLayer ----------------- |
423 | | |
424 | | static const std::unordered_map<int, HttpResponseStatusCode> intStatusCodeMap = { |
425 | | { 100, HttpResponseStatusCode::Http100Continue }, |
426 | | { 101, HttpResponseStatusCode::Http101SwitchingProtocols }, |
427 | | { 102, HttpResponseStatusCode::Http102Processing }, |
428 | | { 103, HttpResponseStatusCode::Http103EarlyHints }, |
429 | | { 200, HttpResponseStatusCode::Http200OK }, |
430 | | { 201, HttpResponseStatusCode::Http201Created }, |
431 | | { 202, HttpResponseStatusCode::Http202Accepted }, |
432 | | { 203, HttpResponseStatusCode::Http203NonAuthoritativeInformation }, |
433 | | { 204, HttpResponseStatusCode::Http204NoContent }, |
434 | | { 205, HttpResponseStatusCode::Http205ResetContent }, |
435 | | { 206, HttpResponseStatusCode::Http206PartialContent }, |
436 | | { 207, HttpResponseStatusCode::Http207MultiStatus }, |
437 | | { 208, HttpResponseStatusCode::Http208AlreadyReported }, |
438 | | { 226, HttpResponseStatusCode::Http226IMUsed }, |
439 | | { 300, HttpResponseStatusCode::Http300MultipleChoices }, |
440 | | { 301, HttpResponseStatusCode::Http301MovedPermanently }, |
441 | | { 302, HttpResponseStatusCode::Http302 }, |
442 | | { 303, HttpResponseStatusCode::Http303SeeOther }, |
443 | | { 304, HttpResponseStatusCode::Http304NotModified }, |
444 | | { 305, HttpResponseStatusCode::Http305UseProxy }, |
445 | | { 306, HttpResponseStatusCode::Http306SwitchProxy }, |
446 | | { 307, HttpResponseStatusCode::Http307TemporaryRedirect }, |
447 | | { 308, HttpResponseStatusCode::Http308PermanentRedirect }, |
448 | | { 400, HttpResponseStatusCode::Http400BadRequest }, |
449 | | { 401, HttpResponseStatusCode::Http401Unauthorized }, |
450 | | { 402, HttpResponseStatusCode::Http402PaymentRequired }, |
451 | | { 403, HttpResponseStatusCode::Http403Forbidden }, |
452 | | { 404, HttpResponseStatusCode::Http404NotFound }, |
453 | | { 405, HttpResponseStatusCode::Http405MethodNotAllowed }, |
454 | | { 406, HttpResponseStatusCode::Http406NotAcceptable }, |
455 | | { 407, HttpResponseStatusCode::Http407ProxyAuthenticationRequired }, |
456 | | { 408, HttpResponseStatusCode::Http408RequestTimeout }, |
457 | | { 409, HttpResponseStatusCode::Http409Conflict }, |
458 | | { 410, HttpResponseStatusCode::Http410Gone }, |
459 | | { 411, HttpResponseStatusCode::Http411LengthRequired }, |
460 | | { 412, HttpResponseStatusCode::Http412PreconditionFailed }, |
461 | | { 413, HttpResponseStatusCode::Http413RequestEntityTooLarge }, |
462 | | { 414, HttpResponseStatusCode::Http414RequestURITooLong }, |
463 | | { 415, HttpResponseStatusCode::Http415UnsupportedMediaType }, |
464 | | { 416, HttpResponseStatusCode::Http416RequestedRangeNotSatisfiable }, |
465 | | { 417, HttpResponseStatusCode::Http417ExpectationFailed }, |
466 | | { 418, HttpResponseStatusCode::Http418ImATeapot }, |
467 | | { 419, HttpResponseStatusCode::Http419AuthenticationTimeout }, |
468 | | { 420, HttpResponseStatusCode::Http420 }, |
469 | | { 421, HttpResponseStatusCode::Http421MisdirectedRequest }, |
470 | | { 422, HttpResponseStatusCode::Http422UnprocessableEntity }, |
471 | | { 423, HttpResponseStatusCode::Http423Locked }, |
472 | | { 424, HttpResponseStatusCode::Http424FailedDependency }, |
473 | | { 425, HttpResponseStatusCode::Http425TooEarly }, |
474 | | { 426, HttpResponseStatusCode::Http426UpgradeRequired }, |
475 | | { 428, HttpResponseStatusCode::Http428PreconditionRequired }, |
476 | | { 429, HttpResponseStatusCode::Http429TooManyRequests }, |
477 | | { 431, HttpResponseStatusCode::Http431RequestHeaderFieldsTooLarge }, |
478 | | { 440, HttpResponseStatusCode::Http440LoginTimeout }, |
479 | | { 444, HttpResponseStatusCode::Http444NoResponse }, |
480 | | { 449, HttpResponseStatusCode::Http449RetryWith }, |
481 | | { 450, HttpResponseStatusCode::Http450BlockedByWindowsParentalControls }, |
482 | | { 451, HttpResponseStatusCode::Http451 }, |
483 | | { 494, HttpResponseStatusCode::Http494RequestHeaderTooLarge }, |
484 | | { 495, HttpResponseStatusCode::Http495CertError }, |
485 | | { 496, HttpResponseStatusCode::Http496NoCert }, |
486 | | { 497, HttpResponseStatusCode::Http497HTTPtoHTTPS }, |
487 | | { 498, HttpResponseStatusCode::Http498TokenExpiredInvalid }, |
488 | | { 499, HttpResponseStatusCode::Http499 }, |
489 | | { 500, HttpResponseStatusCode::Http500InternalServerError }, |
490 | | { 501, HttpResponseStatusCode::Http501NotImplemented }, |
491 | | { 502, HttpResponseStatusCode::Http502BadGateway }, |
492 | | { 503, HttpResponseStatusCode::Http503ServiceUnavailable }, |
493 | | { 504, HttpResponseStatusCode::Http504GatewayTimeout }, |
494 | | { 505, HttpResponseStatusCode::Http505HTTPVersionNotSupported }, |
495 | | { 506, HttpResponseStatusCode::Http506VariantAlsoNegotiates }, |
496 | | { 507, HttpResponseStatusCode::Http507InsufficientStorage }, |
497 | | { 508, HttpResponseStatusCode::Http508LoopDetected }, |
498 | | { 509, HttpResponseStatusCode::Http509BandwidthLimitExceeded }, |
499 | | { 510, HttpResponseStatusCode::Http510NotExtended }, |
500 | | { 511, HttpResponseStatusCode::Http511NetworkAuthenticationRequired }, |
501 | | { 520, HttpResponseStatusCode::Http520OriginError }, |
502 | | { 521, HttpResponseStatusCode::Http521WebServerIsDown }, |
503 | | { 522, HttpResponseStatusCode::Http522ConnectionTimedOut }, |
504 | | { 523, HttpResponseStatusCode::Http523ProxyDeclinedRequest }, |
505 | | { 524, HttpResponseStatusCode::Http524aTimeoutOccurred }, |
506 | | { 598, HttpResponseStatusCode::Http598NetworkReadTimeoutError }, |
507 | | { 599, HttpResponseStatusCode::Http599NetworkConnectTimeoutError }, |
508 | | }; |
509 | | |
510 | | HttpResponseStatusCode::HttpResponseStatusCode(const int& statusCodeNumber, const std::string& statusMessage) |
511 | 52.9k | { |
512 | 52.9k | if (statusMessage != "") |
513 | 52.9k | { |
514 | 52.9k | m_CustomizedMessage = statusMessage; |
515 | 52.9k | } |
516 | | |
517 | 52.9k | if (intStatusCodeMap.find(statusCodeNumber) != intStatusCodeMap.end()) |
518 | 52.3k | { |
519 | 52.3k | m_Value = intStatusCodeMap.at(statusCodeNumber); |
520 | 52.3k | return; |
521 | 52.3k | } |
522 | | |
523 | 636 | if (statusCodeNumber >= 100 && statusCodeNumber <= 199) |
524 | 185 | { |
525 | 185 | m_Value = HttpResponseStatusCode::HttpStatus1xxCodeUnknown; |
526 | 185 | } |
527 | 451 | else if (statusCodeNumber >= 200 && statusCodeNumber <= 299) |
528 | 45 | { |
529 | 45 | m_Value = HttpResponseStatusCode::HttpStatus2xxCodeUnknown; |
530 | 45 | } |
531 | 406 | else if (statusCodeNumber >= 300 && statusCodeNumber <= 399) |
532 | 0 | { |
533 | 0 | m_Value = HttpResponseStatusCode::HttpStatus3xxCodeUnknown; |
534 | 0 | } |
535 | 406 | else if (statusCodeNumber >= 400 && statusCodeNumber <= 499) |
536 | 0 | { |
537 | 0 | m_Value = HttpResponseStatusCode::HttpStatus4xxCodeUnknown; |
538 | 0 | } |
539 | 406 | else if (statusCodeNumber >= 500 && statusCodeNumber <= 599) |
540 | 0 | { |
541 | 0 | m_Value = HttpResponseStatusCode::HttpStatus5xxCodeUnknown; |
542 | 0 | } |
543 | 636 | } |
544 | | |
545 | | /// @struct HttpResponseStatusCodeHash |
546 | | /// @brief The helper structure for hash HttpResponseStatusCode while using std::unordered_map |
547 | | struct HttpResponseStatusCodeHash |
548 | | { |
549 | | size_t operator()(const HttpResponseStatusCode& status) const |
550 | 356 | { |
551 | 356 | return static_cast<int>(status); |
552 | 356 | } |
553 | | }; |
554 | | |
555 | | static const std::unordered_map<HttpResponseStatusCode, std::string, HttpResponseStatusCodeHash> |
556 | | statusCodeExplanationStringMap = { |
557 | | { HttpResponseStatusCode::Http100Continue, "Continue" }, |
558 | | { HttpResponseStatusCode::Http101SwitchingProtocols, "Switching Protocols" }, |
559 | | { HttpResponseStatusCode::Http102Processing, "Processing" }, |
560 | | { HttpResponseStatusCode::Http103EarlyHints, "Early Hints" }, |
561 | | { HttpResponseStatusCode::Http200OK, "OK" }, |
562 | | { HttpResponseStatusCode::Http201Created, "Created" }, |
563 | | { HttpResponseStatusCode::Http202Accepted, "Accepted" }, |
564 | | { HttpResponseStatusCode::Http203NonAuthoritativeInformation, "Non-Authoritative Information" }, |
565 | | { HttpResponseStatusCode::Http204NoContent, "No Content" }, |
566 | | { HttpResponseStatusCode::Http205ResetContent, "Reset Content" }, |
567 | | { HttpResponseStatusCode::Http206PartialContent, "Partial Content" }, |
568 | | { HttpResponseStatusCode::Http207MultiStatus, "Multi-Status" }, |
569 | | { HttpResponseStatusCode::Http208AlreadyReported, "Already Reported" }, |
570 | | { HttpResponseStatusCode::Http226IMUsed, "IM Used" }, |
571 | | { HttpResponseStatusCode::Http300MultipleChoices, "Multiple Choices" }, |
572 | | { HttpResponseStatusCode::Http301MovedPermanently, "Moved Permanently" }, |
573 | | { HttpResponseStatusCode::Http302, "(various messages)" }, |
574 | | { HttpResponseStatusCode::Http303SeeOther, "See Other" }, |
575 | | { HttpResponseStatusCode::Http304NotModified, "Not Modified" }, |
576 | | { HttpResponseStatusCode::Http305UseProxy, "Use Proxy" }, |
577 | | { HttpResponseStatusCode::Http306SwitchProxy, "Switch Proxy" }, |
578 | | { HttpResponseStatusCode::Http307TemporaryRedirect, "Temporary Redirect" }, |
579 | | { HttpResponseStatusCode::Http308PermanentRedirect, "Permanent Redirect" }, |
580 | | { HttpResponseStatusCode::Http400BadRequest, "Bad Request" }, |
581 | | { HttpResponseStatusCode::Http401Unauthorized, "Unauthorized" }, |
582 | | { HttpResponseStatusCode::Http402PaymentRequired, "Payment Required" }, |
583 | | { HttpResponseStatusCode::Http403Forbidden, "Forbidden" }, |
584 | | { HttpResponseStatusCode::Http404NotFound, "Not Found" }, |
585 | | { HttpResponseStatusCode::Http405MethodNotAllowed, "Method Not Allowed" }, |
586 | | { HttpResponseStatusCode::Http406NotAcceptable, "Not Acceptable" }, |
587 | | { HttpResponseStatusCode::Http407ProxyAuthenticationRequired, "Proxy Authentication Required" }, |
588 | | { HttpResponseStatusCode::Http408RequestTimeout, "Request Timeout" }, |
589 | | { HttpResponseStatusCode::Http409Conflict, "Conflict" }, |
590 | | { HttpResponseStatusCode::Http410Gone, "Gone" }, |
591 | | { HttpResponseStatusCode::Http411LengthRequired, "Length Required" }, |
592 | | { HttpResponseStatusCode::Http412PreconditionFailed, "Precondition Failed" }, |
593 | | { HttpResponseStatusCode::Http413RequestEntityTooLarge, "Request Entity Too Large" }, |
594 | | { HttpResponseStatusCode::Http414RequestURITooLong, "Request-URI Too Long" }, |
595 | | { HttpResponseStatusCode::Http415UnsupportedMediaType, "Unsupported Media Type" }, |
596 | | { HttpResponseStatusCode::Http416RequestedRangeNotSatisfiable, "Requested Range Not Satisfiable" }, |
597 | | { HttpResponseStatusCode::Http417ExpectationFailed, "Expectation Failed" }, |
598 | | { HttpResponseStatusCode::Http418ImATeapot, "I'm a teapot" }, |
599 | | { HttpResponseStatusCode::Http419AuthenticationTimeout, "Authentication Timeout" }, |
600 | | { HttpResponseStatusCode::Http420, "(various messages)" }, |
601 | | { HttpResponseStatusCode::Http421MisdirectedRequest, "Misdirected Request" }, |
602 | | { HttpResponseStatusCode::Http422UnprocessableEntity, "Unprocessable Entity" }, |
603 | | { HttpResponseStatusCode::Http423Locked, "Locked" }, |
604 | | { HttpResponseStatusCode::Http424FailedDependency, "Failed Dependency" }, |
605 | | { HttpResponseStatusCode::Http425TooEarly, "Too Early" }, |
606 | | { HttpResponseStatusCode::Http426UpgradeRequired, "Upgrade Required" }, |
607 | | { HttpResponseStatusCode::Http428PreconditionRequired, "Precondition Required" }, |
608 | | { HttpResponseStatusCode::Http429TooManyRequests, "Too Many Requests" }, |
609 | | { HttpResponseStatusCode::Http431RequestHeaderFieldsTooLarge, "Request Header Fields Too Large" }, |
610 | | { HttpResponseStatusCode::Http440LoginTimeout, "Login Timeout" }, |
611 | | { HttpResponseStatusCode::Http444NoResponse, "No Response" }, |
612 | | { HttpResponseStatusCode::Http449RetryWith, "Retry With" }, |
613 | | { HttpResponseStatusCode::Http450BlockedByWindowsParentalControls, "Blocked by Windows Parental Controls" }, |
614 | | { HttpResponseStatusCode::Http451, "(various messages)" }, |
615 | | { HttpResponseStatusCode::Http494RequestHeaderTooLarge, "Request Header Too Large" }, |
616 | | { HttpResponseStatusCode::Http495CertError, "Cert Error" }, |
617 | | { HttpResponseStatusCode::Http496NoCert, "No Cert" }, |
618 | | { HttpResponseStatusCode::Http497HTTPtoHTTPS, "HTTP to HTTPS" }, |
619 | | { HttpResponseStatusCode::Http498TokenExpiredInvalid, "Token expired/invalid" }, |
620 | | { HttpResponseStatusCode::Http499, "(various messages)" }, |
621 | | { HttpResponseStatusCode::Http500InternalServerError, "Internal Server Error" }, |
622 | | { HttpResponseStatusCode::Http501NotImplemented, "Not Implemented" }, |
623 | | { HttpResponseStatusCode::Http502BadGateway, "Bad Gateway" }, |
624 | | { HttpResponseStatusCode::Http503ServiceUnavailable, "Service Unavailable" }, |
625 | | { HttpResponseStatusCode::Http504GatewayTimeout, "Gateway Timeout" }, |
626 | | { HttpResponseStatusCode::Http505HTTPVersionNotSupported, "HTTP Version Not Supported" }, |
627 | | { HttpResponseStatusCode::Http506VariantAlsoNegotiates, "Variant Also Negotiates" }, |
628 | | { HttpResponseStatusCode::Http507InsufficientStorage, "Insufficient Storage" }, |
629 | | { HttpResponseStatusCode::Http508LoopDetected, "Loop Detected" }, |
630 | | { HttpResponseStatusCode::Http509BandwidthLimitExceeded, "Bandwidth Limit Exceeded" }, |
631 | | { HttpResponseStatusCode::Http510NotExtended, "Not Extended" }, |
632 | | { HttpResponseStatusCode::Http511NetworkAuthenticationRequired, "Network Authentication Required" }, |
633 | | { HttpResponseStatusCode::Http520OriginError, "Origin Error" }, |
634 | | { HttpResponseStatusCode::Http521WebServerIsDown, "Web server is down" }, |
635 | | { HttpResponseStatusCode::Http522ConnectionTimedOut, "Connection timed out" }, |
636 | | { HttpResponseStatusCode::Http523ProxyDeclinedRequest, "Proxy Declined Request" }, |
637 | | { HttpResponseStatusCode::Http524aTimeoutOccurred, "A timeout occurred" }, |
638 | | { HttpResponseStatusCode::Http598NetworkReadTimeoutError, "Network read timeout error" }, |
639 | | { HttpResponseStatusCode::Http599NetworkConnectTimeoutError, "Network connect timeout error" }, |
640 | | { HttpResponseStatusCode::HttpStatus1xxCodeUnknown, "1XX Status Code Unknown" }, |
641 | | { HttpResponseStatusCode::HttpStatus2xxCodeUnknown, "2XX Status Code Unknown" }, |
642 | | { HttpResponseStatusCode::HttpStatus3xxCodeUnknown, "3XX Status Code Unknown" }, |
643 | | { HttpResponseStatusCode::HttpStatus4xxCodeUnknown, "4XX Status Code Unknown" }, |
644 | | { HttpResponseStatusCode::HttpStatus5xxCodeUnknown, "5XX Status Code Unknown" }, |
645 | | { HttpResponseStatusCode::HttpStatusCodeUnknown, "Status Code Unknown" }, |
646 | | }; |
647 | | |
648 | | HttpResponseStatusCode::HttpResponseStatusCode(const Value& statusCode, const std::string& statusMessage) |
649 | 0 | : m_Value(statusCode) |
650 | 0 | { |
651 | 0 | if (statusMessage != "") |
652 | 0 | { |
653 | 0 | m_CustomizedMessage = statusMessage; |
654 | 0 | } |
655 | 0 | } |
656 | | |
657 | | std::string HttpResponseStatusCode::getMessage() const |
658 | 0 | { |
659 | 0 | if (m_CustomizedMessage != "") |
660 | 0 | { |
661 | 0 | return m_CustomizedMessage; |
662 | 0 | } |
663 | 0 | return statusCodeExplanationStringMap.at(m_Value); |
664 | 0 | } |
665 | | |
666 | | HttpResponseLayer::HttpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) |
667 | 26.1k | : HttpMessage(data, dataLen, prevLayer, packet, HTTPResponse) |
668 | 26.1k | { |
669 | 26.1k | m_FirstLine = new HttpResponseFirstLine(this); |
670 | 26.1k | m_FieldsOffset = m_FirstLine->getSize(); |
671 | 26.1k | parseFields(); |
672 | 26.1k | } |
673 | | |
674 | | HttpResponseLayer::HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode, |
675 | | const std::string& statusCodeString) |
676 | 0 | { |
677 | 0 | m_Protocol = HTTPResponse; |
678 | 0 | m_FirstLine = new HttpResponseFirstLine(this, version, HttpResponseStatusCode(statusCode, statusCodeString)); |
679 | 0 | m_FieldsOffset = m_FirstLine->getSize(); |
680 | 0 | } |
681 | | |
682 | | HttpResponseLayer::HttpResponseLayer(HttpVersion version, const HttpResponseStatusCode& statusCode) |
683 | 0 | { |
684 | 0 | m_Protocol = HTTPResponse; |
685 | 0 | m_FirstLine = new HttpResponseFirstLine(this, version, statusCode); |
686 | 0 | m_FieldsOffset = m_FirstLine->getSize(); |
687 | 0 | } |
688 | | |
689 | | HttpResponseLayer::~HttpResponseLayer() |
690 | 26.1k | { |
691 | 26.1k | delete m_FirstLine; |
692 | 26.1k | } |
693 | | |
694 | 0 | HttpResponseLayer::HttpResponseLayer(const HttpResponseLayer& other) : HttpMessage(other) |
695 | 0 | { |
696 | 0 | m_FirstLine = new HttpResponseFirstLine(this); |
697 | 0 | } |
698 | | |
699 | | HttpResponseLayer& HttpResponseLayer::operator=(const HttpResponseLayer& other) |
700 | 0 | { |
701 | 0 | HttpMessage::operator=(other); |
702 | |
|
703 | 0 | if (m_FirstLine != nullptr) |
704 | 0 | delete m_FirstLine; |
705 | |
|
706 | 0 | m_FirstLine = new HttpResponseFirstLine(this); |
707 | |
|
708 | 0 | return *this; |
709 | 0 | } |
710 | | |
711 | | HeaderField* HttpResponseLayer::setContentLength(int contentLength, const std::string& prevFieldName) |
712 | 0 | { |
713 | 0 | std::ostringstream contentLengthAsString; |
714 | 0 | contentLengthAsString << contentLength; |
715 | 0 | std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD); |
716 | 0 | HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); |
717 | 0 | if (contentLengthField == nullptr) |
718 | 0 | { |
719 | 0 | HeaderField* prevField = getFieldByName(prevFieldName); |
720 | 0 | contentLengthField = insertField(prevField, PCPP_HTTP_CONTENT_LENGTH_FIELD, contentLengthAsString.str()); |
721 | 0 | } |
722 | 0 | else |
723 | 0 | contentLengthField->setFieldValue(contentLengthAsString.str()); |
724 | |
|
725 | 0 | return contentLengthField; |
726 | 0 | } |
727 | | |
728 | | int HttpResponseLayer::getContentLength() const |
729 | 0 | { |
730 | 0 | std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD); |
731 | 0 | std::transform(contentLengthFieldName.begin(), contentLengthFieldName.end(), contentLengthFieldName.begin(), |
732 | 0 | ::tolower); |
733 | 0 | HeaderField* contentLengthField = getFieldByName(contentLengthFieldName); |
734 | 0 | if (contentLengthField != nullptr) |
735 | 0 | return atoi(contentLengthField->getFieldValue().c_str()); |
736 | 0 | return 0; |
737 | 0 | } |
738 | | |
739 | | std::string HttpResponseLayer::toString() const |
740 | 9.52k | { |
741 | 9.52k | static const int maxLengthToPrint = 120; |
742 | 9.52k | std::string result = "HTTP response, "; |
743 | 9.52k | int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line |
744 | 9.52k | if (size <= maxLengthToPrint) |
745 | 9.24k | { |
746 | 9.24k | char* firstLine = new char[size + 1]; |
747 | 9.24k | strncpy(firstLine, reinterpret_cast<char*>(m_Data), size); |
748 | 9.24k | firstLine[size] = 0; |
749 | 9.24k | result += std::string(firstLine); |
750 | 9.24k | delete[] firstLine; |
751 | 9.24k | } |
752 | 276 | else |
753 | 276 | { |
754 | 276 | char firstLine[maxLengthToPrint + 1]; |
755 | 276 | strncpy(firstLine, reinterpret_cast<char*>(m_Data), maxLengthToPrint - 3); |
756 | 276 | firstLine[maxLengthToPrint - 3] = '.'; |
757 | 276 | firstLine[maxLengthToPrint - 2] = '.'; |
758 | 276 | firstLine[maxLengthToPrint - 1] = '.'; |
759 | 276 | firstLine[maxLengthToPrint] = 0; |
760 | 276 | result += std::string(firstLine); |
761 | 276 | } |
762 | | |
763 | 9.52k | return result; |
764 | 9.52k | } |
765 | | |
766 | | // -------- Class HttpResponseFirstLine ----------------- |
767 | | |
768 | | int HttpResponseFirstLine::getStatusCodeAsInt() const |
769 | 0 | { |
770 | 0 | return m_StatusCode.toInt(); |
771 | 0 | } |
772 | | |
773 | | std::string HttpResponseFirstLine::getStatusCodeString() const |
774 | 0 | { |
775 | 0 | if (!m_StatusCode.isUnsupportedCode()) |
776 | 0 | { |
777 | 0 | return m_StatusCode.getMessage(); |
778 | 0 | } |
779 | | |
780 | | // else first line is illegal, return empty string |
781 | 0 | return ""; |
782 | 0 | } |
783 | | |
784 | | bool HttpResponseFirstLine::setStatusCode(const HttpResponseStatusCode& newStatusCode, |
785 | | const std::string& statusCodeString) |
786 | 0 | { |
787 | 0 | return setStatusCode(HttpResponseStatusCode(newStatusCode, statusCodeString)); |
788 | 0 | } |
789 | | |
790 | | bool HttpResponseFirstLine::setStatusCode(const HttpResponseStatusCode& newStatusCode) |
791 | 0 | { |
792 | 0 | if (newStatusCode.isUnsupportedCode()) |
793 | 0 | { |
794 | 0 | PCPP_LOG_ERROR("Requested status code is " << newStatusCode.toString() << ":" |
795 | 0 | << statusCodeExplanationStringMap.at(newStatusCode)); |
796 | 0 | return false; |
797 | 0 | } |
798 | | |
799 | | // extend or shorten layer |
800 | | |
801 | 0 | size_t statusStringOffset = 13; |
802 | 0 | auto newStatusCodeMessage = newStatusCode.getMessage(); |
803 | |
|
804 | 0 | int lengthDifference = newStatusCodeMessage.length() - getStatusCodeString().length(); |
805 | 0 | if (lengthDifference > 0) |
806 | 0 | { |
807 | 0 | if (!m_HttpResponse->extendLayer(statusStringOffset, lengthDifference)) |
808 | 0 | { |
809 | 0 | PCPP_LOG_ERROR("Cannot change layer size"); |
810 | 0 | return false; |
811 | 0 | } |
812 | 0 | } |
813 | 0 | else if (lengthDifference < 0) |
814 | 0 | { |
815 | 0 | if (!m_HttpResponse->shortenLayer(statusStringOffset, 0 - lengthDifference)) |
816 | 0 | { |
817 | 0 | PCPP_LOG_ERROR("Cannot change layer size"); |
818 | 0 | return false; |
819 | 0 | } |
820 | 0 | } |
821 | | |
822 | 0 | if (lengthDifference != 0) |
823 | 0 | m_HttpResponse->shiftFieldsOffset(m_HttpResponse->getFirstField(), lengthDifference); |
824 | | |
825 | | // copy status string |
826 | 0 | memcpy(m_HttpResponse->m_Data + statusStringOffset, newStatusCodeMessage.c_str(), |
827 | 0 | newStatusCodeMessage.length()); |
828 | | |
829 | | // change status code |
830 | 0 | memcpy(m_HttpResponse->m_Data + 9, newStatusCode.toString().c_str(), 3); |
831 | |
|
832 | 0 | m_StatusCode = newStatusCode; |
833 | |
|
834 | 0 | m_FirstLineEndOffset += lengthDifference; |
835 | |
|
836 | 0 | return true; |
837 | 0 | } |
838 | | |
839 | | void HttpResponseFirstLine::setVersion(HttpVersion newVersion) |
840 | 0 | { |
841 | 0 | if (newVersion == HttpVersionUnknown) |
842 | 0 | return; |
843 | | |
844 | 0 | char* verPos = (char*)(m_HttpResponse->m_Data + 5); |
845 | 0 | memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3); |
846 | |
|
847 | 0 | m_Version = newVersion; |
848 | 0 | } |
849 | | |
850 | | HttpResponseStatusCode HttpResponseFirstLine::parseStatusCode(const char* data, size_t dataLen) |
851 | 53.3k | { |
852 | | // minimum data should be 12B long: "HTTP/x.y XXX" |
853 | 53.3k | if (!data || dataLen < 12) |
854 | 75 | { |
855 | 75 | return HttpResponseStatusCode::HttpStatusCodeUnknown; |
856 | 75 | } |
857 | | |
858 | 53.2k | const std::string codeString = std::string(data + 9, 3); |
859 | | |
860 | 53.2k | if (codeString.empty() || (std::find_if(codeString.begin(), codeString.end(), |
861 | 159k | [](unsigned char c) { return !std::isdigit(c); }) != codeString.end())) |
862 | 178 | { |
863 | 178 | return HttpResponseStatusCode::HttpStatusCodeUnknown; |
864 | 178 | } |
865 | | |
866 | 53.0k | constexpr size_t messageOffset = 13; // expect "HTTP/x.y XXX YYY", YYY starts from 13 |
867 | 53.0k | size_t offset = messageOffset; |
868 | 53.0k | bool isMessageFound = false; |
869 | 562k | while (offset < dataLen) |
870 | 562k | { |
871 | 562k | if (data[offset] == '\n') |
872 | 53.0k | { |
873 | 53.0k | isMessageFound = true; |
874 | 53.0k | break; |
875 | 53.0k | } |
876 | 509k | offset++; |
877 | 509k | } |
878 | | |
879 | 53.0k | if (!isMessageFound) |
880 | 26 | { |
881 | 26 | return HttpResponseStatusCode::HttpStatusCodeUnknown; |
882 | 26 | } |
883 | | |
884 | 53.0k | std::string messageString(data + messageOffset, offset - messageOffset); |
885 | 53.0k | if (!messageString.empty() && messageString.back() == '\r') |
886 | 50.4k | { |
887 | 50.4k | messageString.pop_back(); |
888 | 50.4k | } |
889 | 53.0k | if (messageString.empty()) |
890 | 66 | { |
891 | 66 | return HttpResponseStatusCode::HttpStatusCodeUnknown; |
892 | 66 | } |
893 | | |
894 | 52.9k | return HttpResponseStatusCode(std::stoi(codeString), messageString); |
895 | 53.0k | } |
896 | | |
897 | 26.1k | HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse) : m_HttpResponse(httpResponse) |
898 | 26.1k | { |
899 | 26.1k | m_Version = parseVersion((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen()); |
900 | 26.1k | if (m_Version == HttpVersionUnknown) |
901 | 0 | { |
902 | 0 | m_StatusCode = HttpResponseStatusCode::HttpStatusCodeUnknown; |
903 | 0 | } |
904 | 26.1k | else |
905 | 26.1k | { |
906 | 26.1k | m_StatusCode = parseStatusCode((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen()); |
907 | 26.1k | } |
908 | | |
909 | 26.1k | char* endOfFirstLine; |
910 | 26.1k | if ((endOfFirstLine = (char*)memchr((char*)(m_HttpResponse->m_Data), '\n', m_HttpResponse->m_DataLen)) != |
911 | 26.1k | nullptr) |
912 | 26.1k | { |
913 | 26.1k | m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpResponse->m_Data + 1; |
914 | 26.1k | m_IsComplete = true; |
915 | 26.1k | } |
916 | 0 | else |
917 | 0 | { |
918 | 0 | m_FirstLineEndOffset = m_HttpResponse->getDataLen(); |
919 | 0 | m_IsComplete = false; |
920 | 0 | } |
921 | | |
922 | 26.1k | if (Logger::getInstance().isDebugEnabled(PacketLogModuleHttpLayer)) |
923 | 0 | { |
924 | 0 | std::string version = (m_Version == HttpVersionUnknown ? "Unknown" : VersionEnumToString[m_Version]); |
925 | 0 | int statusCode = (m_StatusCode == HttpResponseStatusCode::HttpStatusCodeUnknown ? 0 : m_StatusCode.toInt()); |
926 | 0 | PCPP_LOG_DEBUG("Version='" << version << "'; Status code=" << statusCode << " '" << getStatusCodeString() |
927 | 0 | << "'"); |
928 | 0 | } |
929 | 26.1k | } |
930 | | |
931 | | HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse, HttpVersion version, |
932 | | const HttpResponseStatusCode& statusCode) |
933 | 0 | { |
934 | 0 | if (statusCode.isUnsupportedCode()) |
935 | 0 | { |
936 | 0 | m_Exception.setMessage("Status code supplied was " + statusCodeExplanationStringMap.at(statusCode)); |
937 | 0 | throw m_Exception; |
938 | 0 | } |
939 | | |
940 | 0 | if (version == HttpVersionUnknown) |
941 | 0 | { |
942 | 0 | m_Exception.setMessage("Version supplied was HttpVersionUnknown"); |
943 | 0 | throw m_Exception; |
944 | 0 | } |
945 | | |
946 | 0 | m_HttpResponse = httpResponse; |
947 | |
|
948 | 0 | m_StatusCode = statusCode; |
949 | 0 | m_Version = version; |
950 | |
|
951 | 0 | std::string firstLine = "HTTP/" + VersionEnumToString[m_Version] + " " + m_StatusCode.toString() + " " + |
952 | 0 | m_StatusCode.getMessage() + "\r\n"; |
953 | |
|
954 | 0 | m_FirstLineEndOffset = firstLine.length(); |
955 | | |
956 | | // TODO: Alloc Data. |
957 | 0 | m_HttpResponse->m_DataLen = firstLine.length(); |
958 | 0 | m_HttpResponse->m_Data = new uint8_t[m_HttpResponse->m_DataLen]; |
959 | 0 | memcpy(m_HttpResponse->m_Data, firstLine.c_str(), m_HttpResponse->m_DataLen); |
960 | |
|
961 | 0 | m_IsComplete = true; |
962 | 0 | } |
963 | | |
964 | | HttpVersion HttpResponseFirstLine::parseVersion(const char* data, size_t dataLen) |
965 | 90.2k | { |
966 | 90.2k | if (!data || dataLen < 8) // "HTTP/x.y" |
967 | 316 | { |
968 | 316 | PCPP_LOG_DEBUG("HTTP response length < 8, cannot identify version"); |
969 | 316 | return HttpVersionUnknown; |
970 | 316 | } |
971 | | |
972 | 89.8k | if (data[0] != 'H' || data[1] != 'T' || data[2] != 'T' || data[3] != 'P' || data[4] != '/') |
973 | 35.8k | { |
974 | 35.8k | PCPP_LOG_DEBUG("HTTP response does not begin with 'HTTP/'"); |
975 | 35.8k | return HttpVersionUnknown; |
976 | 35.8k | } |
977 | | |
978 | 54.0k | const char* verPos = data + 5; |
979 | 54.0k | auto versionAsEnum = HttpVersionStringToEnum.find(std::string(verPos, verPos + 3)); |
980 | 54.0k | if (versionAsEnum == HttpVersionStringToEnum.end()) |
981 | 698 | { |
982 | 698 | return HttpVersionUnknown; |
983 | 698 | } |
984 | 53.3k | return versionAsEnum->second; |
985 | 54.0k | } |
986 | | } // namespace pcpp |