diff options
author | RincewindsHat <12514511+RincewindsHat@users.noreply.github.com> | 2022-11-13 22:07:14 (GMT) |
---|---|---|
committer | Sven Nierlein <sven@nierlein.org> | 2023-01-07 17:34:46 (GMT) |
commit | afe92468a54ec44cdda35e46a1eabd0d0de78840 (patch) | |
tree | 4e5d95e62c21a303fbcee5cdfd5f846ddc75ca76 /plugins/check_http.c | |
parent | 2c658383d5c7742d289e07116c948c6905555405 (diff) | |
download | monitoring-plugins-afe92468a54ec44cdda35e46a1eabd0d0de78840.tar.gz |
Implement chunked encoding decoding
Diffstat (limited to 'plugins/check_http.c')
-rw-r--r-- | plugins/check_http.c | 103 |
1 files changed, 102 insertions, 1 deletions
diff --git a/plugins/check_http.c b/plugins/check_http.c index a2c7571..5710cfe 100644 --- a/plugins/check_http.c +++ b/plugins/check_http.c | |||
@@ -146,6 +146,7 @@ char *perfd_time_transfer(double microsec); | |||
146 | char *perfd_size(int page_len); | 146 | char *perfd_size(int page_len); |
147 | void print_help(void); | 147 | void print_help(void); |
148 | void print_usage(void); | 148 | void print_usage(void); |
149 | char *unchunk_content(char *content); | ||
149 | 150 | ||
150 | int main(int argc, char **argv) { | 151 | int main(int argc, char **argv) { |
151 | int result = STATE_UNKNOWN; | 152 | int result = STATE_UNKNOWN; |
@@ -1252,7 +1253,26 @@ int check_http(void) { | |||
1252 | } | 1253 | } |
1253 | } | 1254 | } |
1254 | 1255 | ||
1255 | if (strlen(string_expect)) { | 1256 | // At this point we should test if the content is chunked and unchunk it, so |
1257 | // it can be searched (and possibly printed) | ||
1258 | const char *chunked_header_regex_string = "Transfer-Encoding:\\s*chunked\\s*"CRLF; | ||
1259 | regex_t chunked_header_regex; | ||
1260 | |||
1261 | if (regcomp(&chunked_header_regex, chunked_header_regex_string, 0)) { | ||
1262 | die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to compile chunked_header_regex regex"); | ||
1263 | } | ||
1264 | |||
1265 | regmatch_t chre_pmatch[1]; // We actually do not care about this, since we only want to know IF it was found | ||
1266 | |||
1267 | if (regexec(&chunked_header_regex, header, 1, chre_pmatch, 0) == 0) { | ||
1268 | // We actually found the chunked header | ||
1269 | char *tmp = unchunk_content(page); | ||
1270 | if (tmp == NULL) { | ||
1271 | die(STATE_UNKNOWN, "HTTP %s: %s\n", state_text(STATE_UNKNOWN), "Failed to unchunk message body"); | ||
1272 | } | ||
1273 | } | ||
1274 | |||
1275 | if (strlen(string_expect) > 0) { | ||
1256 | if (!strstr(page, string_expect)) { | 1276 | if (!strstr(page, string_expect)) { |
1257 | // We found the string the body, the rest is for building the output | 1277 | // We found the string the body, the rest is for building the output |
1258 | char output_string_search[30] = ""; | 1278 | char output_string_search[30] = ""; |
@@ -1342,6 +1362,87 @@ int check_http(void) { | |||
1342 | return STATE_UNKNOWN; | 1362 | return STATE_UNKNOWN; |
1343 | } | 1363 | } |
1344 | 1364 | ||
1365 | /* Receivces a pointer to the beginning of the body of a HTTP message | ||
1366 | * which is chunked and returns a pointer to a freshly allocated memory | ||
1367 | * region containing the unchunked body or NULL if something failed. | ||
1368 | * The result must be freed by the caller. | ||
1369 | */ | ||
1370 | char *unchunk_content(const char *content) { | ||
1371 | // https://en.wikipedia.org/wiki/Chunked_transfer_encoding | ||
1372 | // https://www.rfc-editor.org/rfc/rfc7230#section-4.1 | ||
1373 | char *result = NULL; | ||
1374 | size_t content_length = strlen(content); | ||
1375 | char *start_of_chunk, end_of_chunk; | ||
1376 | long size_of_chunk; | ||
1377 | char *pointer = content; | ||
1378 | char *endptr; | ||
1379 | long length_of_chunk = 0; | ||
1380 | size_t overall_size = 0; | ||
1381 | char *result_ptr; | ||
1382 | |||
1383 | while (true) { | ||
1384 | size_of_chunk = strtol(pointer, &endptr, 16); | ||
1385 | if (size_of_chunk == LONG_MIN || size_of_chunk == LONG_MAX) { | ||
1386 | // Apparently underflow or overflow, should not happen | ||
1387 | if (verbose) { | ||
1388 | printf("Got an underflow or overflow from strtol at: %u\n", __LINE__); | ||
1389 | } | ||
1390 | return NULL; | ||
1391 | } | ||
1392 | if (endptr == pointer) { | ||
1393 | // Apparently this was not a number | ||
1394 | if (verbose) { | ||
1395 | printf("Chunked content did not start with a number at all (Line: %u)\n", __LINE__); | ||
1396 | } | ||
1397 | return NULL | ||
1398 | } | ||
1399 | |||
1400 | // So, we got the length of the chunk | ||
1401 | if (*endptr == ';') { | ||
1402 | // Chunk extension starts here | ||
1403 | // TODO | ||
1404 | while (*endptr != '\r') { | ||
1405 | endptr++; | ||
1406 | } | ||
1407 | } | ||
1408 | |||
1409 | start_of_chunk = endptr + 2; | ||
1410 | end_of_chunk = start_of_chunk + size_of_chunk; | ||
1411 | length_of_chunk = end_of_chunk - start_of_chunk; | ||
1412 | |||
1413 | if (length_of_chunk == 0) { | ||
1414 | // Chunk length is 0, so this is the last one | ||
1415 | break; | ||
1416 | } | ||
1417 | |||
1418 | overall_size += length_of_chunk; | ||
1419 | |||
1420 | if (result == NULL) { | ||
1421 | result = (char *)calloc(length_of_chunk, sizeof(char)); | ||
1422 | if (result == NULL) { | ||
1423 | if (verbose) { | ||
1424 | printf("Failed to allocate memory for unchunked body\n"); | ||
1425 | } | ||
1426 | return NULL; | ||
1427 | } | ||
1428 | result_ptr = result; | ||
1429 | } else { | ||
1430 | void *tmp = realloc(result, overall_size); | ||
1431 | if (tmp == NULL) { | ||
1432 | if (verbose) { | ||
1433 | printf("Failed to allocate memory for unchunked body\n"); | ||
1434 | } | ||
1435 | return NULL; | ||
1436 | } | ||
1437 | } | ||
1438 | |||
1439 | memcpy(result_ptr, start_of_chunk, size_of_chunk); | ||
1440 | result_ptr = result_ptr + size_of_chunk; | ||
1441 | } | ||
1442 | |||
1443 | return result | ||
1444 | } | ||
1445 | |||
1345 | /* per RFC 2396 */ | 1446 | /* per RFC 2396 */ |
1346 | #define URI_HTTP "%5[HTPShtps]" | 1447 | #define URI_HTTP "%5[HTPShtps]" |
1347 | #define URI_HOST \ | 1448 | #define URI_HOST \ |