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